规范化表格中Postgres的行级安全性

时间:2017-03-03 05:51:21

标签: sql postgresql row-level-security

前提

vendored into your application,行级别安全性似乎很棒。基于我所读到的内容,我现在可以停止创建这样的视图:

SELECT data.*
FROM data
JOIN user_data
ON data.id = user_data.data_id
AND user_data.role = CURRENT_ROLE

最重要的是,Postgres对view进行了很好的分析,首先是索引扫描,然后是user_data表上的散列连接,这正是我们想要发生的事情,因为它很快。将其与我的RLS实现进行比较:

CREATE POLICY data_owner
ON data
FOR ALL
TO user
USING (
  (
    SELECT TRUE AS BOOL FROM (
      SELECT data_id FROM user_data WHERE user_role = CURRENT_USER
    ) AS user_data WHERE user_data.data_id = data.id
  ) = true
)
WITH CHECK (TRUE);

策略的这个笨拙执行data表中每一行的条件,而不是通过将查询范围限定为我们的CURRENT_USER有权访问的行来进行优化,就像我们的观点一样。需要说明的是,这意味着select * from data会在data表格中点击每个行。

问题

如何编写内部select的策略,该策略不会在目标表中的每个行上测试所述select。换句话说:在运行对结果的实际查询之前,如何让RLS在目标表上运行我的策略?

P.S。我把这个问题留给了一个含糊不清的小问题,主要是因为In documentation尚未达到9.5。如果我需要添加更多颜色或一些要点来解答我的问题,请告诉我。

1 个答案:

答案 0 :(得分:4)

如果您按照以下方式说明政策,PostgreSQL可能会生成更好的计划:

...
USING (EXISTS
          (SELECT data_id
           FROM user_data
           WHERE user_data.data_id = data.id
             AND role = current_user
          )
      )

你应该有一个(PRIMARY KEY?)索引ON user_data (role, data_id)来加速嵌套循环连接。

但我认为将权限信息包含在data表本身中可能是更好的设计,可能使用name[]类型:

CREATE TABLE data(
   id integer PRIMARY KEY,
   val text,
   acl name[] NOT NULL
);

INSERT INTO data VALUES (1, 'one',   ARRAY[name 'laurenz', name 'advpg']);
INSERT INTO data VALUES (2, 'two',   ARRAY[name 'advpg']);
INSERT INTO data VALUES (3, 'three', ARRAY[name 'laurenz']);

然后你可以使用这样的政策:

CREATE POLICY data_owner ON data FOR ALL TO PUBLIC
   USING (acl @> ARRAY[current_user::name])
   WITH CHECK (TRUE);
ALTER TABLE data ENABLE ROW LEVEL SECURITY;
ALTER TABLE data FORCE ROW LEVEL SECURITY;

当我SELECT时,我只获得了我有权限的行:

SELECT id, val FROM data;
 id |  val
----+-------
  1 | one
  3 | three
(2 rows)

您可以定义GIN索引以支持该条件:

CREATE INDEX ON data USING gin (acl _name_ops);