to queries which are run against tables that have row security enabled.
The default is <literal>on</>. When set to <literal>on</>, all users,
except superusers and the owner of the table, will have the row
- policies for the table applied to their queries. The table owner and
- superuser can request that row policies be applied to their queries by
- setting this to <literal>force</>. Lastly, this can also be set to
- <literal>off</> which will bypass row policies for the table, if
+ policies for the table applied to their queries. When set to
+ <literal>off</>, queries will bypass row policies for the table, if
possible, and error if not.
</para>
returned.
</para>
- <para>
- The allowed values of <varname>row_security</> are
- <literal>on</> (apply normally - not to superuser or table owner),
- <literal>off</> (fail if row security would be applied), and
- <literal>force</> (apply always - even to superuser and table owner).
- </para>
-
<para>
For more information on row security policies,
see <xref linkend="SQL-CREATEPOLICY">.
<para>
The table owners and superusers bypass the row security system when
- querying a table, by default. Row security can be enabled for
- superusers and table owners by setting
- <xref linkend="guc-row-security"> to <literal>force</literal>. Any
- user can request that row security be bypassed by setting
- <xref linkend="guc-row-security"> to <literal>off</literal>. 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 <literal>BYPASSRLS</literal> role attribute. This
- attribute can only be set by a superuser.
+ querying a table. Any user can request that row security be bypassed by
+ setting <xref linkend="guc-row-security"> to <literal>off</literal>. 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 <literal>BYPASSRLS</literal> role attribute. This attribute can only
+ be set by a superuser.
</para>
<para>
{NULL, 0, false}
};
-/*
- * Although only "on", "off", and "force" are documented, we
- * accept all the likely variants of "on" and "off".
- */
-static const struct config_enum_entry row_security_options[] = {
- {"on", ROW_SECURITY_ON, false},
- {"off", ROW_SECURITY_OFF, false},
- {"force", ROW_SECURITY_FORCE, false},
- {"true", ROW_SECURITY_ON, true},
- {"false", ROW_SECURITY_OFF, true},
- {"yes", ROW_SECURITY_ON, true},
- {"no", ROW_SECURITY_OFF, true},
- {"1", ROW_SECURITY_ON, true},
- {"0", ROW_SECURITY_OFF, true},
- {NULL, 0, false}
-};
-
/*
* Options for enum values stored in other modules
*/
bool log_btree_build_stats = false;
char *event_source;
+bool row_security;
bool check_function_bodies = true;
bool default_with_oids = false;
bool SQL_inheritance = true;
int tcp_keepalives_interval;
int tcp_keepalives_count;
-int row_security;
-
/*
* This really belongs in pg_shmem.c, but is defined here so that it doesn't
* need to be duplicated in all the different implementations of pg_shmem.c.
false,
check_transaction_deferrable, NULL, NULL
},
+ {
+ {"row_security", PGC_USERSET, CONN_AUTH_SECURITY,
+ gettext_noop("Enable row security."),
+ gettext_noop("When enabled, row security will be applied to all users.")
+ },
+ &row_security,
+ true,
+ NULL, NULL, NULL
+ },
{
{"check_function_bodies", PGC_USERSET, CLIENT_CONN_STATEMENT,
gettext_noop("Check function bodies during CREATE FUNCTION."),
NULL, NULL, NULL
},
- {
- {"row_security", PGC_USERSET, CONN_AUTH_SECURITY,
- gettext_noop("Enable row security."),
- gettext_noop("When enabled, row security will be applied to all users.")
- },
- &row_security,
- ROW_SECURITY_ON, row_security_options,
- NULL, NULL, NULL
- },
-
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL
/*
* Check permissions
*
- * If the relation has row level security enabled and the row_security GUC
- * is off, then check if the user has rights to bypass RLS for this
- * relation. Table owners can always bypass, as can any role with the
- * BYPASSRLS capability.
- *
- * If the role is the table owner, then we bypass RLS unless row_security
- * is set to 'force'. Note that superuser is always considered an owner.
- *
- * Return RLS_NONE_ENV to indicate that this decision depends on the
- * environment (in this case, what the current values of user_id and
- * row_security are).
+ * 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).
*/
- if (row_security != ROW_SECURITY_FORCE
- && (pg_class_ownercheck(relid, user_id)))
+ if (pg_class_ownercheck(relid, user_id))
return RLS_NONE_ENV;
/*
- * If the row_security GUC is 'off' then check if the user has permission
- * to bypass it. Note that we have already handled the case where the
- * user is the table owner above.
- *
- * Note that row_security is always considered 'on' when querying through
- * a view or other cases where checkAsUser is true, so skip this if
- * checkAsUser is in use.
+ * 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 (!checkAsUser && row_security == ROW_SECURITY_OFF)
+ if (!row_security && !checkAsUser)
{
if (has_bypassrls_privilege(user_id))
/* OK to bypass */
double total_custom_cost; /* total cost of custom plans so far */
int num_custom_plans; /* number of plans included in total */
bool hasRowSecurity; /* planned with row security? */
- int row_security_env; /* row security setting when planned */
+ bool row_security_env; /* row security setting when planned */
bool rowSecurityDisabled; /* is row security disabled? */
} CachedPlanSource;
#define RLS_H
/* GUC variable */
-extern int row_security;
-
-/* Possible values for row_security GUC */
-typedef enum RowSecurityConfigType
-{
- ROW_SECURITY_OFF, /* RLS never applied- error thrown if no priv */
- ROW_SECURITY_ON, /* normal case, RLS applied for regular users */
- ROW_SECURITY_FORCE /* RLS applied for superusers and table owners */
-} RowSecurityConfigType;
+extern bool row_security;
/*
* Used by callers of check_enable_rls.
* RLS could be completely disabled on the tables involved in the query,
* which is the simple case, or it may depend on the current environment
* (the role which is running the query or the value of the row_security
- * GUC- on, off, or force), or it might be simply enabled as usual.
+ * GUC), or it might be simply enabled as usual.
*
* If RLS isn't on the table involved then RLS_NONE is returned to indicate
* that we don't need to worry about invalidating the query plan for RLS
44 | manga
(4 rows)
--- database superuser does not bypass RLS policy when FORCE enabled.
-RESET SESSION AUTHORIZATION;
-SET row_security TO FORCE;
-SELECT * FROM document;
- did | cid | dlevel | dauthor | dtitle
------+-----+--------+---------+--------
-(0 rows)
-
-SELECT * FROM category;
- cid | cname
------+-------
-(0 rows)
-
-- database superuser does bypass RLS policy when disabled
RESET SESSION AUTHORIZATION;
SET row_security TO OFF;
44 | manga
(4 rows)
--- RLS policy applies to table owner when FORCE enabled.
-SET SESSION AUTHORIZATION rls_regress_user0;
-SET row_security TO FORCE;
-SELECT * FROM document;
- did | cid | dlevel | dauthor | dtitle
------+-----+--------+---------+--------
-(0 rows)
-
-SELECT * FROM category;
- cid | cname
------+-------
-(0 rows)
-
-- RLS policy does not apply to table owner when RLS enabled.
SET SESSION AUTHORIZATION rls_regress_user0;
SET row_security TO ON;
Seq Scan on t1
(1 row)
--- Check that default deny does apply to superuser when RLS force.
-SET row_security TO FORCE;
-RESET SESSION AUTHORIZATION;
-SELECT * FROM t1;
- a | b
----+---
-(0 rows)
-
-EXPLAIN (COSTS OFF) SELECT * FROM t1;
- QUERY PLAN
---------------------------
- Result
- One-Time Filter: false
-(2 rows)
-
--- Check that default deny does apply to table owner when RLS force.
-SET SESSION AUTHORIZATION rls_regress_user0;
-SELECT * FROM t1;
- a | b
----+---
-(0 rows)
-
-EXPLAIN (COSTS OFF) SELECT * FROM t1;
- QUERY PLAN
---------------------------
- Result
- One-Time Filter: false
-(2 rows)
-
-- Check that default deny applies to non-owner/non-superuser when RLS on.
SET SESSION AUTHORIZATION rls_regress_user1;
SET row_security TO ON;
8,c9f0f895fb98ab9159f51fd0297e236d
9,45c48cce2e2d7fbdea1afc51c7c6ad26
10,d3d9446802a44259755d38e6d163e820
-SET row_security TO FORCE;
-COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ',';
-0,cfcd208495d565ef66e7dff9f98764da
-2,c81e728d9d4c2f636f067f89cc14862c
-4,a87ff679a2f3e71d9181a67b7542122c
-6,1679091c5a880faf6fb5e6087eb1b2dc
-8,c9f0f895fb98ab9159f51fd0297e236d
-10,d3d9446802a44259755d38e6d163e820
-- Check COPY TO as user with permissions.
SET SESSION AUTHORIZATION rls_regress_user1;
SET row_security TO OFF;
6,1679091c5a880faf6fb5e6087eb1b2dc
8,c9f0f895fb98ab9159f51fd0297e236d
10,d3d9446802a44259755d38e6d163e820
-SET row_security TO FORCE;
-COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
-0,cfcd208495d565ef66e7dff9f98764da
-2,c81e728d9d4c2f636f067f89cc14862c
-4,a87ff679a2f3e71d9181a67b7542122c
-6,1679091c5a880faf6fb5e6087eb1b2dc
-8,c9f0f895fb98ab9159f51fd0297e236d
-10,d3d9446802a44259755d38e6d163e820
-- Check COPY TO as user with permissions and BYPASSRLS
SET SESSION AUTHORIZATION rls_regress_exempt_user;
SET row_security TO OFF;
6,1679091c5a880faf6fb5e6087eb1b2dc
8,c9f0f895fb98ab9159f51fd0297e236d
10,d3d9446802a44259755d38e6d163e820
-SET row_security TO FORCE;
-COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
-0,cfcd208495d565ef66e7dff9f98764da
-2,c81e728d9d4c2f636f067f89cc14862c
-4,a87ff679a2f3e71d9181a67b7542122c
-6,1679091c5a880faf6fb5e6087eb1b2dc
-8,c9f0f895fb98ab9159f51fd0297e236d
-10,d3d9446802a44259755d38e6d163e820
-- Check COPY TO as user without permissions. SET row_security TO OFF;
SET SESSION AUTHORIZATION rls_regress_user2;
SET row_security TO OFF;
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
-SET row_security TO FORCE;
-COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - permission denied
-ERROR: permission denied for relation copy_t
-- Check COPY relation TO; keep it just one row to avoid reordering issues
RESET SESSION AUTHORIZATION;
SET row_security TO ON;
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
1,c4ca4238a0b923820dcc509a6f75849b
-SET row_security TO FORCE;
-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;
ERROR: insufficient privilege to bypass row security.
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
-SET row_security TO FORCE;
-COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
-- Check COPY TO as user with permissions and BYPASSRLS
SET SESSION AUTHORIZATION rls_regress_exempt_user;
SET row_security TO OFF;
1,c4ca4238a0b923820dcc509a6f75849b
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
-SET row_security TO FORCE;
-COPY copy_rel_to 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;
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied
ERROR: permission denied for relation copy_rel_to
-SET row_security TO FORCE;
-COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied
-ERROR: permission denied for relation copy_rel_to
-- Check COPY FROM as Superuser/owner.
RESET SESSION AUTHORIZATION;
SET row_security TO OFF;
COPY copy_t FROM STDIN; --ok
SET row_security TO ON;
COPY copy_t FROM STDIN; --ok
-SET row_security TO FORCE;
-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 FROM as user with permissions.
SET SESSION AUTHORIZATION rls_regress_user1;
SET row_security TO OFF;
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.
-SET row_security TO FORCE;
-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
SET SESSION AUTHORIZATION rls_regress_exempt_user;
SET row_security TO OFF;
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.
-SET row_security TO FORCE;
-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 FROM as user without permissions.
SET SESSION AUTHORIZATION rls_regress_user2;
SET row_security TO OFF;
SET row_security TO ON;
COPY copy_t FROM STDIN; --fail - permission denied.
ERROR: permission denied for relation copy_t
-SET row_security TO FORCE;
-COPY copy_t FROM STDIN; --fail - permission denied.
-ERROR: permission denied for relation copy_t
RESET SESSION AUTHORIZATION;
DROP TABLE copy_t;
DROP TABLE copy_rel_to CASCADE;
-- Collation support
--
BEGIN;
-SET row_security TO FORCE;
CREATE TABLE coll_t (c) AS VALUES ('bar'::text);
CREATE POLICY coll_p ON coll_t USING (c < ('foo'::text COLLATE "C"));
ALTER TABLE coll_t ENABLE ROW LEVEL SECURITY;
+GRANT SELECT ON coll_t TO rls_regress_user0;
SELECT (string_to_array(polqual, ':'))[7] AS inputcollid FROM pg_policy WHERE polrelid = 'coll_t'::regclass;
inputcollid
------------------
inputcollid 950
(1 row)
+SET SESSION AUTHORIZATION rls_regress_user0;
SELECT * FROM coll_t;
c
-----
-- Converting table to view
--
BEGIN;
-SET ROW_SECURITY = FORCE;
CREATE TABLE t (c int);
CREATE POLICY p ON t USING (c % 2 = 1);
ALTER TABLE t ENABLE ROW LEVEL SECURITY;
-- Policy expression handling
--
BEGIN;
-SET row_security = FORCE;
CREATE TABLE t (c) AS VALUES ('bar'::text);
CREATE POLICY p ON t USING (max(c)); -- fails: aggregate functions are not allowed in policy expressions
ERROR: aggregate functions are not allowed in policy expressions
SELECT * FROM document;
SELECT * FROM category;
--- database superuser does not bypass RLS policy when FORCE enabled.
-RESET SESSION AUTHORIZATION;
-SET row_security TO FORCE;
-SELECT * FROM document;
-SELECT * FROM category;
-
-- database superuser does bypass RLS policy when disabled
RESET SESSION AUTHORIZATION;
SET row_security TO OFF;
SELECT * FROM document;
SELECT * FROM category;
--- RLS policy applies to table owner when FORCE enabled.
-SET SESSION AUTHORIZATION rls_regress_user0;
-SET row_security TO FORCE;
-SELECT * FROM document;
-SELECT * FROM category;
-
-- RLS policy does not apply to table owner when RLS enabled.
SET SESSION AUTHORIZATION rls_regress_user0;
SET row_security TO ON;
SELECT * FROM t1;
EXPLAIN (COSTS OFF) SELECT * FROM t1;
--- Check that default deny does apply to superuser when RLS force.
-SET row_security TO FORCE;
-RESET SESSION AUTHORIZATION;
-SELECT * FROM t1;
-EXPLAIN (COSTS OFF) SELECT * FROM t1;
-
--- Check that default deny does apply to table owner when RLS force.
-SET SESSION AUTHORIZATION rls_regress_user0;
-SELECT * FROM t1;
-EXPLAIN (COSTS OFF) SELECT * FROM t1;
-
-- Check that default deny applies to non-owner/non-superuser when RLS on.
SET SESSION AUTHORIZATION rls_regress_user1;
SET row_security TO ON;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ',';
SET row_security TO ON;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ',';
-SET row_security TO FORCE;
-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;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
SET row_security TO ON;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
-SET row_security TO FORCE;
-COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
-- Check COPY TO as user with permissions and BYPASSRLS
SET SESSION AUTHORIZATION rls_regress_exempt_user;
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
-SET row_security TO FORCE;
-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;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
SET row_security TO ON;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - permission denied
-SET row_security TO FORCE;
-COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - permission denied
-- Check COPY relation TO; keep it just one row to avoid reordering issues
RESET SESSION AUTHORIZATION;
COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
-SET row_security TO FORCE;
-COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
-- Check COPY TO as user with permissions.
SET SESSION AUTHORIZATION rls_regress_user1;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
-SET row_security TO FORCE;
-COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
-- Check COPY TO as user with permissions and BYPASSRLS
SET SESSION AUTHORIZATION rls_regress_exempt_user;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
-SET row_security TO FORCE;
-COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
-- Check COPY TO as user without permissions. SET row_security TO OFF;
SET SESSION AUTHORIZATION rls_regress_user2;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied
-SET row_security TO FORCE;
-COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied
-- Check COPY FROM as Superuser/owner.
RESET SESSION AUTHORIZATION;
3 cde
4 def
\.
-SET row_security TO FORCE;
-COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
-- Check COPY FROM as user with permissions.
SET SESSION AUTHORIZATION rls_regress_user1;
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.
-SET row_security TO FORCE;
-COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
-- Check COPY TO as user with permissions and BYPASSRLS
SET SESSION AUTHORIZATION rls_regress_exempt_user;
\.
SET row_security TO ON;
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
-SET row_security TO FORCE;
-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;
COPY copy_t FROM STDIN; --fail - permission denied.
SET row_security TO ON;
COPY copy_t FROM STDIN; --fail - permission denied.
-SET row_security TO FORCE;
-COPY copy_t FROM STDIN; --fail - permission denied.
RESET SESSION AUTHORIZATION;
DROP TABLE copy_t;
-- Collation support
--
BEGIN;
-SET row_security TO FORCE;
CREATE TABLE coll_t (c) AS VALUES ('bar'::text);
CREATE POLICY coll_p ON coll_t USING (c < ('foo'::text COLLATE "C"));
ALTER TABLE coll_t ENABLE ROW LEVEL SECURITY;
+GRANT SELECT ON coll_t TO rls_regress_user0;
SELECT (string_to_array(polqual, ':'))[7] AS inputcollid FROM pg_policy WHERE polrelid = 'coll_t'::regclass;
+SET SESSION AUTHORIZATION rls_regress_user0;
SELECT * FROM coll_t;
ROLLBACK;
-- Converting table to view
--
BEGIN;
-SET ROW_SECURITY = FORCE;
CREATE TABLE t (c int);
CREATE POLICY p ON t USING (c % 2 = 1);
ALTER TABLE t ENABLE ROW LEVEL SECURITY;
-- Policy expression handling
--
BEGIN;
-SET row_security = FORCE;
CREATE TABLE t (c) AS VALUES ('bar'::text);
CREATE POLICY p ON t USING (max(c)); -- fails: aggregate functions are not allowed in policy expressions
ROLLBACK;