Skip to content

Commit 64a7753

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 64a7753

File tree

3 files changed

+168
-6
lines changed

3 files changed

+168
-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: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,105 @@ 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+
user {$user:
88+
ensure => present,
89+
}
90+
postgresql::server::database_grant { "allow connect for ${user}":
91+
privilege => 'CONNECT',
92+
db => $db,
93+
role => $user,
94+
}
95+
96+
class { 'postgresql::server': }
97+
98+
postgresql::server::role { $user:
99+
password_hash => postgresql::postgresql_password($user, $password),
100+
}
101+
102+
postgresql::server::role { $target_user:
103+
password_hash => postgresql::postgresql_password($target_user, $target_password),
104+
}
105+
106+
postgresql::server::database { $db:
107+
require => Postgresql::Server::Role[$user],
108+
}
109+
110+
# Set default privileges on tables
111+
postgresql::server::default_privileges { "alter default privileges grant all on tables to ${user}":
112+
db => $db,
113+
role => $user,
114+
target_role => $target_user,
115+
psql_user => 'postgres',
116+
privilege => 'ALL',
117+
object_type => 'TABLES',
118+
require => Postgresql::Server::Database[$db],
119+
}
120+
MANIFEST
121+
end
122+
123+
let(:pp_target_role_revoke) do
124+
<<-MANIFEST.unindent
125+
$db = #{db}
126+
$user = #{user}
127+
$group = #{group}
128+
$password = #{password}
129+
$target_user = #{target_user}
130+
$target_password = #{target_password}
131+
132+
user {$user:
133+
ensure => present,
134+
}
135+
postgresql::server::database_grant { "allow connect for ${user}":
136+
privilege => 'CONNECT',
137+
db => $db,
138+
role => $user,
139+
}
140+
141+
142+
class { 'postgresql::server': }
143+
144+
postgresql::server::role { $user:
145+
password_hash => postgresql::postgresql_password($user, $password),
146+
}
147+
148+
postgresql::server::role { $target_user:
149+
password_hash => postgresql::postgresql_password($target_user, $target_password),
150+
}
151+
152+
postgresql::server::database { $db:
153+
require => Postgresql::Server::Role[$user],
154+
}
155+
156+
# Set default privileges on tables
157+
postgresql::server::default_privileges { "alter default privileges grant all on tables to ${user}":
158+
db => $db,
159+
role => $user,
160+
target_role => $target_user,
161+
psql_user => 'postgres',
162+
privilege => 'ALL',
163+
object_type => 'TABLES',
164+
ensure => 'absent',
165+
require => Postgresql::Server::Database[$db],
166+
}
167+
MANIFEST
168+
end
169+
71170
it 'grants default privileges to an user' do
72171
if Gem::Version.new(postgresql_version) >= Gem::Version.new('9.6')
73172
idempotent_apply(pp_one)
@@ -90,4 +189,27 @@ class { 'postgresql::server': }
90189
end
91190
end
92191
end
192+
193+
it 'grants default privileges to a user on a specific target role' do
194+
if Gem::Version.new(postgresql_version) >= Gem::Version.new('9.6')
195+
idempotent_apply(pp_target_role)
196+
197+
psql("--command=\"SET client_min_messages = 'error'; #{target_check_command}\" --db=#{db}", user) do |r|
198+
expect(r.stdout).to match(%r{^\(1 row\)$})
199+
expect(r.stderr).to eq('')
200+
end
201+
end
202+
end
203+
204+
it 'revokes default privileges from a user on a specific target role' do
205+
if Gem::Version.new(postgresql_version) >= Gem::Version.new('9.6')
206+
idempotent_apply(pp_target_role)
207+
idempotent_apply(pp_target_role_revoke)
208+
209+
psql("--command=\"SET client_min_messages = 'error'; #{target_check_command}\" --db=#{db}", user) do |r|
210+
expect(r.stdout).to match(%r{^\(0 rows\)$})
211+
expect(r.stderr).to eq('')
212+
end
213+
end
214+
end
93215
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)