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)
# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@ -11,32 +12,140 @@
#
# cdist is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see <http://www.gnu.org/licenses/>.
#
case "$("${__explorer}/os")"
case $("${__explorer:?}/os")
in
netbsd)
postgres_user='pgsql'
;;
openbsd)
postgres_user='_postgresql'
;;
*)
postgres_user='postgres'
;;
(netbsd)
postgres_user='pgsql'
;;
(openbsd)
postgres_user='_postgresql'
;;
(*)
postgres_user='postgres'
;;
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
echo 'present'
# Check if the user's properties match the parameters
for prop in login createdb createrole superuser
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
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
echo 'absent'
state='absent'
fi
echo "${state}"

View file

@ -1,6 +1,7 @@
#!/bin/sh -e
#
# 2011 Steven Armstrong (steven-cdist at armstrong.cc)
# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@ -11,55 +12,117 @@
#
# cdist is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# 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
netbsd)
postgres_user='pgsql'
;;
openbsd)
postgres_user='_postgresql'
;;
*)
postgres_user='postgres'
;;
(netbsd)
postgres_user='pgsql'
;;
(openbsd)
postgres_user='_postgresql'
;;
(*)
postgres_user='postgres'
;;
esac
name="$__object_id"
state_is="$(cat "$__object/explorer/state")"
state_should="$(cat "$__object/parameter/state")"
rolename=${__object_id:?}
state_is=$(cat "${__object:?}/explorer/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
present)
if [ -f "$__object/parameter/password" ]; then
password="$(cat "$__object/parameter/password")"
fi
booleans=""
for boolean in login createdb createrole superuser; do
if [ ! -f "$__object/parameter/$boolean" ]; then
boolean="no${boolean}"
fi
upper=$(echo $boolean | tr '[:lower:]' '[:upper:]')
booleans="$booleans $upper"
done
psql_query() {
printf 'su -l %s -c %s\n' \
"$(quote "${postgres_user}")" \
"$(quote "psql postgres -q -w -c $(quote "$1")")"
}
[ -n "$password" ] && password="PASSWORD '$password'"
cat << EOF
su - '$postgres_user' -c "psql postgres -wc \"CREATE ROLE \\\\\"$name\\\\\" WITH $password $booleans;\""
EOF
;;
absent)
cat << EOF
su - '$postgres_user' -c "dropuser \"$name\""
EOF
;;
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
}
role_properties_should() {
_props=
for _prop in login createdb createrole superuser
do
_props="${_props}${_props:+ }$(
if test -f "${__object:?}/parameter/${_prop}"
then
echo "${_prop}"
else
echo "no${_prop}"
fi \
| tr '[:lower:]' '[:upper:]')"
done
printf '%s\n' "${_props}"
unset _prop _props
}
case ${state_should}
in
(present)
case ${state_is}
in
(absent)
psql_query "CREATE ROLE \"${rolename}\" WITH $(role_properties_should);"
psql_set_password
;;
(different*)
if expr "${state_is}" : 'different.*properties' >/dev/null
then
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