diff --git a/lib/puppet/functions/postgresql/postgresql_password.rb b/lib/puppet/functions/postgresql/postgresql_password.rb index ed9265dea8..740a7640e8 100644 --- a/lib/puppet/functions/postgresql/postgresql_password.rb +++ b/lib/puppet/functions/postgresql/postgresql_password.rb @@ -28,6 +28,9 @@ end def default_impl(username, password, sensitive = false, hash = 'md5', salt = nil) + if password.is_a?(String) && password.match?(%r{^(md5|SCRAM-SHA-256).+}) + return password + end password = password.unwrap if password.respond_to?(:unwrap) pass = if hash == 'md5' 'md5' + Digest::MD5.hexdigest(password.to_s + username.to_s) diff --git a/lib/puppet/functions/postgresql/prepend_sql_password.rb b/lib/puppet/functions/postgresql/prepend_sql_password.rb new file mode 100644 index 0000000000..f938d9af1f --- /dev/null +++ b/lib/puppet/functions/postgresql/prepend_sql_password.rb @@ -0,0 +1,12 @@ +# @summary This function exists for usage of a role password that is a deferred function +Puppet::Functions.create_function(:'postgresql::prepend_sql_password') do + # @param password + # The clear text `password` + dispatch :default_impl do + required_param 'String', :password + return_type 'String' + end + def default_impl(password) + "ENCRYPTED PASSWORD '#{password}'" + end +end diff --git a/manifests/server/role.pp b/manifests/server/role.pp index 00edc75bd5..1741227f8d 100644 --- a/manifests/server/role.pp +++ b/manifests/server/role.pp @@ -84,14 +84,31 @@ $createdb_sql = $createdb ? { true => 'CREATEDB', default => 'NOCREATEDB' } $superuser_sql = $superuser ? { true => 'SUPERUSER', default => 'NOSUPERUSER' } $replication_sql = $replication ? { true => 'REPLICATION', default => '' } - if ($password_hash_unsensitive != false) { - $password_sql = "ENCRYPTED PASSWORD '${password_hash_unsensitive}'" + + if $password_hash_unsensitive =~ Deferred { + $password_sql = Deferred('postgresql::prepend_sql_password', [$password_hash_unsensitive]) + } elsif ($password_hash_unsensitive != false) { + $password_sql = postgresql::prepend_sql_password($password_hash_unsensitive) } else { $password_sql = '' } + if $password_sql =~ Deferred { + $create_role_command = Deferred('sprintf', ["CREATE ROLE \"%s\" %s %s %s %s %s %s CONNECTION LIMIT %s", + $username, + $password_sql, + $login_sql, + $createrole_sql, + $createdb_sql, + $superuser_sql, + $replication_sql, + $connection_limit]) + } else { + $create_role_command = "CREATE ROLE \"${username}\" ${password_sql} ${login_sql} ${createrole_sql} ${createdb_sql} ${superuser_sql} ${replication_sql} CONNECTION LIMIT ${connection_limit}" + } + postgresql_psql { "CREATE ROLE ${username} ENCRYPTED PASSWORD ****": - command => Sensitive("CREATE ROLE \"${username}\" ${password_sql} ${login_sql} ${createrole_sql} ${createdb_sql} ${superuser_sql} ${replication_sql} CONNECTION LIMIT ${connection_limit}"), + command => Sensitive($create_role_command), unless => "SELECT 1 FROM pg_roles WHERE rolname = '${username}'", require => undef, sensitive => true, @@ -134,9 +151,14 @@ } if $password_hash_unsensitive and $update_password { - if($password_hash_unsensitive =~ /^(md5|SCRAM-SHA-256).+/) { - $pwd_hash_sql = $password_hash_unsensitive - } else { + if $password_hash_unsensitive =~ Deferred { + $pwd_hash_sql = Deferred('postgresql::postgresql_password',[$username, + $password_hash, + false, + $hash, + $salt]) + } + else { $pwd_hash_sql = postgresql::postgresql_password( $username, $password_hash, @@ -145,9 +167,18 @@ $salt, ) } + if $pwd_hash_sql =~ Deferred { + $pw_command = Deferred('sprintf', ["ALTER ROLE \"%s\" ENCRYPTED PASSWORD '%s'", $username, $pwd_hash_sql]) + $unless_pw_command = Deferred('sprintf', ["SELECT 1 FROM pg_shadow WHERE usename = '%s' AND passwd = '%s'", + $username, + $pwd_hash_sql]) + } else { + $pw_command = "ALTER ROLE \"${username}\" ENCRYPTED PASSWORD '${pwd_hash_sql}'" + $unless_pw_command = "SELECT 1 FROM pg_shadow WHERE usename = '${username}' AND passwd = '${pwd_hash_sql}'" + } postgresql_psql { "ALTER ROLE ${username} ENCRYPTED PASSWORD ****": - command => Sensitive("ALTER ROLE \"${username}\" ENCRYPTED PASSWORD '${pwd_hash_sql}'"), - unless => Sensitive("SELECT 1 FROM pg_shadow WHERE usename = '${username}' AND passwd = '${pwd_hash_sql}'"), + command => Sensitive($pw_command), + unless => Sensitive($unless_pw_command), sensitive => true, } } diff --git a/spec/acceptance/server/deferred_pw_role_spec.rb b/spec/acceptance/server/deferred_pw_role_spec.rb new file mode 100644 index 0000000000..70cd6d031c --- /dev/null +++ b/spec/acceptance/server/deferred_pw_role_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper_acceptance' + +describe 'postgresql::server::role:' do + let(:user) { 'deferred_user_test' } + let(:password) { 'test_password' } + + let(:pp_one) do + <<-MANIFEST.unindent + $user = #{user} + $password = #{password} + + class { 'postgresql::server': } + $deferred_func = Deferred('new', [String, $password]) + + postgresql::server::role { $user: + password_hash => $deferred_func, + } + MANIFEST + end + + it 'creates a role with the password in the deferred function' do + if run_shell('puppet --version').stdout[0].to_i < 7 + skip # Deferred function fixes only in puppet 7, see https://tickets.puppetlabs.com/browse/PUP-11518 + end + apply_manifest(pp_one) + psql_cmd = "PGPASSWORD=#{password} PGUSER=#{user} psql -h 127.0.0.1 -d postgres -c '\\q'" + run_shell("cd /tmp; su #{shellescape('postgres')} -c #{shellescape(psql_cmd)}", + acceptable_exit_codes: [0]) + end +end