Make BYPASSRLS behave like superuser RLS bypass.
authorNoah Misch
Sun, 4 Oct 2015 00:19:57 +0000 (20:19 -0400)
committerNoah Misch
Sun, 4 Oct 2015 00:20:50 +0000 (20:20 -0400)
Specifically, make its effect independent from the row_security GUC, and
make it affect permission checks pertinent to views the BYPASSRLS role
owns.  The row_security GUC thereby ceases to change successful-query
behavior; it can only make a query fail with an error.  Back-patch to
9.5, where BYPASSRLS was introduced.

doc/src/sgml/catalogs.sgml
doc/src/sgml/config.sgml
doc/src/sgml/ddl.sgml
doc/src/sgml/ref/create_role.sgml
src/backend/utils/misc/rls.c
src/include/catalog/pg_authid.h
src/test/regress/expected/rowsecurity.out
src/test/regress/sql/rowsecurity.sql

index ec6badbc311d947227e839c0a872c96b1fe3f98b..4a75b5f33d4e699425d5165550940cfdd09fd0e5 100644 (file)
       rolbypassrls
       bool
       
-       Role can bypass row level security policies, see
+       Role bypasses every row level security policy, see
         for more information.
       
      
@@ -9385,7 +9385,7 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
       bool
       
       
-       User can bypass row level security policies, see
+       User bypasses every row level security policy, see
         for more information.
       
      
@@ -9860,7 +9860,7 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
       usebypassrls
       bool
       
-       User can bypass row level security policies, see
+       User bypasses every row level security policy, see
         for more information.
       
      
index c8ec219ffc64f7cd60708f10ba9daa5ea1500f3f..5e43de9bbddcd6c10a60d75283f6d219db3a2ffc 100644 (file)
@@ -5537,22 +5537,15 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       
       
        
-        This variable controls if row security policies are to be applied
-        to queries which are run against tables that have row security enabled.
-        The default is on.  When set to on, all users,
-        except superusers and the owner of the table, will have the row
-        policies for the table applied to their queries.  When set to
-        off, queries will bypass row policies for the table, if
-        possible, and error if not.
-       
-
-       
-        For a user who is not a superuser and not the table owner to bypass
-        row policies for the table, they must have the BYPASSRLS
-        role attribute. If this is set to off and the user queries
-        a table which has row policies enabled and the user does not have the
-        right to bypass row policies then a permission denied error will be
-        returned.
+        This variable controls whether to raise an error in lieu of applying a
+        row security policy.  When set to on, policies apply
+        normally.  When set to off, queries fail which would
+        otherwise apply at least one policy.  The default is on.
+        Change to off where limited row visibility could cause
+        incorrect results; for example, pg_dump makes that
+        change by default.  This variable has no effect on roles which bypass
+        every row security policy, to wit, superusers and roles with
+        the BYPASSRLS attribute.
        
 
        
index a796f6c906dfbd0060b04b1e5680705344332ce2..fe5a076fe120da59ca2d7e96ecd59f6039db43f7 100644 (file)
@@ -1543,8 +1543,12 @@ REVOKE ALL ON accounts FROM PUBLIC;
    Row security policies can be specific to commands, or to roles, or to
    both.  The commands available are ALL,
    SELECT, INSERT, UPDATE, and
-   DELETE.  Multiple roles can be assigned to a given policy
-   and normal role membership and inheritance rules apply.
+   DELETE.  Multiple roles can be assigned to a given policy and
+   normal role membership and inheritance rules apply.  Table owners,
+   superusers, and roles with the BYPASSRLS attribute bypass the
+   row security system when querying a table.  Applications that expect to
+   bypass all row security through those mechanisms should
+   set  to off.
   
 
   
@@ -1574,17 +1578,6 @@ REVOKE ALL ON accounts FROM PUBLIC;
     command.
   
 
-  
-   The table owners and superusers bypass the row security system when
-   querying a table.  Any user can request that row security be bypassed by
-   setting  to off.  If
-   the user does not have privileges to bypass row security when querying a
-   given table then an error will be returned instead.  Other users can be
-   granted the ability to bypass the row security system with
-   the BYPASSRLS role attribute.  This attribute can only
-   be set by a superuser.
-  
-
   
    Each policy has a name and multiple policies can be defined for a
    table.  As policies are table-specific, each policy for a table must
index f4a176bff3fe490ad7cc23079350e17f1ae5de9a..240c21ce85fcd1bd4cddd4bb6af109ef319f8175 100644 (file)
@@ -196,16 +196,13 @@ CREATE ROLE name [ [ WITH ] 
       NOBYPASSRLS
       
        
-        These clauses determine whether a role is allowed to bypass row-level security (RLS)
-        policies.  A role having the BYPASSRLS attribute will
-        be allowed to bypass row-security policies by setting
-        row_security to
-        OFFNOBYPASSRLS is the default.
+        These clauses determine whether a role bypasses every row-level
+        security (RLS) policy.  NOBYPASSRLS is the default.
         Note that pg_dump will set row_security to
         OFF by default, to ensure all contents of a table are
         dumped out.  If the user running pg_dump does not have appropriate
         permissions, an error will be returned.  The superuser and owner of the
-        table being dumped are considered to always have the right to bypass RLS.
+        table being dumped always bypass RLS.
        
       
      
index c900c98848b98b199b8cde50c5b0a7467072b59d..eaf9d6e66ffa60b8f0b43cc230580f5be7e6bc1b 100644 (file)
@@ -40,10 +40,8 @@ extern int   check_enable_rls(Oid relid, Oid checkAsUser, bool noError);
  * for the table and the plan cache needs to be invalidated if the environment
  * changes.
  *
- * Handle checking as another role via checkAsUser (for views, etc). Note that
- * if *not* checking as another role, the caller should pass InvalidOid rather
- * than GetUserId(). Otherwise the check for row_security = OFF is skipped, and
- * so we may falsely report that RLS is active when the user has bypassed it.
+ * Handle checking as another role via checkAsUser (for views, etc).  Pass
+ * InvalidOid to check the current user.
  *
  * If noError is set to 'true' then we just return RLS_ENABLED instead of doing
  * an ereport() if the user has attempted to bypass RLS and they are not
@@ -78,32 +76,19 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
        return RLS_NONE;
 
    /*
-    * Check permissions
-    *
-    * Table owners always bypass RLS.  Note that superuser is always
-    * considered an owner.  Return RLS_NONE_ENV to indicate that this
-    * decision depends on the environment (in this case, the user_id).
+    * 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).
     */
-   if (pg_class_ownercheck(relid, user_id))
+   if (pg_class_ownercheck(relid, user_id) ||
+       has_bypassrls_privilege(user_id))
        return RLS_NONE_ENV;
 
-   /*
-    * If the row_security GUC is 'off', check if the user has permission to
-    * bypass RLS.  row_security is always considered 'on' when querying
-    * through a view or other cases where checkAsUser is valid.
-    */
-   if (!row_security && !checkAsUser)
-   {
-       if (has_bypassrls_privilege(user_id))
-           /* OK to bypass */
-           return RLS_NONE_ENV;
-       else if (noError)
-           return RLS_ENABLED;
-       else
-           ereport(ERROR,
-                   (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                 errmsg("insufficient privilege to bypass row security.")));
-   }
+   /* row_security GUC says to bypass RLS, but user lacks permission */
+   if (!row_security && !noError)
+       ereport(ERROR,
+               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                errmsg("insufficient privilege to bypass row security.")));
 
    /* RLS should be fully enabled for this relation. */
    return RLS_ENABLED;
index d5f19d6aabbd1840ee9be688fdc48b26a3a8a1b1..2c8565ea2542c79bd50d102e9bc51b821f6a52d5 100644 (file)
@@ -51,7 +51,7 @@ CATALOG(pg_authid,1260) BKI_SHARED_RELATION BKI_ROWTYPE_OID(2842) BKI_SCHEMA_MAC
    bool        rolcreatedb;    /* allowed to create databases? */
    bool        rolcanlogin;    /* allowed to log in as session user? */
    bool        rolreplication; /* role used for streaming replication */
-   bool        rolbypassrls;   /* allowed to bypass row level security? */
+   bool        rolbypassrls;   /* bypasses row level security? */
    int32       rolconnlimit;   /* max connections allowed (-1=no limit) */
 
    /* remaining fields may be null; use heap_getattr to read them! */
index 0cde8fd0e3f5fbc27922d1e51e19bfd9e7ef1e41..0363dfd07ff5edde7d9f50b8cfa244effde4becb 100644 (file)
@@ -2584,10 +2584,15 @@ COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
 SET row_security TO ON;
 COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
 0,cfcd208495d565ef66e7dff9f98764da
+1,c4ca4238a0b923820dcc509a6f75849b
 2,c81e728d9d4c2f636f067f89cc14862c
+3,eccbc87e4b5ce2fe28308fd9f2a7baf3
 4,a87ff679a2f3e71d9181a67b7542122c
+5,e4da3b7fbbce2345d7772b0674a318d5
 6,1679091c5a880faf6fb5e6087eb1b2dc
+7,8f14e45fceea167a5a36dedd4bea2543
 8,c9f0f895fb98ab9159f51fd0297e236d
+9,45c48cce2e2d7fbdea1afc51c7c6ad26
 10,d3d9446802a44259755d38e6d163e820
 -- Check COPY TO as user without permissions. SET row_security TO OFF;
 SET SESSION AUTHORIZATION rls_regress_user2;
@@ -2627,6 +2632,7 @@ COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
 1,c4ca4238a0b923820dcc509a6f75849b
 SET row_security TO ON;
 COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
+1,c4ca4238a0b923820dcc509a6f75849b
 -- Check COPY TO as user without permissions. SET row_security TO OFF;
 SET SESSION AUTHORIZATION rls_regress_user2;
 SET row_security TO OFF;
@@ -2650,14 +2656,10 @@ 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.
 HINT:  Use direct INSERT statements instead.
--- Check COPY TO as user with permissions and BYPASSRLS
+-- Check COPY FROM as user with permissions and BYPASSRLS
 SET SESSION AUTHORIZATION rls_regress_exempt_user;
-SET row_security TO OFF;
-COPY copy_t FROM STDIN; --ok
 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.
-HINT:  Use direct INSERT statements instead.
+COPY copy_t FROM STDIN; --ok
 -- Check COPY FROM as user without permissions.
 SET SESSION AUTHORIZATION rls_regress_user2;
 SET row_security TO OFF;
index 6ed0daf345f7091040c0230b49ce20f0c5c957f0..7f8772fa26cbaaf070b2c77c34f060758bf12695 100644 (file)
@@ -1070,17 +1070,15 @@ COPY copy_t FROM STDIN; --fail - insufficient privilege to bypass rls.
 SET row_security TO ON;
 COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
 
--- Check COPY TO as user with permissions and BYPASSRLS
+-- Check COPY FROM as user with permissions and BYPASSRLS
 SET SESSION AUTHORIZATION rls_regress_exempt_user;
-SET row_security TO OFF;
+SET row_security TO ON;
 COPY copy_t FROM STDIN; --ok
 1  abc
 2  bcd
 3  cde
 4  def
 \.
-SET row_security TO ON;
-COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
 
 -- Check COPY FROM as user without permissions.
 SET SESSION AUTHORIZATION rls_regress_user2;