0

来自 JS 世界的 SQL 新手,需要有关触发器的一些建议。
我的用户有一个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();

搜索了很多,老实说,我想我可能找不到任何东西,因为我缺少正确的术语来找到我需要找到的东西。

不仅希望帮助您解决这个特定问题,还希望为需要特权的来宾用户提供有关政策的任何相关建议。

语境:

这些是相关的表格和功能

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为表的iduser匹配的行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();
4

1 回答 1

1

你没有说什么是客人,但你的方法似乎是错误的。

您应该选择以下方法之一,而不是在低级别禁用检查,这会给您带来不好的感觉:

  • 修复您的策略以允许必要的操作(可能通过添加许可策略)

  • SECURITY DEFINER具有由属于不受限制的用户的功能执行的某些操作。

如果您坚持使用基于触发器的解决方案,则必须使用BEFORE触发器。另外,请考虑您不能将参数与SET语句一起使用。您要么必须使用动态 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();
于 2020-02-20T07:26:28.183 回答