表 240.按命令类型应用的策略

CREATE POLICY

创建策略—为表定义新的行级安全策略

Synopsis

CREATE POLICY name ON table_name
    [ AS { PERMISSIVE | RESTRICTIVE } ]
    [ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ]
    [ TO { role_name | PUBLIC | CURRENT_USER | SESSION_USER } [, ...] ]
    [ USING ( using_expression ) ]
    [ WITH CHECK ( check_expression ) ]

Description

CREATE POLICY命令为表定义新的行级安全策略。请注意,必须在表上启用行级安全性(使用ALTER TABLE ... ENABLE ROW LEVEL SECURITY),才能应用创建的策略。

策略授予选择,插入,更新或删除与相关策略表达式匹配的行的权限。将对照USING中指定的表达式检查现有的表行,而对照WITH CHECK中指定的表达式检查将通过INSERTUPDATE创建的新行。当USING表达式对于给定的行返回 true 时,则该行对用户可见,而如果返回 false 或 null,则该行不可见。当WITH CHECK表达式的一行返回 true 时,该行将被插入或更新,而如果返回 false 或 null 则将发生错误。

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

策略名称是按表的。因此,一个策略名称可用于许多不同的表,并为每个表定义一个适合该表的定义。

策略可以应用于特定命令或特定角色。除非另有说明,否则新创建的策略的默认设置是它们适用于所有命令和角色。多个策略可能适用于单个命令。请参阅下面的更多细节。 Table 240总结了不同类型的策略如何应用于特定命令。

对于同时具有USINGWITH CHECK表达式(ALLUPDATE)的策略,如果未定义WITH CHECK表达式,则将使用USING表达式来确定哪些行可见(正常USING情况)以及允许哪些新行被添加(WITH CHECK大小写)。

如果为表启用了行级安全性,但是不存在适用的策略,那么将采用“默认拒绝”策略,这样就不会显示或更新任何行。

Parameters

  • name

    • 要创建的策略的名称。该名称必须与表的任何其他策略的名称不同。
  • table_name

    • 该策略适用于的表的名称(可选,由模式限定)。
  • PERMISSIVE

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

    • 指定该策略将被创建为限制性策略。适用于给定查询的所有限制性策略将使用布尔值“ AND”运算符组合在一起。通过创建限制性策略,Management 员可以减少可访问的记录集,因为必须为每个记录传递所有限制性策略。

请注意,在限制性策略可以有效地用于减少记录访问之前,至少需要一个许可策略来授予对记录的访问权限。如果仅存在限制性策略,则将无法访问任何记录。当存在宽松策略和限制性策略的混合时,除了所有限制性策略之外,仅当至少一项宽松策略通过时,才可以访问记录。

  • command

    • 策略适用的命令。有效选项为ALLSELECTINSERTUPDATEDELETEALL是默认值。有关如何应用这些内容的详细信息,请参见下文。
  • role_name

    • 将策略应用到的角色。默认值为PUBLIC,它将对所有角色应用该策略。
  • using_expression

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

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

Per-Command Policies

  • ALL
    • 对策略使用ALL意味着它将适用于所有命令,而不管命令的类型如何。如果存在ALL策略并且存在更具体的策略,则将同时应用ALL策略和一个或更具体的策略。另外,如果只定义了USING表达式,则在两种情况下都使用USING表达式将ALL策略应用于查询的选择端和修改端。

例如,如果发出了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命令将被中止。 INSERT策略不能具有USING表达式,因为它仅适用于将记录添加到关系中的情况。

请注意,INSERTON CONFLICT DO UPDATE仅检查INSERT路径附加到关系的行的INSERT策略的WITH CHECK表达式。

  • UPDATE
    • 对策略使用UPDATE意味着它将应用于UPDATESELECT FOR UPDATESELECT FOR SHARE命令以及INSERT命令的辅助ON CONFLICT DO 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表达式,因为它仅适用于从关系中删除记录的情况,因此没有要检查的新行。

CommandSELECT/ALL policyINSERT/ALL policyUPDATE/ALL policy DELETE/ALL policy
USING expressionWITH CHECK expressionUSING expressionWITH CHECK expressionUSING expression
SELECTExisting row
SELECT FOR UPDATE/SHAREExisting rowExisting row
INSERTNew row
INSERT ... RETURNING新行[a]New row
UPDATE现有行和新行[a]Existing rowNew row
DELETE现有的[a]Existing row
ON CONFLICT DO UPDATE现有和新行Existing rowNew row
[a]如果需要对现有或新行进行读取访问(例如,引用该关系中的列的WHERERETURNING子句)。

多种 Policy 的应用

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

当同一命令类型的多个策略应用于同一命令时,则必须至少有一个PERMISSIVE策略授予对该关系的访问权限,并且所有RESTRICTIVE策略都必须通过。因此,使用OR组合所有PERMISSIVE策略表达式,使用AND组合所有RESTRICTIVE策略表达式,使用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
  ...
)

Notes

您必须是表的所有者才能为其创建或更改策略。

尽管策略将用于对数据库中的表进行显式查询,但是当系统执行内部参照完整性检查或验证约束时,策略将不会应用。这意味着可以通过间接方式确定给定值的存在。例如,尝试将重复值插入到为主键或具有唯一约束的列中。如果插入失败,则用户可以推断该值已经存在。 (此示例假定策略允许用户插入他们不允许查看的记录.)另一个示例是允许用户将其插入引用另一个表(否则为隐藏表)的表中。用户可以通过将值插入到引用表中来确定其存在,如果成功,则表明该值存在于引用表中。可以通过精心设计策略来防止用户完全无法插入,删除或更新记录(这些记录可能表明他们原本无法查看的值)或使用生成的值(例如,替代键)来解决这些问题。而不是具有外部含义的键。

通常,系统将在出现在用户查询中的资格之前强制执行使用安全策略强加的筛选条件,以防止将受保护的数据无意中暴露给可能不值得信赖的用户定义功能。但是,系统(或系统 Management 员)标记为LEAKPROOF的功能和运算符可能会在策略表达式之前进行评估,因为它们被认为是可信任的。

由于策略表达式是直接添加到用户查询中的,因此它们将以运行整个查询的用户权限运行。因此,使用给定策略的用户必须能够访问该表达式中引用的任何表或函数,否则在尝试查询已启用行级安全性的表时,他们将仅收到权限拒绝错误。但是,这不会改变视图的工作方式。与普通查询和视图一样,视图引用的表的权限检查和策略将使用视图所有者的权限以及适用于该视图所有者的任何策略。

其他讨论和实际示例可以在Section 5.7中找到。

Compatibility

CREATE POLICY是 PostgreSQL 扩展。

See Also

ALTER POLICY, DROP POLICY, ALTER TABLE