Skip to content

Commit 79eb259

Browse files
authored
Merge pull request #891 from infoxchange/grant_ensure_absent_is_revoke
postgresql::server::grant with ensure => absent uses REVOKE instead of GRANT
2 parents 26ce6fa + 9a5ffdb commit 79eb259

File tree

6 files changed

+400
-72
lines changed

6 files changed

+400
-72
lines changed

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,6 +1282,16 @@ Consult
12821282
[the PostgreSQL documentation for `GRANT`](http://www.postgresql.org/docs/current/static/sql-grant.html)
12831283
for more information.
12841284

1285+
##### `ensure`
1286+
1287+
Specifies whether to grant or revoke the privilege. Default is to grant the privilege.
1288+
1289+
Valid values: 'present', 'absent'.
1290+
* 'present' to grant the privilege
1291+
* 'absent' to revoke the privilege
1292+
1293+
Default value: 'present'.
1294+
12851295
#### `connect_settings`
12861296

12871297
Specifies a hash of environment variables used when connecting to a remote
@@ -1379,6 +1389,16 @@ Manages grant-based access privileges for roles. See
13791389
[PostgreSQL documentation for `grant`](http://www.postgresql.org/docs/current/static/sql-grant.html)
13801390
for more information.
13811391

1392+
##### `ensure`
1393+
1394+
Specifies whether to grant or revoke the privilege. Default is to grant the privilege.
1395+
1396+
Valid values: 'present', 'absent'.
1397+
* 'present' to grant the privilege
1398+
* 'absent' to revoke the privilege
1399+
1400+
Default value: 'present'.
1401+
13821402
##### `db`
13831403

13841404
Specifies the database to which you are granting access.
@@ -1780,6 +1800,16 @@ Manages grant-based access privileges for users. Consult
17801800
[the PostgreSQL documentation for `GRANT`](http://www.postgresql.org/docs/current/static/sql-grant.html)
17811801
for more information.
17821802

1803+
##### `ensure`
1804+
1805+
Specifies whether to grant or revoke the privilege. Default is to grant the privilege.
1806+
1807+
Valid values: 'present', 'absent'.
1808+
* 'present' to grant the privilege
1809+
* 'absent' to revoke the privilege
1810+
1811+
Default value: 'present'.
1812+
17831813
##### `connect_settings`
17841814

17851815
Specifies a hash of environment variables used when connecting to a remote

manifests/server/database_grant.pp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
$privilege,
44
$db,
55
$role,
6+
$ensure = undef,
67
$psql_db = undef,
78
$psql_user = undef,
89
$connect_settings = undef,
910
) {
1011
postgresql::server::grant { "database:${name}":
12+
ensure => $ensure,
1113
role => $role,
1214
db => $db,
1315
privilege => $privilege,

manifests/server/grant.pp

Lines changed: 149 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,23 @@
2727
Integer $port = $postgresql::server::port,
2828
Boolean $onlyif_exists = false,
2929
Hash $connect_settings = $postgresql::server::default_connect_settings,
30+
Enum['present',
31+
'absent'
32+
] $ensure = 'present',
3033
) {
3134

35+
case $ensure {
36+
default: {
37+
# default is 'present'
38+
$sql_command = 'GRANT %s ON %s "%s" TO "%s"'
39+
$unless_is = true
40+
}
41+
'absent': {
42+
$sql_command = 'REVOKE %s ON %s "%s" FROM "%s"'
43+
$unless_is = false
44+
}
45+
}
46+
3247
$group = $postgresql::server::group
3348
$psql_path = $postgresql::server::psql_path
3449

@@ -81,7 +96,10 @@
8196
}
8297
$unless_function = 'has_database_privilege'
8398
$on_db = $psql_db
84-
$onlyif_function = undef
99+
$onlyif_function = $ensure ? {
100+
default => undef,
101+
'absent' => 'role_exists',
102+
}
85103
}
86104
'SCHEMA': {
87105
$unless_privilege = $_privilege ? {
@@ -149,43 +167,78 @@
149167
# If this number is not zero then there is at least one sequence for which
150168
# the role does not have the specified privilege, making it necessary to
151169
# execute the GRANT statement.
152-
$custom_unless = "SELECT 1 FROM (
153-
SELECT sequence_name
154-
FROM information_schema.sequences
155-
WHERE sequence_schema='${schema}'
156-
EXCEPT DISTINCT
157-
SELECT object_name as sequence_name
158-
FROM (
159-
SELECT object_schema,
160-
object_name,
161-
grantee,
162-
CASE privs_split
163-
WHEN 'r' THEN 'SELECT'
164-
WHEN 'w' THEN 'UPDATE'
165-
WHEN 'U' THEN 'USAGE'
166-
END AS privilege_type
167-
FROM (
168-
SELECT DISTINCT
169-
object_schema,
170-
object_name,
171-
(regexp_split_to_array(regexp_replace(privs,E'/.*',''),'='))[1] AS grantee,
172-
regexp_split_to_table((regexp_split_to_array(regexp_replace(privs,E'/.*',''),'='))[2],E'\\s*') AS privs_split
173-
FROM (
174-
SELECT n.nspname as object_schema,
175-
c.relname as object_name,
176-
regexp_split_to_table(array_to_string(c.relacl,','),',') AS privs
177-
FROM pg_catalog.pg_class c
178-
LEFT JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
179-
WHERE c.relkind = 'S'
180-
AND n.nspname NOT IN ( 'pg_catalog', 'information_schema' )
181-
) P1
182-
) P2
183-
) P3
184-
WHERE grantee='${role}'
185-
AND object_schema='${schema}'
186-
AND privilege_type='${custom_privilege}'
187-
) P
188-
HAVING count(P.sequence_name) = 0"
170+
if $ensure == 'present' {
171+
$custom_unless = "SELECT 1 WHERE NOT EXISTS (
172+
SELECT sequence_name
173+
FROM information_schema.sequences
174+
WHERE sequence_schema='${schema}'
175+
EXCEPT DISTINCT
176+
SELECT object_name as sequence_name
177+
FROM (
178+
SELECT object_schema,
179+
object_name,
180+
grantee,
181+
CASE privs_split
182+
WHEN 'r' THEN 'SELECT'
183+
WHEN 'w' THEN 'UPDATE'
184+
WHEN 'U' THEN 'USAGE'
185+
END AS privilege_type
186+
FROM (
187+
SELECT DISTINCT
188+
object_schema,
189+
object_name,
190+
(regexp_split_to_array(regexp_replace(privs,E'/.*',''),'='))[1] AS grantee,
191+
regexp_split_to_table((regexp_split_to_array(regexp_replace(privs,E'/.*',''),'='))[2],E'\\s*') AS privs_split
192+
FROM (
193+
SELECT n.nspname as object_schema,
194+
c.relname as object_name,
195+
regexp_split_to_table(array_to_string(c.relacl,','),',') AS privs
196+
FROM pg_catalog.pg_class c
197+
LEFT JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
198+
WHERE c.relkind = 'S'
199+
AND n.nspname NOT IN ( 'pg_catalog', 'information_schema' )
200+
) P1
201+
) P2
202+
) P3
203+
WHERE grantee='${role}'
204+
AND object_schema='${schema}'
205+
AND privilege_type='${custom_privilege}'
206+
)"
207+
} else {
208+
# ensure == absent
209+
$custom_unless = "SELECT 1 WHERE NOT EXISTS (
210+
SELECT object_name as sequence_name
211+
FROM (
212+
SELECT object_schema,
213+
object_name,
214+
grantee,
215+
CASE privs_split
216+
WHEN 'r' THEN 'SELECT'
217+
WHEN 'w' THEN 'UPDATE'
218+
WHEN 'U' THEN 'USAGE'
219+
END AS privilege_type
220+
FROM (
221+
SELECT DISTINCT
222+
object_schema,
223+
object_name,
224+
(regexp_split_to_array(regexp_replace(privs,E'/.*',''),'='))[1] AS grantee,
225+
regexp_split_to_table((regexp_split_to_array(regexp_replace(privs,E'/.*',''),'='))[2],E'\\s*') AS privs_split
226+
FROM (
227+
SELECT n.nspname as object_schema,
228+
c.relname as object_name,
229+
regexp_split_to_table(array_to_string(c.relacl,','),',') AS privs
230+
FROM pg_catalog.pg_class c
231+
LEFT JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
232+
WHERE c.relkind = 'S'
233+
AND n.nspname NOT IN ( 'pg_catalog', 'information_schema' )
234+
) P1
235+
) P2
236+
) P3
237+
WHERE grantee='${role}'
238+
AND object_schema='${schema}'
239+
AND privilege_type='${custom_privilege}'
240+
)"
241+
}
189242
}
190243
'TABLE': {
191244
$unless_privilege = $_privilege ? {
@@ -233,37 +286,62 @@
233286
$schema = $object_name
234287

235288
# Again there seems to be no easy way in plain SQL to check if ALL
236-
# PRIVILEGES are granted on a table. By convention we use INSERT
237-
# here to represent ALL PRIVILEGES (truly terrible).
238-
$custom_privilege = $_privilege ? {
239-
'ALL' => 'INSERT',
240-
'ALL PRIVILEGES' => 'INSERT',
241-
default => $_privilege,
289+
# PRIVILEGES are granted on a table.
290+
# There are currently 7 possible priviliges:
291+
# ('SELECT','UPDATE','INSERT','DELETE','TRIGGER','REFERENCES','TRUNCATE')
292+
# This list is consistant from Postgresql 8.0
293+
#
294+
# There are 4 cases to cover, each with it's own distinct unless clause:
295+
# grant ALL
296+
# grant SELECT (or INSERT or DELETE ...)
297+
# revoke ALL
298+
# revoke SELECT (or INSERT or DELETE ...)
299+
300+
if $ensure == 'present' {
301+
if $_privilege == 'ALL' or $_privilege == 'ALL PRIVILEGES' {
302+
# GRANT ALL
303+
$custom_unless = "SELECT 1 WHERE NOT EXISTS
304+
( SELECT 1 FROM pg_catalog.pg_tables AS t,
305+
(VALUES ('SELECT'), ('UPDATE'), ('INSERT'), ('DELETE'), ('TRIGGER'), ('REFERENCES'), ('TRUNCATE')) AS p(privilege_type)
306+
WHERE t.schemaname = '${schema}'
307+
AND NOT EXISTS (
308+
SELECT 1 FROM information_schema.role_table_grants AS g
309+
WHERE g.grantee = '${role}'
310+
AND g.table_schema = '${schema}'
311+
AND g.privilege_type = p.privilege_type
312+
)
313+
)"
314+
315+
} else {
316+
# GRANT $_privilege
317+
$custom_unless = "SELECT 1 WHERE NOT EXISTS
318+
( SELECT 1 FROM pg_catalog.pg_tables AS t
319+
WHERE t.schemaname = '${schema}'
320+
AND NOT EXISTS (
321+
SELECT 1 FROM information_schema.role_table_grants AS g
322+
WHERE g.grantee = '${role}'
323+
AND g.table_schema = '${schema}'
324+
AND g.privilege_type = '${_privilege}'
325+
)
326+
)"
327+
}
328+
} else {
329+
if $_privilege == 'ALL' or $_privilege == 'ALL PRIVILEGES' {
330+
# REVOKE ALL
331+
$custom_unless = "SELECT 1 WHERE NOT EXISTS
332+
( SELECT table_name FROM information_schema.role_table_grants
333+
WHERE grantee = '${role}' AND table_schema ='${schema}'
334+
)"
335+
} else {
336+
# REVOKE $_privilege
337+
$custom_unless = "SELECT 1 WHERE NOT EXISTS
338+
( SELECT table_name FROM information_schema.role_table_grants
339+
WHERE grantee = '${role}' AND table_schema ='${schema}'
340+
AND privilege_type = '${_privilege}'
341+
)"
342+
}
242343
}
243344

244-
# This checks if there is a difference between the tables in the
245-
# specified schema and the tables for which the role has the specified
246-
# privilege. It uses the EXCEPT clause which computes the set of rows
247-
# that are in the result of the first SELECT statement but not in the
248-
# result of the second one. It then counts the number of rows from this
249-
# operation. If this number is zero then the role has the specified
250-
# privilege for all tables in the schema and the whole query returns a
251-
# single row, which satisfies the `unless` parameter of Postgresql_psql.
252-
# If this number is not zero then there is at least one table for which
253-
# the role does not have the specified privilege, making it necessary to
254-
# execute the GRANT statement.
255-
$custom_unless = "SELECT 1 FROM (
256-
SELECT table_name
257-
FROM information_schema.tables
258-
WHERE table_schema='${schema}'
259-
EXCEPT DISTINCT
260-
SELECT table_name
261-
FROM information_schema.role_table_grants
262-
WHERE grantee='${role}'
263-
AND table_schema='${schema}'
264-
AND privilege_type='${custom_privilege}'
265-
) P
266-
HAVING count(P.table_name) = 0"
267345
}
268346
'LANGUAGE': {
269347
$unless_privilege = $_privilege ? {
@@ -313,17 +391,18 @@
313391
false => undef,
314392
'custom' => $custom_unless,
315393
default => "SELECT 1 WHERE ${unless_function}('${role}',
316-
'${_granted_object}', '${unless_privilege}')",
394+
'${_granted_object}', '${unless_privilege}') = ${unless_is}",
317395
}
318396

319397
$_onlyif = $onlyif_function ? {
320398
'table_exists' => "SELECT true FROM pg_tables WHERE tablename = '${_togrant_object}'",
321399
'language_exists' => "SELECT true from pg_language WHERE lanname = '${_togrant_object}'",
400+
'role_exists' => "SELECT 1 FROM pg_roles WHERE rolname = '${role}'",
322401
default => undef,
323402
}
324403

325-
$grant_cmd = "GRANT ${_privilege} ON ${_object_type} \"${_togrant_object}\" TO
326-
\"${role}\""
404+
$grant_cmd = sprintf($sql_command, $_privilege, $_object_type, $_togrant_object, $role)
405+
327406
postgresql_psql { "grant:${name}":
328407
command => $grant_cmd,
329408
db => $on_db,

manifests/server/table_grant.pp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
$table,
66
$db,
77
$role,
8+
$ensure = undef,
89
$port = undef,
910
$psql_db = undef,
1011
$psql_user = undef,
1112
$connect_settings = undef,
1213
$onlyif_exists = false,
1314
) {
1415
postgresql::server::grant { "table:${name}":
16+
ensure => $ensure,
1517
role => $role,
1618
db => $db,
1719
port => $port,
@@ -23,4 +25,4 @@
2325
onlyif_exists => $onlyif_exists,
2426
connect_settings => $connect_settings,
2527
}
26-
}
28+
}

0 commit comments

Comments
 (0)