CREATE POLICY name ON table_name
[ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ]
[ TO { role_name | PUBLIC } [, ...] ]
[ USING ( using_expression ) ]
[ WITH CHECK ( check_expression ) ]
http://git.postgresql.org/gitweb/?p=postgresql.git;a=summary
[root@db-172-16-3-150 soft_bak]# tar -zxvf postgresql-7320681.tar.gz
[root@db-172-16-3-150 soft_bak]# cd postgresql-7320681
[root@db-172-16-3-150 postgresql-7320681]# ./configure --prefix=/opt/pgsql9.5 --with-pgport=1922 --with-perl --with-python --with-tcl --with-openssl --with-pam --with-ldap --with-libxml --with-libxslt --enable-thread-safety --with-blocksize=32 --enable-debug
[root@db-172-16-3-150 postgresql-7320681]# gmake world && gmake install-world
[root@db-172-16-3-150 postgresql-7320681]# useradd pg95
[root@db-172-16-3-150 ~]# su - pg95
[pg95@db-172-16-3-150 ~]$ vi .bash_profile
# add by digoal
export PS1="$USER@`/bin/hostname -s`-> "
export PGPORT=1922
export PGDATA=/data02/pgdata95/pg_root
export LANG=en_US.utf8
export PGHOME=/opt/pgsql9.5
export LD_LIBRARY_PATH=$PGHOME/lib:/lib64:/usr/lib64:/usr/local/lib64:/lib:/usr/lib:/usr/local/lib:$LD_LIBRARY_PATH
export DATE=`date +"%Y%m%d%H%M"`
export PATH=$PGHOME/bin:$PATH:.
export MANPATH=$PGHOME/share/man:$MANPATH
export PGHOST=$PGDATA
export PGDATABASE=postgres
alias rm='rm -i'
alias ll='ls -lh'
unalias vi
[root@db-172-16-3-150 ~]# mkdir /data02/pgdata95
[root@db-172-16-3-150 ~]# chown pg95:pg95 /data02/pgdata95
[root@db-172-16-3-150 ~]# su - pg95
pg95@db-172-16-3-150-> initdb -D $PGDATA -E UTF8 --locale=C -U postgres -W
创建三个角色
postgres=# create role r1 login;
CREATE ROLE
postgres=# create role r2 login;
CREATE ROLE
postgres=# create role r3 login;
CREATE ROLE
创建测试表
postgres=# create table test(id int, r name);
CREATE TABLE
postgres=# insert into test values(1, 'r1');
INSERT 0 1
postgres=# insert into test values(2, 'r2');
INSERT 0 1
postgres=# insert into test values(3, 'r3');
INSERT 0 1
postgres=# grant all on table test to public;
GRANT
创建一个新增数据的策略(使用with check)
postgres=# create policy p on test for insert to r1 with check( r = current_user);
CREATE POLICY
默认情况下策略是disable状态的,
postgres=# \d+ test
Table "public.test"
Column | Type | Modifiers | Storage | Stats target | Description
--------+---------+-----------+---------+--------------+-------------
id | integer | | plain | |
r | name | | plain | |
Policies (Row Security Disabled):
POLICY "p" FOR INSERT
TO r1
WITH CHECK (r = "current_user"())
通过pg_policies视图可以查看已经创建的策略.
postgres=# select * from pg_policies ;
schemaname | tablename | policyname | roles | cmd | qual | with_check
------------+-----------+------------+-------+--------+------+------------------------
public | test | p | {r1} | INSERT | | (r = "current_user"())
(1 row)
在策略enable前, 是无视策略的.
postgres=> insert into test values(4,'r1');
INSERT 0 1
postgres=> insert into test values(4,'r2');
INSERT 0 1
使策略生效
postgres=# alter table test enable row level security;
ALTER TABLE
postgres=> \d+ test
Table "public.test"
Column | Type | Modifiers | Storage | Stats target | Description
--------+---------+-----------+---------+--------------+-------------
id | integer | | plain | |
r | name | | plain | |
Policies:
POLICY "p" FOR INSERT
TO r1
WITH CHECK (r = "current_user"())
现在策略生效了, 再次插入, 你会看到只能插入和r1角色同名的r值.
postgres=# \c postgres r1
You are now connected to database "postgres" as user "r1".
postgres=> insert into test values(4,'r2');
ERROR: new row violates WITH CHECK OPTION for "test"
postgres=> insert into test values(4,'r1');
INSERT 0 1
再新增一个策略, 现在r1角色插入test表时, 允许r字段的值为'r1','r2'.
postgres=# create policy p1 on test for insert to r1 with check( r = 'r2');
CREATE POLICY
postgres=# \c postgres r1
You are now connected to database "postgres" as user "r1".
postgres=> insert into test values(4,'r2');
INSERT 0 1
postgres=> insert into test values(4,'r1');
INSERT 0 1
postgres=> insert into test values(4,'r3');
ERROR: new row violates WITH CHECK OPTION for "test"
接下来创建旧值的策略. r1用户只能查看到r=current_user的值.
postgres=> \c postgres postgres
You are now connected to database "postgres" as user "postgres".\
postgres=# create policy p2 on test for select to r1 using ( r = current_user);
CREATE POLICY
postgres=# \c postgres r1
You are now connected to database "postgres" as user "r1".
postgres=> select * from test;
id | r
----+----
1 | r1
4 | r1
4 | r1
4 | r1
(4 rows)
当然, 我们也可以创建一个针对所有用户的策略, 例如, 所有用户只能看到 r = current_user 的值.
postgres=> \c postgres postgres
You are now connected to database "postgres" as user "postgres".
postgres=# create policy p3 on test for select to public using ( r = current_user);
CREATE POLICY
postgres=# \c postgres r2
You are now connected to database "postgres" as user "r2".
postgres=> select * from test;
id | r
----+----
2 | r2
4 | r2
4 | r2
(3 rows)
当然了, 所有这些策略只针对非超级用户以及非owner
postgres=> \c postgres postgres
You are now connected to database "postgres" as user "postgres".
postgres=# select * from test;
id | r
----+----
1 | r1
2 | r2
3 | r3
4 | r1
4 | r2
4 | r1
4 | r2
4 | r1
(8 rows)
把r1改为超级用户, 策略失效.
postgres=# alter role r1 superuser;
ALTER ROLE
postgres=# \c postgres r1
You are now connected to database "postgres" as user "r1".
postgres=# select * from test;
id | r
----+----
1 | r1
2 | r2
3 | r3
4 | r1
4 | r2
4 | r1
4 | r2
4 | r1
(8 rows)
postgres=# \d test
Table "public.test"
Column | Type | Modifiers
--------+---------+-----------
id | integer |
r | name |
Policies:
POLICY "p4" FOR UPDATE
TO r3
USING (r = "current_user"())
postgres=# \c postgres r3
You are now connected to database "postgres" as user "r3".
因为针对r3角色创建了update策略, 但是没有创建其他命令的策略, 所以其他命令的策略默认为FALSE?
postgres=> select * from test;
id | r
----+---
(0 rows)
更新操作应用了策略.
postgres=> update test set id=4 where r='r3';
UPDATE 1
postgres=> select * from test;
id | r
----+---
(0 rows)
现在创建SELECT的策略, 可以查询了
postgres=# create policy p1 on test for select to r3 using ( r = current_user);
CREATE POLICY
postgres=# \d+ test
Table "public.test"
Column | Type | Modifiers | Storage | Stats target | Description
--------+---------+-----------+---------+--------------+-------------
id | integer | | plain | |
r | name | | plain | |
Policies:
POLICY "p1" FOR SELECT
TO r3
USING (r = "current_user"())
POLICY "p4" FOR UPDATE
TO r3
USING (r = "current_user"())
postgres=# \c postgres r3
You are now connected to database "postgres" as user "r3".
postgres=> select * from test;
id | r
----+----
4 | r3
(1 row)
但是delete命令上还没有创建策略, 所以删除操作直接FALSE.
postgres=> delete from test ;
DELETE 0
在r1角色上, 没有创建任何策略, 所以操作是允许的.
postgres=> \c postgres r1
You are now connected to database "postgres" as user "r1".
postgres=# select * from test;
id | r
----+----
1 | r1
2 | r2
4 | r1
4 | r2
4 | r1
4 | r2
4 | r1
4 | r3
(8 rows)
在使用pg_dump导出或者使用pg_restore导入时. 如果表enable了row security策略.
pg95@db-172-16-3-150-> pg_dump -U r2 -t test postgres
.....
COPY test (id, r) FROM stdin;
pg_dump: [archiver (db)] query failed: ERROR: insufficient privilege to bypass row security.
pg_dump: [archiver (db)] query was: COPY public.test (id, r) TO stdout;
最后要讲一下使用策略的过滤顺序, 还记得视图攻击吗?
普通函数
postgres=> \c postgres postgres
You are now connected to database "postgres" as user "postgres".
postgres=# create or replace function attack(int,name) returns boolean as $$
postgres$# declare
postgres$# begin
postgres$# raise notice '%,%', $1,$2;
postgres$# return true;
postgres$# end;
postgres$# $$ language plpgsql cost 0.00000000000000000000001 ;
CREATE FUNCTION
postgres=# \c postgres r3
You are now connected to database "postgres" as user "r3".
postgres=> select * from test where attack(id,r);
NOTICE: 4,r3
id | r
----+----
4 | r3
(1 row)
普通函数在语句中的过滤是在策略过滤器后面执行
postgres=> explain select * from test where attack(id,r);
QUERY PLAN
--------------------------------------------------------------------
Subquery Scan on test (cost=0.00..61.32 rows=6 width=68)
Filter: attack(test.id, test.r)
-> Seq Scan on test test_1 (cost=0.00..61.15 rows=17 width=68)
Filter: (r = "current_user"())
(4 rows)
如果修改为leakproof函数的话
postgres=> \c postgres postgres
You are now connected to database "postgres" as user "postgres".
postgres=# alter function attack(int,name) LEAKPROOF;
ALTER FUNCTION
就直接上升到和过滤器一起执行了
postgres=# \c postgres r3
postgres=> explain select * from test where attack(id,r);
QUERY PLAN
------------------------------------------------------
Seq Scan on test (cost=0.00..61.15 rows=6 width=68)
Filter: (attack(id, r) AND (r = "current_user"()))
(2 rows)
可以从函数中读取出所有数据
postgres=> select * from test where attack(id,r);
NOTICE: 1,r1
NOTICE: 2,r2
NOTICE: 4,r1
NOTICE: 4,r2
NOTICE: 4,r1
NOTICE: 4,r2
NOTICE: 4,r1
NOTICE: 4,r3
id | r
----+----
4 | r3
(1 row)
[参考]
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. The table owner and superuser can request that row policies be applied to their queries by setting this to 'force'. Lastly, this can also be set to 'off' which 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.
The allowed values of row_security are on (apply normally - not to superuser or table owner), off (fail if row security would be applied), and force (apply always - even to superuser and table owner).
For more information on row security policies, see CREATE POLICY.