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
中指定的表达式进行检查,而将通过INSERT
或UPDATE
创建的新行根据WITH CHECK
中指定的表达式进行检查。当USING
表达式对给定行返回 true 时,该行对用户可见,而如果返回 false 或 null,则该行不可见。当WITH CHECK
表达式对行返回 true 时,该行将被插入或更新,而如果返回 false 或 null,则会发生错误。
对于INSERT
、UPDATE
和MERGE
语句,WITH CHECK
表达式在触发BEFORE
触发器后以及在进行任何实际数据修改之前执行。因此,BEFORE ROW
触发器可能会修改要插入的数据,从而影响安全策略检查的结果。WITH CHECK
表达式在任何其他约束之前执行。
策略名称是按表设置的。因此,一个策略名称可用于许多不同的表,并且针对每个表都有一个针对该表的适当定义。
策略可应用于特定命令或特定角色。除非另有指定,否则新创建策略的默认设置是针对所有命令和角色应用。多个策略可应用于单个命令;有关更多详细信息,请参见下文。表 292总结了不同类型的策略如何应用于特定命令。
对于既可以具有USING
又可以具有WITH CHECK
表达式的策略(ALL
和UPDATE
),如果未定义WITH CHECK
表达式,则USING
表达式将用于确定哪些行可见(常规USING
情况)以及允许添加哪些新行(WITH CHECK
情况)。
如果针对某个表启用了行级安全性,但不存在任何适用的策略,则假定为“默认拒绝”策略,因此任何行都不可见或不可更新。
参数
name
要创建的策略的名称。这必须与表中任何其他策略的名称不同。
table_name
策略适用的表(可选,可以限定模式)的名称。
PERMISSIVE
指定要将策略创建为宽松策略。所有适用于给定查询的宽松策略都将使用布尔 “OR” 运算符组合在一起。通过创建宽松策略,管理员可以添加到可访问的记录集中。默认情况下,策略是宽松的。
RESTRICTIVE
指定要将策略创建为严格策略。所有适用于给定查询的严格策略都将使用布尔 “AND” 运算符组合在一起。通过创建严格策略,管理员可以减少可访问的记录集,因为必须针对每个记录通过所有严格策略。
请注意,在严格策略可用于有效减少访问权限之前,必须至少有一个宽松策略来授予对记录的访问权限。如果仅存在严格策略,则任何记录都不可访问。当宽松策略和严格策略混合存在时,只有当至少一个宽松策略通过并且所有严格策略都通过时,才能访问记录。
命令
策略适用的命令。有效选项为
ALL
、SELECT
、INSERT
、UPDATE
和DELETE
。ALL
为默认值。有关如何应用这些选项的具体信息,请参见下文。role_name
要应用策略的角色。默认值为
PUBLIC
,它将策略应用于所有角色。using_expression
任何 条件表达式(返回
boolean
)。条件表达式不能包含任何聚合或窗口函数。如果启用了行级安全性,此表达式将添加到引用该表的查询中。表达式返回 true 的行将可见。表达式返回 false 或 null 的任何行对于用户(在SELECT
中)将不可见,并且不可用于修改(在UPDATE
或DELETE
中)。此类行将被静默抑制;不会报告错误。check_expression
任何 条件表达式(返回
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
子句。MERGE
命令包含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
表达式,因为它仅适用于从关系中删除记录的情况,因此没有要检查的新行。
表 292. 按命令类型应用的策略
命令 | 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]如果需要对现有行或新行进行读取访问(例如,引用关系中列的 |
应用多项策略
当针对同一命令应用不同命令类型的多项策略(例如,针对UPDATE
命令应用SELECT
和UPDATE
策略)时,用户必须具备两种类型的权限(例如,从关系中选择行以及更新它们的权限)。因此,一种策略类型的表达式与另一种策略类型的表达式使用AND
运算符组合在一起。
当针对同一命令应用同一命令类型的多项策略时,必须至少有一项PERMISSIVE
策略授予对关系的访问权限,并且所有RESTRICTIVE
策略都必须通过。因此,所有PERMISSIVE
策略表达式都使用OR
组合在一起,所有RESTRICTIVE
策略表达式都使用AND
组合在一起,并且结果使用AND
组合在一起。如果没有PERMISSIVE
策略,则拒绝访问。
请注意,为了组合多项策略,ALL
策略被视为与正在应用的任何其他策略类型具有相同的类型。
例如,在需要SELECT
和UPDATE
权限的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
时,将应用为SELECT
、INSERT
、UPDATE
和DELETE
定义的策略,具体取决于执行的操作。
可以在第 5.8 节中找到其他讨论和实际示例。
兼容性
CREATE POLICY
是PostgreSQL扩展。