Skip to content

Commit f9bfba7

Browse files
committed
Implement default privileges changes
1 parent 36ee5f7 commit f9bfba7

File tree

4 files changed

+538
-0
lines changed

4 files changed

+538
-0
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,19 @@ This applies to objects within the nominated database, 'test_db' only.
131131

132132
For Postgresql >= 9.3, the ownership of the database is also updated.
133133

134+
### Manage default permissions (PostgreSQL >= 9.6)
135+
136+
To change default permissions for newly created objects using ALTER DEFAULT PRIVILEGES:
137+
138+
```puppet
139+
postgresql::server::default_privileges { 'marmot access to new tables on test_db':
140+
db => 'test_db',
141+
role => 'marmot',
142+
privilege => 'ALL',
143+
object_type => 'TABLES',
144+
}
145+
```
146+
134147
### Override defaults
135148

136149
The `postgresql::globals` class allows you to configure the main settings for this module globally, so that other classes and defined resources can use them. By itself, it does nothing.
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
define postgresql::server::default_privileges (
2+
String $role,
3+
String $db,
4+
String $privilege,
5+
Pattern[
6+
/(?i:^FUNCTIONS$)/,
7+
/(?i:^ROUTINES$)/,
8+
/(?i:^SEQUENCES$)/,
9+
/(?i:^TABLES$)/,
10+
/(?i:^TYPES$)/
11+
] $object_type,
12+
String $schema = 'public',
13+
String $psql_db = $postgresql::server::default_database,
14+
String $psql_user = $postgresql::server::user,
15+
Integer $port = $postgresql::server::port,
16+
Boolean $onlyif_exists = false,
17+
Hash $connect_settings = $postgresql::server::default_connect_settings,
18+
Enum['present',
19+
'absent'
20+
] $ensure = 'present',
21+
String $group = $postgresql::server::group,
22+
String $psql_path = $postgresql::server::psql_path,
23+
) {
24+
25+
# If possible use the version of the remote database, otherwise
26+
# fallback to our local DB version
27+
if $connect_settings != undef and has_key( $connect_settings, 'DBVERSION') {
28+
$version = $connect_settings['DBVERSION']
29+
} else {
30+
$version = $postgresql::server::_version
31+
}
32+
33+
if (versioncmp($version, '9.6') == -1) {
34+
fail 'Default_privileges is only useable with PostgreSQL >= 9.6'
35+
}
36+
37+
case $ensure {
38+
default: {
39+
# default is 'present'
40+
$sql_command = 'ALTER DEFAULT PRIVILEGES IN SCHEMA %s GRANT %s ON %s TO "%s"'
41+
$unless_is = true
42+
}
43+
'absent': {
44+
$sql_command = 'ALTER DEFAULT PRIVILEGES IN SCHEMA %s REVOKE %s ON %s FROM "%s"'
45+
$unless_is = false
46+
}
47+
}
48+
49+
#
50+
# Port, order of precedence: $port parameter, $connect_settings[PGPORT], $postgresql::server::port
51+
#
52+
if $port != undef {
53+
$port_override = $port
54+
} elsif $connect_settings != undef and has_key( $connect_settings, 'PGPORT') {
55+
$port_override = undef
56+
} else {
57+
$port_override = $postgresql::server::port
58+
}
59+
60+
## Munge the input values
61+
$_object_type = upcase($object_type)
62+
$_privilege = upcase($privilege)
63+
64+
case $_object_type {
65+
# Routines and functions ends up with the same definition
66+
Pattern[
67+
/^ROUTINES$/,
68+
/^FUNCTIONS$/,
69+
]: {
70+
case $_privilege {
71+
Pattern[
72+
/^ALL$/,
73+
/^EXECUTE$/,
74+
]: {
75+
$_check_privilege = 'X'
76+
}
77+
default: { fail('Illegal value for $privilege parameter') }
78+
}
79+
$_check_type = 'f'
80+
}
81+
'SEQUENCES': {
82+
case $_privilege {
83+
/^(ALL)$/: { $_check_privilege = 'rwU' }
84+
/^SELECT$/: { $_check_privilege = 'r'}
85+
/^UPDATE$/: { $_check_privilege = 'w'}
86+
/^USAGE$/: { $_check_privilege = 'U'}
87+
default: { fail('Illegal value for $privilege parameter') }
88+
}
89+
$_check_type = 'S'
90+
}
91+
'TABLES': {
92+
case $_privilege {
93+
/^ALL$/: { $_check_privilege = 'arwdDxt' }
94+
/^DELETE$/: { $_check_privilege = 'd' }
95+
/^INSERT$/: { $_check_privilege = 'a' }
96+
/^REFERENCES$/: { $_check_privilege = 'x' }
97+
/^SELECT$/: { $_check_privilege = 'r' }
98+
/^TRIGGER$/: { $_check_privilege = 'd' }
99+
/^TRUNCATE$/: { $_check_privilege = 'D' }
100+
/^UPDATE$/: { $_check_privilege = 'w' }
101+
default: { fail('Illegal value for $privilege parameter') }
102+
}
103+
$_check_type = 'r'
104+
}
105+
'TYPES': {
106+
case $_privilege {
107+
/^(ALL|USAGE)$/: { $_check_privilege = 'U'}
108+
default: { fail('Illegal value for $privilege parameter') }
109+
}
110+
$_check_type = 'T'
111+
}
112+
default: {
113+
fail("Missing privilege validation for object type ${_object_type}")
114+
}
115+
}
116+
117+
$_unless = $ensure ? {
118+
'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')",
119+
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')"
120+
}
121+
122+
$unless_cmd = sprintf($_unless, $role, $_check_privilege, $schema, $_check_type)
123+
$grant_cmd = sprintf($sql_command, $schema, $_privilege, $_object_type, $role)
124+
125+
postgresql_psql { "default_privileges:${name}":
126+
command => $grant_cmd,
127+
db => $db,
128+
port => $port_override,
129+
connect_settings => $connect_settings,
130+
psql_user => $psql_user,
131+
psql_group => $group,
132+
psql_path => $psql_path,
133+
unless => $unless_cmd,
134+
environment => "PGOPTIONS=--client-min-messages=error"
135+
}
136+
137+
if($role != undef and defined(Postgresql::Server::Role[$role])) {
138+
Postgresql::Server::Role[$role]->Postgresql_psql["default_privileges:${name}"]
139+
}
140+
141+
if($db != undef and defined(Postgresql::Server::Database[$db])) {
142+
Postgresql::Server::Database[$db]->Postgresql_psql["default_privileges:${name}"]
143+
}
144+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper_acceptance'
4+
5+
describe 'postgresql::server::default_privileges:' do
6+
let(:db) { 'grant_role_test' }
7+
let(:user) { 'psql_grant_role_tester' }
8+
let(:group) { 'test_group' }
9+
let(:password) { 'psql_grant_role_pw' }
10+
11+
# Check that the default privileges were revoked
12+
let(:check_command) do
13+
"SELECT * FROM pg_default_acl a JOIN pg_namespace b ON a.defaclnamespace = b.oid WHERE '#{user}=arwdDxt' = ANY (defaclacl) AND nspname = 'public' and defaclobjtype = 'r';"
14+
end
15+
16+
let(:pp_one) do
17+
<<-MANIFEST.unindent
18+
$db = #{db}
19+
$user = #{user}
20+
$group = #{group}
21+
$password = #{password}
22+
23+
class { 'postgresql::server': }
24+
25+
postgresql::server::role { $user:
26+
password_hash => postgresql::postgresql_password($user, $password),
27+
}
28+
29+
postgresql::server::database { $db:
30+
require => Postgresql::Server::Role[$user],
31+
}
32+
33+
# Set default privileges on tables
34+
postgresql::server::default_privileges { "alter default privileges grant all on tables to ${user}":
35+
db => $db,
36+
role => $user,
37+
privilege => 'ALL',
38+
object_type => 'TABLES',
39+
require => Postgresql::Server::Database[$db],
40+
}
41+
MANIFEST
42+
end
43+
let(:pp_two) do
44+
<<-MANIFEST
45+
$db = #{db}
46+
$user = #{user}
47+
$group = #{group}
48+
$password = #{password}
49+
50+
class { 'postgresql::server': }
51+
52+
postgresql::server::role { $user:
53+
password_hash => postgresql::postgresql_password($user, $password),
54+
}
55+
postgresql::server::database { $db:
56+
require => Postgresql::Server::Role[$user],
57+
}
58+
59+
# Removes default privileges on tables
60+
postgresql::server::default_privileges { "alter default privileges revoke all on tables for ${user}":
61+
db => $db,
62+
role => $user,
63+
privilege => 'ALL',
64+
object_type => 'TABLES',
65+
ensure => 'absent',
66+
require => Postgresql::Server::Database[$db],
67+
}
68+
MANIFEST
69+
end
70+
71+
it 'grants default privileges to an user' do
72+
if Gem::Version.new(postgresql_version) >= Gem::Version.new('9.6')
73+
idempotent_apply(pp_one)
74+
75+
psql("--command=\"SET client_min_messages = 'error';#{check_command}\" --db=#{db}") do |r|
76+
expect(r.stdout).to match(%r{\(1 row\)})
77+
expect(r.stderr).to eq('')
78+
end
79+
end
80+
end
81+
82+
it 'revokes default privileges for an user' do
83+
if Gem::Version.new(postgresql_version) >= Gem::Version.new('9.6')
84+
apply_manifest(pp_one, catch_failures: true)
85+
apply_manifest(pp_two, expect_changes: true)
86+
87+
psql("--command=\"SET client_min_messages = 'error';#{check_command}\" --db=#{db}") do |r|
88+
expect(r.stdout).to match(%r{\(0 rows\)})
89+
expect(r.stderr).to eq('')
90+
end
91+
end
92+
end
93+
end

0 commit comments

Comments
 (0)