From ab2bb5e227255679e42cb80eb4853c5d60ac6aa1 Mon Sep 17 00:00:00 2001 From: Majid Burney Date: Tue, 30 Aug 2022 17:14:55 -0700 Subject: [PATCH 1/2] fix: Use pg catalog tables for column grant info --- postgresql/resource_postgresql_grant.go | 61 ++++++++----------------- 1 file changed, 18 insertions(+), 43 deletions(-) diff --git a/postgresql/resource_postgresql_grant.go b/postgresql/resource_postgresql_grant.go index d7b3ad17..5fc9f887 100644 --- a/postgresql/resource_postgresql_grant.go +++ b/postgresql/resource_postgresql_grant.go @@ -39,7 +39,7 @@ func resourcePostgreSQLGrant() *schema.Resource { Create: PGResourceFunc(resourcePostgreSQLGrantCreate), // Since all of this resource's arguments force a recreation // there's no need for an Update function - //Update: + // Update: Read: PGResourceFunc(resourcePostgreSQLGrantRead), Delete: PGResourceFunc(resourcePostgreSQLGrantDelete), @@ -325,52 +325,27 @@ func readColumnRolePrivileges(txn *sql.Tx, d *schema.ResourceData) error { missingColumns := d.Get("columns").(*schema.Set) // Getting columns from state. // If the query returns a column, it is a removed from the missingColumns. - roleOID, err := getRoleOID(txn, d.Get("role").(string)) - if err != nil { - return err - } - var rows *sql.Rows - // The following query is made up of 3 parts - // The first one simply aggregates all privileges on one column in one table into one line. - // The second part fetches all permissions on all columns for a given user & a given table in a give schema. - // The third part fetches all table-level permissions for the aforementioned table. - // Subtracting the third part from the second part allows us - // to get column-level privileges without those created by table-level privileges. + // The attacl column of pg_attribute contains information only about explicit column grants query := ` -SELECT table_name, column_name, array_agg(privilege_type) AS column_privileges -FROM ( - SELECT table_name, column_name, privilege_type - FROM information_schema.column_privileges - WHERE - grantee = $1 - AND - table_schema = $2 - AND - table_name = $3 - AND - privilege_type = $6 - EXCEPT - SELECT pg_class.relname, pg_attribute.attname, privilege_type AS table_grant - FROM pg_class - JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace - LEFT JOIN ( - SELECT acls.* - FROM - (SELECT relname, relnamespace, relkind, (aclexplode(relacl)).* FROM pg_class c) as acls - WHERE grantee=$4 - ) privs - USING (relname, relnamespace, relkind) - LEFT JOIN pg_attribute ON pg_class.oid = pg_attribute.attrelid - WHERE nspname = $2 AND relkind = $5 - ) -AS col_privs_without_table_privs -GROUP BY col_privs_without_table_privs.table_name, col_privs_without_table_privs.column_name, col_privs_without_table_privs.privilege_type -ORDER BY col_privs_without_table_privs.column_name +SELECT relname AS table_name, attname AS column_name, array_agg(privilege_type) AS column_privileges +FROM (SELECT relname, attname, (aclexplode(attacl)).* + FROM pg_class + JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid + JOIN pg_attribute ON pg_class.oid = attrelid + WHERE nspname = $2 + AND relname = $3 + AND relkind = $4) + AS col_privs + JOIN pg_roles ON pg_roles.oid = col_privs.grantee +WHERE rolname = $1 + AND privilege_type = $5 +GROUP BY col_privs.relname, col_privs.attname, col_privs.privilege_type +ORDER BY col_privs.attname ;` - rows, err = txn.Query( - query, d.Get("role").(string), d.Get("schema"), objects.List()[0], roleOID, objectTypes["table"], d.Get("privileges").(*schema.Set).List()[0], + rows, err := txn.Query( + query, d.Get("role").(string), d.Get("schema"), objects.List()[0], objectTypes["table"], d.Get("privileges").(*schema.Set).List()[0], ) if err != nil { From 8d37b78358cf4070f484bee1c00933ba22942c60 Mon Sep 17 00:00:00 2001 From: Majid Burney Date: Tue, 25 Oct 2022 12:46:46 -0700 Subject: [PATCH 2/2] fix: Quote identifiers in column grant/revoke statements --- postgresql/helpers.go | 8 ++++++++ postgresql/resource_postgresql_grant.go | 8 ++------ postgresql/resource_postgresql_grant_test.go | 4 ++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/postgresql/helpers.go b/postgresql/helpers.go index 5b48abe0..33c6f389 100644 --- a/postgresql/helpers.go +++ b/postgresql/helpers.go @@ -285,6 +285,14 @@ func setToPgIdentList(schema string, idents *schema.Set) string { return strings.Join(quotedIdents, ",") } +func setToPgIdentListWithoutSchema(idents *schema.Set) string { + quotedIdents := make([]string, idents.Len()) + for i, ident := range idents.List() { + quotedIdents[i] = pq.QuoteIdentifier(ident.(string)) + } + return strings.Join(quotedIdents, ",") +} + func setToPgIdentSimpleList(idents *schema.Set) string { quotedIdents := make([]string, idents.Len()) for i, ident := range idents.List() { diff --git a/postgresql/resource_postgresql_grant.go b/postgresql/resource_postgresql_grant.go index 5fc9f887..34fa205f 100644 --- a/postgresql/resource_postgresql_grant.go +++ b/postgresql/resource_postgresql_grant.go @@ -540,14 +540,10 @@ func createGrantQuery(d *schema.ResourceData, privileges []string) string { ) case "COLUMN": objects := d.Get("objects").(*schema.Set) - columns := []string{} - for _, col := range d.Get("columns").(*schema.Set).List() { - columns = append(columns, col.(string)) - } query = fmt.Sprintf( "GRANT %s (%s) ON TABLE %s TO %s", strings.Join(privileges, ","), - strings.Join(columns, ","), + setToPgIdentListWithoutSchema(d.Get("columns").(*schema.Set)), setToPgIdentList(d.Get("schema").(string), objects), pq.QuoteIdentifier(d.Get("role").(string)), ) @@ -620,7 +616,7 @@ func createRevokeQuery(d *schema.ResourceData) string { query = fmt.Sprintf( "REVOKE %s (%s) ON TABLE %s FROM %s", setToPgIdentSimpleList(privileges), - setToPgIdentSimpleList(columns), + setToPgIdentListWithoutSchema(columns), setToPgIdentList(d.Get("schema").(string), objects), pq.QuoteIdentifier(d.Get("role").(string)), ) diff --git a/postgresql/resource_postgresql_grant_test.go b/postgresql/resource_postgresql_grant_test.go index 743618e9..e89ec312 100644 --- a/postgresql/resource_postgresql_grant_test.go +++ b/postgresql/resource_postgresql_grant_test.go @@ -125,7 +125,7 @@ func TestCreateGrantQuery(t *testing.T) { "role": roleName, }), privileges: []string{"SELECT"}, - expected: fmt.Sprintf(`GRANT SELECT (col2,col1) ON TABLE %[1]s."o1" TO %s`, pq.QuoteIdentifier(databaseName), pq.QuoteIdentifier(roleName)), + expected: fmt.Sprintf(`GRANT SELECT (%[2]s,%[3]s) ON TABLE %[1]s."o1" TO %[4]s`, pq.QuoteIdentifier(databaseName), pq.QuoteIdentifier("col2"), pq.QuoteIdentifier("col1"), pq.QuoteIdentifier(roleName)), }, { resource: schema.TestResourceDataRaw(t, resourcePostgreSQLGrant().Schema, map[string]interface{}{ @@ -270,7 +270,7 @@ func TestCreateRevokeQuery(t *testing.T) { "role": roleName, "privileges": []interface{}{"SELECT"}, }), - expected: fmt.Sprintf(`REVOKE SELECT (col2,col1) ON TABLE %[1]s."o1" FROM %s`, pq.QuoteIdentifier(databaseName), pq.QuoteIdentifier(roleName)), + expected: fmt.Sprintf(`REVOKE SELECT ("col2","col1") ON TABLE %[1]s."o1" FROM %s`, pq.QuoteIdentifier(databaseName), pq.QuoteIdentifier(roleName)), }, { resource: schema.TestResourceDataRaw(t, resourcePostgreSQLGrant().Schema, map[string]interface{}{