Postgres RLS 政策和功能

问题描述

我在 Postgres 函数上违反了 RLS 政策。我相信这是因为该策略依赖于在函数中创建的行。 SELECT 命令在函数中运行。新行不可用,因为它们仍在事务中。

功能如下:

CREATE FUNCTION public.create_message(organization_id int,content text,tags Int[])
RETURNS setof public.message
AS $$

-- insert message,return PK
WITH moved_rows AS (
  INSERT INTO public.message (organization_id,content)
    VALUES($1,$2)
  RETURNING *
),-- many to many relation
moved_tags AS (
  INSERT INTO public.message_tag (message_id,tag_id)
  SELECT moved_rows.id,tagInput.tag_id
  FROM moved_rows,UNnesT($3) as tagInput(tag_id)
  RETURNING *
)

SELECT moved_rows.* FROM moved_rows LEFT JOIN moved_tags ON moved_rows.id = moved_tags.message_id

$$ LANGUAGE sql VOLATILE STRICT;

这是政策:

CREATE POLICY select_if_organization
      on message_tag
      for select 
      USING ( message_id IN (
        SELECT message.id 
          FROM message
          INNER JOIN organization_user ON (organization_user.organization_id = message.organization_id) 
          INNER JOIN sessions ON (sessions.user_id = organization_user.user_id)
          WHERE sessions.session_token = current_user_id()));

想法:

  1. 在连接表中添加一个字段以简化策略,但它违反了正常形式。
  2. 返回用户输入而不是运行 SELECT,但输入可能会被转义,我应该能够运行 SELECT 命令
  3. 分为两个功能。创建 message 行,然后添加 message_tag。我正在运行 postgraphile,所以有两个突变。我在两者之间设置了外键关系。我不知道 graphile 是否会自动做到这一点。

错误信息:

ERROR:  new row violates row-level security policy for table "message_tag"
CONTEXT:  sql function "create_message" statement 1

我在运行该函数时收到错误消息。我希望函数成功运行,在 message 表中插入一行,并将输入数组转换为 message_tag 表的行,其中 message_tag.message_id=message.id,最后插入的 id。我需要制定一项政策,以便来自该加入关系的用户只能看到他们自己组织的 message_tag 行。

这是关于 INSERT 命令的另一个策略。如果用户登录,则允许插入:

create policy insert_message_tag_if_author
  on message_tag
  for insert
  with check (EXISTS (SELECT * FROM sessions WHERE sessions.session_token = current_user_id()));

解决方法

根据错误提示,你的这部分SQL语句导致错误:

INSERT INTO public.message_tag (message_id,tag_id)
  SELECT moved_rows.id,tagInput.tag_id
  FROM moved_rows,UNNEST($3) as tagInput(tag_id)
  RETURNING *

您需要使用适当的 FOR INSERT 子句添加另一个策略 WITH CHECK

,

我最终在连接表中添加了一个字段,并用它创建了一个策略。这样,RLS 验证不需要在函数中间创建的行。