@@ -14,6 +14,12 @@ import (
14
14
"github.com/lib/pq"
15
15
)
16
16
17
+ var allowedObjectTypes = []string {
18
+ "database" ,
19
+ "table" ,
20
+ "sequence" ,
21
+ }
22
+
17
23
var objectTypes = map [string ]string {
18
24
"table" : "r" ,
19
25
"sequence" : "S" ,
@@ -42,19 +48,16 @@ func resourcePostgreSQLGrant() *schema.Resource {
42
48
},
43
49
"schema" : {
44
50
Type : schema .TypeString ,
45
- Required : true ,
51
+ Optional : true ,
46
52
ForceNew : true ,
47
53
Description : "The database schema to grant privileges on for this role" ,
48
54
},
49
55
"object_type" : {
50
- Type : schema .TypeString ,
51
- Required : true ,
52
- ForceNew : true ,
53
- ValidateFunc : validation .StringInSlice ([]string {
54
- "table" ,
55
- "sequence" ,
56
- }, false ),
57
- Description : "The PostgreSQL object type to grant the privileges on (one of: table, sequence)" ,
56
+ Type : schema .TypeString ,
57
+ Required : true ,
58
+ ForceNew : true ,
59
+ ValidateFunc : validation .StringInSlice (allowedObjectTypes , false ),
60
+ Description : "The PostgreSQL object type to grant the privileges on (one of: " + strings .Join (allowedObjectTypes , ", " ) + ")" ,
58
61
},
59
62
"privileges" : & schema.Schema {
60
63
Type : schema .TypeSet ,
@@ -64,6 +67,13 @@ func resourcePostgreSQLGrant() *schema.Resource {
64
67
MinItems : 1 ,
65
68
Description : "The list of privileges to grant" ,
66
69
},
70
+ "with_grant_option" : {
71
+ Type : schema .TypeBool ,
72
+ Optional : true ,
73
+ ForceNew : true ,
74
+ Default : false ,
75
+ Description : "Permit the grant recipient to grant it to others" ,
76
+ },
67
77
},
68
78
}
69
79
}
@@ -110,7 +120,7 @@ func resourcePostgreSQLGrantCreate(d *schema.ResourceData, meta interface{}) err
110
120
)
111
121
}
112
122
113
- if err := validatePrivileges (d . Get ( "object_type" ).( string ), d . Get ( "privileges" ).( * schema. Set ). List () ); err != nil {
123
+ if err := validatePrivileges (d ); err != nil {
114
124
return err
115
125
}
116
126
@@ -181,7 +191,38 @@ func resourcePostgreSQLGrantDelete(d *schema.ResourceData, meta interface{}) err
181
191
return nil
182
192
}
183
193
194
+ func readDatabaseRolePriviges (txn * sql.Tx , d * schema.ResourceData ) error {
195
+ query := `
196
+ SELECT privilege_type
197
+ FROM (
198
+ SELECT (aclexplode(datacl)).* FROM pg_database WHERE datname=$1
199
+ ) as privileges
200
+ JOIN pg_roles ON grantee = pg_roles.oid WHERE rolname = $2
201
+ `
202
+
203
+ privileges := []string {}
204
+ rows , err := txn .Query (query , d .Get ("database" ), d .Get ("role" ))
205
+ if err != nil {
206
+ return errwrap .Wrapf ("could not read database privileges: {{err}}" , err )
207
+ }
208
+
209
+ for rows .Next () {
210
+ var privilegeType string
211
+ if err := rows .Scan (& privilegeType ); err != nil {
212
+ return errwrap .Wrapf ("could not scan database privilege: {{err}}" , err )
213
+ }
214
+ privileges = append (privileges , privilegeType )
215
+ }
216
+
217
+ d .Set ("privileges" , privileges )
218
+ return nil
219
+ }
220
+
184
221
func readRolePrivileges (txn * sql.Tx , d * schema.ResourceData ) error {
222
+ if d .Get ("object_type" ).(string ) == "database" {
223
+ return readDatabaseRolePriviges (txn , d )
224
+ }
225
+
185
226
// This returns, for the specified role (rolname),
186
227
// the list of all object of the specified type (relkind) in the specified schema (namespace)
187
228
// with the list of the currently applied privileges (aggregation of privilege_type)
@@ -236,34 +277,74 @@ GROUP BY pg_class.relname;
236
277
return nil
237
278
}
238
279
280
+ func createGrantQuery (d * schema.ResourceData , privileges []string ) string {
281
+ var query string
282
+
283
+ switch strings .ToUpper (d .Get ("object_type" ).(string )) {
284
+ case "DATABASE" :
285
+ query = fmt .Sprintf (
286
+ "GRANT %s ON DATABASE %s TO %s" ,
287
+ strings .Join (privileges , "," ),
288
+ pq .QuoteIdentifier (d .Get ("database" ).(string )),
289
+ pq .QuoteIdentifier (d .Get ("role" ).(string )),
290
+ )
291
+ case "TABLE" , "SEQUENCE" :
292
+ query = fmt .Sprintf (
293
+ "GRANT %s ON ALL %sS IN SCHEMA %s TO %s" ,
294
+ strings .Join (privileges , "," ),
295
+ strings .ToUpper (d .Get ("object_type" ).(string )),
296
+ pq .QuoteIdentifier (d .Get ("schema" ).(string )),
297
+ pq .QuoteIdentifier (d .Get ("role" ).(string )),
298
+ )
299
+ }
300
+
301
+ if d .Get ("with_grant_option" ).(bool ) == true {
302
+ query = query + " WITH GRANT OPTION"
303
+ }
304
+
305
+ return query
306
+ }
307
+
308
+ func createRevokeQuery (d * schema.ResourceData ) string {
309
+ var query string
310
+
311
+ switch strings .ToUpper (d .Get ("object_type" ).(string )) {
312
+ case "DATABASE" :
313
+ query = fmt .Sprintf (
314
+ "REVOKE ALL PRIVILEGES ON DATABASE %s FROM %s" ,
315
+ pq .QuoteIdentifier (d .Get ("database" ).(string )),
316
+ pq .QuoteIdentifier (d .Get ("role" ).(string )),
317
+ )
318
+ case "TABLE" , "SEQUENCE" :
319
+ query = fmt .Sprintf (
320
+ "REVOKE ALL PRIVILEGES ON ALL %sS IN SCHEMA %s FROM %s" ,
321
+ strings .ToUpper (d .Get ("object_type" ).(string )),
322
+ pq .QuoteIdentifier (d .Get ("schema" ).(string )),
323
+ pq .QuoteIdentifier (d .Get ("role" ).(string )),
324
+ )
325
+ }
326
+
327
+ return query
328
+ }
329
+
239
330
func grantRolePrivileges (txn * sql.Tx , d * schema.ResourceData ) error {
240
331
privileges := []string {}
241
332
for _ , priv := range d .Get ("privileges" ).(* schema.Set ).List () {
242
333
privileges = append (privileges , priv .(string ))
243
334
}
244
335
245
- query := fmt .Sprintf (
246
- "GRANT %s ON ALL %sS IN SCHEMA %s TO %s" ,
247
- strings .Join (privileges , "," ),
248
- strings .ToUpper (d .Get ("object_type" ).(string )),
249
- pq .QuoteIdentifier (d .Get ("schema" ).(string )),
250
- pq .QuoteIdentifier (d .Get ("role" ).(string )),
251
- )
336
+ query := createGrantQuery (d , privileges )
252
337
253
338
_ , err := txn .Exec (query )
254
339
return err
255
340
}
256
341
257
342
func revokeRolePrivileges (txn * sql.Tx , d * schema.ResourceData ) error {
258
- query := fmt .Sprintf (
259
- "REVOKE ALL PRIVILEGES ON ALL %sS IN SCHEMA %s FROM %s" ,
260
- strings .ToUpper (d .Get ("object_type" ).(string )),
261
- pq .QuoteIdentifier (d .Get ("schema" ).(string )),
262
- pq .QuoteIdentifier (d .Get ("role" ).(string )),
263
- )
264
-
265
- _ , err := txn .Exec (query )
266
- return err
343
+ query := createRevokeQuery (d )
344
+ if _ , err := txn .Exec (query ); err != nil {
345
+ return errwrap .Wrapf ("could not execute revoke query: {{err}}" , err )
346
+ }
347
+ return nil
267
348
}
268
349
269
350
func checkRoleDBSchemaExists (client * Client , d * schema.ResourceData ) (bool , error ) {
@@ -295,30 +376,37 @@ func checkRoleDBSchemaExists(client *Client, d *schema.ResourceData) (bool, erro
295
376
return false , nil
296
377
}
297
378
298
- // Connect on this database to check if schema exists
299
- dbTxn , err := startTransaction (client , database )
300
- if err != nil {
301
- return false , err
302
- }
303
- defer dbTxn .Rollback ()
379
+ if d .Get ("object_type" ).(string ) != "database" {
380
+ // Connect on this database to check if schema exists
381
+ dbTxn , err := startTransaction (client , database )
382
+ if err != nil {
383
+ return false , err
384
+ }
385
+ defer dbTxn .Rollback ()
304
386
305
- // Check the schema exists (the SQL connection needs to be on the right database)
306
- pgSchema := d .Get ("schema" ).(string )
307
- exists , err = schemaExists (dbTxn , pgSchema )
308
- if err != nil {
309
- return false , err
310
- }
311
- if ! exists {
312
- log .Printf ("[DEBUG] schema %s does not exists" , pgSchema )
313
- return false , nil
387
+ // Check the schema exists (the SQL connection needs to be on the right database)
388
+ pgSchema := d .Get ("schema" ).(string )
389
+ exists , err = schemaExists (dbTxn , pgSchema )
390
+ if err != nil {
391
+ return false , err
392
+ }
393
+ if ! exists {
394
+ log .Printf ("[DEBUG] schema %s does not exists" , pgSchema )
395
+ return false , nil
396
+ }
314
397
}
315
398
316
399
return true , nil
317
400
}
318
401
319
402
func generateGrantID (d * schema.ResourceData ) string {
320
- return strings .Join ([]string {
321
- d .Get ("role" ).(string ), d .Get ("database" ).(string ),
322
- d .Get ("schema" ).(string ), d .Get ("object_type" ).(string ),
323
- }, "_" )
403
+ parts := []string {d .Get ("role" ).(string ), d .Get ("database" ).(string )}
404
+
405
+ objectType := d .Get ("object_type" ).(string )
406
+ if objectType != "database" {
407
+ parts = append (parts , d .Get ("schema" ).(string ))
408
+ }
409
+ parts = append (parts , objectType )
410
+
411
+ return strings .Join (parts , "_" )
324
412
}
0 commit comments