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
)令创建的安全策略得以应用。
策略赋予选择、插入、更新或删除符合相应策略条件的行权限。在 USING
中指定的条件会检查现有表格行,在 WITH CHECK
中指定的条件会检查经由 INSERT
或 UPDATE
新创建的行。当某个 USING
条件对某行返回 true 时,那么这行可见于用户,当返回 false 或 null 时,那么这行不可见。当某个 WITH CHECK
条件对某行返回 true 时,那么这行可插入或更新,当返回 false 或 null 时,那么就会发生错误。
对于 INSERT
、UPDATE
和 MERGE
语句,在 BEFORE
触发器触发之后并实际执行任何数据修改之前会强制执行 WITH CHECK
表达式。因此,BEFORE ROW
触发器可能会修改要插入的数据,从而影响安全策略检查的结果。WITH CHECK
表达式在执行任何其他约束之前强制执行。
策略名称属于每个表。因此,一个策略名称可用于许多不同的表,并为每个表都有一个定义,此定义适用于该表。
可以针对特定命令或特定角色应用策略。对于新创建的策略,其默认值为适用于所有命令和角色,除非另行指定。多个策略可能适用于单个命令;有关更多详细信息,请参见下文。 表 297 总结了针对特定命令如何应用不同类型的策略。
对于可能同时具有 USING
和 WITH CHECK
表达式的策略(ALL
和 UPDATE
),如果未定义任何 WITH CHECK
表达式,则 USING
表达式将用于确定哪些行可见(常规 USING
用例)以及哪些新行可被添加到(WITH CHECK
用例)。
如果针对某个表启用了行级安全性,但不存在适用的策略,则假定一个 “default deny” 策略,以便没有行可见或可更新。
name
要创建的策略的名称。此名称必须不同于针对该表的任何其他策略的名称。
table_name
策略所应用的表 (可选择采用架构限定) 的名称。
PERMISSIVE
指定要创建的策略为允许性策略。所有针对给定查询适用的允许性策略都将使用布尔 “OR” 运算符组合在一起。通过创建允许性策略,管理员可以添加到可以访问的记录集中。策略默认情况下是允许性的。
RESTRICTIVE
指定要创建的策略为限制性策略。所有针对给定查询适用的限制性策略都将使用布尔 “AND” 运算符组合在一起。通过创建限制性策略,管理员可以减少可以访问的记录集,因为所有限制性策略必须针对每个记录通过。
请注意,在限制性策略可有效地用于减少访问权限之前,需要至少有一项允许策略才能授予对记录的访问权限。如果仅存在限制性策略,那么就无法访问任何记录。在存在允许策略和限制性策略的混合时,除了满足所有限制性策略外,仅当满足至少一项允许策略时才能访问记录。
命令
策略应用到的命令。有效的选项是 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
策略和更具体的策略(或策略)。此外,如果仅定义了 USING
表达式,那么将在查询的选择侧和修改侧同时使用 ALL
策略,在两种情况下都使用 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
命令可见。可能有一些行可以通过 SELECT
查看,但如果它们未通过 DELETE
策略的 USING
表达式,则不可删除。
在大多数情况下,DELETE
命令还需要读取删除所在关系中的列数据(例如,在 WHERE
子句或 RETURNING
子句中)。在这种情况下,还需要对该关系具有 SELECT
权限,除了 DELETE
策略以外,还将应用相应的 SELECT
或 ALL
策略。因此,用户必须除了通过 DELETE
或 ALL
策略授予删除行的权限以外,还需要通过 SELECT
或 ALL
策略访问要删除的行。
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]如果现有行或新行需要读取访问权限(例如,引用了来自关系的列的 |
当针对相同命令(例如,SELECT
和 UPDATE
策略应用于 UPDATE
命令)应用不同命令类型的多个策略时,那么用户必须同时具有两种类型的权限(例如,从关系中选择行的权限以及更新该行的权限)。因此,一种策略类型的表达式与另一种策略类型的表达式使用 AND
运算符进行组合。
当针对相同命令应用相同命令类型的多个策略时,则必须至少有一个 PERMISSIVE
策略授予关系访问权限,并且所有 RESTRICTIVE
策略都必须通过。因此,将使用 OR
组合所有 PERMISSIVE
策略表达式,使用 AND
组合所有 RESTRICTIVE
策略表达式,并将结果使用 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 扩展。