Skip to content

Commit 3e4580e

Browse files
make postgresql_default_privileges postgres schema optional (#59)
* make postgresql_default_privileges postgres schema optional * remove commented code * add schema check in resourcePostgreSQLGrantCreate function Co-authored-by: [email protected] <[email protected]>
1 parent dc0f33d commit 3e4580e

File tree

5 files changed

+136
-19
lines changed

5 files changed

+136
-19
lines changed

postgresql/helpers.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,6 @@ func validatePrivileges(d *schema.ResourceData) error {
254254
objectType := d.Get("object_type").(string)
255255
privileges := d.Get("privileges").(*schema.Set).List()
256256

257-
// Verify fields that are mandatory for specific object types
258-
if objectType != "database" && d.Get("schema").(string) == "" {
259-
return fmt.Errorf("parameter 'schema' is mandatory for object_type %s", objectType)
260-
}
261-
262257
allowed, ok := allowedPrivileges[objectType]
263258
if !ok {
264259
return fmt.Errorf("unknown object type %s", objectType)

postgresql/resource_postgresql_default_privileges.go

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func resourcePostgreSQLDefaultPrivileges() *schema.Resource {
4141
},
4242
"schema": {
4343
Type: schema.TypeString,
44-
Required: true,
44+
Optional: true,
4545
ForceNew: true,
4646
Description: "The database schema to set default privileges for this role",
4747
},
@@ -89,6 +89,7 @@ func resourcePostgreSQLDefaultPrivilegesRead(db *DBConnection, d *schema.Resourc
8989
}
9090

9191
func resourcePostgreSQLDefaultPrivilegesCreate(db *DBConnection, d *schema.ResourceData) error {
92+
9293
if err := validatePrivileges(d); err != nil {
9394
return err
9495
}
@@ -169,21 +170,36 @@ func readRoleDefaultPrivileges(txn *sql.Tx, d *schema.ResourceData) error {
169170
return err
170171
}
171172

172-
// This query aggregates the list of default privileges type (prtype)
173-
// for the role (grantee), owner (grantor), schema (namespace name)
174-
// and the specified object type (defaclobjtype).
175-
query := `SELECT array_agg(prtype) FROM (
173+
var query string
174+
var queryArgs []interface{}
175+
176+
if pgSchema != "" {
177+
query = `SELECT array_agg(prtype) FROM (
176178
SELECT defaclnamespace, (aclexplode(defaclacl)).* FROM pg_default_acl
177179
WHERE defaclobjtype = $3
178180
) AS t (namespace, grantor_oid, grantee_oid, prtype, grantable)
179-
180181
JOIN pg_namespace ON pg_namespace.oid = namespace
181182
WHERE grantee_oid = $1 AND nspname = $2 AND pg_get_userbyid(grantor_oid) = $4;
182183
`
184+
queryArgs = []interface{}{roleOID, pgSchema, objectTypes[objectType], owner}
185+
} else {
186+
query = `SELECT array_agg(prtype) FROM (
187+
SELECT defaclnamespace, (aclexplode(defaclacl)).* FROM pg_default_acl
188+
WHERE defaclobjtype = $2
189+
) AS t (namespace, grantor_oid, grantee_oid, prtype, grantable)
190+
WHERE grantee_oid = $1 AND namespace = 0 AND pg_get_userbyid(grantor_oid) = $3;
191+
`
192+
queryArgs = []interface{}{roleOID, objectTypes[objectType], owner}
193+
}
194+
195+
// This query aggregates the list of default privileges type (prtype)
196+
// for the role (grantee), owner (grantor), schema (namespace name)
197+
// and the specified object type (defaclobjtype).
198+
183199
var privileges pq.ByteaArray
184200

185201
if err := txn.QueryRow(
186-
query, roleOID, pgSchema, objectTypes[objectType], owner,
202+
query, queryArgs...,
187203
).Scan(&privileges); err != nil {
188204
return fmt.Errorf("could not read default privileges: %w", err)
189205
}
@@ -211,9 +227,16 @@ func grantRoleDefaultPrivileges(txn *sql.Tx, d *schema.ResourceData) error {
211227
privileges = append(privileges, priv.(string))
212228
}
213229

214-
query := fmt.Sprintf("ALTER DEFAULT PRIVILEGES FOR ROLE %s IN SCHEMA %s GRANT %s ON %sS TO %s",
230+
var inSchema string
231+
232+
// If a schema is specified we need to build the part of the query string to action this
233+
if pgSchema != "" {
234+
inSchema = fmt.Sprintf("IN SCHEMA %s", pq.QuoteIdentifier(pgSchema))
235+
}
236+
237+
query := fmt.Sprintf("ALTER DEFAULT PRIVILEGES FOR ROLE %s %s GRANT %s ON %sS TO %s",
215238
pq.QuoteIdentifier(d.Get("owner").(string)),
216-
pq.QuoteIdentifier(pgSchema),
239+
inSchema,
217240
strings.Join(privileges, ","),
218241
strings.ToUpper(d.Get("object_type").(string)),
219242
pq.QuoteIdentifier(role),
@@ -230,10 +253,18 @@ func grantRoleDefaultPrivileges(txn *sql.Tx, d *schema.ResourceData) error {
230253
}
231254

232255
func revokeRoleDefaultPrivileges(txn *sql.Tx, d *schema.ResourceData) error {
256+
pgSchema := d.Get("schema").(string)
257+
258+
var inSchema string
259+
260+
// If a schema is specified we need to build the part of the query string to action this
261+
if pgSchema != "" {
262+
inSchema = fmt.Sprintf("IN SCHEMA %s", pq.QuoteIdentifier(pgSchema))
263+
}
233264
query := fmt.Sprintf(
234-
"ALTER DEFAULT PRIVILEGES FOR ROLE %s IN SCHEMA %s REVOKE ALL ON %sS FROM %s",
265+
"ALTER DEFAULT PRIVILEGES FOR ROLE %s %s REVOKE ALL ON %sS FROM %s",
235266
pq.QuoteIdentifier(d.Get("owner").(string)),
236-
pq.QuoteIdentifier(d.Get("schema").(string)),
267+
inSchema,
237268
strings.ToUpper(d.Get("object_type").(string)),
238269
pq.QuoteIdentifier(d.Get("role").(string)),
239270
)
@@ -245,8 +276,14 @@ func revokeRoleDefaultPrivileges(txn *sql.Tx, d *schema.ResourceData) error {
245276
}
246277

247278
func generateDefaultPrivilegesID(d *schema.ResourceData) string {
279+
pgSchema := d.Get("schema").(string)
280+
if pgSchema == "" {
281+
pgSchema = "noschema"
282+
}
283+
248284
return strings.Join([]string{
249-
d.Get("role").(string), d.Get("database").(string), d.Get("schema").(string),
285+
d.Get("role").(string), d.Get("database").(string), pgSchema,
250286
d.Get("owner").(string), d.Get("object_type").(string),
251287
}, "_")
288+
252289
}

postgresql/resource_postgresql_default_privileges_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,80 @@ resource "postgresql_default_privileges" "test_ro" {
145145
},
146146
})
147147
}
148+
149+
// Test the case where we define default priviliges without specifying a schema. These
150+
// priviliges should apply to newly created resources for the named role in all schema.
151+
func TestAccPostgresqlDefaultPrivileges_NoSchema(t *testing.T) {
152+
skipIfNotAcc(t)
153+
154+
// We have to create the database outside of resource.Test
155+
// because we need to create a table to assert that grant are correctly applied
156+
// and we don't have this resource yet
157+
dbSuffix, teardown := setupTestDatabase(t, true, true)
158+
defer teardown()
159+
160+
config := getTestConfig(t)
161+
dbName, roleName := getTestDBNames(dbSuffix)
162+
163+
// Set default privileges to the test role then to public (i.e.: everyone)
164+
for _, role := range []string{roleName, "public"} {
165+
t.Run(role, func(t *testing.T) {
166+
167+
hclText := `
168+
resource "postgresql_default_privileges" "test_ro" {
169+
database = "%s"
170+
owner = "%s"
171+
role = "%s"
172+
object_type = "table"
173+
privileges = %%s
174+
}
175+
`
176+
// We set PGUSER as owner as he will create the test table
177+
var tfConfig = fmt.Sprintf(hclText, dbName, config.Username, role)
178+
179+
resource.Test(t, resource.TestCase{
180+
PreCheck: func() {
181+
testAccPreCheck(t)
182+
testCheckCompatibleVersion(t, featurePrivileges)
183+
},
184+
Providers: testAccProviders,
185+
Steps: []resource.TestStep{
186+
{
187+
Config: fmt.Sprintf(tfConfig, `["SELECT"]`),
188+
Check: resource.ComposeTestCheckFunc(
189+
func(*terraform.State) error {
190+
tables := []string{"test_schema.test_table", "dev_schema.test_table"}
191+
// To test default privileges, we need to create tables
192+
// in both dev and test schema after having applied the state.
193+
dropFunc := createTestTables(t, dbSuffix, tables, "")
194+
defer dropFunc()
195+
196+
return testCheckTablesPrivileges(t, dbName, roleName, tables, []string{"SELECT"})
197+
},
198+
resource.TestCheckResourceAttr("postgresql_default_privileges.test_ro", "object_type", "table"),
199+
resource.TestCheckResourceAttr("postgresql_default_privileges.test_ro", "privileges.#", "1"),
200+
resource.TestCheckResourceAttr("postgresql_default_privileges.test_ro", "privileges.3138006342", "SELECT"),
201+
),
202+
},
203+
{
204+
Config: fmt.Sprintf(tfConfig, `["SELECT", "UPDATE"]`),
205+
Check: resource.ComposeTestCheckFunc(
206+
func(*terraform.State) error {
207+
tables := []string{"test_schema.test_table", "dev_schema.test_table"}
208+
// To test default privileges, we need to create tables
209+
// in both dev and test schema after having applied the state.
210+
dropFunc := createTestTables(t, dbSuffix, tables, "")
211+
defer dropFunc()
212+
213+
return testCheckTablesPrivileges(t, dbName, roleName, tables, []string{"SELECT", "UPDATE"})
214+
},
215+
resource.TestCheckResourceAttr("postgresql_default_privileges.test_ro", "privileges.#", "2"),
216+
resource.TestCheckResourceAttr("postgresql_default_privileges.test_ro", "privileges.3138006342", "SELECT"),
217+
resource.TestCheckResourceAttr("postgresql_default_privileges.test_ro", "privileges.1759376126", "UPDATE"),
218+
),
219+
},
220+
},
221+
})
222+
})
223+
}
224+
}

postgresql/resource_postgresql_grant.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ func resourcePostgreSQLGrantCreate(db *DBConnection, d *schema.ResourceData) err
115115
)
116116
}
117117

118+
// Verify schema is set for postgresql_grant
119+
if d.Get("schema").(string) == "" && d.Get("object_type").(string) != "database" {
120+
return fmt.Errorf("parameter 'schema' is mandatory for postgresql_grant resource")
121+
}
122+
118123
if err := validatePrivileges(d); err != nil {
119124
return err
120125
}
@@ -442,7 +447,9 @@ func checkRoleDBSchemaExists(client *Client, d *schema.ResourceData) (bool, erro
442447
return false, nil
443448
}
444449

445-
if d.Get("object_type").(string) != "database" {
450+
pgSchema := d.Get("schema").(string)
451+
452+
if d.Get("object_type").(string) != "database" && pgSchema != "" {
446453
// Connect on this database to check if schema exists
447454
dbTxn, err := startTransaction(client, database)
448455
if err != nil {
@@ -451,7 +458,6 @@ func checkRoleDBSchemaExists(client *Client, d *schema.ResourceData) (bool, erro
451458
defer dbTxn.Rollback()
452459

453460
// Check the schema exists (the SQL connection needs to be on the right database)
454-
pgSchema := d.Get("schema").(string)
455461
exists, err = schemaExists(dbTxn, pgSchema)
456462
if err != nil {
457463
return false, err

postgresql/utils_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ func setupTestDatabase(t *testing.T, createDB, createRole bool) (string, func())
111111
// Create a test schema in this new database and grant usage to rolName
112112
dbExecute(t, config.connStr(dbName), "CREATE SCHEMA test_schema")
113113
dbExecute(t, config.connStr(dbName), fmt.Sprintf("GRANT usage ON SCHEMA test_schema to %s", roleName))
114+
dbExecute(t, config.connStr(dbName), "CREATE SCHEMA dev_schema")
115+
dbExecute(t, config.connStr(dbName), fmt.Sprintf("GRANT usage ON SCHEMA dev_schema to %s", roleName))
114116
}
115117

116118
return suffix, func() {

0 commit comments

Comments
 (0)