Установка локальной конфигурации в SQL перед INSERT - PullRequest
0 голосов
/ 20 февраля 2020

newb ie в SQL из мира JS, которому нужен совет по триггерам.
У моего пользователя есть столбец id, и мой GraphQL API всегда вызывает INSERT INTO .... RETURNING *, а затем выполняет преобразования на слой GraphQL для возврата того, что я хочу.
Цель состоит в том, чтобы запрос типа INSERT INTO .... RETURNING * работал с RLS на месте.

Политики:

CREATE POLICY USER_SELECT_RESTRICTIONS 
ON "user" 
FOR SELECT
USING ("id" = current_user_id() OR current_user_role() = 'admin' OR current_user_role() = 'partner');

CREATE POLICY USER_INSERT_RESTRICTIONS
ON "user" 
FOR INSERT
WITH CHECK (true);

Это нарушает для гостевых пользователей (больше контекста внизу), потому что им разрешено INSERT, но не может SELECT (из-за ограничения, что только авторизованные пользователи могут выбирать свои собственные строки).

Поэтому у меня возникла идея каким-то образом установить пользовательские настройки вручную в триггере до / после вставки (не знаю, какие, потому что я не мог двигаться вперед из-за синтаксической ошибки).

Я пробовал это, но у меня есть синтаксис ошибка в NEW.id? (он просто говорит мне «Синтаксическая ошибка»)

CREATE OR REPLACE FUNCTION after_user_insert()
RETURNS TRIGGER AS $$
BEGIN
  SET LOCAL jwt.claims.userId NEW.id;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER set_user_id_on_insert
AFTER INSERT ON "user"
FOR EACH ROW
EXECUTE PROCEDURE after_user_insert();

Много искал и, честно говоря, я думаю, что могу просто ничего не найти, потому что мне не хватает правильной терминологии, чтобы найти то, что мне нужно найти .

Буду признателен не только за помощь в решении этой конкретной проблемы c, но и за любые рекомендации по политикам для гостевых пользователей, которым требуются привилегии.

Контекст:

Эти являются соответствующими таблицами и функциями

CREATE TYPE "user_role" AS ENUM (
  'customer',
  'partner',
  'admin'
);
​
CREATE TABLE "user" (
  "id" uuid UNIQUE PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(),
  "first_name" varchar NOT NULL,
  "last_name" varchar NOT NULL,
  "email" text UNIQUE NOT NULL,
  "role" user_role NOT NULL DEFAULT 'customer',
  "created_at" timestamp NOT NULL DEFAULT NOW(),
  "updated_at" timestamp NOT NULL DEFAULT NOW()
);
​
CREATE FUNCTION current_user_id() RETURNS uuid AS $$
  SELECT nullif(current_setting('jwt.claims.userId', true), '')::uuid;
$$ LANGUAGE SQL stable;
​
CREATE FUNCTION current_user_role() RETURNS user_role AS $$
  SELECT nullif(current_setting('jwt.claims.role', true), '')::user_role;
$$ LANGUAGE SQL stable

RLS ограничивает SELECT строками, в которых столбец id таблицы user соответствует current_setting('jwt.claims.userId').
. Ранее он был установлен Postgraphile как видел здесь (https://www.graphile.org/postgraphile/security/).

Один из возможных вариантов обхода, о котором я подумал, был бы следующим, но я не знаю, приведет ли это к какой-либо уязвимости из-за очевидного повышения роли :

CREATE OR REPLACE FUNCTION after_user_insert()
RETURNS TRIGGER AS $$
BEGIN
  SET LOCAL jwt.claims.role TO "customer";
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION before_user_insert()
RETURNS TRIGGER AS $$
BEGIN
  SET LOCAL jwt.claims.role TO "admin";
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER before_insert
BEFORE INSERT ON "user"
FOR EACH ROW
EXECUTE PROCEDURE before_user_insert();

CREATE TRIGGER after_insert
AFTER INSERT ON "user"
FOR EACH ROW
EXECUTE PROCEDURE after_user_insert();

1 Ответ

1 голос
/ 20 февраля 2020

Вы не говорите, что гость, но ваш подход кажется неправильным.

Вместо того, чтобы отключать ваши чеки на низком уровне, что вызывает у вас плохое предчувствие по уважительным причинам, вы должны выбрать один из следующие подходы:

  • исправьте ваши политики, чтобы разрешить необходимую операцию (возможно, путем добавления разрешающей политики)

  • имеют определенные операции, выполняемые SECURITY DEFINER функция, которая принадлежит пользователю без ограничений.

Если вы настаиваете на решении, основанном на триггере, вы должны использовать триггер BEFORE. Также учтите, что вы не можете использовать параметры с оператором SET. Вы должны будете использовать Dynami c SQL или (лучше) использовать функцию:

CREATE OR REPLACE FUNCTION before_user_insert() RETURNS TRIGGER AS
$$BEGIN
   SELECT set_config('jwt.claims.userId', NEW.id::text, TRUE);
   RETURN NEW;
END;$$ LANGUAGE plpgsql;

CREATE TRIGGER set_user_id_on_insert BEFORE INSERT ON "user"
FOR EACH ROW EXECUTE PROCEDURE before_user_insert();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...