diff --git a/CHANGELOG.md b/CHANGELOG.md index 59d19ef4..095a9bc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ ## 1.7.0 (Unreleased) + +FEATURES: + +* `postgresql_grant`: Implement grant on functions + ## 1.6.0 (May 22, 2020) FEATURES: diff --git a/postgresql/helpers.go b/postgresql/helpers.go index c9af129b..e9a2084d 100644 --- a/postgresql/helpers.go +++ b/postgresql/helpers.go @@ -102,6 +102,7 @@ var allowedPrivileges = map[string][]string{ "database": []string{"ALL", "CREATE", "CONNECT", "TEMPORARY", "TEMP"}, "table": []string{"ALL", "SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER"}, "sequence": []string{"ALL", "USAGE", "SELECT", "UPDATE"}, + "function": []string{"ALL", "EXECUTE"}, } // validatePrivileges checks that privileges to apply are allowed for this object type. diff --git a/postgresql/resource_postgresql_default_privileges.go b/postgresql/resource_postgresql_default_privileges.go index 1046b392..0718289d 100644 --- a/postgresql/resource_postgresql_default_privileges.go +++ b/postgresql/resource_postgresql_default_privileges.go @@ -52,8 +52,9 @@ func resourcePostgreSQLDefaultPrivileges() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{ "table", "sequence", + "function", }, false), - Description: "The PostgreSQL object type to set the default privileges on (one of: table, sequence)", + Description: "The PostgreSQL object type to set the default privileges on (one of: table, sequence, function)", }, "privileges": &schema.Schema{ Type: schema.TypeSet, diff --git a/postgresql/resource_postgresql_grant.go b/postgresql/resource_postgresql_grant.go index 6604a81e..b7fc6492 100644 --- a/postgresql/resource_postgresql_grant.go +++ b/postgresql/resource_postgresql_grant.go @@ -17,11 +17,13 @@ var allowedObjectTypes = []string{ "database", "table", "sequence", + "function", } var objectTypes = map[string]string{ "table": "r", "sequence": "S", + "function": "f", } func resourcePostgreSQLGrant() *schema.Resource { @@ -218,16 +220,30 @@ JOIN pg_roles ON grantee = pg_roles.oid WHERE rolname = $2 } func readRolePrivileges(txn *sql.Tx, d *schema.ResourceData) error { - if d.Get("object_type").(string) == "database" { + var query string + object_type := strings.ToUpper(d.Get("object_type").(string)) + switch object_type { + case "DATABASE": return readDatabaseRolePriviges(txn, d) - } - - // This returns, for the specified role (rolname), - // the list of all object of the specified type (relkind) in the specified schema (namespace) - // with the list of the currently applied privileges (aggregation of privilege_type) - // - // Our goal is to check that every object has the same privileges as saved in the state. - query := ` + case "FUNCTION": + query = ` +SELECT pg_proc.proname, array_remove(array_agg(privilege_type), NULL) +FROM pg_proc +JOIN pg_namespace ON pg_namespace.oid = pg_proc.pronamespace +LEFT JOIN ( + select acls.* + from ( + SELECT proname, prokind, pronamespace, (aclexplode(proacl)).* FROM pg_proc + ) acls + JOIN pg_roles on grantee = pg_roles.oid + WHERE rolname = $1 +) privs +USING (proname, pronamespace, prokind) + WHERE nspname = $2 AND prokind = $3 +GROUP BY pg_proc.proname +` + default: + query = ` SELECT pg_class.relname, array_remove(array_agg(privilege_type), NULL) FROM pg_class JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace @@ -240,8 +256,15 @@ LEFT JOIN ( ) privs USING (relname, relnamespace, relkind) WHERE nspname = $2 AND relkind = $3 -GROUP BY pg_class.relname; +GROUP BY pg_class.relname ` + } + + // This returns, for the specified role (rolname), + // the list of all object of the specified type (relkind) in the specified schema (namespace) + // with the list of the currently applied privileges (aggregation of privilege_type) + // + // Our goal is to check that every object has the same privileges as saved in the state. objectType := d.Get("object_type").(string) rows, err := txn.Query( @@ -287,7 +310,7 @@ func createGrantQuery(d *schema.ResourceData, privileges []string) string { pq.QuoteIdentifier(d.Get("database").(string)), pq.QuoteIdentifier(d.Get("role").(string)), ) - case "TABLE", "SEQUENCE": + case "TABLE", "SEQUENCE", "FUNCTION": query = fmt.Sprintf( "GRANT %s ON ALL %sS IN SCHEMA %s TO %s", strings.Join(privileges, ","), @@ -314,7 +337,7 @@ func createRevokeQuery(d *schema.ResourceData) string { pq.QuoteIdentifier(d.Get("database").(string)), pq.QuoteIdentifier(d.Get("role").(string)), ) - case "TABLE", "SEQUENCE": + case "TABLE", "SEQUENCE", "FUNCTION": query = fmt.Sprintf( "REVOKE ALL PRIVILEGES ON ALL %sS IN SCHEMA %s FROM %s", strings.ToUpper(d.Get("object_type").(string)), diff --git a/postgresql/resource_postgresql_grant_test.go b/postgresql/resource_postgresql_grant_test.go index 942fe3a9..3622a33e 100644 --- a/postgresql/resource_postgresql_grant_test.go +++ b/postgresql/resource_postgresql_grant_test.go @@ -37,6 +37,15 @@ func TestCreateGrantQuery(t *testing.T) { privileges: []string{"SELECT"}, expected: fmt.Sprintf("GRANT SELECT ON ALL SEQUENCES IN SCHEMA %s TO %s", pq.QuoteIdentifier(databaseName), pq.QuoteIdentifier(roleName)), }, + { + resource: schema.TestResourceDataRaw(t, resourcePostgreSQLGrant().Schema, map[string]interface{}{ + "object_type": "function", + "schema": databaseName, + "role": roleName, + }), + privileges: []string{"EXECUTE"}, + expected: fmt.Sprintf("GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA %s TO %s", pq.QuoteIdentifier(databaseName), pq.QuoteIdentifier(roleName)), + }, { resource: schema.TestResourceDataRaw(t, resourcePostgreSQLGrant().Schema, map[string]interface{}{ "object_type": "TABLE", diff --git a/website/docs/r/postgresql_default_privileges.html.markdown b/website/docs/r/postgresql_default_privileges.html.markdown index 8534d621..a4e0094a 100644 --- a/website/docs/r/postgresql_default_privileges.html.markdown +++ b/website/docs/r/postgresql_default_privileges.html.markdown @@ -32,5 +32,5 @@ resource "postgresql_default_privileges" "read_only_tables" { * `database` - (Required) The database to grant default privileges for this role. * `owner` - (Required) Role for which apply default privileges (You can change default privileges only for objects that will be created by yourself or by roles that you are a member of). * `schema` - (Required) The database schema to set default privileges for this role. -* `object_type` - (Required) The PostgreSQL object type to set the default privileges on (one of: table, sequence). +* `object_type` - (Required) The PostgreSQL object type to set the default privileges on (one of: table, sequence,function). * `privileges` - (Required) The list of privileges to apply as default privileges. diff --git a/website/docs/r/postgresql_grant.html.markdown b/website/docs/r/postgresql_grant.html.markdown index b09562bf..2e2e634b 100644 --- a/website/docs/r/postgresql_grant.html.markdown +++ b/website/docs/r/postgresql_grant.html.markdown @@ -29,5 +29,5 @@ resource "postgresql_grant" "readonly_tables" { * `role` - (Required) The name of the role to grant privileges on. * `database` - (Required) The database to grant privileges on for this role. * `schema` - (Required) The database schema to grant privileges on for this role. -* `object_type` - (Required) The PostgreSQL object type to grant the privileges on (one of: table, sequence). +* `object_type` - (Required) The PostgreSQL object type to grant the privileges on (one of: table, sequence,function). * `privileges` - (Required) The list of privileges to grant.