Skip to content

Commit 1f2b118

Browse files
committed
Support target_role in default_privileges
ALTER DEFAULT PRIVILEGES supports the FOR ROLE <target_role> argument, without which the statement applies only to objects created by the *current* role, which may not be most useful. Support specifying the target role.
1 parent ecc63f1 commit 1f2b118

File tree

3 files changed

+149
-6
lines changed

3 files changed

+149
-6
lines changed

manifests/server/default_privileges.pp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# @summary Manage a database defaults privileges. Only works with PostgreSQL version 9.6 and above.
22
#
3+
# @param target_role Target role whose created objects will receive the default privileges. Defaults to the current user.
34
# @param ensure Specifies whether to grant or revoke the privilege.
45
# @param role Specifies the role or user whom you are granting access to.
56
# @param db Specifies the database to which you are granting access.
@@ -13,6 +14,7 @@
1314
# @param connect_settings Specifies a hash of environment variables used when connecting to a remote server.
1415
# @param psql_path Specifies the path to the psql command.
1516
define postgresql::server::default_privileges (
17+
Optional[String] $target_role = undef,
1618
String $role,
1719
String $db,
1820
String $privilege,
@@ -50,11 +52,11 @@
5052
case $ensure {
5153
default: {
5254
# default is 'present'
53-
$sql_command = 'ALTER DEFAULT PRIVILEGES IN SCHEMA %s GRANT %s ON %s TO "%s"'
55+
$sql_command = 'ALTER DEFAULT PRIVILEGES%s IN SCHEMA %s GRANT %s ON %s TO "%s"'
5456
$unless_is = true
5557
}
5658
'absent': {
57-
$sql_command = 'ALTER DEFAULT PRIVILEGES IN SCHEMA %s REVOKE %s ON %s FROM "%s"'
59+
$sql_command = 'ALTER DEFAULT PRIVILEGES%s IN SCHEMA %s REVOKE %s ON %s FROM "%s"'
5860
$unless_is = false
5961
}
6062
}
@@ -70,6 +72,14 @@
7072
$port_override = $postgresql::server::port
7173
}
7274

75+
if $target_role != undef {
76+
$_target_role = " FOR ROLE $target_role"
77+
$_check_target_role = "/$target_role"
78+
} else {
79+
$_target_role = ''
80+
$_check_target_role = ''
81+
}
82+
7383
## Munge the input values
7484
$_object_type = upcase($object_type)
7585
$_privilege = upcase($privilege)
@@ -128,12 +138,12 @@
128138
}
129139

130140
$_unless = $ensure ? {
131-
'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')",
132-
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')"
141+
'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')",
142+
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')"
133143
}
134144

135-
$unless_cmd = sprintf($_unless, $role, $_check_privilege, $schema, $_check_type)
136-
$grant_cmd = sprintf($sql_command, $schema, $_privilege, $_object_type, $role)
145+
$unless_cmd = sprintf($_unless, $role, $_check_privilege, $_check_target_role, $schema, $_check_type)
146+
$grant_cmd = sprintf($sql_command, $_target_role, $schema, $_privilege, $_object_type, $role)
137147

138148
postgresql_psql { "default_privileges:${name}":
139149
command => $grant_cmd,

spec/acceptance/server/default_privileges_spec.rb

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,86 @@ class { 'postgresql::server': }
6868
MANIFEST
6969
end
7070

71+
let(:target_user) { 'target_role_user' }
72+
let(:target_password) { 'target_role_password' }
73+
74+
let(:target_check_command) do
75+
"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';"
76+
end
77+
78+
let(:pp_target_role) do
79+
<<-MANIFEST.unindent
80+
$db = #{db}
81+
$user = #{user}
82+
$group = #{group}
83+
$password = #{password}
84+
$target_user = #{target_user}
85+
$target_password = #{target_password}
86+
87+
class { 'postgresql::server': }
88+
89+
postgresql::server::role { $user:
90+
password_hash => postgresql::postgresql_password($user, $password),
91+
}
92+
93+
postgresql::server::role { $target_user:
94+
password_hash => postgresql::postgresql_password($target_user, $target_password),
95+
}
96+
97+
postgresql::server::database { $db:
98+
require => Postgresql::Server::Role[$user],
99+
}
100+
101+
# Set default privileges on tables
102+
postgresql::server::default_privileges { "alter default privileges grant all on tables to ${user}":
103+
db => $db,
104+
role => $user,
105+
target_role => $target_user,
106+
psql_user => 'postgres',
107+
privilege => 'ALL',
108+
object_type => 'TABLES',
109+
require => Postgresql::Server::Database[$db],
110+
}
111+
MANIFEST
112+
end
113+
114+
let(:pp_target_role_revoke) do
115+
<<-MANIFEST.unindent
116+
$db = #{db}
117+
$user = #{user}
118+
$group = #{group}
119+
$password = #{password}
120+
$target_user = #{target_user}
121+
$target_password = #{target_password}
122+
123+
class { 'postgresql::server': }
124+
125+
postgresql::server::role { $user:
126+
password_hash => postgresql::postgresql_password($user, $password),
127+
}
128+
129+
postgresql::server::role { $target_user:
130+
password_hash => postgresql::postgresql_password($target_user, $target_password),
131+
}
132+
133+
postgresql::server::database { $db:
134+
require => Postgresql::Server::Role[$user],
135+
}
136+
137+
# Set default privileges on tables
138+
postgresql::server::default_privileges { "alter default privileges grant all on tables to ${user}":
139+
db => $db,
140+
role => $user,
141+
target_role => $target_user,
142+
psql_user => 'postgres',
143+
privilege => 'ALL',
144+
object_type => 'TABLES',
145+
ensure => 'absent',
146+
require => Postgresql::Server::Database[$db],
147+
}
148+
MANIFEST
149+
end
150+
71151
it 'grants default privileges to an user' do
72152
if Gem::Version.new(postgresql_version) >= Gem::Version.new('9.6')
73153
idempotent_apply(pp_one)
@@ -90,4 +170,27 @@ class { 'postgresql::server': }
90170
end
91171
end
92172
end
173+
174+
it 'grants default privileges to a user on a specific target role' do
175+
if Gem::Version.new(postgresql_version) >= Gem::Version.new('9.6')
176+
idempotent_apply(pp_target_role)
177+
178+
psql("--command=\"SET client_min_messages = 'error'; #{target_check_command}\" --db=#{db}", user) do |r|
179+
expect(r.stdout).to match(%r{^\(1 row\)$})
180+
expect(r.stderr).to eq('')
181+
end
182+
end
183+
end
184+
185+
it 'revokes default privileges from a user on a specific target role' do
186+
if Gem::Version.new(postgresql_version) >= Gem::Version.new('9.6')
187+
idempotent_apply(pp_target_role)
188+
idempotent_apply(pp_target_role_revoke)
189+
190+
psql("--command=\"SET client_min_messages = 'error'; #{target_check_command}\" --db=#{db}", user) do |r|
191+
expect(r.stdout).to match(%r{^\(0 rows\)$})
192+
expect(r.stderr).to eq('')
193+
end
194+
end
195+
end
93196
end

spec/unit/defines/server/default_privileges_spec.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,36 @@ class {'postgresql::server':}
253253
end
254254
end
255255

256+
context 'with a target role' do
257+
let :params do
258+
{
259+
target_role: 'target',
260+
db: 'test',
261+
role: 'test',
262+
privilege: 'all',
263+
object_type: 'tables',
264+
}
265+
end
266+
267+
let :pre_condition do
268+
<<-EOS
269+
class {'postgresql::server':}
270+
postgresql::server::role { 'target': }
271+
EOS
272+
end
273+
274+
it { is_expected.to compile.with_all_deps }
275+
it { is_expected.to contain_postgresql__server__default_privileges('test') }
276+
it { is_expected.to contain_postgresql__server__role('target') }
277+
it do
278+
# rubocop:disable Layout/LineLength
279+
is_expected.to contain_postgresql_psql('default_privileges:test')
280+
.with_command('ALTER DEFAULT PRIVILEGES FOR ROLE target IN SCHEMA public GRANT ALL ON TABLES TO "test"')
281+
.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')")
282+
# rubocop:enable Layout/LineLength
283+
end
284+
end
285+
256286
context 'standalone not managing server' do
257287
let :params do
258288
{

0 commit comments

Comments
 (0)