diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index 2386cf016f..562dbc90e9 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -147,8 +147,18 @@ get_row_security_policies(Query *root, CmdType commandType, RangeTblEntry *rte, return; } - /* Grab the built-in policies which should be applied to this relation. */ + /* + * RLS is enabled for this relation. + * + * Get the security policies that should be applied, based on the command + * type. Note that if this isn't the target relation, we actually want + * the relation's SELECT policies, regardless of the query command type, + * for example in UPDATE t1 ... FROM t2 we need to apply t1's UPDATE + * policies and t2's SELECT policies. + */ rel = heap_open(rte->relid, NoLock); + if (rt_index != root->resultRelation) + commandType = CMD_SELECT; rowsec_policies = pull_row_security_policies(commandType, rel, user_id); diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index b0556c2ff1..6fc80af30e 100644 --- a/src/test/regress/expected/rowsecurity.out +++ b/src/test/regress/expected/rowsecurity.out @@ -3033,6 +3033,89 @@ CREATE POLICY p ON t USING (max(c)); -- fails: aggregate functions are not allow ERROR: aggregate functions are not allowed in policy expressions ROLLBACK; -- +-- Non-target relations are only subject to SELECT policies +-- +SET SESSION AUTHORIZATION rls_regress_user0; +CREATE TABLE r1 (a int); +CREATE TABLE r2 (a int); +INSERT INTO r1 VALUES (10), (20); +INSERT INTO r2 VALUES (10), (20); +GRANT ALL ON r1, r2 TO rls_regress_user1; +CREATE POLICY p1 ON r1 USING (true); +ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; +CREATE POLICY p1 ON r2 FOR SELECT USING (true); +CREATE POLICY p2 ON r2 FOR INSERT WITH CHECK (false); +CREATE POLICY p3 ON r2 FOR UPDATE USING (false); +CREATE POLICY p4 ON r2 FOR DELETE USING (false); +ALTER TABLE r2 ENABLE ROW LEVEL SECURITY; +SET SESSION AUTHORIZATION rls_regress_user1; +SELECT * FROM r1; + a +---- + 10 + 20 +(2 rows) + +SELECT * FROM r2; + a +---- + 10 + 20 +(2 rows) + +-- r2 is read-only +INSERT INTO r2 VALUES (2); -- Not allowed +ERROR: new row violates row level security policy for "r2" +UPDATE r2 SET a = 2 RETURNING *; -- Updates nothing + a +--- +(0 rows) + +DELETE FROM r2 RETURNING *; -- Deletes nothing + a +--- +(0 rows) + +-- r2 can be used as a non-target relation in DML +INSERT INTO r1 SELECT a + 1 FROM r2 RETURNING *; -- OK + a +---- + 11 + 21 +(2 rows) + +UPDATE r1 SET a = r2.a + 2 FROM r2 WHERE r1.a = r2.a RETURNING *; -- OK + a | a +----+---- + 12 | 10 + 22 | 20 +(2 rows) + +DELETE FROM r1 USING r2 WHERE r1.a = r2.a + 2 RETURNING *; -- OK + a | a +----+---- + 12 | 10 + 22 | 20 +(2 rows) + +SELECT * FROM r1; + a +---- + 11 + 21 +(2 rows) + +SELECT * FROM r2; + a +---- + 10 + 20 +(2 rows) + +SET SESSION AUTHORIZATION rls_regress_user0; +DROP TABLE r1; +DROP TABLE r2; +-- -- Clean up objects -- RESET SESSION AUTHORIZATION; diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql index 300f34ad4b..e8c09e9043 100644 --- a/src/test/regress/sql/rowsecurity.sql +++ b/src/test/regress/sql/rowsecurity.sql @@ -1298,6 +1298,46 @@ CREATE TABLE t (c) AS VALUES ('bar'::text); CREATE POLICY p ON t USING (max(c)); -- fails: aggregate functions are not allowed in policy expressions ROLLBACK; +-- +-- Non-target relations are only subject to SELECT policies +-- +SET SESSION AUTHORIZATION rls_regress_user0; +CREATE TABLE r1 (a int); +CREATE TABLE r2 (a int); +INSERT INTO r1 VALUES (10), (20); +INSERT INTO r2 VALUES (10), (20); + +GRANT ALL ON r1, r2 TO rls_regress_user1; + +CREATE POLICY p1 ON r1 USING (true); +ALTER TABLE r1 ENABLE ROW LEVEL SECURITY; + +CREATE POLICY p1 ON r2 FOR SELECT USING (true); +CREATE POLICY p2 ON r2 FOR INSERT WITH CHECK (false); +CREATE POLICY p3 ON r2 FOR UPDATE USING (false); +CREATE POLICY p4 ON r2 FOR DELETE USING (false); +ALTER TABLE r2 ENABLE ROW LEVEL SECURITY; + +SET SESSION AUTHORIZATION rls_regress_user1; +SELECT * FROM r1; +SELECT * FROM r2; + +-- r2 is read-only +INSERT INTO r2 VALUES (2); -- Not allowed +UPDATE r2 SET a = 2 RETURNING *; -- Updates nothing +DELETE FROM r2 RETURNING *; -- Deletes nothing + +-- r2 can be used as a non-target relation in DML +INSERT INTO r1 SELECT a + 1 FROM r2 RETURNING *; -- OK +UPDATE r1 SET a = r2.a + 2 FROM r2 WHERE r1.a = r2.a RETURNING *; -- OK +DELETE FROM r1 USING r2 WHERE r1.a = r2.a + 2 RETURNING *; -- OK +SELECT * FROM r1; +SELECT * FROM r2; + +SET SESSION AUTHORIZATION rls_regress_user0; +DROP TABLE r1; +DROP TABLE r2; + -- -- Clean up objects --