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扩展。
