Merge branch 'fix/type/__postgres_role/implement-alter' into 'master'

__postgres_role: implement modification of roles

See merge request ungleich-public/cdist!973
This commit is contained in:
poljakowski 2021-02-22 08:58:58 +01:00
commit e854db096e
2 changed files with 226 additions and 54 deletions

View file

@ -1,6 +1,7 @@
#!/bin/sh #!/bin/sh -e
# #
# 2011 Steven Armstrong (steven-cdist at armstrong.cc) # 2011 Steven Armstrong (steven-cdist at armstrong.cc)
# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
# #
# This file is part of cdist. # This file is part of cdist.
# #
@ -18,25 +19,133 @@
# along with cdist. If not, see <http://www.gnu.org/licenses/>. # along with cdist. If not, see <http://www.gnu.org/licenses/>.
# #
case "$("${__explorer}/os")" case $("${__explorer:?}/os")
in in
netbsd) (netbsd)
postgres_user='pgsql' postgres_user='pgsql'
;; ;;
openbsd) (openbsd)
postgres_user='_postgresql' postgres_user='_postgresql'
;; ;;
*) (*)
postgres_user='postgres' postgres_user='postgres'
;; ;;
esac esac
rolename=${__object_id:?}
name="$__object_id"
if test -n "$(su - "$postgres_user" -c "psql postgres -twAc \"SELECT 1 FROM pg_roles WHERE rolname='$name'\"")" psql_query() {
su -l "${postgres_user}" -c "$(
printf "psql -q -F '\034' -R '\036' -wAc '%s'" \
"$(printf %s "$*" | sed "s/'/'\\\\''/g")"
)"
}
password_check_login() (
PGPASSWORD=$(cat "${__object:?}/parameter/password"; printf .)
PGPASSWORD=${PGPASSWORD%?.}
export PGPASSWORD
psql -q -w -h localhost -U "${rolename}" template1 -c '\q' >/dev/null 2>&1
)
role_properties=$(
psql_query "SELECT * FROM pg_roles WHERE rolname = '${rolename}'" \
| awk '
BEGIN { RS = "\036"; FS = "\034" }
/^\([0-9]+ rows?\)/ { exit }
NR == 1 { for (i = 1; i <= NF; i++) cols[i] = $i; next }
NR == 2 { for (i = 1; i <= NF; i++) printf "%s=%s\n", cols[i], $i }
'
)
if test -n "${role_properties}"
then then
echo 'present' # Check if the user's properties match the parameters
else for prop in login createdb createrole superuser
echo 'absent' do
bool_should=$(test -f "${__object:?}/parameter/${prop}" && echo 't' || echo 'f')
bool_is=$(
printf '%s\n' "${role_properties}" |
awk -F '=' -v key="${prop}" '
BEGIN {
if (key == "login")
key = "canlogin"
else if (key == "superuser")
key = "super"
key = "rol" key
}
$1 == key {
sub(/^[^=]*=/, "")
print
}
'
)
test "${bool_is}" = "${bool_should}" || {
state='different properties'
}
done
# Check password
passwd_stored=$(
psql_query "SELECT rolpassword FROM pg_authid WHERE rolname = '${rolename}'" \
| awk 'BEGIN { RS = "\036" } NR == 2'
printf .
)
passwd_stored=${passwd_stored%?.}
if test -f "${__object:?}/parameter/password"
then
passwd_should=$(cat "${__object:?}/parameter/password"; printf .)
fi fi
passwd_should=${passwd_should%?.}
if test -z "${passwd_stored}"
then
test -z "${passwd_should}" || state="${state:-different} password"
elif expr "${passwd_stored}" : 'SCRAM-SHA-256\$.*$' >/dev/null
then
# SCRAM-SHA-256 "encrypted" password
# NOTE: There is currently no easy way to check SCRAM passwords without
# logging in
password_check_login || state="${state:-different} password"
elif expr "${passwd_stored}" : 'md5[0-9a-f]\{32\}$' >/dev/null
then
# MD5 "encrypted" password
if command -v md5sum >/dev/null 2>&1
then
should_md5=$(
printf '%s%s' "${passwd_should}" "${rolename}" \
| md5sum - | sed -e 's/[^0-9a-f]*$//')
elif command -v gmd5sum >/dev/null 2>&1
then
should_md5=$(
printf '%s%s' "${passwd_should}" "${rolename}" \
| gmd5sum - | sed -e 's/[^0-9a-f]*$//')
elif command -v openssl >/dev/null 2>&1
then
should_md5=$(
printf '%s%s' "${passwd_should}" "${rolename}" \
| openssl dgst -md5 | sed 's/^.* //')
fi
if test -n "${should_md5}"
then
test "${passwd_stored}" = "md5${should_md5}" \
|| state="${state:-different} password"
else
password_check_login || state="${state:-different} password"
fi
else
# unencrypted password (unsupported since PostgreSQL 10)
test "${passwd_stored}" = "${passwd_should}" \
|| state="${state:-different} password"
fi
test -n "${state}" || state='present'
else
state='absent'
fi
echo "${state}"

View file

@ -1,6 +1,7 @@
#!/bin/sh -e #!/bin/sh -e
# #
# 2011 Steven Armstrong (steven-cdist at armstrong.cc) # 2011 Steven Armstrong (steven-cdist at armstrong.cc)
# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
# #
# This file is part of cdist. # This file is part of cdist.
# #
@ -18,48 +19,110 @@
# along with cdist. If not, see <http://www.gnu.org/licenses/>. # along with cdist. If not, see <http://www.gnu.org/licenses/>.
# #
case "$(cat "${__global}/explorer/os")" quote() {
if test $# -gt 0
then
printf '%s' "$*"
else
cat -
fi | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/"
}
case $(cat "${__global:?}/explorer/os")
in in
netbsd) (netbsd)
postgres_user='pgsql' postgres_user='pgsql'
;; ;;
openbsd) (openbsd)
postgres_user='_postgresql' postgres_user='_postgresql'
;; ;;
*) (*)
postgres_user='postgres' postgres_user='postgres'
;; ;;
esac esac
name="$__object_id" rolename=${__object_id:?}
state_is="$(cat "$__object/explorer/state")" state_is=$(cat "${__object:?}/explorer/state")
state_should="$(cat "$__object/parameter/state")" state_should=$(cat "${__object:?}/parameter/state")
[ "$state_is" = "$state_should" ] && exit 0 if test "${state_is}" = "${state_should}"
then
exit 0
fi
case "$state_should" in psql_query() {
present) printf 'su -l %s -c %s\n' \
if [ -f "$__object/parameter/password" ]; then "$(quote "${postgres_user}")" \
password="$(cat "$__object/parameter/password")" "$(quote "psql postgres -q -w -c $(quote "$1")")"
}
psql_set_password() {
# NOTE: Always make sure that the password does not end up in psql_history!
# NOTE: Never set an empty string as the password, because they can be
# interpreted differently by different tooling.
if test -s "${__object:?}/parameter/password"
then
cat <<-EOF
exec 3< "\${__object:?}/parameter/password"
su -l '${postgres_user}' -c 'psql -q -w postgres' <<'SQL'
\set HISTFILE /dev/null
\set pw \`cat <&3\`
ALTER ROLE "${rolename}" WITH PASSWORD :'pw';
SQL
exec 3<&-
EOF
else
psql_query "ALTER ROLE \"${rolename}\" WITH PASSWORD NULL;"
fi fi
booleans="" }
for boolean in login createdb createrole superuser; do
if [ ! -f "$__object/parameter/$boolean" ]; then role_properties_should() {
boolean="no${boolean}" _props=
fi for _prop in login createdb createrole superuser
upper=$(echo $boolean | tr '[:lower:]' '[:upper:]') do
booleans="$booleans $upper" _props="${_props}${_props:+ }$(
if test -f "${__object:?}/parameter/${_prop}"
then
echo "${_prop}"
else
echo "no${_prop}"
fi \
| tr '[:lower:]' '[:upper:]')"
done done
printf '%s\n' "${_props}"
unset _prop _props
}
[ -n "$password" ] && password="PASSWORD '$password'" case ${state_should}
cat << EOF in
su - '$postgres_user' -c "psql postgres -wc \"CREATE ROLE \\\\\"$name\\\\\" WITH $password $booleans;\"" (present)
EOF case ${state_is}
in
(absent)
psql_query "CREATE ROLE \"${rolename}\" WITH $(role_properties_should);"
psql_set_password
;; ;;
absent) (different*)
cat << EOF if expr "${state_is}" : 'different.*properties' >/dev/null
su - '$postgres_user' -c "dropuser \"$name\"" then
EOF psql_query "ALTER ROLE \"${rolename}\" WITH $(role_properties_should);"
fi
if expr "${state_is}" : 'different.*password' >/dev/null
then
psql_set_password
fi
;;
(*)
printf 'Invalid state reported by state explorer: %s\n' "${state_is}" >&2
exit 1
;;
esac
;;
(absent)
printf 'su -l %s -c %s\n' \
"$(quote "${postgres_user}")" \
"$(quote "dropuser $(quote "${rolename}")")"
;; ;;
esac esac