Redrock Postgres 搜索 英文
版本: 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17

CREATE POLICY

CREATE POLICY — 定义表格行级别安全策略

概要

CREATE POLICY name ON table_name
    [ AS { PERMISSIVE | RESTRICTIVE } ]
    [ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ]
    [ TO { role_name | PUBLIC | CURRENT_ROLE | CURRENT_USER | SESSION_USER } [, ...] ]
    [ USING ( using_expression ) ]
    [ WITH CHECK ( check_expression ) ]

描述

CREATE POLICY 命令定义表格行级别安全策略。请注意,表格必须启用行级别安全(使用 ALTER TABLE ... ENABLE ROW LEVEL SECURITY)令创建的安全策略得以应用。

策略赋予选择、插入、更新或删除符合相应策略条件的行权限。在 USING 中指定的条件会检查现有表格行,在 WITH CHECK 中指定的条件会检查经由 INSERTUPDATE 新创建的行。当某个 USING 条件对某行返回 true 时,那么这行可见于用户,当返回 false 或 null 时,那么这行不可见。当某个 WITH CHECK 条件对某行返回 true 时,那么这行可插入或更新,当返回 false 或 null 时,那么就会发生错误。

对于 INSERTUPDATEMERGE 语句,在 BEFORE 触发器触发之后并实际执行任何数据修改之前会强制执行 WITH CHECK 表达式。因此,BEFORE ROW 触发器可能会修改要插入的数据,从而影响安全策略检查的结果。WITH CHECK 表达式在执行任何其他约束之前强制执行。

策略名称属于每个表。因此,一个策略名称可用于许多不同的表,并为每个表都有一个定义,此定义适用于该表。

可以针对特定命令或特定角色应用策略。对于新创建的策略,其默认值为适用于所有命令和角色,除非另行指定。多个策略可能适用于单个命令;有关更多详细信息,请参见下文。 表 297 总结了针对特定命令如何应用不同类型的策略。

对于可能同时具有 USINGWITH CHECK 表达式的策略(ALLUPDATE),如果未定义任何 WITH CHECK 表达式,则 USING 表达式将用于确定哪些行可见(常规 USING 用例)以及哪些新行可被添加到(WITH CHECK 用例)。

如果针对某个表启用了行级安全性,但不存在适用的策略,则假定一个 default deny 策略,以便没有行可见或可更新。

参数

name

要创建的策略的名称。此名称必须不同于针对该表的任何其他策略的名称。

table_name

策略所应用的表 (可选择采用架构限定) 的名称。

PERMISSIVE

指定要创建的策略为允许性策略。所有针对给定查询适用的允许性策略都将使用布尔 OR 运算符组合在一起。通过创建允许性策略,管理员可以添加到可以访问的记录集中。策略默认情况下是允许性的。

RESTRICTIVE

指定要创建的策略为限制性策略。所有针对给定查询适用的限制性策略都将使用布尔 AND 运算符组合在一起。通过创建限制性策略,管理员可以减少可以访问的记录集,因为所有限制性策略必须针对每个记录通过。

请注意,在限制性策略可有效地用于减少访问权限之前,需要至少有一项允许策略才能授予对记录的访问权限。如果仅存在限制性策略,那么就无法访问任何记录。在存在允许策略和限制性策略的混合时,除了满足所有限制性策略外,仅当满足至少一项允许策略时才能访问记录。

命令

策略应用到的命令。有效的选项是 ALLSELECTINSERTUPDATEDELETEALL 是默认值。请参阅以下内容以了解如何具体应用这些选项。

role_name

策略要应用到的角色。默认值为 PUBLIC,这会将策略应用到所有角色。

using_expression

任意SQL条件表达式(返回 boolean)。条件表达式不能包含任何聚合或窗口函数。如果启用了行级安全,此表达式将添加到引用表中的查询中。将显示表达式返回 true 的行。表达式返回 false 或 null 的任何行对于用户都不可见(在 SELECT 中),也无法进行修改(在 UPDATEDELETE 中)。这些行会被静默抑制;不会报告错误。

check_expression

任意SQL条件表达式(返回 boolean)。条件表达式不能包含任何聚合或窗口函数。如果启用了行级安全,此表达式将在对表的 INSERTUPDATE 查询中使用。只有满足表达式求值为 true 的行才允许。如果插入的任何记录或更新产生的任何记录的表达式的求值为 false 或 null,将引发错误。请注意,check_expression 根据行的拟议新内容评估,而不是原始内容评估。

按命令划分策略

ALL #

ALL 用于策略表示将策略应用于所有命令,无论命令的类型如何。如果存在 ALL 策略且存在更具体的策略,那么将会应用 ALL 策略和更具体的策略(或策略)。此外,如果仅定义了 USING 表达式,那么将在查询的选择侧和修改侧同时使用 ALL 策略,在两种情况下都使用 USING 表达式。

例如,如果调用了 UPDATE,则 ALL 策略将同时适用于 UPDATE 可以选择为待更新行(应用 USING 表达式)和选出的已更新行,以检查是否被允许添加到表(应用 WITH CHECK 表达式(若已定义),否则应用 USING 表达式)。如果 INSERTUPDATE 命令尝试向表添加不通过 ALL 策略的 WITH CHECK 表达式的行,将中止整个命令。

SELECT #

为策略使用 SELECT 意味着它将会应用于 SELECT 查询,并且在被定义的策略关系上需要 SELECT 权限时。其结果是,在 SELECT 查询期间,只返回来自关系的那些通过 SELECT 策略的记录,并且需要 SELECT 权限的查询,例如 UPDATE,也只看到 SELECT 策略允许的那些记录。SELECT 策略无法具有 WITH CHECK 表达式,因为它只在从关系检索记录的情况下应用。

INSERT #

为策略使用 INSERT 意味着它将会应用于包含 INSERT 操作的 INSERT 命令和 MERGE 命令。不通过此策略的被插入行将导致策略违规错误,并且将中止整个 INSERT 命令。INSERT 策略无法具有 USING 表达式,因为它只在向关系添加记录的情况下应用。

请注意,带 ON CONFLICT DO UPDATEINSERT 会在 INSERT 路径追加到关系的行中检查 INSERT 策略的 WITH CHECK 表达式。

UPDATE #

UPDATE 用于策略意味着它将应用于 UPDATESELECT FOR UPDATESELECT FOR SHARE 命令,以及 INSERT 命令的辅助 ON CONFLICT DO UPDATE 子句。MERGE 命令(包含 UPDATE 操作)也会受到影响。由于 UPDATE 包括提取现有记录并用新修改的记录替换它,因此 UPDATE 策略接受 USING 表达式和 WITH CHECK 表达式。 USING 表达式确定 UPDATE 命令将看到哪些记录来进行操作,而 WITH CHECK 表达式定义哪些修改的行允许存储回到关系中。

任何更新的值未通过 WITH CHECK 表达式的行都会导致错误,并且整个命令将被中止。如果仅指定了 USING 子句,那么该子句将用于 USINGWITH CHECK 两种情况。

通常,UPDATE 命令还需要从正在更新的关系中的列读取数据(例如,在 WHERE 子句或 RETURNING 子句中,或在 SET 子句右侧的表达式中)。在这种情况下,还需要对正在更新的关系具有 SELECT 权限,并且除了 UPDATE 策略之外,还会应用适当的 SELECTALL 策略。因此,除了通过 UPDATEALL 策略被授予更新该行 (行) 的权限之外,该用户还必须能够通过 SELECTALL 策略访问正在更新的行 (行)。

INSERT 命令具有辅助 ON CONFLICT DO UPDATE 子句时,如果采取 UPDATE 路径,则将首先根据任何 UPDATE 策略的 USING 表达式检查要更新的行,然后根据 WITH CHECK 表达式检查新更新的行。但请注意,不同于独立的 UPDATE 命令,如果现有行未通过 USING 表达式,将引发一个错误(UPDATE 路径永远不会被静默避免)。

DELETE #

对一项策略使用 DELETE 意味着它将应用于 DELETE 命令。仅通过此策略的行对 DELETE 命令可见。可能有一些行可以通过 SELECT 查看,但如果它们未通过 DELETE 策略的 USING 表达式,则不可删除。

在大多数情况下,DELETE 命令还需要读取删除所在关系中的列数据(例如,在 WHERE 子句或 RETURNING 子句中)。在这种情况下,还需要对该关系具有 SELECT 权限,除了 DELETE 策略以外,还将应用相应的 SELECTALL 策略。因此,用户必须除了通过 DELETEALL 策略授予删除行的权限以外,还需要通过 SELECTALL 策略访问要删除的行。

DELETE 策略不能具有 WITH CHECK 表达式,因为它仅适用于从关系中删除记录的情况,在这种情况下没有要检查的新行。

表 297。按命令类型应用的策略

命令 SELECT/ALL 策略 INSERT/ALL 策略 UPDATE/ALL 策略 DELETE/ALL 策略
USING 表达式 WITH CHECK 表达式 USING 表达式 WITH CHECK 表达式 USING 表达式
SELECT 现有行
SELECT FOR UPDATE/SHARE 现有行 现有行
INSERT / MERGE ... THEN INSERT 新行
INSERT ... RETURNING 新行 [a] 新行
UPDATE / MERGE ... THEN UPDATE 现有和新行 [a] 现有行 新行
DELETE 现有行 [a] 现有行
ON CONFLICT DO UPDATE 现有和新行 现有行 新行

[a]如果现有行或新行需要读取访问权限(例如,引用了来自关系的列的 WHERERETURNING 子句)。


应用多个策略

当针对相同命令(例如,SELECTUPDATE 策略应用于 UPDATE 命令)应用不同命令类型的多个策略时,那么用户必须同时具有两种类型的权限(例如,从关系中选择行的权限以及更新该行的权限)。因此,一种策略类型的表达式与另一种策略类型的表达式使用 AND 运算符进行组合。

当针对相同命令应用相同命令类型的多个策略时,则必须至少有一个 PERMISSIVE 策略授予关系访问权限,并且所有 RESTRICTIVE 策略都必须通过。因此,将使用 OR 组合所有 PERMISSIVE 策略表达式,使用 AND 组合所有 RESTRICTIVE 策略表达式,并将结果使用 AND 组合。如果没有 PERMISSIVE 策略,则拒绝访问权限。

请注意,为了组合多个策略,所有 ALL 策略都将被视为与正在应用的任何其他策略类型具有相同的类型。

例如,在一个同时需要 SELECTUPDATE 权限的 UPDATE 命令中,如果存在多个适用于每种类型的策略,则将按如下所示进行组合

expression from RESTRICTIVE SELECT/ALL policy 1
AND
expression from RESTRICTIVE SELECT/ALL policy 2
AND
...
AND
(
  expression from PERMISSIVE SELECT/ALL policy 1
  OR
  expression from PERMISSIVE SELECT/ALL policy 2
  OR
  ...
)
AND
expression from RESTRICTIVE UPDATE/ALL policy 1
AND
expression from RESTRICTIVE UPDATE/ALL policy 2
AND
...
AND
(
  expression from PERMISSIVE UPDATE/ALL policy 1
  OR
  expression from PERMISSIVE UPDATE/ALL policy 2
  OR
  ...
)

注意

你必须是某张表的拥有者以创建或更改针对它的策略。

系统在执行内部参照完整性检查或验证约束条件时不会应用针对数据库中表的显式查询的策略。这意味着有间接方法可以确定给定值是否存在。一个示例是尝试向主键或具有唯一约束的列中插入重复值。如果插入失败,那么用户可以推断出该值已存在。(此示例假定系统允许用户根据策略插入他们无权查看的记录。)另一个示例是用户被允许插入引用其他隐藏表中的表。可以通过向该引用表中插入值来确定存在,成功说明该值存在于被引用表中。可以通过谨慎制定策略来解决这些问题,以防止用户能够插入、删除或更新所有可能表明他们无法看到的值的记录,或者通过使用生成的值(例如替代键)代替带有外部含义的键。

通常,系统将在用户查询中显示的限定条件之前执行使用安全策略施加的筛选条件,以防止意外地向可能不可信任的用户定义的函数公开受保护数据。但是,系统(或系统管理员)标记为 LEAKPROOF 的函数和运算符可在策略表达式之前求值,因为假定它们是可信任的。

由于策略表达式会直接添加到用户的查询中,因此它们将使用正在运行整体查询的用户的权限进行运行。因此,使用给定策略的用户必须能够访问表达式中引用的所有表或函数,否则,在尝试查询启用行级别安全性的表时,他们只会收到一个拒绝访问权限的错误。然而,这并不会改变视图的工作方式。与正常查询和视图一样,由视图引用的表的权限检查和策略将使用视图所有者的权限和适用于视图所有者的所有策略,但视图是使用 security_invoker 选项定义的情况除外(请参见 CREATE VIEW)。

MERGE 没有单独的策略。相反,在执行 MERGE 时,会应用为 SELECTINSERTUPDATEDELETE 定义的策略,具体取决于执行的操作。

可以在 第 5.9 节 中找到更多讨论和实用示例。

兼容性

CREATE POLICY 是一个 PostgreSQL 扩展。

另请参见

ALTER POLICYDROP POLICYALTER TABLE