mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
ALTER TABLE .. FORCE ROW LEVEL SECURITY
To allow users to force RLS to always be applied, even for table owners, add ALTER TABLE .. FORCE ROW LEVEL SECURITY. row_security=off overrides FORCE ROW LEVEL SECURITY, to ensure pg_dump output is complete (by default). Also add SECURITY_NOFORCE_RLS context to avoid data corruption when ALTER TABLE .. FORCE ROW SECURITY is being used. The SECURITY_NOFORCE_RLS security context is used only during referential integrity checks and is only considered in check_enable_rls() after we have already checked that the current user is the owner of the relation (which should always be the case during referential integrity checks). Back-patch to 9.5 where RLS was added.
This commit is contained in:
@ -275,6 +275,12 @@ get_altertable_subcmdtypes(PG_FUNCTION_ARGS)
|
||||
case AT_DisableRowSecurity:
|
||||
strtype = "DISABLE ROW SECURITY";
|
||||
break;
|
||||
case AT_ForceRowSecurity:
|
||||
strtype = "FORCE ROW SECURITY";
|
||||
break;
|
||||
case AT_NoForceRowSecurity:
|
||||
strtype = "NO FORCE ROW SECURITY";
|
||||
break;
|
||||
case AT_GenericOptions:
|
||||
strtype = "SET OPTIONS";
|
||||
break;
|
||||
|
@ -3009,6 +3009,155 @@ SET SESSION AUTHORIZATION rls_regress_user0;
|
||||
DROP TABLE r1;
|
||||
DROP TABLE r2;
|
||||
--
|
||||
-- FORCE ROW LEVEL SECURITY applies RLS to owners but
|
||||
-- only when row_security = on
|
||||
--
|
||||
SET SESSION AUTHORIZATION rls_regress_user0;
|
||||
SET row_security = on;
|
||||
CREATE TABLE r1 (a int);
|
||||
INSERT INTO r1 VALUES (10), (20);
|
||||
CREATE POLICY p1 ON r1 USING (false);
|
||||
ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
|
||||
-- No error, but no rows
|
||||
TABLE r1;
|
||||
a
|
||||
---
|
||||
(0 rows)
|
||||
|
||||
-- RLS error
|
||||
INSERT INTO r1 VALUES (1);
|
||||
ERROR: new row violates row level security policy for "r1"
|
||||
-- No error (unable to see any rows to update)
|
||||
UPDATE r1 SET a = 1;
|
||||
TABLE r1;
|
||||
a
|
||||
---
|
||||
(0 rows)
|
||||
|
||||
-- No error (unable to see any rows to delete)
|
||||
DELETE FROM r1;
|
||||
TABLE r1;
|
||||
a
|
||||
---
|
||||
(0 rows)
|
||||
|
||||
SET row_security = off;
|
||||
-- Shows all rows
|
||||
TABLE r1;
|
||||
a
|
||||
----
|
||||
10
|
||||
20
|
||||
(2 rows)
|
||||
|
||||
-- Update all rows
|
||||
UPDATE r1 SET a = 1;
|
||||
TABLE r1;
|
||||
a
|
||||
---
|
||||
1
|
||||
1
|
||||
(2 rows)
|
||||
|
||||
-- Delete all rows
|
||||
DELETE FROM r1;
|
||||
TABLE r1;
|
||||
a
|
||||
---
|
||||
(0 rows)
|
||||
|
||||
DROP TABLE r1;
|
||||
--
|
||||
-- FORCE ROW LEVEL SECURITY does not break RI
|
||||
--
|
||||
SET SESSION AUTHORIZATION rls_regress_user0;
|
||||
SET row_security = on;
|
||||
CREATE TABLE r1 (a int PRIMARY KEY);
|
||||
CREATE TABLE r2 (a int REFERENCES r1);
|
||||
INSERT INTO r1 VALUES (10), (20);
|
||||
INSERT INTO r2 VALUES (10), (20);
|
||||
-- Create policies on r2 which prevent the
|
||||
-- owner from seeing any rows, but RI should
|
||||
-- still see them.
|
||||
CREATE POLICY p1 ON r2 USING (false);
|
||||
ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
|
||||
-- Errors due to rows in r2
|
||||
DELETE FROM r1;
|
||||
ERROR: update or delete on table "r1" violates foreign key constraint "r2_a_fkey" on table "r2"
|
||||
DETAIL: Key (a)=(10) is still referenced from table "r2".
|
||||
-- Reset r2 to no-RLS
|
||||
DROP POLICY p1 ON r2;
|
||||
ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
|
||||
ALTER TABLE r2 DISABLE ROW LEVEL SECURITY;
|
||||
-- clean out r2 for INSERT test below
|
||||
DELETE FROM r2;
|
||||
-- Change r1 to not allow rows to be seen
|
||||
CREATE POLICY p1 ON r1 USING (false);
|
||||
ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
|
||||
-- No rows seen
|
||||
TABLE r1;
|
||||
a
|
||||
---
|
||||
(0 rows)
|
||||
|
||||
-- No error, RI still sees that row exists in r1
|
||||
INSERT INTO r2 VALUES (10);
|
||||
DROP TABLE r2;
|
||||
DROP TABLE r1;
|
||||
-- Ensure cascaded DELETE works
|
||||
CREATE TABLE r1 (a int PRIMARY KEY);
|
||||
CREATE TABLE r2 (a int REFERENCES r1 ON DELETE CASCADE);
|
||||
INSERT INTO r1 VALUES (10), (20);
|
||||
INSERT INTO r2 VALUES (10), (20);
|
||||
-- Create policies on r2 which prevent the
|
||||
-- owner from seeing any rows, but RI should
|
||||
-- still see them.
|
||||
CREATE POLICY p1 ON r2 USING (false);
|
||||
ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
|
||||
-- Deletes all records from both
|
||||
DELETE FROM r1;
|
||||
-- Remove FORCE from r2
|
||||
ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
|
||||
-- As owner, we now bypass RLS
|
||||
-- verify no rows in r2 now
|
||||
TABLE r2;
|
||||
a
|
||||
---
|
||||
(0 rows)
|
||||
|
||||
DROP TABLE r2;
|
||||
DROP TABLE r1;
|
||||
-- Ensure cascaded UPDATE works
|
||||
CREATE TABLE r1 (a int PRIMARY KEY);
|
||||
CREATE TABLE r2 (a int REFERENCES r1 ON UPDATE CASCADE);
|
||||
INSERT INTO r1 VALUES (10), (20);
|
||||
INSERT INTO r2 VALUES (10), (20);
|
||||
-- Create policies on r2 which prevent the
|
||||
-- owner from seeing any rows, but RI should
|
||||
-- still see them.
|
||||
CREATE POLICY p1 ON r2 USING (false);
|
||||
ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
|
||||
-- Updates records in both
|
||||
UPDATE r1 SET a = a+5;
|
||||
-- Remove FORCE from r2
|
||||
ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
|
||||
-- As owner, we now bypass RLS
|
||||
-- verify records in r2 updated
|
||||
TABLE r2;
|
||||
a
|
||||
----
|
||||
15
|
||||
25
|
||||
(2 rows)
|
||||
|
||||
DROP TABLE r2;
|
||||
DROP TABLE r1;
|
||||
--
|
||||
-- Clean up objects
|
||||
--
|
||||
RESET SESSION AUTHORIZATION;
|
||||
@ -3031,3 +3180,10 @@ CREATE POLICY p1 ON rls_tbl USING (c1 > 5);
|
||||
CREATE POLICY p2 ON rls_tbl FOR SELECT USING (c1 <= 3);
|
||||
CREATE POLICY p3 ON rls_tbl FOR UPDATE USING (c1 <= 3) WITH CHECK (c1 > 5);
|
||||
CREATE POLICY p4 ON rls_tbl FOR DELETE USING (c1 <= 3);
|
||||
CREATE TABLE rls_tbl_force (c1 int);
|
||||
ALTER TABLE rls_tbl_force ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE rls_tbl_force FORCE ROW LEVEL SECURITY;
|
||||
CREATE POLICY p1 ON rls_tbl_force USING (c1 = 5) WITH CHECK (c1 < 5);
|
||||
CREATE POLICY p2 ON rls_tbl_force FOR SELECT USING (c1 = 8);
|
||||
CREATE POLICY p3 ON rls_tbl_force FOR UPDATE USING (c1 = 8) WITH CHECK (c1 >= 5);
|
||||
CREATE POLICY p4 ON rls_tbl_force FOR DELETE USING (c1 = 8);
|
||||
|
@ -672,6 +672,7 @@ SELECT user_relns() AS user_relns
|
||||
real_city
|
||||
reltime_tbl
|
||||
rls_tbl
|
||||
rls_tbl_force
|
||||
road
|
||||
shighway
|
||||
slow_emp4000
|
||||
@ -709,7 +710,7 @@ SELECT user_relns() AS user_relns
|
||||
tvvmv
|
||||
varchar_tbl
|
||||
xacttest
|
||||
(131 rows)
|
||||
(132 rows)
|
||||
|
||||
SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer')));
|
||||
name
|
||||
|
@ -1288,6 +1288,141 @@ SET SESSION AUTHORIZATION rls_regress_user0;
|
||||
DROP TABLE r1;
|
||||
DROP TABLE r2;
|
||||
|
||||
--
|
||||
-- FORCE ROW LEVEL SECURITY applies RLS to owners but
|
||||
-- only when row_security = on
|
||||
--
|
||||
SET SESSION AUTHORIZATION rls_regress_user0;
|
||||
SET row_security = on;
|
||||
CREATE TABLE r1 (a int);
|
||||
INSERT INTO r1 VALUES (10), (20);
|
||||
|
||||
CREATE POLICY p1 ON r1 USING (false);
|
||||
ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
|
||||
|
||||
-- No error, but no rows
|
||||
TABLE r1;
|
||||
|
||||
-- RLS error
|
||||
INSERT INTO r1 VALUES (1);
|
||||
|
||||
-- No error (unable to see any rows to update)
|
||||
UPDATE r1 SET a = 1;
|
||||
TABLE r1;
|
||||
|
||||
-- No error (unable to see any rows to delete)
|
||||
DELETE FROM r1;
|
||||
TABLE r1;
|
||||
|
||||
SET row_security = off;
|
||||
-- Shows all rows
|
||||
TABLE r1;
|
||||
|
||||
-- Update all rows
|
||||
UPDATE r1 SET a = 1;
|
||||
TABLE r1;
|
||||
|
||||
-- Delete all rows
|
||||
DELETE FROM r1;
|
||||
TABLE r1;
|
||||
|
||||
DROP TABLE r1;
|
||||
|
||||
--
|
||||
-- FORCE ROW LEVEL SECURITY does not break RI
|
||||
--
|
||||
SET SESSION AUTHORIZATION rls_regress_user0;
|
||||
SET row_security = on;
|
||||
CREATE TABLE r1 (a int PRIMARY KEY);
|
||||
CREATE TABLE r2 (a int REFERENCES r1);
|
||||
INSERT INTO r1 VALUES (10), (20);
|
||||
INSERT INTO r2 VALUES (10), (20);
|
||||
|
||||
-- Create policies on r2 which prevent the
|
||||
-- owner from seeing any rows, but RI should
|
||||
-- still see them.
|
||||
CREATE POLICY p1 ON r2 USING (false);
|
||||
ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
|
||||
|
||||
-- Errors due to rows in r2
|
||||
DELETE FROM r1;
|
||||
|
||||
-- Reset r2 to no-RLS
|
||||
DROP POLICY p1 ON r2;
|
||||
ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
|
||||
ALTER TABLE r2 DISABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- clean out r2 for INSERT test below
|
||||
DELETE FROM r2;
|
||||
|
||||
-- Change r1 to not allow rows to be seen
|
||||
CREATE POLICY p1 ON r1 USING (false);
|
||||
ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
|
||||
|
||||
-- No rows seen
|
||||
TABLE r1;
|
||||
|
||||
-- No error, RI still sees that row exists in r1
|
||||
INSERT INTO r2 VALUES (10);
|
||||
|
||||
DROP TABLE r2;
|
||||
DROP TABLE r1;
|
||||
|
||||
-- Ensure cascaded DELETE works
|
||||
CREATE TABLE r1 (a int PRIMARY KEY);
|
||||
CREATE TABLE r2 (a int REFERENCES r1 ON DELETE CASCADE);
|
||||
INSERT INTO r1 VALUES (10), (20);
|
||||
INSERT INTO r2 VALUES (10), (20);
|
||||
|
||||
-- Create policies on r2 which prevent the
|
||||
-- owner from seeing any rows, but RI should
|
||||
-- still see them.
|
||||
CREATE POLICY p1 ON r2 USING (false);
|
||||
ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
|
||||
|
||||
-- Deletes all records from both
|
||||
DELETE FROM r1;
|
||||
|
||||
-- Remove FORCE from r2
|
||||
ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
|
||||
|
||||
-- As owner, we now bypass RLS
|
||||
-- verify no rows in r2 now
|
||||
TABLE r2;
|
||||
|
||||
DROP TABLE r2;
|
||||
DROP TABLE r1;
|
||||
|
||||
-- Ensure cascaded UPDATE works
|
||||
CREATE TABLE r1 (a int PRIMARY KEY);
|
||||
CREATE TABLE r2 (a int REFERENCES r1 ON UPDATE CASCADE);
|
||||
INSERT INTO r1 VALUES (10), (20);
|
||||
INSERT INTO r2 VALUES (10), (20);
|
||||
|
||||
-- Create policies on r2 which prevent the
|
||||
-- owner from seeing any rows, but RI should
|
||||
-- still see them.
|
||||
CREATE POLICY p1 ON r2 USING (false);
|
||||
ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
|
||||
|
||||
-- Updates records in both
|
||||
UPDATE r1 SET a = a+5;
|
||||
|
||||
-- Remove FORCE from r2
|
||||
ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
|
||||
|
||||
-- As owner, we now bypass RLS
|
||||
-- verify records in r2 updated
|
||||
TABLE r2;
|
||||
|
||||
DROP TABLE r2;
|
||||
DROP TABLE r1;
|
||||
|
||||
--
|
||||
-- Clean up objects
|
||||
--
|
||||
@ -1315,3 +1450,11 @@ CREATE POLICY p1 ON rls_tbl USING (c1 > 5);
|
||||
CREATE POLICY p2 ON rls_tbl FOR SELECT USING (c1 <= 3);
|
||||
CREATE POLICY p3 ON rls_tbl FOR UPDATE USING (c1 <= 3) WITH CHECK (c1 > 5);
|
||||
CREATE POLICY p4 ON rls_tbl FOR DELETE USING (c1 <= 3);
|
||||
|
||||
CREATE TABLE rls_tbl_force (c1 int);
|
||||
ALTER TABLE rls_tbl_force ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE rls_tbl_force FORCE ROW LEVEL SECURITY;
|
||||
CREATE POLICY p1 ON rls_tbl_force USING (c1 = 5) WITH CHECK (c1 < 5);
|
||||
CREATE POLICY p2 ON rls_tbl_force FOR SELECT USING (c1 = 8);
|
||||
CREATE POLICY p3 ON rls_tbl_force FOR UPDATE USING (c1 = 8) WITH CHECK (c1 >= 5);
|
||||
CREATE POLICY p4 ON rls_tbl_force FOR DELETE USING (c1 = 8);
|
||||
|
Reference in New Issue
Block a user