1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-07 19:06:32 +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

@@ -55,6 +55,7 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
HeapTuple tuple;
Form_pg_class classform;
bool relrowsecurity;
bool relforcerowsecurity;
Oid user_id = checkAsUser ? checkAsUser : GetUserId();
/* Nothing to do for built-in relations */
@@ -68,6 +69,7 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
classform = (Form_pg_class) GETSTRUCT(tuple);
relrowsecurity = classform->relrowsecurity;
relforcerowsecurity = classform->relforcerowsecurity;
ReleaseSysCache(tuple);
@@ -76,14 +78,46 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
return RLS_NONE;
/*
* Table owners and BYPASSRLS users bypass RLS. Note that a superuser
* qualifies as both. Return RLS_NONE_ENV to indicate that this decision
* depends on the environment (in this case, the user_id).
* BYPASSRLS users always bypass RLS. Note that superusers are always
* considered to have BYPASSRLS.
*
* 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) ||
has_bypassrls_privilege(user_id))
if (has_bypassrls_privilege(user_id))
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.
*
* 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))
{
/*
* 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.
*
* 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.
*
* 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
return RLS_NONE_ENV;
}
/* row_security GUC says to bypass RLS, but user lacks permission */
if (!row_security && !noError)
ereport(ERROR,