CREATE POLICY — 为表定义新的行级安全策略
CREATE POLICYname
ONtable_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)。
策略授予匹配相关策略表达式的行的 select、insert、update 或 delete 权限。现有表行根据 USING 中指定的表达式进行检查,而通过 INSERT 或 UPDATE 将要创建的新行根据 WITH CHECK 中指定的表达式进行检查。当 USING 表达式对给定行返回 true 时,该行对用户可见;如果返回 false 或 null,则该行不可见。当 WITH CHECK 表达式对一行返回 true 时,该行将被插入或更新;如果返回 false 或 null,则会发生错误。
对于 INSERT、UPDATE 和 MERGE 语句,WITH CHECK 表达式在 BEFORE 触发器触发后、在任何实际数据修改发生之前强制执行。因此,BEFORE ROW 触发器可能会修改要插入的数据,从而影响安全策略检查的结果。WITH CHECK 表达式在所有其他约束之前强制执行。
策略名称是按表划分的。因此,一个策略名称可以用于许多不同的表,并且每个表都可以有一个适合该表的定义。
策略可以应用于特定命令或特定角色。新创建策略的默认设置是应用于所有命令和角色,除非另有说明。多个策略可能会应用于单个命令;有关更多详细信息,请参阅下文。表 300 “按命令类型应用的策略” 总结了不同类型的策略如何应用于特定命令。
对于可以同时具有 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
AnySQL条件表达式(返回 boolean)。条件表达式不能包含任何聚合或窗口函数。如果启用了行级安全,此表达式将添加到引用表的查询中。返回 true 的行将可见。任何返回 false 或 null 的行将对用户不可见(在 SELECT 中),并且不可用于修改(在 UPDATE 或 DELETE 中)。这些行将被静默忽略;不报告错误。
check_expression
AnySQL条件表达式(返回 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 操作的 MERGE 命令。插入不通过此策略的行将导致策略违反错误,并且整个 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 操作的 MERGE 命令也会受到影响。由于 UPDATE 涉及检索现有记录并用修改后的新记录替换它,因此 UPDATE 策略同时接受 USING 表达式和 WITH CHECK 表达式。USING 表达式确定 UPDATE 命令将看到哪些记录进行操作,而 WITH CHECK 表达式定义了哪些修改后的行允许写回关联对象。
任何更新值不通过 WITH CHECK 表达式的行都将导致错误,并且整个命令将被中止。如果只指定了 USING 子句,则该子句将同时用于 USING 和 WITH CHECK 情况。
通常,UPDATE 命令还需要从被更新的关联对象中的列读取数据(例如,在 WHERE 子句或 RETURNING 子句中,或在 SET 子句的右侧表达式中)。在这种情况下,还需要对被更新的关联对象具有 SELECT 权限,并且除了 UPDATE 策略之外,还将应用适当的 SELECT 或 ALL 策略。因此,用户必须通过 SELECT 或 ALL 策略访问要更新的行(们),同时通过 UPDATE 或 ALL 策略获得更新行(们)的权限。
当 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 策略之外,还将应用适当的 SELECT 或 ALL 策略。因此,用户必须通过 SELECT 或 ALL 策略访问要删除的行(们),同时通过 DELETE 或 ALL 策略获得删除行(们)的权限。
DELETE 策略不能有 WITH CHECK 表达式,因为它只适用于从关联对象中删除记录的情况,因此没有要检查的新行。
表 300. 按命令类型应用的策略
命令 | 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] 如果需要对现有行或新行进行读取访问(例如,引用关联对象中列的 WHERE 或 RETURNING 子句)。 |
当不同命令类型的多个策略应用于同一命令时(例如,应用于 UPDATE 命令的 SELECT 和 UPDATE 策略),用户必须同时拥有这两种类型的权限(例如,从关联对象中选择行的权限以及更新它们的权限)。因此,一种类型的策略的表达式与另一种类型的策略的表达式结合使用 AND 运算符。
当同一命令的同一命令类型的多个策略适用时,至少必须有一个 PERMISSIVE 策略授予对关联对象的访问权限,并且所有 RESTRICTIVE 策略都必须通过。因此,所有 PERMISSIVE 策略表达式使用 OR 组合,所有 RESTRICTIVE 策略表达式使用 AND 组合,并且结果使用 AND 组合。如果没有 PERMISSIVE 策略,则拒绝访问。
请注意,为了组合多个策略,ALL 策略将被视为具有与正在应用的另一类策略相同的类型。
例如,在需要 SELECT 和 UPDATE 权限的 UPDATE 命令中,如果每种类型的多个策略适用,它们将按如下方式组合:
expression
from RESTRICTIVE SELECT/ALL policy 1 ANDexpression
from RESTRICTIVE SELECT/ALL policy 2 AND ... AND (expression
from PERMISSIVE SELECT/ALL policy 1 ORexpression
from PERMISSIVE SELECT/ALL policy 2 OR ... ) ANDexpression
from RESTRICTIVE UPDATE/ALL policy 1 ANDexpression
from RESTRICTIVE UPDATE/ALL policy 2 AND ... AND (expression
from PERMISSIVE UPDATE/ALL policy 1 ORexpression
from PERMISSIVE UPDATE/ALL policy 2 OR ... )
您必须是表的拥有者才能为其创建或更改策略。
虽然策略将应用于对数据库中表的显式查询,但当系统执行内部参照完整性检查或验证约束时,它们不会被应用。这意味着存在确定给定值是否存在的间接方法。一个例子是尝试将重复值插入到作为主键或具有唯一约束的列中。如果插入失败,用户可以推断该值已存在。(此示例假设用户有权插入他们无权查看的记录。)另一个例子是用户被允许插入引用另一个(否则隐藏的)表的表。可以通过用户在引用表中插入值来确定是否存在,如果成功则表示值存在于被引用表中。这些问题可以通过仔细制定策略来解决,以防止用户能够插入、删除或更新任何可能指示他们无法查看的值的记录,或者通过使用生成值(例如,代理键)而不是具有外部含义的键。
通常,系统会在用户查询中出现的条件之前强制执行使用安全策略施加的过滤条件,以防止将受保护的数据意外暴露给可能不可信的用户定义函数。但是,被系统(或系统管理员)标记为 LEAKPROOF 的函数和运算符可能会在策略表达式之前进行评估,因为它们被假定为可信。
由于策略表达式直接添加到用户的查询中,因此它们将以运行整个查询的用户权限运行。因此,使用给定策略的用户必须能够访问表达式中引用的任何表或函数,否则在尝试查询启用了行级安全功能的表时,他们将收到权限被拒绝的错误。但这不会改变视图的工作方式。与普通查询和视图一样,对视图引用的表进行权限检查和策略将使用视图拥有者的权限和适用于视图拥有者的任何策略,除非视图是使用 security_invoker 选项定义的(请参阅 CREATE VIEW)。
MERGE 没有单独的策略。相反,在执行 MERGE 时,将根据所执行的操作来应用为 SELECT、INSERT、UPDATE 和 DELETE 定义的策略。
更多讨论和实际示例可在第 5.9 节“行安全策略”中找到。
CREATE POLICY 是 PostgreSQL 的扩展。
如果您在文档中发现任何不正确、与您的实际使用经验不符或需要进一步澄清的内容,请使用 此表单 报告文档问题。