Ниже вы можете найти код минимального тестового примера для проблемы, с которой я сталкиваюсь в системе, где RLS используется для управления доступом к иерархической структуре данных. Я использую Postgres v11.
В коде у меня units
, это объект верхнего уровня. units
имеют subunits
в отношении 1-n.
Есть также users
, где user
может иметь доступ к нескольким units
через таблицу unit_owner
.
Политики RLS предназначены для того, чтобы позволить user
вставить новый subunits
в units
, которым он владеет.
Все это прекрасно работает, как видно до 2-й последней строчки кода.
НО вот моя проблема: Эта база данных предоставляется через промежуточное программное обеспечение GraphQL (Postgraphile), которому требуется результат вставки обратно с помощью функции INSERT ... RETURNING
.
И, как видно из последнего оператора вставки, это не работает, возникает ОШИБКА: новая строка нарушает политику безопасности на уровне строк.
Проблема, по-видимому, возникает из-за того, что для RETURNING требуются права выбора, а функция политики выбора оценивается с использованием набора subunit
идентификаторов, доступных до вставки, а не после.
Любые советы о том, как я могу позволить моим пользователям вставлять подразделения в свои устройства, будут оценены!
CREATE SCHEMA insert_returning;
CREATE ROLE users;
GRANT USAGE ON SCHEMA insert_returning TO users;
DROP TABLE IF EXISTS insert_returning.unit;
DROP TABLE IF EXISTS insert_returning.subunit;
DROP TABLE IF EXISTS insert_returning.unit_owner;
CREATE TABLE insert_returning.unit (
id integer NOT NULL,
description varchar NULL,
CONSTRAINT unit_pk PRIMARY KEY (id)
);
CREATE TABLE insert_returning.subunit (
id integer NOT NULL,
unit_id integer NOT NULL,
description varchar NULL,
CONSTRAINT subunit_pk PRIMARY KEY (id)
);
CREATE TABLE insert_returning.unit_owner (
user_id integer NOT NULL,
unit_id integer NOT NULL
);
GRANT SELECT,INSERT,UPDATE ON TABLE insert_returning.unit TO users;
GRANT SELECT,INSERT,UPDATE ON TABLE insert_returning.subunit TO users;
GRANT SELECT ON TABLE insert_returning.unit_owner TO users;
CREATE OR REPLACE FUNCTION insert_returning.get_users_units()
RETURNS SETOF integer
LANGUAGE sql VOLATILE SECURITY DEFINER AS
$$
SELECT uo.unit_id FROM insert_returning.unit_owner uo
WHERE uo.user_id = 17;
$$;
CREATE OR REPLACE FUNCTION insert_returning.get_users_subunits()
RETURNS SETOF integer
LANGUAGE sql VOLATILE SECURITY DEFINER AS
$$
SELECT s.id FROM insert_returning.subunit s
JOIN insert_returning.unit_owner uo ON uo.unit_id = s.unit_id
WHERE uo.user_id = 17;
$$;
ALTER TABLE insert_returning.unit ENABLE ROW LEVEL SECURITY;
ALTER TABLE insert_returning.subunit ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS select_unit ON insert_returning.unit;
DROP POLICY IF EXISTS select_subunit ON insert_returning.subunit;
DROP POLICY IF EXISTS insert_subunit ON insert_returning.subunit;
CREATE POLICY select_unit ON insert_returning.unit FOR SELECT TO PUBLIC USING ((
SELECT (id IN ( SELECT unit_id FROM insert_returning.unit_owner WHERE user_id = 17))
));
CREATE POLICY select_subunit ON insert_returning.subunit FOR SELECT TO PUBLIC USING ((
SELECT (id IN (SELECT insert_returning.get_users_subunits()) )
));
CREATE POLICY insert_subunit ON insert_returning.subunit FOR INSERT TO PUBLIC WITH CHECK ((
SELECT (unit_id IN (SELECT insert_returning.get_users_units()) )
));
INSERT INTO insert_returning.unit (id, description) VALUES (1, 'I am visible');
INSERT INTO insert_returning.unit (id, description) VALUES (2, 'I am hidden');
INSERT INTO insert_returning.subunit (id, unit_id, description) VALUES (1, 1, 'I belong to a visible unit');
INSERT INTO insert_returning.subunit (id, unit_id, description) VALUES (2, 2, 'I belong to a hidden unit');
INSERT INTO insert_returning.subunit (id, unit_id, description) VALUES (3, 1, 'I too belong to a visible unit');
INSERT INTO insert_returning.unit_owner (user_id,unit_id) VALUES (17,1);
SET ROLE users;
SELECT * FROM insert_returning.subunit; -- works
INSERT INTO insert_returning.subunit VALUES (4, 1, 'I am a new subunit'); -- works
INSERT INTO insert_returning.subunit VALUES (5, 1, 'I am another new subunit') RETURNING *; -- FAILS
--
id IN get_users_subunits()
дляSELECT
политики вместо более простогоunit_id in get_users_units()
? Этот подход работал для политикиINSERT
, я думаю, его следует использовать и для политикиSELECT
. - person Bergi   schedule 07.10.2020SECURITY DEFINER
функция, которая работает с rls, проверяет разрешение вручную и возвращает идентификатор вставленной строки. - person Bergi   schedule 07.10.2020