diff --git a/cdist/conf/type/__postgres_role/explorer/state b/cdist/conf/type/__postgres_role/explorer/state index 110d29d5..033afcb2 100755 --- a/cdist/conf/type/__postgres_role/explorer/state +++ b/cdist/conf/type/__postgres_role/explorer/state @@ -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. # @@ -31,11 +32,48 @@ in ;; esac - rolename=${__object_id:?} -if test -n "$(su - "${postgres_user}" -c "psql postgres -twAc \"SELECT 1 FROM pg_roles WHERE rolname='${rolename}'\"")" +role_properties=$( + cmd=$(printf "psql -F '\034' -R '\036' -wAc \"SELECT * FROM pg_roles WHERE rolname='%s'\"" "${rolename}") + su -l "${postgres_user}" -c "${cmd}" \ + | 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 + # 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}" || { + echo 'different' + exit 0 + } + done + echo 'present' else echo 'absent' diff --git a/cdist/conf/type/__postgres_role/gencode-remote b/cdist/conf/type/__postgres_role/gencode-remote index 4fb761ed..7837976c 100755 --- a/cdist/conf/type/__postgres_role/gencode-remote +++ b/cdist/conf/type/__postgres_role/gencode-remote @@ -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. # @@ -18,6 +19,15 @@ # along with cdist. If not, see . # +quote() { + if test $# -gt 0 + then + printf '%s' "$*" + else + cat - + fi | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/" +} + case $(cat "${__global:?}/explorer/os") in (netbsd) @@ -44,28 +54,55 @@ fi case ${state_should} in (present) - if test -f "${__object:?}/parameter/password" + if test -s "${__object:?}/parameter/password" then - password=$(cat "${__object:?}/parameter/password") + quoted_password=$( + delim='$$' + while grep -q -F "${delim}" "${__object:?}/parameter/password" + do + delim="\$$(LC_ALL=C tr -cd '[:alpha:]' /dev/null)$" + done + + raw_passwd=$(cat "${__object:?}/parameter/password"; printf .) + # shellcheck disable=SC2016 + printf '%s%s%s' "${delim}" "${raw_passwd%?.}" "${delim}" + ) fi + booleans= for boolean in login createdb createrole superuser do - if test ! -f "${__object:?}/parameter/${boolean}" - then - boolean="no${boolean}" - fi - booleans="${booleans} $(echo ${boolean} | tr '[:lower:]' '[:upper:]')" + booleans="${booleans}${booleans:+ }$( + if test -f "${__object:?}/parameter/${boolean}" + then + echo "${boolean}" + else + echo "no${boolean}" + fi \ + | tr '[:lower:]' '[:upper:]')" done - [ -n "${password}" ] && password="PASSWORD '${password}'" - cat << EOF -su - '${postgres_user}' -c "psql postgres -wc 'CREATE ROLE \\"${rolename}\\" WITH ${password} ${booleans};'" -EOF + case ${state_is} + in + (absent) + query=$(printf 'CREATE ROLE "%s" WITH %s PASSWORD %s;' \ + "${rolename}" "${booleans}" "${quoted_password:-NULL}") + ;; + (different) + query=$(printf 'ALTER ROLE "%s" WITH %s PASSWORD %s;' \ + "${rolename}" "${booleans}" "${quoted_password:-NULL}") + ;; + (*) + exit 1 # TODO: error msg + ;; + esac + + psql_cmd=$(printf 'psql postgres -wc %s' "$(quote "${query}")" | quote) + printf "su -l '%s' -c %s\\n" "${postgres_user}" "${psql_cmd}" ;; (absent) - cat << EOF -su - '${postgres_user}' -c "dropuser '${rolename}'" -EOF + printf "su -l '%s' -c 'dropuser '\\\\'%s\\\\'\\n" \ + "${postgres_user}" \ + "$(quote "${rolename}")" ;; esac