Skip to content

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中指定的表达式进行检查,而将通过INSERTUPDATE创建的新行根据WITH CHECK中指定的表达式进行检查。当USING表达式对给定行返回 true 时,该行对用户可见,而如果返回 false 或 null,则该行不可见。当WITH CHECK表达式对行返回 true 时,该行将被插入或更新,而如果返回 false 或 null,则会发生错误。

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

策略名称是按表设置的。因此,一个策略名称可用于许多不同的表,并且针对每个表都有一个针对该表的适当定义。

策略可应用于特定命令或特定角色。除非另有指定,否则新创建策略的默认设置是针对所有命令和角色应用。多个策略可应用于单个命令;有关更多详细信息,请参见下文。表 292总结了不同类型的策略如何应用于特定命令。

对于既可以具有USING又可以具有WITH CHECK表达式的策略(ALLUPDATE),如果未定义WITH CHECK表达式,则USING表达式将用于确定哪些行可见(常规USING情况)以及允许添加哪些新行(WITH CHECK情况)。

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

参数

name

要创建的策略的名称。这必须与表中任何其他策略的名称不同。

table_name

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

PERMISSIVE

指定要将策略创建为宽松策略。所有适用于给定查询的宽松策略都将使用布尔 OR 运算符组合在一起。通过创建宽松策略,管理员可以添加到可访问的记录集中。默认情况下,策略是宽松的。

RESTRICTIVE

指定要将策略创建为严格策略。所有适用于给定查询的严格策略都将使用布尔 AND 运算符组合在一起。通过创建严格策略,管理员可以减少可访问的记录集,因为必须针对每个记录通过所有严格策略。

请注意,在严格策略可用于有效减少访问权限之前,必须至少有一个宽松策略来授予对记录的访问权限。如果仅存在严格策略,则任何记录都不可访问。当宽松策略和严格策略混合存在时,只有当至少一个宽松策略通过并且所有严格策略都通过时,才能访问记录。

命令

策略适用的命令。有效选项为 ALLSELECTINSERTUPDATEDELETEALL 为默认值。有关如何应用这些选项的具体信息,请参见下文。

role_name

要应用策略的角色。默认值为 PUBLIC,它将策略应用于所有角色。

using_expression

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

check_expression

任何 条件表达式(返回 boolean)。条件表达式不能包含任何聚合或窗口函数。如果启用了行级安全性,此表达式将用于针对表的 INSERTUPDATE 查询。只有表达式计算为 true 的行才允许。如果表达式对任何插入的记录或更新产生的任何记录计算为 false 或 null,将引发错误。请注意,check_expression 是针对行的提议新内容计算的,而不是针对原始内容计算的。

按命令策略

ALL #

对策略使用 ALL 意味着它将应用于所有命令,无论命令类型如何。如果存在 ALL 策略和更具体的策略,则将应用 ALL 策略和更具体的策略(或策略)。此外,ALL 策略将应用于查询的选择端和修改端,如果仅定义了 USING 表达式,则在两种情况下都使用 USING 表达式。

例如,如果发出 UPDATE,则 ALL 策略将同时适用于 UPDATE 能够选择哪些行作为要更新的行(应用 USING 表达式),以及适用于更新后的行,以检查是否允许将它们添加到表中(如果已定义,则应用 WITH CHECK 表达式,否则应用 USING 表达式)。如果 INSERTUPDATE 命令尝试向表中添加不通过 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 UPDATEINSERT 仅对通过 INSERT 路径附加到关系的行检查 INSERT 策略的 WITH CHECK 表达式。

UPDATE #

对策略使用 UPDATE 意味着它将应用于 UPDATESELECT FOR UPDATESELECT FOR SHARE 命令,以及 INSERT 命令的辅助 ON CONFLICT DO UPDATE 子句。MERGE 命令包含 UPDATE 操作也会受到影响。由于 UPDATE 涉及提取现有记录并用新的修改记录替换它,UPDATE 策略接受 USING 表达式和 WITH CHECK 表达式。 USING 表达式确定 UPDATE 命令将看到哪些记录以进行操作,而 WITH CHECK 表达式定义了哪些修改后的行可以存储回关系中。

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

通常,UPDATE 命令还需要从正在更新的关系中的列读取数据(例如,在 WHERE 子句或 RETURNING 子句中,或在 SET 子句的右侧表达式中)。在这种情况下,还需要对正在更新的关系具有 SELECT 权限,除了 UPDATE 策略之外,还将应用适当的 SELECTALL 策略。因此,除了通过 UPDATEALL 策略授予更新行的权限之外,用户还必须通过 SELECTALL 策略访问正在更新的行。

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 策略之外,还将应用适当的 SELECTALL 策略。因此,除了通过 DELETEALL 策略授予删除行的权限之外,用户还必须通过 SELECTALL 策略访问正在删除的行。

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]如果需要对现有行或新行进行读取访问(例如,引用关系中列的 WHERERETURNING 子句)。

应用多项策略

当针对同一命令应用不同命令类型的多项策略(例如,针对UPDATE命令应用SELECTUPDATE策略)时,用户必须具备两种类型的权限(例如,从关系中选择行以及更新它们的权限)。因此,一种策略类型的表达式与另一种策略类型的表达式使用AND运算符组合在一起。

当针对同一命令应用同一命令类型的多项策略时,必须至少有一项PERMISSIVE策略授予对关系的访问权限,并且所有RESTRICTIVE策略都必须通过。因此,所有PERMISSIVE策略表达式都使用OR组合在一起,所有RESTRICTIVE策略表达式都使用AND组合在一起,并且结果使用AND组合在一起。如果没有PERMISSIVE策略,则拒绝访问。

请注意,为了组合多项策略,ALL策略被视为与正在应用的任何其他策略类型具有相同的类型。

例如,在需要SELECTUPDATE权限的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时,将应用为SELECTINSERTUPDATEDELETE定义的策略,具体取决于执行的操作。

可以在第 5.8 节中找到其他讨论和实际示例。

兼容性

CREATE POLICY是PostgreSQL扩展。

另请参见

ALTER POLICYDROP POLICYALTER TABLE