Пример политики защиты строк

Алиса и Боб работают в разных отделах одной компании.

postgres=# CREATE ROLE alice LOGIN;
CREATE ROLE
postgres=# CREATE ROLE bob LOGIN;
CREATE ROLE
postgres=# CREATE TABLE users_depts(
    login text,
    department text);
CREATE TABLE
postgres=# INSERT INTO users_depts VALUES ('alice','PR'), ('bob','Sales');
INSERT 0 2

Они обращаются к одной таблице, содержащей информацию обо всех отделах. При этом и Алиса, и Боб должны видеть данные только своего отдела.

postgres=# CREATE TABLE revenue(
    department text,
    amount numeric(10,2));
CREATE TABLE
postgres=# INSERT INTO revenue SELECT 'PR',   -random()* 100.00 FROM generate_series(1,100000);
INSERT 0 100000
postgres=# INSERT INTO revenue SELECT 'Sales', random()*1000.00 FROM generate_series(1,10000);
INSERT 0 10000

Определим соответствующую политику и включим ее:

postgres=# CREATE POLICY departments ON revenue
    USING (department = (SELECT department FROM users_depts WHERE login = current_user));
CREATE POLICY
postgres=# ALTER TABLE revenue ENABLE ROW LEVEL SECURITY;
ALTER TABLE

И нужно выдать Алисе и Бобу привилегии:

postgres=# GRANT SELECT ON users_depts, revenue TO alice;
GRANT
postgres=# GRANT SELECT ON users_depts, revenue TO bob;
GRANT

Суперпользователь (он же владелец в данном случае) видит все строки независимо от политики:

postgres=# SELECT department, SUM(amount) FROM revenue GROUP BY department;
 department |     sum     
------------+-------------
 PR         | -5000016.58
 Sales      |  5024930.43
(2 rows)


А что увидят Алиса и Боб?

postgres=# \c - alice
You are now connected to database "access_rls" as user "alice".
alice=> SELECT department, SUM(amount) FROM revenue GROUP BY department;
 department |     sum     
------------+-------------
 PR         | -5000016.58
(1 row)


=> \c - bob
You are now connected to database "access_rls" as user "bob".
bob=> SELECT department, SUM(amount) FROM revenue GROUP BY department;
 department |    sum     
------------+------------
 Sales      | 5024930.43
(1 row)


Разрешим теперь Бобу добавлять строки в таблицу, но только для своего отдела и только в пределах 100 рублей:

alice=> \c - postgres
You are now connected to database "access_rls" as user "postgres".
postgres=# CREATE POLICY amount ON revenue AS RESTRICTIVE
    USING (true)                        -- видны все существующие строки
    WITH CHECK (abs(amount) <= 100.00); -- новые строки должны удовлетворять
CREATE POLICY
postgres=# GRANT INSERT ON revenue TO bob;
GRANT

Проверим:

bob=> INSERT INTO revenue VALUES ('Sales', 42.00);
INSERT 0 1
bob=> INSERT INTO revenue VALUES ('PR', 42.00);
ERROR:  new row violates row-level security policy for table "revenue"
bob=> INSERT INTO revenue VALUES ('Sales', 1000.00);
ERROR:  new row violates row-level security policy "amount" for table "revenue"

Конец демонстрации.