* Alvaro Herrera (alvhe...@2ndquadrant.com) wrote: > Stephen Frost wrote: > > Still, I'll get a patch worked up for it and then we can discuss the > > merits of that patch going in to 9.5 now versus just into HEAD. > > Cool.
While working on the DROP OWNED BY patch, and part of what took me a bit longer with it, I came to the realiziation that ALTER POLICY wasn't handling dependencies quite right. All of the policy's dependencies would be dropped, but then only those objects referred to in the ALTER POLICY command would have dependencies recreated for them. The attached patch fixes that (using the same approach that I used in the DROP OWNED BY patch). Comments welcome, as always. I'll plan to apply these two patches in a couple of days. Thanks! Stephen
From c3d396f0ca6b5caedb96cd3d00f15e271fda08a3 Mon Sep 17 00:00:00 2001 From: Stephen Frost <sfr...@snowman.net> Date: Fri, 4 Dec 2015 14:01:23 -0500 Subject: [PATCH] Handle dependencies properly in ALTER POLICY ALTER POLICY hadn't fully considered partial policy alternation (eg: change just the roles on the policy, or just change one of the expressions) when rebuilding the dependencies. Instead, it would happily remove all dependencies which existed for the policy and then only recreate the dependencies for the objects referred to in the specific ALTER POLICY command. Correct that by extracting and building the dependencies for all objects referenced by the policy, regardless of if they were provided as part of the ALTER POLICY command or were already in place as part of the pre-existing policy. --- src/backend/commands/policy.c | 97 +++++++++++++++++++++++++++++++ src/test/regress/expected/rowsecurity.out | 43 ++++++++++++++ src/test/regress/sql/rowsecurity.sql | 31 ++++++++++ 3 files changed, 171 insertions(+) diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c index 8851fe7..2db8125 100644 --- a/src/backend/commands/policy.c +++ b/src/backend/commands/policy.c @@ -766,6 +766,35 @@ AlterPolicy(AlterPolicyStmt *stmt) replaces[Anum_pg_policy_polroles - 1] = true; values[Anum_pg_policy_polroles - 1] = PointerGetDatum(role_ids); } + else + { + Oid *roles; + Datum roles_datum; + bool attr_isnull; + ArrayType *policy_roles; + + /* + * We need to pull the set of roles this policy applies to from + * what's in the catalog, so that we can recreate the dependencies + * correctly for the policy. + */ + + roles_datum = heap_getattr(policy_tuple, Anum_pg_policy_polroles, + RelationGetDescr(pg_policy_rel), + &attr_isnull); + Assert(!attr_isnull); + + policy_roles = DatumGetArrayTypePCopy(roles_datum); + + roles = (Oid *) ARR_DATA_PTR(policy_roles); + + nitems = ARR_DIMS(policy_roles)[0]; + + role_oids = (Datum *) palloc(nitems * sizeof(Datum)); + + for (i = 0; i < nitems; i++) + role_oids[i] = ObjectIdGetDatum(roles[i]); + } if (qual != NULL) { @@ -773,6 +802,40 @@ AlterPolicy(AlterPolicyStmt *stmt) values[Anum_pg_policy_polqual - 1] = CStringGetTextDatum(nodeToString(qual)); } + else + { + Datum value_datum; + bool attr_isnull; + + /* + * We need to pull the USING expression and build the range table for + * the policy from what's in the catalog, so that we can recreate + * the dependencies correctly for the policy. + */ + + /* Check if the policy has a USING expr */ + value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polqual, + RelationGetDescr(pg_policy_rel), + &attr_isnull); + if (!attr_isnull) + { + char *qual_value; + ParseState *qual_pstate = make_parsestate(NULL); + + /* parsestate is built just to build the range table */ + qual_pstate = make_parsestate(NULL); + + qual_value = TextDatumGetCString(value_datum); + qual = stringToNode(qual_value); + + /* Add this rel to the parsestate's rangetable, for dependencies */ + addRangeTableEntryForRelation(qual_pstate, target_table, NULL, + false, false); + + qual_parse_rtable = qual_pstate->p_rtable; + free_parsestate(qual_pstate); + } + } if (with_check_qual != NULL) { @@ -780,6 +843,40 @@ AlterPolicy(AlterPolicyStmt *stmt) values[Anum_pg_policy_polwithcheck - 1] = CStringGetTextDatum(nodeToString(with_check_qual)); } + else + { + Datum value_datum; + bool attr_isnull; + + /* + * We need to pull the WITH CHECK expression and build the range table + * for the policy from what's in the catalog, so that we can recreate + * the dependencies correctly for the policy. + */ + + /* Check if the policy has a WITH CHECK expr */ + value_datum = heap_getattr(policy_tuple, Anum_pg_policy_polwithcheck, + RelationGetDescr(pg_policy_rel), + &attr_isnull); + if (!attr_isnull) + { + char *with_check_value; + ParseState *with_check_pstate = make_parsestate(NULL); + + /* parsestate is built just to build the range table */ + with_check_pstate = make_parsestate(NULL); + + with_check_value = TextDatumGetCString(value_datum); + with_check_qual = stringToNode(with_check_value); + + /* Add this rel to the parsestate's rangetable, for dependencies */ + addRangeTableEntryForRelation(with_check_pstate, target_table, NULL, + false, false); + + with_check_parse_rtable = with_check_pstate->p_rtable; + free_parsestate(with_check_pstate); + } + } new_tuple = heap_modify_tuple(policy_tuple, RelationGetDescr(pg_policy_rel), diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index 0f91ebb..e7b6ff4 100644 --- a/src/test/regress/expected/rowsecurity.out +++ b/src/test/regress/expected/rowsecurity.out @@ -3246,6 +3246,49 @@ SET row_security = on; UPDATE r1 SET a = 30 RETURNING *; ERROR: new row violates row-level security policy for table "r1" DROP TABLE r1; +-- Check dependency handling +RESET SESSION AUTHORIZATION; +CREATE TABLE dep1 (c1 int); +CREATE TABLE dep2 (c1 int); +CREATE POLICY dep_p1 ON dep1 TO rls_regress_user1 USING (c1 > (select max(dep2.c1) from dep2)); +ALTER POLICY dep_p1 ON dep1 TO rls_regress_user1,rls_regress_user2; +-- Should return one +SELECT count(*) = 1 FROM pg_depend + WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1') + AND refobjid = (SELECT oid FROM pg_class WHERE relname = 'dep2'); + ?column? +---------- + t +(1 row) + +ALTER POLICY dep_p1 ON dep1 USING (true); +-- Should return one +SELECT count(*) = 1 FROM pg_shdepend + WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1') + AND refobjid = (SELECT oid FROM pg_authid WHERE rolname = 'rls_regress_user1'); + ?column? +---------- + t +(1 row) + +-- Should return one +SELECT count(*) = 1 FROM pg_shdepend + WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1') + AND refobjid = (SELECT oid FROM pg_authid WHERE rolname = 'rls_regress_user2'); + ?column? +---------- + t +(1 row) + +-- Should return zero +SELECT count(*) = 0 FROM pg_depend + WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1') + AND refobjid = (SELECT oid FROM pg_class WHERE relname = 'dep2'); + ?column? +---------- + t +(1 row) + -- -- Clean up objects -- diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql index b230a0f..b06a206 100644 --- a/src/test/regress/sql/rowsecurity.sql +++ b/src/test/regress/sql/rowsecurity.sql @@ -1490,6 +1490,37 @@ UPDATE r1 SET a = 30 RETURNING *; DROP TABLE r1; +-- Check dependency handling +RESET SESSION AUTHORIZATION; +CREATE TABLE dep1 (c1 int); +CREATE TABLE dep2 (c1 int); + +CREATE POLICY dep_p1 ON dep1 TO rls_regress_user1 USING (c1 > (select max(dep2.c1) from dep2)); +ALTER POLICY dep_p1 ON dep1 TO rls_regress_user1,rls_regress_user2; + +-- Should return one +SELECT count(*) = 1 FROM pg_depend + WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1') + AND refobjid = (SELECT oid FROM pg_class WHERE relname = 'dep2'); + +ALTER POLICY dep_p1 ON dep1 USING (true); + +-- Should return one +SELECT count(*) = 1 FROM pg_shdepend + WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1') + AND refobjid = (SELECT oid FROM pg_authid WHERE rolname = 'rls_regress_user1'); + +-- Should return one +SELECT count(*) = 1 FROM pg_shdepend + WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1') + AND refobjid = (SELECT oid FROM pg_authid WHERE rolname = 'rls_regress_user2'); + +-- Should return zero +SELECT count(*) = 0 FROM pg_depend + WHERE objid = (SELECT oid FROM pg_policy WHERE polname = 'dep_p1') + AND refobjid = (SELECT oid FROM pg_class WHERE relname = 'dep2'); + + -- -- Clean up objects -- -- 2.5.0
signature.asc
Description: Digital signature