2024年9月26日: PostgreSQL 17 发布!
支持的版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:devel
不支持的版本:11 / 10 / 9.6 / 9.5

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 中指定的表达式进行检查,而通过 INSERTUPDATE 创建的新行则根据 WITH CHECK 中指定的表达式进行检查。当 USING 表达式对于给定行返回 true 时,该行对用户可见,而如果返回 false 或 null,则该行不可见。当 WITH CHECK 表达式对于某行返回 true 时,该行将被插入或更新,而如果返回 false 或 null,则会发生错误。

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

策略名称是针对每个表的。因此,一个策略名称可以用于许多不同的表,并且每个表都有一个适合该表的定义。

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

对于既可以有 USING 又可以有 WITH CHECK 表达式的策略(ALLUPDATE),如果未定义 WITH CHECK 表达式,则 USING 表达式将同时用于确定哪些行可见(正常的 USING 案例)以及允许添加哪些新行(WITH CHECK 案例)。

如果为表启用了行级安全,但不存在任何适用的策略,则假定为 默认拒绝 策略,因此任何行都不可见或不可更新。

参数

name

要创建的策略的名称。这必须与表中任何其他策略的名称不同。

table_name

策略应用到的表的名称(可选地带模式限定)。

PERMISSIVE

指定策略要作为许可策略创建。所有适用于给定查询的许可策略将使用布尔 OR 运算符组合在一起。通过创建许可策略,管理员可以添加可以访问的记录集。策略默认为许可策略。

RESTRICTIVE

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

请注意,在可以有效地使用限制性策略来减少访问之前,需要至少一个许可策略来授予对记录的访问权限。如果仅存在限制性策略,则任何记录都不可访问。当存在许可策略和限制性策略的混合时,只有在至少一个许可策略通过以及所有限制性策略都通过的情况下,记录才可访问。

command

策略应用到的命令。有效选项为 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 策略和更具体的策略(或策略)都将应用。此外,ALL 策略将应用于查询的选择端和修改端,如果仅定义了 USING 表达式,则在这两种情况下都使用 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 策略的 WITH CHECK 表达式,以获取通过 INSERT 路径添加到关联的行。

UPDATE #

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

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

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

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

DELETE #

在策略中使用 DELETE 表示它将应用于 DELETE 命令。只有通过此策略的行才能被 DELETE 命令看到。如果某些行未通过 DELETE 策略的 USING 表达式,则它们可能可以通过 SELECT 看到,但无法删除。

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

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 策略都必须通过。因此,所有 PERMISSIVE 策略表达式使用 OR 组合,所有 RESTRICTIVE 策略表达式使用 AND 组合,结果使用 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 POLICYPostgreSQL 扩展。

提交更正

如果您在文档中看到任何不正确的内容、与您对特定功能的体验不符的内容或需要进一步澄清的内容,请使用 此表单 报告文档问题。