diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2016-01-04 12:21:31 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2016-01-04 12:21:41 -0500 |
commit | 5d35438273c4523a4dc4b48c3bd575e64310d3d4 (patch) | |
tree | 85d72efc1307a3f7276666caba80469ce6eaedfe /src/backend/utils/misc/rls.c | |
parent | 8978eb03a8dcfafd9e0839bc430749839476c34a (diff) | |
download | postgresql-5d35438273c4523a4dc4b48c3bd575e64310d3d4.tar.gz postgresql-5d35438273c4523a4dc4b48c3bd575e64310d3d4.zip |
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.
Diffstat (limited to 'src/backend/utils/misc/rls.c')
-rw-r--r-- | src/backend/utils/misc/rls.c | 50 |
1 files changed, 27 insertions, 23 deletions
diff --git a/src/backend/utils/misc/rls.c b/src/backend/utils/misc/rls.c index b6c1d75fadd..c33f29e9f2e 100644 --- a/src/backend/utils/misc/rls.c +++ b/src/backend/utils/misc/rls.c @@ -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; |