Skip to content

Support target_role in default_privileges #1297

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions manifests/server/default_privileges.pp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# @summary Manage a database defaults privileges. Only works with PostgreSQL version 9.6 and above.
#
# @param target_role Target role whose created objects will receive the default privileges. Defaults to the current user.
# @param ensure Specifies whether to grant or revoke the privilege.
# @param role Specifies the role or user whom you are granting access to.
# @param db Specifies the database to which you are granting access.
Expand All @@ -13,6 +14,7 @@
# @param connect_settings Specifies a hash of environment variables used when connecting to a remote server.
# @param psql_path Specifies the path to the psql command.
define postgresql::server::default_privileges (
Optional[String] $target_role = undef,
String $role,
String $db,
String $privilege,
Expand Down Expand Up @@ -50,11 +52,11 @@
case $ensure {
default: {
# default is 'present'
$sql_command = 'ALTER DEFAULT PRIVILEGES IN SCHEMA %s GRANT %s ON %s TO "%s"'
$sql_command = 'ALTER DEFAULT PRIVILEGES%s IN SCHEMA %s GRANT %s ON %s TO "%s"'
$unless_is = true
}
'absent': {
$sql_command = 'ALTER DEFAULT PRIVILEGES IN SCHEMA %s REVOKE %s ON %s FROM "%s"'
$sql_command = 'ALTER DEFAULT PRIVILEGES%s IN SCHEMA %s REVOKE %s ON %s FROM "%s"'
$unless_is = false
}
}
Expand All @@ -70,6 +72,14 @@
$port_override = $postgresql::server::port
}

if $target_role != undef {
$_target_role = " FOR ROLE $target_role"
$_check_target_role = "/$target_role"
} else {
$_target_role = ''
$_check_target_role = ''
}

## Munge the input values
$_object_type = upcase($object_type)
$_privilege = upcase($privilege)
Expand Down Expand Up @@ -128,12 +138,12 @@
}

$_unless = $ensure ? {
'absent' => "SELECT 1 WHERE NOT EXISTS (SELECT * FROM pg_default_acl AS da JOIN pg_namespace AS n ON da.defaclnamespace = n.oid WHERE '%s=%s' = ANY (defaclacl) AND nspname = '%s' and defaclobjtype = '%s')",
default => "SELECT 1 WHERE EXISTS (SELECT * FROM pg_default_acl AS da JOIN pg_namespace AS n ON da.defaclnamespace = n.oid WHERE '%s=%s' = ANY (defaclacl) AND nspname = '%s' and defaclobjtype = '%s')"
'absent' => "SELECT 1 WHERE NOT EXISTS (SELECT * FROM pg_default_acl AS da JOIN pg_namespace AS n ON da.defaclnamespace = n.oid WHERE '%s=%s%s' = ANY (defaclacl) AND nspname = '%s' and defaclobjtype = '%s')",
default => "SELECT 1 WHERE EXISTS (SELECT * FROM pg_default_acl AS da JOIN pg_namespace AS n ON da.defaclnamespace = n.oid WHERE '%s=%s%s' = ANY (defaclacl) AND nspname = '%s' and defaclobjtype = '%s')"
}

$unless_cmd = sprintf($_unless, $role, $_check_privilege, $schema, $_check_type)
$grant_cmd = sprintf($sql_command, $schema, $_privilege, $_object_type, $role)
$unless_cmd = sprintf($_unless, $role, $_check_privilege, $_check_target_role, $schema, $_check_type)
$grant_cmd = sprintf($sql_command, $_target_role, $schema, $_privilege, $_object_type, $role)

postgresql_psql { "default_privileges:${name}":
command => $grant_cmd,
Expand Down
122 changes: 122 additions & 0 deletions spec/acceptance/server/default_privileges_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,105 @@ class { 'postgresql::server': }
MANIFEST
end

let(:target_user) { 'target_role_user' }
let(:target_password) { 'target_role_password' }

let(:target_check_command) do
"SELECT 1 FROM pg_default_acl a LEFT JOIN pg_namespace AS b ON a.defaclnamespace = b.oid WHERE '#{user}=arwdDxt/#{target_user}' = ANY (defaclacl) AND nspname = 'public' AND defaclobjtype = 'r';"
end

let(:pp_target_role) do
<<-MANIFEST.unindent
$db = #{db}
$user = #{user}
$group = #{group}
$password = #{password}
$target_user = #{target_user}
$target_password = #{target_password}

user {$user:
ensure => present,
}
postgresql::server::database_grant { "allow connect for ${user}":
privilege => 'CONNECT',
db => $db,
role => $user,
}

class { 'postgresql::server': }

postgresql::server::role { $user:
password_hash => postgresql::postgresql_password($user, $password),
}

postgresql::server::role { $target_user:
password_hash => postgresql::postgresql_password($target_user, $target_password),
}

postgresql::server::database { $db:
require => Postgresql::Server::Role[$user],
}

# Set default privileges on tables
postgresql::server::default_privileges { "alter default privileges grant all on tables to ${user}":
db => $db,
role => $user,
target_role => $target_user,
psql_user => 'postgres',
privilege => 'ALL',
object_type => 'TABLES',
require => Postgresql::Server::Database[$db],
}
MANIFEST
end

let(:pp_target_role_revoke) do
<<-MANIFEST.unindent
$db = #{db}
$user = #{user}
$group = #{group}
$password = #{password}
$target_user = #{target_user}
$target_password = #{target_password}

user {$user:
ensure => present,
}
postgresql::server::database_grant { "allow connect for ${user}":
privilege => 'CONNECT',
db => $db,
role => $user,
}


class { 'postgresql::server': }

postgresql::server::role { $user:
password_hash => postgresql::postgresql_password($user, $password),
}

postgresql::server::role { $target_user:
password_hash => postgresql::postgresql_password($target_user, $target_password),
}

postgresql::server::database { $db:
require => Postgresql::Server::Role[$user],
}

# Set default privileges on tables
postgresql::server::default_privileges { "alter default privileges grant all on tables to ${user}":
db => $db,
role => $user,
target_role => $target_user,
psql_user => 'postgres',
privilege => 'ALL',
object_type => 'TABLES',
ensure => 'absent',
require => Postgresql::Server::Database[$db],
}
MANIFEST
end

it 'grants default privileges to an user' do
if Gem::Version.new(postgresql_version) >= Gem::Version.new('9.6')
idempotent_apply(pp_one)
Expand All @@ -90,4 +189,27 @@ class { 'postgresql::server': }
end
end
end

it 'grants default privileges to a user on a specific target role' do
if Gem::Version.new(postgresql_version) >= Gem::Version.new('9.6')
idempotent_apply(pp_target_role)

psql("--command=\"SET client_min_messages = 'error'; #{target_check_command}\" --db=#{db}", user) do |r|
expect(r.stdout).to match(%r{^\(1 row\)$})
expect(r.stderr).to eq('')
end
end
end

it 'revokes default privileges from a user on a specific target role' do
if Gem::Version.new(postgresql_version) >= Gem::Version.new('9.6')
idempotent_apply(pp_target_role)
idempotent_apply(pp_target_role_revoke)

psql("--command=\"SET client_min_messages = 'error'; #{target_check_command}\" --db=#{db}", user) do |r|
expect(r.stdout).to match(%r{^\(0 rows\)$})
expect(r.stderr).to eq('')
end
end
end
end
30 changes: 30 additions & 0 deletions spec/unit/defines/server/default_privileges_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,36 @@ class {'postgresql::server':}
end
end

context 'with a target role' do
let :params do
{
target_role: 'target',
db: 'test',
role: 'test',
privilege: 'all',
object_type: 'tables',
}
end

let :pre_condition do
<<-EOS
class {'postgresql::server':}
postgresql::server::role { 'target': }
EOS
end

it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_postgresql__server__default_privileges('test') }
it { is_expected.to contain_postgresql__server__role('target') }
it do
# rubocop:disable Layout/LineLength
is_expected.to contain_postgresql_psql('default_privileges:test')
.with_command('ALTER DEFAULT PRIVILEGES FOR ROLE target IN SCHEMA public GRANT ALL ON TABLES TO "test"')
.with_unless("SELECT 1 WHERE EXISTS (SELECT * FROM pg_default_acl AS da JOIN pg_namespace AS n ON da.defaclnamespace = n.oid WHERE 'test=arwdDxt/target' = ANY (defaclacl) AND nspname = 'public' and defaclobjtype = 'r')")
# rubocop:enable Layout/LineLength
end
end

context 'standalone not managing server' do
let :params do
{
Expand Down