mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
RLS fixes, new hooks, and new test module
In prepend_row_security_policies(), defaultDeny was always true, so if there were any hook policies, the RLS policies on the table would just get discarded. Fixed to start off with defaultDeny as false and then properly set later if we detect that only the default deny policy exists for the internal policies. The infinite recursion detection in fireRIRrules() didn't properly manage the activeRIRs list in the case of WCOs, so it would incorrectly report infinite recusion if the same relation with RLS appeared more than once in the rtable, for example "UPDATE t ... FROM t ...". Further, the RLS expansion code in fireRIRrules() was handling RLS in the main loop through the rtable, which lead to RTEs being visited twice if they contained sublink subqueries, which prepend_row_security_policies() attempted to handle by exiting early if the RTE already had securityQuals. That doesn't work, however, since if the query involved a security barrier view on top of a table with RLS, the RTE would already have securityQuals (from the view) by the time fireRIRrules() was invoked, and so the table's RLS policies would be ignored. This is fixed in fireRIRrules() by handling RLS in a separate loop at the end, after dealing with any other sublink subqueries, thus ensuring that each RTE is only visited once for RLS expansion. The inheritance planner code didn't correctly handle non-target relations with RLS, which would get turned into subqueries during planning. Thus an update of the form "UPDATE t1 ... FROM t2 ..." where t1 has inheritance and t2 has RLS quals would fail. Fix by making sure to copy in and update the securityQuals when they exist for non-target relations. process_policies() was adding WCOs to non-target relations, which is unnecessary, and could lead to a lot of wasted time in the rewriter and the planner. Fix by only adding WCO policies when working on the result relation. Also in process_policies, we should be copying the USING policies to the WITH CHECK policies on a per-policy basis, fix by moving the copying up into the per-policy loop. Lastly, as noted by Dean, we were simply adding policies returned by the hook provided to the list of quals being AND'd, meaning that they would actually restrict records returned and there was no option to have internal policies and hook-based policies work together permissively (as all internal policies currently work). Instead, explicitly add support for both permissive and restrictive policies by having a hook for each and combining the results appropriately. To ensure this is all done correctly, add a new test module (test_rls_hooks) to test the various combinations of internal, permissive, and restrictive hook policies. Largely from Dean Rasheed (thanks!): CAEZATCVmFUfUOwwhnBTcgi6AquyjQ0-1fyKd0T3xBWJvn+xsFA@mail.gmail.com Author: Dean Rasheed, though I added the new hooks and test module.
This commit is contained in:
@ -8,6 +8,7 @@ SUBDIRS = \
|
||||
commit_ts \
|
||||
worker_spi \
|
||||
dummy_seclabel \
|
||||
test_rls_hooks \
|
||||
test_shm_mq \
|
||||
test_parser
|
||||
|
||||
|
4
src/test/modules/test_rls_hooks/.gitignore
vendored
Normal file
4
src/test/modules/test_rls_hooks/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# Generated subdirectories
|
||||
/log/
|
||||
/results/
|
||||
/tmp_check/
|
22
src/test/modules/test_rls_hooks/Makefile
Normal file
22
src/test/modules/test_rls_hooks/Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
# src/test/modules/test_rls_hooks/Makefile
|
||||
|
||||
MODULE_big = test_rls_hooks
|
||||
OBJS = test_rls_hooks.o $(WIN32RES)
|
||||
PGFILEDESC = "test_rls_hooks - example use of RLS hooks"
|
||||
|
||||
EXTENSION = test_rls_hooks
|
||||
# DATA = test_rls_hooks--1.0.sql
|
||||
|
||||
REGRESS = test_rls_hooks
|
||||
REGRESS_OPTS = --temp-config=$(top_srcdir)/src/test/modules/test_rls_hooks/rls_hooks.conf
|
||||
|
||||
ifdef USE_PGXS
|
||||
PG_CONFIG = pg_config
|
||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||
include $(PGXS)
|
||||
else
|
||||
subdir = src/test/modules/test_rls_hooks
|
||||
top_builddir = ../../../..
|
||||
include $(top_builddir)/src/Makefile.global
|
||||
include $(top_srcdir)/contrib/contrib-global.mk
|
||||
endif
|
16
src/test/modules/test_rls_hooks/README
Normal file
16
src/test/modules/test_rls_hooks/README
Normal file
@ -0,0 +1,16 @@
|
||||
test_rls_hooks is an example of how to use the hooks provided for RLS to
|
||||
define additional policies to be used.
|
||||
|
||||
Functions
|
||||
=========
|
||||
test_rls_hook_permissive(CmdType cmdtype, Relation relation)
|
||||
RETURNS List*
|
||||
|
||||
Returns a list of policies which should be added to any existing
|
||||
policies on the relation, combined with OR.
|
||||
|
||||
test_rls_hook_restrictive(CmdType cmdtype, Relation relation)
|
||||
RETURNS List*
|
||||
|
||||
Returns a list of policies which should be added to any existing
|
||||
policies on the relation, combined with AND.
|
193
src/test/modules/test_rls_hooks/expected/test_rls_hooks.out
Normal file
193
src/test/modules/test_rls_hooks/expected/test_rls_hooks.out
Normal file
@ -0,0 +1,193 @@
|
||||
CREATE TABLE rls_test_permissive (
|
||||
username name,
|
||||
supervisor name,
|
||||
data integer
|
||||
);
|
||||
-- initial test data
|
||||
INSERT INTO rls_test_permissive VALUES ('r1','s1',4);
|
||||
INSERT INTO rls_test_permissive VALUES ('r2','s2',5);
|
||||
INSERT INTO rls_test_permissive VALUES ('r3','s3',6);
|
||||
CREATE TABLE rls_test_restrictive (
|
||||
username name,
|
||||
supervisor name,
|
||||
data integer
|
||||
);
|
||||
-- initial test data
|
||||
INSERT INTO rls_test_restrictive VALUES ('r1','s1',1);
|
||||
INSERT INTO rls_test_restrictive VALUES ('r2','s2',2);
|
||||
INSERT INTO rls_test_restrictive VALUES ('r3','s3',3);
|
||||
CREATE TABLE rls_test_both (
|
||||
username name,
|
||||
supervisor name,
|
||||
data integer
|
||||
);
|
||||
-- initial test data
|
||||
INSERT INTO rls_test_both VALUES ('r1','s1',7);
|
||||
INSERT INTO rls_test_both VALUES ('r2','s2',8);
|
||||
INSERT INTO rls_test_both VALUES ('r3','s3',9);
|
||||
ALTER TABLE rls_test_permissive ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE rls_test_restrictive ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE rls_test_both ENABLE ROW LEVEL SECURITY;
|
||||
CREATE ROLE r1;
|
||||
CREATE ROLE s1;
|
||||
GRANT SELECT,INSERT ON rls_test_permissive TO r1;
|
||||
GRANT SELECT,INSERT ON rls_test_restrictive TO r1;
|
||||
GRANT SELECT,INSERT ON rls_test_both TO r1;
|
||||
GRANT SELECT,INSERT ON rls_test_permissive TO s1;
|
||||
GRANT SELECT,INSERT ON rls_test_restrictive TO s1;
|
||||
GRANT SELECT,INSERT ON rls_test_both TO s1;
|
||||
SET ROLE r1;
|
||||
-- With only the hook's policies, permissive
|
||||
-- hook's policy is current_user = username
|
||||
EXPLAIN (costs off) SELECT * FROM rls_test_permissive;
|
||||
QUERY PLAN
|
||||
-----------------------------------------
|
||||
Seq Scan on rls_test_permissive
|
||||
Filter: ("current_user"() = username)
|
||||
(2 rows)
|
||||
|
||||
SELECT * FROM rls_test_permissive;
|
||||
username | supervisor | data
|
||||
----------+------------+------
|
||||
r1 | s1 | 4
|
||||
(1 row)
|
||||
|
||||
-- success
|
||||
INSERT INTO rls_test_permissive VALUES ('r1','s1',10);
|
||||
-- failure
|
||||
INSERT INTO rls_test_permissive VALUES ('r4','s4',10);
|
||||
ERROR: new row violates WITH CHECK OPTION for "rls_test_permissive"
|
||||
SET ROLE s1;
|
||||
-- With only the hook's policies, restrictive
|
||||
-- hook's policy is current_user = supervisor
|
||||
EXPLAIN (costs off) SELECT * FROM rls_test_restrictive;
|
||||
QUERY PLAN
|
||||
-------------------------------------------
|
||||
Seq Scan on rls_test_restrictive
|
||||
Filter: ("current_user"() = supervisor)
|
||||
(2 rows)
|
||||
|
||||
SELECT * FROM rls_test_restrictive;
|
||||
username | supervisor | data
|
||||
----------+------------+------
|
||||
r1 | s1 | 1
|
||||
(1 row)
|
||||
|
||||
-- success
|
||||
INSERT INTO rls_test_restrictive VALUES ('r1','s1',10);
|
||||
-- failure
|
||||
INSERT INTO rls_test_restrictive VALUES ('r4','s4',10);
|
||||
ERROR: new row violates WITH CHECK OPTION for "rls_test_restrictive"
|
||||
SET ROLE s1;
|
||||
-- With only the hook's policies, both
|
||||
-- permissive hook's policy is current_user = username
|
||||
-- restrictive hook's policy is current_user = superuser
|
||||
-- combined with AND, results in nothing being allowed
|
||||
EXPLAIN (costs off) SELECT * FROM rls_test_both;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------
|
||||
Subquery Scan on rls_test_both
|
||||
Filter: ("current_user"() = rls_test_both.username)
|
||||
-> Seq Scan on rls_test_both rls_test_both_1
|
||||
Filter: ("current_user"() = supervisor)
|
||||
(4 rows)
|
||||
|
||||
SELECT * FROM rls_test_both;
|
||||
username | supervisor | data
|
||||
----------+------------+------
|
||||
(0 rows)
|
||||
|
||||
-- failure
|
||||
INSERT INTO rls_test_both VALUES ('r1','s1',10);
|
||||
ERROR: new row violates WITH CHECK OPTION for "rls_test_both"
|
||||
-- failure
|
||||
INSERT INTO rls_test_both VALUES ('r4','s1',10);
|
||||
ERROR: new row violates WITH CHECK OPTION for "rls_test_both"
|
||||
-- failure
|
||||
INSERT INTO rls_test_both VALUES ('r4','s4',10);
|
||||
ERROR: new row violates WITH CHECK OPTION for "rls_test_both"
|
||||
RESET ROLE;
|
||||
-- Create "internal" policies, to check that the policies from
|
||||
-- the hooks are combined correctly.
|
||||
CREATE POLICY p1 ON rls_test_permissive USING (data % 2 = 0);
|
||||
CREATE POLICY p1 ON rls_test_restrictive USING (data % 2 = 0);
|
||||
CREATE POLICY p1 ON rls_test_both USING (data % 2 = 0);
|
||||
SET ROLE r1;
|
||||
-- With both internal and hook policies, permissive
|
||||
EXPLAIN (costs off) SELECT * FROM rls_test_permissive;
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------
|
||||
Seq Scan on rls_test_permissive
|
||||
Filter: (("current_user"() = username) OR ((data % 2) = 0))
|
||||
(2 rows)
|
||||
|
||||
SELECT * FROM rls_test_permissive;
|
||||
username | supervisor | data
|
||||
----------+------------+------
|
||||
r1 | s1 | 4
|
||||
r3 | s3 | 6
|
||||
r1 | s1 | 10
|
||||
(3 rows)
|
||||
|
||||
-- success
|
||||
INSERT INTO rls_test_permissive VALUES ('r1','s1',7);
|
||||
-- success
|
||||
INSERT INTO rls_test_permissive VALUES ('r3','s3',10);
|
||||
-- failure
|
||||
INSERT INTO rls_test_permissive VALUES ('r4','s4',7);
|
||||
ERROR: new row violates WITH CHECK OPTION for "rls_test_permissive"
|
||||
SET ROLE s1;
|
||||
-- With both internal and hook policies, restrictive
|
||||
EXPLAIN (costs off) SELECT * FROM rls_test_restrictive;
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------
|
||||
Subquery Scan on rls_test_restrictive
|
||||
Filter: ((rls_test_restrictive.data % 2) = 0)
|
||||
-> Seq Scan on rls_test_restrictive rls_test_restrictive_1
|
||||
Filter: ("current_user"() = supervisor)
|
||||
(4 rows)
|
||||
|
||||
SELECT * FROM rls_test_restrictive;
|
||||
username | supervisor | data
|
||||
----------+------------+------
|
||||
r1 | s1 | 10
|
||||
(1 row)
|
||||
|
||||
-- success
|
||||
INSERT INTO rls_test_restrictive VALUES ('r1','s1',8);
|
||||
-- failure
|
||||
INSERT INTO rls_test_restrictive VALUES ('r3','s3',10);
|
||||
ERROR: new row violates WITH CHECK OPTION for "rls_test_restrictive"
|
||||
-- failure
|
||||
INSERT INTO rls_test_restrictive VALUES ('r1','s1',7);
|
||||
ERROR: new row violates WITH CHECK OPTION for "rls_test_restrictive"
|
||||
-- failure
|
||||
INSERT INTO rls_test_restrictive VALUES ('r4','s4',7);
|
||||
ERROR: new row violates WITH CHECK OPTION for "rls_test_restrictive"
|
||||
-- With both internal and hook policies, both permissive
|
||||
-- and restrictive hook policies
|
||||
EXPLAIN (costs off) SELECT * FROM rls_test_both;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------------------------------------
|
||||
Subquery Scan on rls_test_both
|
||||
Filter: (("current_user"() = rls_test_both.username) OR ((rls_test_both.data % 2) = 0))
|
||||
-> Seq Scan on rls_test_both rls_test_both_1
|
||||
Filter: ("current_user"() = supervisor)
|
||||
(4 rows)
|
||||
|
||||
SELECT * FROM rls_test_both;
|
||||
username | supervisor | data
|
||||
----------+------------+------
|
||||
(0 rows)
|
||||
|
||||
-- success
|
||||
INSERT INTO rls_test_both VALUES ('r1','s1',8);
|
||||
-- failure
|
||||
INSERT INTO rls_test_both VALUES ('r3','s3',10);
|
||||
ERROR: new row violates WITH CHECK OPTION for "rls_test_both"
|
||||
-- failure
|
||||
INSERT INTO rls_test_both VALUES ('r1','s1',7);
|
||||
ERROR: new row violates WITH CHECK OPTION for "rls_test_both"
|
||||
-- failure
|
||||
INSERT INTO rls_test_both VALUES ('r4','s4',7);
|
||||
ERROR: new row violates WITH CHECK OPTION for "rls_test_both"
|
1
src/test/modules/test_rls_hooks/rls_hooks.conf
Normal file
1
src/test/modules/test_rls_hooks/rls_hooks.conf
Normal file
@ -0,0 +1 @@
|
||||
shared_preload_libraries = test_rls_hooks
|
157
src/test/modules/test_rls_hooks/sql/test_rls_hooks.sql
Normal file
157
src/test/modules/test_rls_hooks/sql/test_rls_hooks.sql
Normal file
@ -0,0 +1,157 @@
|
||||
CREATE TABLE rls_test_permissive (
|
||||
username name,
|
||||
supervisor name,
|
||||
data integer
|
||||
);
|
||||
|
||||
-- initial test data
|
||||
INSERT INTO rls_test_permissive VALUES ('r1','s1',4);
|
||||
INSERT INTO rls_test_permissive VALUES ('r2','s2',5);
|
||||
INSERT INTO rls_test_permissive VALUES ('r3','s3',6);
|
||||
|
||||
CREATE TABLE rls_test_restrictive (
|
||||
username name,
|
||||
supervisor name,
|
||||
data integer
|
||||
);
|
||||
|
||||
-- initial test data
|
||||
INSERT INTO rls_test_restrictive VALUES ('r1','s1',1);
|
||||
INSERT INTO rls_test_restrictive VALUES ('r2','s2',2);
|
||||
INSERT INTO rls_test_restrictive VALUES ('r3','s3',3);
|
||||
|
||||
CREATE TABLE rls_test_both (
|
||||
username name,
|
||||
supervisor name,
|
||||
data integer
|
||||
);
|
||||
|
||||
-- initial test data
|
||||
INSERT INTO rls_test_both VALUES ('r1','s1',7);
|
||||
INSERT INTO rls_test_both VALUES ('r2','s2',8);
|
||||
INSERT INTO rls_test_both VALUES ('r3','s3',9);
|
||||
|
||||
ALTER TABLE rls_test_permissive ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE rls_test_restrictive ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE rls_test_both ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE ROLE r1;
|
||||
CREATE ROLE s1;
|
||||
|
||||
GRANT SELECT,INSERT ON rls_test_permissive TO r1;
|
||||
GRANT SELECT,INSERT ON rls_test_restrictive TO r1;
|
||||
GRANT SELECT,INSERT ON rls_test_both TO r1;
|
||||
|
||||
GRANT SELECT,INSERT ON rls_test_permissive TO s1;
|
||||
GRANT SELECT,INSERT ON rls_test_restrictive TO s1;
|
||||
GRANT SELECT,INSERT ON rls_test_both TO s1;
|
||||
|
||||
SET ROLE r1;
|
||||
|
||||
-- With only the hook's policies, permissive
|
||||
-- hook's policy is current_user = username
|
||||
EXPLAIN (costs off) SELECT * FROM rls_test_permissive;
|
||||
|
||||
SELECT * FROM rls_test_permissive;
|
||||
|
||||
-- success
|
||||
INSERT INTO rls_test_permissive VALUES ('r1','s1',10);
|
||||
|
||||
-- failure
|
||||
INSERT INTO rls_test_permissive VALUES ('r4','s4',10);
|
||||
|
||||
SET ROLE s1;
|
||||
|
||||
-- With only the hook's policies, restrictive
|
||||
-- hook's policy is current_user = supervisor
|
||||
EXPLAIN (costs off) SELECT * FROM rls_test_restrictive;
|
||||
|
||||
SELECT * FROM rls_test_restrictive;
|
||||
|
||||
-- success
|
||||
INSERT INTO rls_test_restrictive VALUES ('r1','s1',10);
|
||||
|
||||
-- failure
|
||||
INSERT INTO rls_test_restrictive VALUES ('r4','s4',10);
|
||||
|
||||
SET ROLE s1;
|
||||
|
||||
-- With only the hook's policies, both
|
||||
-- permissive hook's policy is current_user = username
|
||||
-- restrictive hook's policy is current_user = superuser
|
||||
-- combined with AND, results in nothing being allowed
|
||||
EXPLAIN (costs off) SELECT * FROM rls_test_both;
|
||||
|
||||
SELECT * FROM rls_test_both;
|
||||
|
||||
-- failure
|
||||
INSERT INTO rls_test_both VALUES ('r1','s1',10);
|
||||
|
||||
-- failure
|
||||
INSERT INTO rls_test_both VALUES ('r4','s1',10);
|
||||
|
||||
-- failure
|
||||
INSERT INTO rls_test_both VALUES ('r4','s4',10);
|
||||
|
||||
RESET ROLE;
|
||||
|
||||
-- Create "internal" policies, to check that the policies from
|
||||
-- the hooks are combined correctly.
|
||||
CREATE POLICY p1 ON rls_test_permissive USING (data % 2 = 0);
|
||||
|
||||
CREATE POLICY p1 ON rls_test_restrictive USING (data % 2 = 0);
|
||||
|
||||
CREATE POLICY p1 ON rls_test_both USING (data % 2 = 0);
|
||||
|
||||
SET ROLE r1;
|
||||
|
||||
-- With both internal and hook policies, permissive
|
||||
EXPLAIN (costs off) SELECT * FROM rls_test_permissive;
|
||||
|
||||
SELECT * FROM rls_test_permissive;
|
||||
|
||||
-- success
|
||||
INSERT INTO rls_test_permissive VALUES ('r1','s1',7);
|
||||
|
||||
-- success
|
||||
INSERT INTO rls_test_permissive VALUES ('r3','s3',10);
|
||||
|
||||
-- failure
|
||||
INSERT INTO rls_test_permissive VALUES ('r4','s4',7);
|
||||
|
||||
SET ROLE s1;
|
||||
|
||||
-- With both internal and hook policies, restrictive
|
||||
EXPLAIN (costs off) SELECT * FROM rls_test_restrictive;
|
||||
|
||||
SELECT * FROM rls_test_restrictive;
|
||||
|
||||
-- success
|
||||
INSERT INTO rls_test_restrictive VALUES ('r1','s1',8);
|
||||
|
||||
-- failure
|
||||
INSERT INTO rls_test_restrictive VALUES ('r3','s3',10);
|
||||
|
||||
-- failure
|
||||
INSERT INTO rls_test_restrictive VALUES ('r1','s1',7);
|
||||
|
||||
-- failure
|
||||
INSERT INTO rls_test_restrictive VALUES ('r4','s4',7);
|
||||
|
||||
-- With both internal and hook policies, both permissive
|
||||
-- and restrictive hook policies
|
||||
EXPLAIN (costs off) SELECT * FROM rls_test_both;
|
||||
|
||||
SELECT * FROM rls_test_both;
|
||||
|
||||
-- success
|
||||
INSERT INTO rls_test_both VALUES ('r1','s1',8);
|
||||
|
||||
-- failure
|
||||
INSERT INTO rls_test_both VALUES ('r3','s3',10);
|
||||
|
||||
-- failure
|
||||
INSERT INTO rls_test_both VALUES ('r1','s1',7);
|
||||
|
||||
-- failure
|
||||
INSERT INTO rls_test_both VALUES ('r4','s4',7);
|
170
src/test/modules/test_rls_hooks/test_rls_hooks.c
Normal file
170
src/test/modules/test_rls_hooks/test_rls_hooks.c
Normal file
@ -0,0 +1,170 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
*
|
||||
* test.c
|
||||
* Test harness code for shared memory message queues.
|
||||
*
|
||||
* Copyright (C) 2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/test/modules/test_rls_hooks/test_rls_hooks.c
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "fmgr.h"
|
||||
#include "miscadmin.h"
|
||||
|
||||
#include "test_rls_hooks.h"
|
||||
|
||||
#include <catalog/pg_type.h>
|
||||
#include <nodes/makefuncs.h>
|
||||
#include <nodes/makefuncs.h>
|
||||
#include <parser/parse_clause.h>
|
||||
#include <parser/parse_node.h>
|
||||
#include <parser/parse_relation.h>
|
||||
#include <rewrite/rowsecurity.h>
|
||||
#include <utils/acl.h>
|
||||
#include <utils/rel.h>
|
||||
#include <utils/relcache.h>
|
||||
|
||||
PG_MODULE_MAGIC;
|
||||
|
||||
/* Saved hook values in case of unload */
|
||||
static row_security_policy_hook_type prev_row_security_policy_hook_permissive = NULL;
|
||||
static row_security_policy_hook_type prev_row_security_policy_hook_restrictive = NULL;
|
||||
|
||||
void _PG_init(void);
|
||||
void _PG_fini(void);
|
||||
|
||||
/* Install hooks */
|
||||
void _PG_init(void)
|
||||
{
|
||||
/* Save values for unload */
|
||||
prev_row_security_policy_hook_permissive = row_security_policy_hook_permissive;
|
||||
prev_row_security_policy_hook_restrictive = row_security_policy_hook_restrictive;
|
||||
|
||||
/* Set our hooks */
|
||||
row_security_policy_hook_permissive = test_rls_hooks_permissive;
|
||||
row_security_policy_hook_restrictive = test_rls_hooks_restrictive;
|
||||
}
|
||||
|
||||
/* Uninstall hooks */
|
||||
void _PG_fini(void)
|
||||
{
|
||||
row_security_policy_hook_permissive = prev_row_security_policy_hook_permissive;
|
||||
row_security_policy_hook_restrictive = prev_row_security_policy_hook_restrictive;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return permissive policies to be added
|
||||
*/
|
||||
List*
|
||||
test_rls_hooks_permissive(CmdType cmdtype, Relation relation)
|
||||
{
|
||||
List *policies = NIL;
|
||||
RowSecurityPolicy *policy = palloc0(sizeof(RowSecurityPolicy));
|
||||
Datum role;
|
||||
FuncCall *n;
|
||||
Node *e;
|
||||
ColumnRef *c;
|
||||
ParseState *qual_pstate;
|
||||
RangeTblEntry *rte;
|
||||
|
||||
if (strcmp(RelationGetRelationName(relation),"rls_test_permissive")
|
||||
&& strcmp(RelationGetRelationName(relation),"rls_test_both"))
|
||||
return NIL;
|
||||
|
||||
qual_pstate = make_parsestate(NULL);
|
||||
|
||||
rte = addRangeTableEntryForRelation(qual_pstate, relation, NULL, false,
|
||||
false);
|
||||
addRTEtoQuery(qual_pstate, rte, false, true, true);
|
||||
|
||||
role = ObjectIdGetDatum(ACL_ID_PUBLIC);
|
||||
|
||||
policy->policy_name = pstrdup("extension policy");
|
||||
policy->policy_id = InvalidOid;
|
||||
policy->polcmd = '*';
|
||||
policy->roles = construct_array(&role, 1, OIDOID, sizeof(Oid), true, 'i');
|
||||
/*
|
||||
policy->qual = (Expr *) makeConst(BOOLOID, -1, InvalidOid,
|
||||
sizeof(bool), BoolGetDatum(true),
|
||||
false, true);
|
||||
*/
|
||||
|
||||
n = makeFuncCall(list_make2(makeString("pg_catalog"),
|
||||
makeString("current_user")), NIL, 0);
|
||||
|
||||
c = makeNode(ColumnRef);
|
||||
c->fields = list_make1(makeString("username"));
|
||||
c->location = 0;
|
||||
|
||||
e = (Node*) makeSimpleA_Expr(AEXPR_OP, "=", (Node*) n, (Node*) c, 0);
|
||||
|
||||
policy->qual = (Expr*) transformWhereClause(qual_pstate, copyObject(e),
|
||||
EXPR_KIND_WHERE,
|
||||
"POLICY");
|
||||
|
||||
policy->with_check_qual = copyObject(policy->qual);
|
||||
policy->hassublinks = false;
|
||||
|
||||
policies = list_make1(policy);
|
||||
|
||||
return policies;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return restrictive policies to be added
|
||||
*/
|
||||
List*
|
||||
test_rls_hooks_restrictive(CmdType cmdtype, Relation relation)
|
||||
{
|
||||
List *policies = NIL;
|
||||
RowSecurityPolicy *policy = palloc0(sizeof(RowSecurityPolicy));
|
||||
Datum role;
|
||||
FuncCall *n;
|
||||
Node *e;
|
||||
ColumnRef *c;
|
||||
ParseState *qual_pstate;
|
||||
RangeTblEntry *rte;
|
||||
|
||||
|
||||
if (strcmp(RelationGetRelationName(relation),"rls_test_restrictive")
|
||||
&& strcmp(RelationGetRelationName(relation),"rls_test_both"))
|
||||
return NIL;
|
||||
|
||||
qual_pstate = make_parsestate(NULL);
|
||||
|
||||
rte = addRangeTableEntryForRelation(qual_pstate, relation, NULL, false,
|
||||
false);
|
||||
addRTEtoQuery(qual_pstate, rte, false, true, true);
|
||||
|
||||
role = ObjectIdGetDatum(ACL_ID_PUBLIC);
|
||||
|
||||
policy->policy_name = pstrdup("extension policy");
|
||||
policy->policy_id = InvalidOid;
|
||||
policy->polcmd = '*';
|
||||
policy->roles = construct_array(&role, 1, OIDOID, sizeof(Oid), true, 'i');
|
||||
|
||||
n = makeFuncCall(list_make2(makeString("pg_catalog"),
|
||||
makeString("current_user")), NIL, 0);
|
||||
|
||||
c = makeNode(ColumnRef);
|
||||
c->fields = list_make1(makeString("supervisor"));
|
||||
c->location = 0;
|
||||
|
||||
e = (Node*) makeSimpleA_Expr(AEXPR_OP, "=", (Node*) n, (Node*) c, 0);
|
||||
|
||||
policy->qual = (Expr*) transformWhereClause(qual_pstate, copyObject(e),
|
||||
EXPR_KIND_WHERE,
|
||||
"POLICY");
|
||||
|
||||
policy->with_check_qual = copyObject(policy->qual);
|
||||
policy->hassublinks = false;
|
||||
|
||||
policies = list_make1(policy);
|
||||
|
||||
return policies;
|
||||
}
|
4
src/test/modules/test_rls_hooks/test_rls_hooks.control
Normal file
4
src/test/modules/test_rls_hooks/test_rls_hooks.control
Normal file
@ -0,0 +1,4 @@
|
||||
comment = 'Test code for RLS hooks'
|
||||
default_version = '1.0'
|
||||
module_pathname = '$libdir/test_rls_hooks'
|
||||
relocatable = true
|
25
src/test/modules/test_rls_hooks/test_rls_hooks.h
Normal file
25
src/test/modules/test_rls_hooks/test_rls_hooks.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
*
|
||||
* test_rls_hooks.h
|
||||
* Definitions for RLS hooks
|
||||
*
|
||||
* Copyright (C) 2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/test/modules/test_rls_hooks/test_rls_hooks.h
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef TEST_RLS_HOOKS_H
|
||||
#define TEST_RLS_HOOKS_H
|
||||
|
||||
#include <rewrite/rowsecurity.h>
|
||||
|
||||
/* Return set of permissive hooks based on CmdType and Relation */
|
||||
extern List *test_rls_hooks_permissive(CmdType cmdtype, Relation relation);
|
||||
|
||||
/* Return set of restrictive hooks based on CmdType and Relation */
|
||||
extern List *test_rls_hooks_restrictive(CmdType cmdtype, Relation relation);
|
||||
|
||||
#endif
|
@ -466,9 +466,11 @@ ALTER TABLE t1 DROP COLUMN junk1; -- just a disturbing factor
|
||||
GRANT ALL ON t1 TO public;
|
||||
COPY t1 FROM stdin WITH (oids);
|
||||
CREATE TABLE t2 (c float) INHERITS (t1);
|
||||
GRANT ALL ON t2 TO public;
|
||||
COPY t2 FROM stdin WITH (oids);
|
||||
CREATE TABLE t3 (c text, b text, a int) WITH OIDS;
|
||||
ALTER TABLE t3 INHERIT t1;
|
||||
GRANT ALL ON t3 TO public;
|
||||
COPY t3(a,b,c) FROM stdin WITH (oids);
|
||||
CREATE POLICY p1 ON t1 FOR ALL TO PUBLIC USING (a % 2 = 0); -- be even number
|
||||
CREATE POLICY p2 ON t2 FOR ALL TO PUBLIC USING (a % 2 = 1); -- be odd number
|
||||
@ -1117,22 +1119,216 @@ NOTICE: f_leak => yyyyyy
|
||||
302 | 2 | yyyyyy | (2,yyyyyy)
|
||||
(5 rows)
|
||||
|
||||
-- updates with from clause
|
||||
EXPLAIN (COSTS OFF) UPDATE t2 SET b=t2.b FROM t3
|
||||
WHERE t2.a = 3 and t3.a = 2 AND f_leak(t2.b) AND f_leak(t3.b);
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------
|
||||
Update on t2 t2_1
|
||||
-> Nested Loop
|
||||
-> Subquery Scan on t2
|
||||
Filter: f_leak(t2.b)
|
||||
-> LockRows
|
||||
-> Seq Scan on t2 t2_2
|
||||
Filter: ((a = 3) AND ((a % 2) = 1))
|
||||
-> Seq Scan on t3
|
||||
Filter: (f_leak(b) AND (a = 2))
|
||||
(9 rows)
|
||||
|
||||
UPDATE t2 SET b=t2.b FROM t3
|
||||
WHERE t2.a = 3 and t3.a = 2 AND f_leak(t2.b) AND f_leak(t3.b);
|
||||
NOTICE: f_leak => cde
|
||||
NOTICE: f_leak => xxx
|
||||
NOTICE: f_leak => zzz
|
||||
NOTICE: f_leak => yyyyyy
|
||||
EXPLAIN (COSTS OFF) UPDATE t1 SET b=t1.b FROM t2
|
||||
WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b);
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------
|
||||
Update on t1 t1_3
|
||||
Update on t1 t1_3
|
||||
Update on t2 t1
|
||||
Update on t3 t1
|
||||
-> Nested Loop
|
||||
-> Subquery Scan on t1
|
||||
Filter: f_leak(t1.b)
|
||||
-> LockRows
|
||||
-> Seq Scan on t1 t1_4
|
||||
Filter: ((a = 3) AND ((a % 2) = 0))
|
||||
-> Subquery Scan on t2
|
||||
Filter: f_leak(t2.b)
|
||||
-> Seq Scan on t2 t2_3
|
||||
Filter: ((a = 3) AND ((a % 2) = 1))
|
||||
-> Nested Loop
|
||||
-> Subquery Scan on t1_1
|
||||
Filter: f_leak(t1_1.b)
|
||||
-> LockRows
|
||||
-> Seq Scan on t2 t2_4
|
||||
Filter: ((a = 3) AND ((a % 2) = 0))
|
||||
-> Subquery Scan on t2_1
|
||||
Filter: f_leak(t2_1.b)
|
||||
-> Seq Scan on t2 t2_5
|
||||
Filter: ((a = 3) AND ((a % 2) = 1))
|
||||
-> Nested Loop
|
||||
-> Subquery Scan on t1_2
|
||||
Filter: f_leak(t1_2.b)
|
||||
-> LockRows
|
||||
-> Seq Scan on t3
|
||||
Filter: ((a = 3) AND ((a % 2) = 0))
|
||||
-> Subquery Scan on t2_2
|
||||
Filter: f_leak(t2_2.b)
|
||||
-> Seq Scan on t2 t2_6
|
||||
Filter: ((a = 3) AND ((a % 2) = 1))
|
||||
(34 rows)
|
||||
|
||||
UPDATE t1 SET b=t1.b FROM t2
|
||||
WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b);
|
||||
EXPLAIN (COSTS OFF) UPDATE t2 SET b=t2.b FROM t1
|
||||
WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b);
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------------
|
||||
Update on t2 t2_1
|
||||
-> Nested Loop
|
||||
-> Subquery Scan on t2
|
||||
Filter: f_leak(t2.b)
|
||||
-> LockRows
|
||||
-> Seq Scan on t2 t2_2
|
||||
Filter: ((a = 3) AND ((a % 2) = 1))
|
||||
-> Subquery Scan on t1
|
||||
Filter: f_leak(t1.b)
|
||||
-> Result
|
||||
-> Append
|
||||
-> Seq Scan on t1 t1_1
|
||||
Filter: ((a = 3) AND ((a % 2) = 0))
|
||||
-> Seq Scan on t2 t2_3
|
||||
Filter: ((a = 3) AND ((a % 2) = 0))
|
||||
-> Seq Scan on t3
|
||||
Filter: ((a = 3) AND ((a % 2) = 0))
|
||||
(17 rows)
|
||||
|
||||
UPDATE t2 SET b=t2.b FROM t1
|
||||
WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b);
|
||||
NOTICE: f_leak => cde
|
||||
-- updates with from clause self join
|
||||
EXPLAIN (COSTS OFF) UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2
|
||||
WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b
|
||||
AND f_leak(t2_1.b) AND f_leak(t2_2.b) RETURNING *, t2_1, t2_2;
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------
|
||||
Update on t2 t2_1_1
|
||||
-> Nested Loop
|
||||
Join Filter: (t2_1.b = t2_2.b)
|
||||
-> Subquery Scan on t2_1
|
||||
Filter: f_leak(t2_1.b)
|
||||
-> LockRows
|
||||
-> Seq Scan on t2 t2_1_2
|
||||
Filter: ((a = 3) AND ((a % 2) = 1))
|
||||
-> Subquery Scan on t2_2
|
||||
Filter: f_leak(t2_2.b)
|
||||
-> Seq Scan on t2 t2_2_1
|
||||
Filter: ((a = 3) AND ((a % 2) = 1))
|
||||
(12 rows)
|
||||
|
||||
UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2
|
||||
WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b
|
||||
AND f_leak(t2_1.b) AND f_leak(t2_2.b) RETURNING *, t2_1, t2_2;
|
||||
NOTICE: f_leak => cde
|
||||
NOTICE: f_leak => cde
|
||||
a | b | c | a | b | c | t2_1 | t2_2
|
||||
---+-----+-----+---+-----+-----+-------------+-------------
|
||||
3 | cde | 3.3 | 3 | cde | 3.3 | (3,cde,3.3) | (3,cde,3.3)
|
||||
(1 row)
|
||||
|
||||
EXPLAIN (COSTS OFF) UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2
|
||||
WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b
|
||||
AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2;
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------
|
||||
Update on t1 t1_1_3
|
||||
Update on t1 t1_1_3
|
||||
Update on t2 t1_1
|
||||
Update on t3 t1_1
|
||||
-> Nested Loop
|
||||
Join Filter: (t1_1.b = t1_2.b)
|
||||
-> Subquery Scan on t1_1
|
||||
Filter: f_leak(t1_1.b)
|
||||
-> LockRows
|
||||
-> Seq Scan on t1 t1_1_4
|
||||
Filter: ((a = 4) AND ((a % 2) = 0))
|
||||
-> Subquery Scan on t1_2
|
||||
Filter: f_leak(t1_2.b)
|
||||
-> Append
|
||||
-> Seq Scan on t1 t1_2_3
|
||||
Filter: ((a = 4) AND ((a % 2) = 0))
|
||||
-> Seq Scan on t2 t1_2_4
|
||||
Filter: ((a = 4) AND ((a % 2) = 0))
|
||||
-> Seq Scan on t3 t1_2_5
|
||||
Filter: ((a = 4) AND ((a % 2) = 0))
|
||||
-> Nested Loop
|
||||
Join Filter: (t1_1_1.b = t1_2_1.b)
|
||||
-> Subquery Scan on t1_1_1
|
||||
Filter: f_leak(t1_1_1.b)
|
||||
-> LockRows
|
||||
-> Seq Scan on t2 t1_1_5
|
||||
Filter: ((a = 4) AND ((a % 2) = 0))
|
||||
-> Subquery Scan on t1_2_1
|
||||
Filter: f_leak(t1_2_1.b)
|
||||
-> Append
|
||||
-> Seq Scan on t1 t1_2_6
|
||||
Filter: ((a = 4) AND ((a % 2) = 0))
|
||||
-> Seq Scan on t2 t1_2_7
|
||||
Filter: ((a = 4) AND ((a % 2) = 0))
|
||||
-> Seq Scan on t3 t1_2_8
|
||||
Filter: ((a = 4) AND ((a % 2) = 0))
|
||||
-> Nested Loop
|
||||
Join Filter: (t1_1_2.b = t1_2_2.b)
|
||||
-> Subquery Scan on t1_1_2
|
||||
Filter: f_leak(t1_1_2.b)
|
||||
-> LockRows
|
||||
-> Seq Scan on t3 t1_1_6
|
||||
Filter: ((a = 4) AND ((a % 2) = 0))
|
||||
-> Subquery Scan on t1_2_2
|
||||
Filter: f_leak(t1_2_2.b)
|
||||
-> Append
|
||||
-> Seq Scan on t1 t1_2_9
|
||||
Filter: ((a = 4) AND ((a % 2) = 0))
|
||||
-> Seq Scan on t2 t1_2_10
|
||||
Filter: ((a = 4) AND ((a % 2) = 0))
|
||||
-> Seq Scan on t3 t1_2_11
|
||||
Filter: ((a = 4) AND ((a % 2) = 0))
|
||||
(52 rows)
|
||||
|
||||
UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2
|
||||
WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b
|
||||
AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2;
|
||||
NOTICE: f_leak => dddddd_updt
|
||||
NOTICE: f_leak => dddddd_updt
|
||||
NOTICE: f_leak => defdef
|
||||
NOTICE: f_leak => defdef
|
||||
NOTICE: f_leak => dddddd_updt
|
||||
NOTICE: f_leak => defdef
|
||||
a | b | a | b | t1_1 | t1_2
|
||||
---+-------------+---+-------------+-----------------+-----------------
|
||||
4 | dddddd_updt | 4 | dddddd_updt | (4,dddddd_updt) | (4,dddddd_updt)
|
||||
4 | defdef | 4 | defdef | (4,defdef) | (4,defdef)
|
||||
(2 rows)
|
||||
|
||||
RESET SESSION AUTHORIZATION;
|
||||
SET row_security TO OFF;
|
||||
SELECT * FROM t1;
|
||||
SELECT * FROM t1 ORDER BY a,b;
|
||||
a | b
|
||||
---+-------------
|
||||
1 | aaa
|
||||
3 | ccc
|
||||
2 | bbbbbb_updt
|
||||
4 | dddddd_updt
|
||||
1 | abc
|
||||
3 | cde
|
||||
2 | bcdbcd
|
||||
4 | defdef
|
||||
1 | xxx
|
||||
3 | zzz
|
||||
2 | bbbbbb_updt
|
||||
2 | bcdbcd
|
||||
2 | yyyyyy
|
||||
3 | ccc
|
||||
3 | cde
|
||||
3 | zzz
|
||||
4 | dddddd_updt
|
||||
4 | defdef
|
||||
(11 rows)
|
||||
|
||||
SET SESSION AUTHORIZATION rls_regress_user1;
|
||||
@ -1192,6 +1388,103 @@ NOTICE: f_leak => yyyyyy
|
||||
302 | 2 | yyyyyy | (2,yyyyyy)
|
||||
(3 rows)
|
||||
|
||||
--
|
||||
-- S.b. view on top of Row-level security
|
||||
--
|
||||
SET SESSION AUTHORIZATION rls_regress_user0;
|
||||
CREATE TABLE b1 (a int, b text);
|
||||
INSERT INTO b1 (SELECT x, md5(x::text) FROM generate_series(-10,10) x);
|
||||
CREATE POLICY p1 ON b1 USING (a % 2 = 0);
|
||||
ALTER TABLE b1 ENABLE ROW LEVEL SECURITY;
|
||||
GRANT ALL ON b1 TO rls_regress_user1;
|
||||
SET SESSION AUTHORIZATION rls_regress_user1;
|
||||
CREATE VIEW bv1 WITH (security_barrier) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
|
||||
GRANT ALL ON bv1 TO rls_regress_user2;
|
||||
SET SESSION AUTHORIZATION rls_regress_user2;
|
||||
EXPLAIN (COSTS OFF) SELECT * FROM bv1 WHERE f_leak(b);
|
||||
QUERY PLAN
|
||||
---------------------------------------------
|
||||
Subquery Scan on bv1
|
||||
Filter: f_leak(bv1.b)
|
||||
-> Seq Scan on b1
|
||||
Filter: ((a > 0) AND ((a % 2) = 0))
|
||||
(4 rows)
|
||||
|
||||
SELECT * FROM bv1 WHERE f_leak(b);
|
||||
NOTICE: f_leak => c81e728d9d4c2f636f067f89cc14862c
|
||||
NOTICE: f_leak => a87ff679a2f3e71d9181a67b7542122c
|
||||
NOTICE: f_leak => 1679091c5a880faf6fb5e6087eb1b2dc
|
||||
NOTICE: f_leak => c9f0f895fb98ab9159f51fd0297e236d
|
||||
NOTICE: f_leak => d3d9446802a44259755d38e6d163e820
|
||||
a | b
|
||||
----+----------------------------------
|
||||
2 | c81e728d9d4c2f636f067f89cc14862c
|
||||
4 | a87ff679a2f3e71d9181a67b7542122c
|
||||
6 | 1679091c5a880faf6fb5e6087eb1b2dc
|
||||
8 | c9f0f895fb98ab9159f51fd0297e236d
|
||||
10 | d3d9446802a44259755d38e6d163e820
|
||||
(5 rows)
|
||||
|
||||
INSERT INTO bv1 VALUES (-1, 'xxx'); -- should fail view WCO
|
||||
ERROR: new row violates WITH CHECK OPTION for "b1"
|
||||
INSERT INTO bv1 VALUES (11, 'xxx'); -- should fail RLS check
|
||||
ERROR: new row violates WITH CHECK OPTION for "b1"
|
||||
INSERT INTO bv1 VALUES (12, 'xxx'); -- ok
|
||||
EXPLAIN (COSTS OFF) UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak(b);
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------------------
|
||||
Update on b1 b1_1
|
||||
-> Subquery Scan on b1
|
||||
Filter: f_leak(b1.b)
|
||||
-> Subquery Scan on b1_2
|
||||
-> LockRows
|
||||
-> Seq Scan on b1 b1_3
|
||||
Filter: ((a > 0) AND (a = 4) AND ((a % 2) = 0))
|
||||
(7 rows)
|
||||
|
||||
UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak(b);
|
||||
NOTICE: f_leak => a87ff679a2f3e71d9181a67b7542122c
|
||||
EXPLAIN (COSTS OFF) DELETE FROM bv1 WHERE a = 6 AND f_leak(b);
|
||||
QUERY PLAN
|
||||
---------------------------------------------------------------------------
|
||||
Delete on b1 b1_1
|
||||
-> Subquery Scan on b1
|
||||
Filter: f_leak(b1.b)
|
||||
-> Subquery Scan on b1_2
|
||||
-> LockRows
|
||||
-> Seq Scan on b1 b1_3
|
||||
Filter: ((a > 0) AND (a = 6) AND ((a % 2) = 0))
|
||||
(7 rows)
|
||||
|
||||
DELETE FROM bv1 WHERE a = 6 AND f_leak(b);
|
||||
NOTICE: f_leak => 1679091c5a880faf6fb5e6087eb1b2dc
|
||||
SET SESSION AUTHORIZATION rls_regress_user0;
|
||||
SELECT * FROM b1;
|
||||
a | b
|
||||
-----+----------------------------------
|
||||
-10 | 1b0fd9efa5279c4203b7c70233f86dbf
|
||||
-9 | 252e691406782824eec43d7eadc3d256
|
||||
-8 | a8d2ec85eaf98407310b72eb73dda247
|
||||
-7 | 74687a12d3915d3c4d83f1af7b3683d5
|
||||
-6 | 596a3d04481816330f07e4f97510c28f
|
||||
-5 | 47c1b025fa18ea96c33fbb6718688c0f
|
||||
-4 | 0267aaf632e87a63288a08331f22c7c3
|
||||
-3 | b3149ecea4628efd23d2f86e5a723472
|
||||
-2 | 5d7b9adcbe1c629ec722529dd12e5129
|
||||
-1 | 6bb61e3b7bce0931da574d19d1d82c88
|
||||
0 | cfcd208495d565ef66e7dff9f98764da
|
||||
1 | c4ca4238a0b923820dcc509a6f75849b
|
||||
2 | c81e728d9d4c2f636f067f89cc14862c
|
||||
3 | eccbc87e4b5ce2fe28308fd9f2a7baf3
|
||||
5 | e4da3b7fbbce2345d7772b0674a318d5
|
||||
7 | 8f14e45fceea167a5a36dedd4bea2543
|
||||
8 | c9f0f895fb98ab9159f51fd0297e236d
|
||||
9 | 45c48cce2e2d7fbdea1afc51c7c6ad26
|
||||
10 | d3d9446802a44259755d38e6d163e820
|
||||
12 | xxx
|
||||
4 | yyy
|
||||
(21 rows)
|
||||
|
||||
--
|
||||
-- ROLE/GROUP
|
||||
--
|
||||
|
@ -207,6 +207,8 @@ COPY t1 FROM stdin WITH (oids);
|
||||
\.
|
||||
|
||||
CREATE TABLE t2 (c float) INHERITS (t1);
|
||||
GRANT ALL ON t2 TO public;
|
||||
|
||||
COPY t2 FROM stdin WITH (oids);
|
||||
201 1 abc 1.1
|
||||
202 2 bcd 2.2
|
||||
@ -216,6 +218,8 @@ COPY t2 FROM stdin WITH (oids);
|
||||
|
||||
CREATE TABLE t3 (c text, b text, a int) WITH OIDS;
|
||||
ALTER TABLE t3 INHERIT t1;
|
||||
GRANT ALL ON t3 TO public;
|
||||
|
||||
COPY t3(a,b,c) FROM stdin WITH (oids);
|
||||
301 1 xxx X
|
||||
302 2 yyy Y
|
||||
@ -423,9 +427,45 @@ UPDATE only t1 SET b = b WHERE f_leak(b) RETURNING oid, *, t1;
|
||||
UPDATE t1 SET b = b WHERE f_leak(b) RETURNING *;
|
||||
UPDATE t1 SET b = b WHERE f_leak(b) RETURNING oid, *, t1;
|
||||
|
||||
-- updates with from clause
|
||||
EXPLAIN (COSTS OFF) UPDATE t2 SET b=t2.b FROM t3
|
||||
WHERE t2.a = 3 and t3.a = 2 AND f_leak(t2.b) AND f_leak(t3.b);
|
||||
|
||||
UPDATE t2 SET b=t2.b FROM t3
|
||||
WHERE t2.a = 3 and t3.a = 2 AND f_leak(t2.b) AND f_leak(t3.b);
|
||||
|
||||
EXPLAIN (COSTS OFF) UPDATE t1 SET b=t1.b FROM t2
|
||||
WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b);
|
||||
|
||||
UPDATE t1 SET b=t1.b FROM t2
|
||||
WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b);
|
||||
|
||||
EXPLAIN (COSTS OFF) UPDATE t2 SET b=t2.b FROM t1
|
||||
WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b);
|
||||
|
||||
UPDATE t2 SET b=t2.b FROM t1
|
||||
WHERE t1.a = 3 and t2.a = 3 AND f_leak(t1.b) AND f_leak(t2.b);
|
||||
|
||||
-- updates with from clause self join
|
||||
EXPLAIN (COSTS OFF) UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2
|
||||
WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b
|
||||
AND f_leak(t2_1.b) AND f_leak(t2_2.b) RETURNING *, t2_1, t2_2;
|
||||
|
||||
UPDATE t2 t2_1 SET b = t2_2.b FROM t2 t2_2
|
||||
WHERE t2_1.a = 3 AND t2_2.a = t2_1.a AND t2_2.b = t2_1.b
|
||||
AND f_leak(t2_1.b) AND f_leak(t2_2.b) RETURNING *, t2_1, t2_2;
|
||||
|
||||
EXPLAIN (COSTS OFF) UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2
|
||||
WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b
|
||||
AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2;
|
||||
|
||||
UPDATE t1 t1_1 SET b = t1_2.b FROM t1 t1_2
|
||||
WHERE t1_1.a = 4 AND t1_2.a = t1_1.a AND t1_2.b = t1_1.b
|
||||
AND f_leak(t1_1.b) AND f_leak(t1_2.b) RETURNING *, t1_1, t1_2;
|
||||
|
||||
RESET SESSION AUTHORIZATION;
|
||||
SET row_security TO OFF;
|
||||
SELECT * FROM t1;
|
||||
SELECT * FROM t1 ORDER BY a,b;
|
||||
|
||||
SET SESSION AUTHORIZATION rls_regress_user1;
|
||||
SET row_security TO ON;
|
||||
@ -435,6 +475,39 @@ EXPLAIN (COSTS OFF) DELETE FROM t1 WHERE f_leak(b);
|
||||
DELETE FROM only t1 WHERE f_leak(b) RETURNING oid, *, t1;
|
||||
DELETE FROM t1 WHERE f_leak(b) RETURNING oid, *, t1;
|
||||
|
||||
--
|
||||
-- S.b. view on top of Row-level security
|
||||
--
|
||||
SET SESSION AUTHORIZATION rls_regress_user0;
|
||||
CREATE TABLE b1 (a int, b text);
|
||||
INSERT INTO b1 (SELECT x, md5(x::text) FROM generate_series(-10,10) x);
|
||||
|
||||
CREATE POLICY p1 ON b1 USING (a % 2 = 0);
|
||||
ALTER TABLE b1 ENABLE ROW LEVEL SECURITY;
|
||||
GRANT ALL ON b1 TO rls_regress_user1;
|
||||
|
||||
SET SESSION AUTHORIZATION rls_regress_user1;
|
||||
CREATE VIEW bv1 WITH (security_barrier) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
|
||||
GRANT ALL ON bv1 TO rls_regress_user2;
|
||||
|
||||
SET SESSION AUTHORIZATION rls_regress_user2;
|
||||
|
||||
EXPLAIN (COSTS OFF) SELECT * FROM bv1 WHERE f_leak(b);
|
||||
SELECT * FROM bv1 WHERE f_leak(b);
|
||||
|
||||
INSERT INTO bv1 VALUES (-1, 'xxx'); -- should fail view WCO
|
||||
INSERT INTO bv1 VALUES (11, 'xxx'); -- should fail RLS check
|
||||
INSERT INTO bv1 VALUES (12, 'xxx'); -- ok
|
||||
|
||||
EXPLAIN (COSTS OFF) UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak(b);
|
||||
UPDATE bv1 SET b = 'yyy' WHERE a = 4 AND f_leak(b);
|
||||
|
||||
EXPLAIN (COSTS OFF) DELETE FROM bv1 WHERE a = 6 AND f_leak(b);
|
||||
DELETE FROM bv1 WHERE a = 6 AND f_leak(b);
|
||||
|
||||
SET SESSION AUTHORIZATION rls_regress_user0;
|
||||
SELECT * FROM b1;
|
||||
|
||||
--
|
||||
-- ROLE/GROUP
|
||||
--
|
||||
|
Reference in New Issue
Block a user