在 INSERT 之前在 SQL 中设置本地配置
Setting local config in SQL before INSERT
来自 JS 世界的 SQL 新手需要一些有关触发器的建议。
我的用户有一个 id
列,我的 GraphQL API 总是调用 INSERT INTO .... RETURNING *
,然后在 GraphQL 层上进行转换以 return 我想要的。
目标是允许像 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
(因为只有授权用户才能 select 他们的限制自己的行)。
所以我想以某种方式在触发器 before/after 插入中手动设置用户设置(不知道是哪个,因为语法错误我无法前进)。
我已经试过了,但我在 NEW.id
? (它只是对我说 "Syntax Error")
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();
搜索了很多,老实说,我想我可能找不到任何东西,因为我缺少正确的术语来找到我需要找到的东西。
不仅希望就此特定问题提供帮助,还希望为需要特权的来宾用户提供有关政策的任何相关建议。
上下文:
这些是相关的 table 和函数
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
限制为 user
table 的 id
列匹配 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();
你没有说客人是什么,但你的做法似乎不对。
您应该选择以下方法之一,而不是在低级别上禁用您的检查,这会给您带来不好的感觉:
修复您的策略以允许必要的操作(可能通过添加许可策略)
某些操作由属于不受限制的用户的 SECURITY DEFINER
函数执行。
如果您坚持使用基于触发器的解决方案,则必须使用 BEFORE
触发器。另外,请注意您不能将 parameters 与 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();
来自 JS 世界的 SQL 新手需要一些有关触发器的建议。
我的用户有一个 id
列,我的 GraphQL API 总是调用 INSERT INTO .... RETURNING *
,然后在 GraphQL 层上进行转换以 return 我想要的。
目标是允许像 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
(因为只有授权用户才能 select 他们的限制自己的行)。
所以我想以某种方式在触发器 before/after 插入中手动设置用户设置(不知道是哪个,因为语法错误我无法前进)。
我已经试过了,但我在 NEW.id
? (它只是对我说 "Syntax Error")
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();
搜索了很多,老实说,我想我可能找不到任何东西,因为我缺少正确的术语来找到我需要找到的东西。
不仅希望就此特定问题提供帮助,还希望为需要特权的来宾用户提供有关政策的任何相关建议。
上下文:
这些是相关的 table 和函数
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
限制为 user
table 的 id
列匹配 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();
你没有说客人是什么,但你的做法似乎不对。
您应该选择以下方法之一,而不是在低级别上禁用您的检查,这会给您带来不好的感觉:
修复您的策略以允许必要的操作(可能通过添加许可策略)
某些操作由属于不受限制的用户的
SECURITY DEFINER
函数执行。
如果您坚持使用基于触发器的解决方案,则必须使用 BEFORE
触发器。另外,请注意您不能将 parameters 与 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();