1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-27 12:41:57 +03:00

Adjust behavior of row_security GUC to match the docs.

Some time back we agreed that row_security=off should not be a way to
bypass RLS entirely, but only a way to get an error if it was being
applied.  However, the code failed to act that way for table owners.
Per discussion, this is a must-fix bug for 9.5.0.

Adjust the logic in rls.c to behave as expected; also, modify the
error message to be more consistent with the new interpretation.
The regression tests need minor corrections as well.  Also update
the comments about row_security in ddl.sgml to be correct.  (The
official description of the GUC in config.sgml is already correct.)

I failed to resist the temptation to do some other very minor
cleanup as well, such as getting rid of a duplicate extern declaration.
This commit is contained in:
Tom Lane
2016-01-04 12:21:31 -05:00
parent 8978eb03a8
commit 5d35438273
4 changed files with 78 additions and 92 deletions

View File

@ -17,18 +17,17 @@
#include "access/htup.h"
#include "access/htup_details.h"
#include "access/transam.h"
#include "catalog/pg_class.h"
#include "catalog/namespace.h"
#include "catalog/pg_class.h"
#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/elog.h"
#include "utils/lsyscache.h"
#include "utils/rls.h"
#include "utils/syscache.h"
extern int check_enable_rls(Oid relid, Oid checkAsUser, bool noError);
/*
* check_enable_rls
*
@ -52,20 +51,21 @@ extern int check_enable_rls(Oid relid, Oid checkAsUser, bool noError);
int
check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
{
Oid user_id = checkAsUser ? checkAsUser : GetUserId();
HeapTuple tuple;
Form_pg_class classform;
bool relrowsecurity;
bool relforcerowsecurity;
Oid user_id = checkAsUser ? checkAsUser : GetUserId();
bool amowner;
/* Nothing to do for built-in relations */
if (relid < FirstNormalObjectId)
if (relid < (Oid) FirstNormalObjectId)
return RLS_NONE;
/* Fetch relation's relrowsecurity and relforcerowsecurity flags */
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(tuple))
return RLS_NONE;
classform = (Form_pg_class) GETSTRUCT(tuple);
relrowsecurity = classform->relrowsecurity;
@ -88,41 +88,45 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
return RLS_NONE_ENV;
/*
* Table owners generally bypass RLS, except if row_security=true and the
* table has been set (by an owner) to FORCE ROW SECURITY, and this is not
* a referential integrity check.
* Table owners generally bypass RLS, except if the table has been set (by
* an owner) to FORCE ROW SECURITY, and this is not a referential
* integrity check.
*
* Return RLS_NONE_ENV to indicate that this decision depends on the
* environment (in this case, the user_id).
*/
if (pg_class_ownercheck(relid, user_id))
amowner = pg_class_ownercheck(relid, user_id);
if (amowner)
{
/*
* If row_security=true and FORCE ROW LEVEL SECURITY has been set on
* the relation then we return RLS_ENABLED to indicate that RLS should
* still be applied. If we are in a SECURITY_NOFORCE_RLS context or if
* row_security=false then we return RLS_NONE_ENV.
* If FORCE ROW LEVEL SECURITY has been set on the relation then we
* should return RLS_ENABLED to indicate that RLS should be applied.
* If not, or if we are in an InNoForceRLSOperation context, we return
* RLS_NONE_ENV.
*
* The SECURITY_NOFORCE_RLS indicates that we should not apply RLS even
* if the table has FORCE RLS set- IF the current user is the owner.
* This is specifically to ensure that referential integrity checks are
* able to still run correctly.
* InNoForceRLSOperation indicates that we should not apply RLS even
* if the table has FORCE RLS set - IF the current user is the owner.
* This is specifically to ensure that referential integrity checks
* are able to still run correctly.
*
* This is intentionally only done after we have checked that the user
* is the table owner, which should always be the case for referential
* integrity checks.
*/
if (row_security && relforcerowsecurity && !InNoForceRLSOperation())
return RLS_ENABLED;
else
if (!relforcerowsecurity || InNoForceRLSOperation())
return RLS_NONE_ENV;
}
/* row_security GUC says to bypass RLS, but user lacks permission */
/*
* We should apply RLS. However, the user may turn off the row_security
* GUC to get a forced error instead.
*/
if (!row_security && !noError)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("insufficient privilege to bypass row-level security")));
errmsg("query would be affected by row-level security policy for table \"%s\"",
get_rel_name(relid)),
amowner ? errhint("To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.") : 0));
/* RLS should be fully enabled for this relation. */
return RLS_ENABLED;

View File

@ -2728,8 +2728,8 @@ COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ',';
-- Check COPY TO as user with permissions.
SET SESSION AUTHORIZATION rls_regress_user1;
SET row_security TO OFF;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
ERROR: insufficient privilege to bypass row-level security
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
ERROR: query would be affected by row-level security policy for table "copy_t"
SET row_security TO ON;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
0,cfcd208495d565ef66e7dff9f98764da
@ -2769,8 +2769,8 @@ COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
-- Check COPY TO as user without permissions. SET row_security TO OFF;
SET SESSION AUTHORIZATION rls_regress_user2;
SET row_security TO OFF;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
ERROR: insufficient privilege to bypass row-level security
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
ERROR: query would be affected by row-level security policy for table "copy_t"
SET row_security TO ON;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - permission denied
ERROR: permission denied for relation copy_t
@ -2793,8 +2793,8 @@ COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
-- Check COPY TO as user with permissions.
SET SESSION AUTHORIZATION rls_regress_user1;
SET row_security TO OFF;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
ERROR: insufficient privilege to bypass row-level security
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
ERROR: query would be affected by row-level security policy for table "copy_rel_to"
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
-- Check COPY TO as user with permissions and BYPASSRLS
@ -2822,8 +2822,8 @@ COPY copy_t FROM STDIN; --ok
-- Check COPY FROM as user with permissions.
SET SESSION AUTHORIZATION rls_regress_user1;
SET row_security TO OFF;
COPY copy_t FROM STDIN; --fail - insufficient privilege to bypass rls.
ERROR: insufficient privilege to bypass row-level security
COPY copy_t FROM STDIN; --fail - would be affected by RLS.
ERROR: query would be affected by row-level security policy for table "copy_t"
SET row_security TO ON;
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
ERROR: COPY FROM not supported with row-level security
@ -3181,8 +3181,7 @@ 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
-- FORCE ROW LEVEL SECURITY applies RLS to owners too
--
SET SESSION AUTHORIZATION rls_regress_user0;
SET row_security = on;
@ -3215,30 +3214,16 @@ TABLE r1;
(0 rows)
SET row_security = off;
-- Shows all rows
-- these all fail, would be affected by RLS
TABLE r1;
a
----
10
20
(2 rows)
-- Update all rows
ERROR: query would be affected by row-level security policy for table "r1"
HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.
UPDATE r1 SET a = 1;
TABLE r1;
a
---
1
1
(2 rows)
-- Delete all rows
ERROR: query would be affected by row-level security policy for table "r1"
HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.
DELETE FROM r1;
TABLE r1;
a
---
(0 rows)
ERROR: query would be affected by row-level security policy for table "r1"
HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.
DROP TABLE r1;
--
-- FORCE ROW LEVEL SECURITY does not break RI
@ -3349,14 +3334,10 @@ TABLE r1;
(0 rows)
SET row_security = off;
-- Rows shown now
-- fail, would be affected by RLS
TABLE r1;
a
----
10
20
(2 rows)
ERROR: query would be affected by row-level security policy for table "r1"
HINT: To disable the policy for the table's owner, use ALTER TABLE NO FORCE ROW LEVEL SECURITY.
SET row_security = on;
-- Error
INSERT INTO r1 VALUES (10), (20) RETURNING *;
@ -3377,7 +3358,7 @@ ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
-- Works fine
UPDATE r1 SET a = 30;
-- Show updated rows
SET row_security = off;
ALTER TABLE r1 NO FORCE ROW LEVEL SECURITY;
TABLE r1;
a
----
@ -3393,7 +3374,7 @@ TABLE r1;
10
(1 row)
SET row_security = on;
ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
-- Error
UPDATE r1 SET a = 30 RETURNING *;
ERROR: new row violates row-level security policy for table "r1"

View File

@ -1014,7 +1014,7 @@ COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ',';
-- Check COPY TO as user with permissions.
SET SESSION AUTHORIZATION rls_regress_user1;
SET row_security TO OFF;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
SET row_security TO ON;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
@ -1028,7 +1028,7 @@ COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
-- Check COPY TO as user without permissions. SET row_security TO OFF;
SET SESSION AUTHORIZATION rls_regress_user2;
SET row_security TO OFF;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
SET row_security TO ON;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - permission denied
@ -1054,7 +1054,7 @@ COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
-- Check COPY TO as user with permissions.
SET SESSION AUTHORIZATION rls_regress_user1;
SET row_security TO OFF;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - would be affected by RLS
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
@ -1092,7 +1092,7 @@ COPY copy_t FROM STDIN; --ok
-- Check COPY FROM as user with permissions.
SET SESSION AUTHORIZATION rls_regress_user1;
SET row_security TO OFF;
COPY copy_t FROM STDIN; --fail - insufficient privilege to bypass rls.
COPY copy_t FROM STDIN; --fail - would be affected by RLS.
SET row_security TO ON;
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
@ -1315,8 +1315,7 @@ DROP TABLE r1;
DROP TABLE r2;
--
-- FORCE ROW LEVEL SECURITY applies RLS to owners but
-- only when row_security = on
-- FORCE ROW LEVEL SECURITY applies RLS to owners too
--
SET SESSION AUTHORIZATION rls_regress_user0;
SET row_security = on;
@ -1342,16 +1341,10 @@ DELETE FROM r1;
TABLE r1;
SET row_security = off;
-- Shows all rows
-- these all fail, would be affected by RLS
TABLE r1;
-- Update all rows
UPDATE r1 SET a = 1;
TABLE r1;
-- Delete all rows
DELETE FROM r1;
TABLE r1;
DROP TABLE r1;
@ -1469,7 +1462,7 @@ INSERT INTO r1 VALUES (10), (20);
TABLE r1;
SET row_security = off;
-- Rows shown now
-- fail, would be affected by RLS
TABLE r1;
SET row_security = on;
@ -1497,7 +1490,7 @@ ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
UPDATE r1 SET a = 30;
-- Show updated rows
SET row_security = off;
ALTER TABLE r1 NO FORCE ROW LEVEL SECURITY;
TABLE r1;
-- reset value in r1 for test with RETURNING
UPDATE r1 SET a = 10;
@ -1505,7 +1498,7 @@ UPDATE r1 SET a = 10;
-- Verify row reset
TABLE r1;
SET row_security = on;
ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
-- Error
UPDATE r1 SET a = 30 RETURNING *;