Postgres 规范化表上的行级安全性

Posted

技术标签:

【中文标题】Postgres 规范化表上的行级安全性【英文标题】:Row Level Security in Postgres on Normalized Tables 【发布时间】:2017-07-23 03:10:10 【问题描述】:

前提

In documentation,行级安全性似乎很棒。根据我读到的内容,我现在可以停止创建这样的视图:

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 在目标表上运行我的策略

附言我把这个问题留给了一些人含糊不清,主要是因为sqlfiddle 还没有达到 9.5。如果我需要添加更多颜色或一些要点来解决我的问题,请告诉我。

【问题讨论】:

我停止使用 SQLFiddle,因为它基本上对我不起作用。对于我使用的 Postgres:rextester.com/l/postgresql_online_compiler 【参考方案1】:

如果您将策略表述如下,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);

【讨论】:

这里用name[]合适吗? Postgres 文档指出设计人员不应使用name[],因为它仅用于系统目录。来自文档:“PostgreSQL 中还有另外两种固定长度的字符类型,如表 8-5 所示。名称类型仅用于存储内部系统目录中的标识符,不适合一般用户使用。 "链接在这里:postgresql.org/docs/9.0/static/datatype-character.html name 应该没问题,因为只存储了 PostgreSQL 标识符。但也可以使用text

以上是关于Postgres 规范化表上的行级安全性的主要内容,如果未能解决你的问题,请参考以下文章

为啥没有为 Postgres 视图启用行级安全性?

如何合并表上的行并更新 postgres 上的联结表

实体框架的行级安全性

JOOQ 中的行级安全性实现

组的行级安全性或使组可以访问行

SQL Server 2019 中 Polybase 外部表的行级安全性可能吗?