1
0
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:
Stephen Frost
2015-10-04 21:05:08 -04:00
parent 16a70e3059
commit 088c83363a
19 changed files with 537 additions and 64 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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);