1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-05 07:21:24 +03:00

Perform RLS WITH CHECK before constraints, etc

The RLS capability is built on top of the WITH CHECK OPTION
system which was added for auto-updatable views, however, unlike
WCOs on views (which are mandated by the SQL spec to not fire until
after all other constraints and checks are done), it makes much more
sense for RLS checks to happen earlier than constraint and uniqueness
checks.

This patch reworks the structure which holds the WCOs a bit to be
explicitly either VIEW or RLS checks and the RLS-related checks are
done prior to the constraint and uniqueness checks.  This also allows
better error reporting as we are now reporting when a violation is due
to a WITH CHECK OPTION and when it's due to an RLS policy violation,
which was independently noted by Craig Ringer as being confusing.

The documentation is also updated to include a paragraph about when RLS
WITH CHECK handling is performed, as there have been a number of
questions regarding that and the documentation was previously silent on
the matter.

Author: Dean Rasheed, with some kabitzing and comment changes by me.
This commit is contained in:
Stephen Frost
2015-04-24 20:34:26 -04:00
parent c8aa893862
commit e89bd02f58
15 changed files with 174 additions and 55 deletions

View File

@ -57,7 +57,7 @@ SELECT * FROM rls_test_permissive;
INSERT INTO rls_test_permissive VALUES ('r1','s1',10);
-- failure
INSERT INTO rls_test_permissive VALUES ('r4','s4',10);
ERROR: new row violates WITH CHECK OPTION for "rls_test_permissive"
ERROR: new row violates row level security policy for "rls_test_permissive"
SET ROLE s1;
-- With only the hook's policies, restrictive
-- hook's policy is current_user = supervisor
@ -78,7 +78,7 @@ SELECT * FROM rls_test_restrictive;
INSERT INTO rls_test_restrictive VALUES ('r1','s1',10);
-- failure
INSERT INTO rls_test_restrictive VALUES ('r4','s4',10);
ERROR: new row violates WITH CHECK OPTION for "rls_test_restrictive"
ERROR: new row violates row level security policy for "rls_test_restrictive"
SET ROLE s1;
-- With only the hook's policies, both
-- permissive hook's policy is current_user = username
@ -100,13 +100,13 @@ SELECT * FROM rls_test_both;
-- failure
INSERT INTO rls_test_both VALUES ('r1','s1',10);
ERROR: new row violates WITH CHECK OPTION for "rls_test_both"
ERROR: new row violates row level security policy for "rls_test_both"
-- failure
INSERT INTO rls_test_both VALUES ('r4','s1',10);
ERROR: new row violates WITH CHECK OPTION for "rls_test_both"
ERROR: new row violates row level security policy for "rls_test_both"
-- failure
INSERT INTO rls_test_both VALUES ('r4','s4',10);
ERROR: new row violates WITH CHECK OPTION for "rls_test_both"
ERROR: new row violates row level security policy for "rls_test_both"
RESET ROLE;
-- Create "internal" policies, to check that the policies from
-- the hooks are combined correctly.
@ -136,7 +136,7 @@ INSERT INTO rls_test_permissive VALUES ('r1','s1',7);
INSERT INTO rls_test_permissive VALUES ('r3','s3',10);
-- failure
INSERT INTO rls_test_permissive VALUES ('r4','s4',7);
ERROR: new row violates WITH CHECK OPTION for "rls_test_permissive"
ERROR: new row violates row level security policy for "rls_test_permissive"
SET ROLE s1;
-- With both internal and hook policies, restrictive
EXPLAIN (costs off) SELECT * FROM rls_test_restrictive;
@ -158,13 +158,13 @@ SELECT * FROM rls_test_restrictive;
INSERT INTO rls_test_restrictive VALUES ('r1','s1',8);
-- failure
INSERT INTO rls_test_restrictive VALUES ('r3','s3',10);
ERROR: new row violates WITH CHECK OPTION for "rls_test_restrictive"
ERROR: new row violates row level security policy for "rls_test_restrictive"
-- failure
INSERT INTO rls_test_restrictive VALUES ('r1','s1',7);
ERROR: new row violates WITH CHECK OPTION for "rls_test_restrictive"
ERROR: new row violates row level security policy for "rls_test_restrictive"
-- failure
INSERT INTO rls_test_restrictive VALUES ('r4','s4',7);
ERROR: new row violates WITH CHECK OPTION for "rls_test_restrictive"
ERROR: new row violates row level security policy for "rls_test_restrictive"
-- With both internal and hook policies, both permissive
-- and restrictive hook policies
EXPLAIN (costs off) SELECT * FROM rls_test_both;
@ -185,13 +185,13 @@ SELECT * FROM rls_test_both;
INSERT INTO rls_test_both VALUES ('r1','s1',8);
-- failure
INSERT INTO rls_test_both VALUES ('r3','s3',10);
ERROR: new row violates WITH CHECK OPTION for "rls_test_both"
ERROR: new row violates row level security policy for "rls_test_both"
-- failure
INSERT INTO rls_test_both VALUES ('r1','s1',7);
ERROR: new row violates WITH CHECK OPTION for "rls_test_both"
ERROR: new row violates row level security policy for "rls_test_both"
-- failure
INSERT INTO rls_test_both VALUES ('r4','s4',7);
ERROR: new row violates WITH CHECK OPTION for "rls_test_both"
ERROR: new row violates row level security policy for "rls_test_both"
RESET ROLE;
DROP TABLE rls_test_restrictive;
DROP TABLE rls_test_permissive;

View File

@ -300,6 +300,11 @@ SELECT * FROM document WHERE did = 8; -- and confirm we can't see it
-----+-----+--------+---------+--------
(0 rows)
-- RLS policies are checked before constraints
INSERT INTO document VALUES (8, 44, 1, 'rls_regress_user2', 'my third manga'); -- Should fail with RLS check violation, not duplicate key violation
ERROR: new row violates row level security policy for "document"
UPDATE document SET did = 8, dauthor = 'rls_regress_user2' WHERE did = 5; -- Should fail with RLS check violation, not duplicate key violation
ERROR: new row violates row level security policy for "document"
-- database superuser does bypass RLS policy when enabled
RESET SESSION AUTHORIZATION;
SET row_security TO ON;
@ -1426,9 +1431,9 @@ NOTICE: f_leak => d3d9446802a44259755d38e6d163e820
(5 rows)
INSERT INTO bv1 VALUES (-1, 'xxx'); -- should fail view WCO
ERROR: new row violates WITH CHECK OPTION for "b1"
ERROR: new row violates row level security policy for "b1"
INSERT INTO bv1 VALUES (11, 'xxx'); -- should fail RLS check
ERROR: new row violates WITH CHECK OPTION for "b1"
ERROR: new row violates row level security policy for "b1"
INSERT INTO bv1 VALUES (12, 'xxx'); -- ok
EXPLAIN (COSTS OFF) UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak(b);
QUERY PLAN
@ -1988,7 +1993,7 @@ EXPLAIN (COSTS OFF) WITH cte1 AS (SELECT * FROM t1 WHERE f_leak(b)) SELECT * FRO
(6 rows)
WITH cte1 AS (UPDATE t1 SET a = a + 1 RETURNING *) SELECT * FROM cte1; --fail
ERROR: new row violates WITH CHECK OPTION for "t1"
ERROR: new row violates row level security policy for "t1"
WITH cte1 AS (UPDATE t1 SET a = a RETURNING *) SELECT * FROM cte1; --ok
a | b
----+----------------------------------
@ -2006,7 +2011,7 @@ WITH cte1 AS (UPDATE t1 SET a = a RETURNING *) SELECT * FROM cte1; --ok
(11 rows)
WITH cte1 AS (INSERT INTO t1 VALUES (21, 'Fail') RETURNING *) SELECT * FROM cte1; --fail
ERROR: new row violates WITH CHECK OPTION for "t1"
ERROR: new row violates row level security policy for "t1"
WITH cte1 AS (INSERT INTO t1 VALUES (20, 'Success') RETURNING *) SELECT * FROM cte1; --ok
a | b
----+---------

View File

@ -146,6 +146,10 @@ SET SESSION AUTHORIZATION rls_regress_user1;
INSERT INTO document VALUES (8, 44, 1, 'rls_regress_user1', 'my third manga'); -- Must fail with unique violation, revealing presence of did we can't see
SELECT * FROM document WHERE did = 8; -- and confirm we can't see it
-- RLS policies are checked before constraints
INSERT INTO document VALUES (8, 44, 1, 'rls_regress_user2', 'my third manga'); -- Should fail with RLS check violation, not duplicate key violation
UPDATE document SET did = 8, dauthor = 'rls_regress_user2' WHERE did = 5; -- Should fail with RLS check violation, not duplicate key violation
-- database superuser does bypass RLS policy when enabled
RESET SESSION AUTHORIZATION;
SET row_security TO ON;