↑↓ 选择↵ 打开⌫ 切换范围完整搜索

PG.CENTER 连接 PostgreSQL 文档、百科与生态知识。由 Pigsty 维护。

支持中的版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发中的版本: 19 / 20devel
已结束支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5
历史版本。 PostgreSQL 10 已结束支持。 2022-11-10. 请参阅 当前版本手册.

CREATE POLICY

CREATE POLICY — 为表定义新的行级安全策略

大纲

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

描述

CREATE POLICY 命令为一个表定义一条新的行级安全策略。请注意,必须先在该表上启用行级安全(使用 ALTER TABLE ... ENABLE ROW LEVEL SECURITY),已创建的策略才会被应用。

策略授予选择、插入、更新或删除符合相应策略表达式的行的权限。使用 USING 中指定的表达式检查现有表行,而使用 WITH CHECK 中指定的表达式检查通过 INSERT 或 UPDATE 创建的新行。当 USING 表达式对给定行返回 true 时,该行对用户可见;如果返回 false 或 null,则该行不可见。当 WITH CHECK 表达式对某行返回 true 时,该行会被插入或更新;如果返回 false 或 null,则发生错误。

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

策略名称按表区分。因此,同一个策略名可以用于许多不同的表,并且在每个表上都可以有适合该表的定义。

策略可以针对特定命令或特定角色应用。除非另有指定,新建策略默认适用于所有命令和角色。多个策略可以应用于同一命令;更多细节见下文。表 241 总结了不同类型的策略如何应用于特定命令。

对于既可以具有 USING 又可以具有 WITH CHECK 表达式的策略(ALL 和 UPDATE),如果未定义 WITH CHECK 表达式,那么 USING 表达式将同时用于决定哪些行可见(普通 USING 情形)以及允许写入哪些新行(WITH CHECK 情形)。

如果某个表启用了行级安全,但不存在适用的策略,则会假定存在一条“默认拒绝”策略,因此没有任何行可见或可更新。

参数

name

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

table_name

该策略适用的表的名称(可选模式限定)。

PERMISSIVE

指定将该策略创建为宽松策略。适用于给定查询的所有宽松策略都会使用布尔“OR”操作符组合在一起。通过创建宽松策略,管理员可以扩大可访问行的集合。策略默认是宽松的。

RESTRICTIVE

指定将该策略创建为限制性策略。适用于给定查询的所有限制性策略都会使用布尔“AND”操作符组合在一起。通过创建限制性策略,管理员可以缩小可访问行的集合,因为每一行都必须通过所有限制性策略。

请注意,要让限制性策略能够有效缩小访问范围,必须先至少有一条宽松策略授予对行的访问。如果只存在限制性策略,则没有任何行可访问。当宽松策略和限制性策略混合存在时,只有在至少一条宽松策略通过且所有限制性策略也都通过时,某一行才可访问。

command

该策略适用的命令。有效选项是 ALL、SELECT、INSERT、UPDATE 和 DELETE。ALL 是默认值。有关这些策略如何应用的细节见下文。

role_name

该策略适用的角色(或多个角色)。默认是 PUBLIC,即对所有角色应用该策略。

using_expression

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

check_expression

任何 SQL 条件表达式(返回 boolean)。条件表达式不能包含聚合函数或窗口函数。如果启用行级安全,该表达式会用于针对该表的 INSERT 和 UPDATE 查询。只有表达式计算结果为 true 的行才会被允许。如果对于任何插入的记录或更新产生的任何记录,表达式计算结果为 false 或 null,则会抛出错误。请注意,check_expression 是针对行预计的新内容计算的,而不是针对原始内容。

针对每种命令的策略

ALL #

对策略使用 ALL 意味着无论命令类型如何,该策略都适用于所有命令。如果存在 ALL 策略且还存在更具体的策略,则 ALL 策略和更具体的策略(或多个策略)都会被应用。此外,ALL 策略会同时应用于查询的选择端和修改端,并在两端使用 USING 表达式(如果只定义了 USING 表达式)。

例如,如果发出 UPDATE,则 ALL 策略既适用于 UPDATE 能够选择哪些行进行更新(应用 USING 表达式),也适用于更新后的行,以检查它们是否允许被添加回表中(如果定义了 WITH CHECK 表达式则应用该表达式,否则应用 USING 表达式)。如果 INSERT 或 UPDATE 命令尝试向表中添加不通过 ALL 策略的 WITH CHECK 表达式的行,则整个命令都会中止。

SELECT #

对策略使用 SELECT 意味着该策略适用于 SELECT 查询,以及在策略所定义的关系上需要 SELECT 权限的任何时候。因此,在 SELECT 查询期间,只会返回关系中通过 SELECT 策略的记录;需要 SELECT 权限的查询(例如 UPDATE)也只能看到被 SELECT 策略允许的记录。SELECT 策略不能有 WITH CHECK 表达式,因为它只适用于从关系中检索记录的情况。

INSERT #

对策略使用 INSERT,意味着它将适用于 INSERT 命令。插入的行如果未通过该策略,将导致策略违规错误,并且整个 INSERT 命令将被中止。INSERT 策略不能带有 USING 表达式,因为它只适用于向关系添加行的情况。

请注意,带有 ON CONFLICT DO UPDATE 的 INSERT 只会为通过 INSERT 路径添加到关系中的行检查 INSERT 策略的 WITH CHECK 表达式。

UPDATE #

对策略使用 UPDATE,意味着它将适用于 UPDATE、SELECT FOR UPDATE、SELECT FOR SHARE 命令,以及 INSERT 命令中辅助的 ON CONFLICT DO UPDATE 子句。由于 UPDATE 命令需要取出现有行并用修改后的新行替换它,因此 UPDATE 策略同时接受 USING 表达式和 WITH CHECK 表达式。USING 表达式决定 UPDATE 命令能够看到哪些行来执行操作,而 WITH CHECK 表达式则定义哪些修改后的行允许被写回该关系。

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

通常,UPDATE 命令还需要从待更新关系的列中读取数据(例如在 WHERE 子句、RETURNING 子句,或 SET 子句右侧的表达式中)。这种情况下,正在被更新的关系上也需要 SELECT 权限,并且除了 UPDATE 策略外,还会应用适当的 SELECT 或 ALL 策略。这样,用户除了必须通过 UPDATE 或 ALL 策略获准更新这些行之外,还必须通过 SELECT 或 ALL 策略访问正在被更新的行。

当 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 策略外,还会应用适当的 SELECT 或 ALL 策略。这样,用户除了必须通过 DELETE 或 ALL 策略获准删除这些行之外,还必须通过 SELECT 或 ALL 策略访问正在被删除的行。

DELETE 策略不能具有 WITH CHECK 表达式,因为它只适用于正在从关系中删除行的情况,所以没有新行需要检查。

表 241. 按命令类型应用的策略

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

[a] 如果需要读取现有行或新行(例如引用关系中列的 WHERE 或 RETURNING 子句)。


多条策略的应用

当不同命令类型的多条策略应用于同一命令时(例如 SELECT 和 UPDATE 策略应用于 UPDATE 命令),用户必须同时具有这两种权限(例如既有从该关系中选取行的权限,也有更新这些行的权限)。因此,一种策略类型的表达式会与另一种策略类型的表达式使用 AND 操作符组合。

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

请注意,就组合多条策略而言,ALL 策略会被视为与当前正在应用的其他策略同一类型。

例如,对于一个同时需要 SELECT 和 UPDATE 权限的 UPDATE 命令,如果这两类策略各自都有多个适用项,它们会按以下方式组合:

RESTRICTIVE SELECT/ALL 策略 1 中的 表达式
AND
RESTRICTIVE SELECT/ALL 策略 2 中的 表达式
AND
...
AND
(
  PERMISSIVE SELECT/ALL 策略 1 中的 表达式
  OR
  PERMISSIVE SELECT/ALL 策略 2 中的 表达式
  OR
  ...
)
AND
RESTRICTIVE UPDATE/ALL 策略 1 中的 表达式
AND
RESTRICTIVE UPDATE/ALL 策略 2 中的 表达式
AND
...
AND
(
  PERMISSIVE UPDATE/ALL 策略 1 中的 表达式
  OR
  PERMISSIVE UPDATE/ALL 策略 2 中的 表达式
  OR
  ...
)

注解

要为一个表创建或修改策略,你必须是该表的拥有者。

虽然策略会应用于针对数据库中表的显式查询,但当系统执行内部引用完整性检查或验证约束时,并不会应用这些策略。这意味着仍然存在间接判断某个给定值是否存在的方法。一个例子是,尝试向某个主键列或带有唯一约束的列插入重复值。如果插入失败,用户就能推断该值已经存在。(这个例子假定策略允许该用户插入自己无权看见的行。)另一个例子是,用户被允许向一个引用了另一张表的表中插入数据,而被引用的那张表本身对其是隐藏的。用户可以通过向引用表插入值来判断其存在性;插入成功就表示该值存在于被引用表中。要解决这些问题,可以仔细设计策略,防止用户插入、删除或更新那些可能暗示其本来无权看见的值是否存在的行,或者改用生成的值(例如代理键)来代替具有外部含义的键。

通常,为了防止受保护的数据无意间暴露给可能不可信的用户定义函数,系统会在应用用户查询中出现的条件之前,先强制执行安全策略施加的过滤条件。不过,被系统(或系统管理员)标记为 LEAKPROOF 的函数和操作符由于被假定为可信,可以在策略表达式之前求值。

由于策略表达式会被直接添加到用户查询中,因此它们将以执行整个查询的用户权限运行。因此,使用某条策略的用户必须能够访问表达式中引用的所有表或函数,否则在尝试查询启用了行级安全的表时,只会收到权限被拒绝的错误。不过,这并不改变视图的工作方式。与普通查询和视图一样,被视图引用的表的权限检查和策略将使用视图所有者的权限,以及适用于视图所有者的任何策略。

更多讨论和实际示例见第 5.7 节。

兼容性

CREATE POLICY 是一种 PostgreSQL 扩展。

报告文档问题

阅读 上游文档. 反馈更正前请先核对 当前版本手册.