============= CREATE POLICY ============= .. container:: refentry :name: SQL-CREATEPOLICY .. container:: titlepage .. container:: refnamediv .. rubric:: CREATE POLICY :name: create-policy CREATE POLICY — 为一个表定义一条新的行级安全性策略 .. container:: refsynopsisdiv .. rubric:: 大纲 :name: 大纲 .. code:: 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 ) ] .. container:: refsect1 :name: id-1.9.3.75.5 .. rubric:: 描述 :name: 描述 ``CREATE POLICY``\ 为一个表定义一条行级 安全性策略。注意为了应用已被创建的策略,在表上必须启用行级安全 性(使用\ ``ALTER TABLE ... ENABLE ROW LEVEL SECURITY``\ )。 一条策略授予权限以选择、插入、更新或者删除匹配相关策略表达式的行。 现有的表行会按照\ ``USING``\ 中指定的表达式进行检查, 而将要通过\ ``INSERT``\ 或\ ``UPDATE``\ 创建 的新行会按照\ ``WITH CHECK``\ 中指定的表达式进行检查。 当\ ``USING``\ 表达式对于一个给定行返回真时,该行对用户 可见,而返回假或空时该行不可见。当一个\ ``WITH CHECK`` 表达式对一行返回真时,该行会被插入或更新,而如果返回假或空时会发生 一个错误。 对于\ ``INSERT``\ 和 ``UPDATE``\ 语句,在\ ``BEFORE`` 触发器被引发后并且在任何数据修改真正发生之前, ``WITH CHECK``\ 表达式会被强制。因此, 一个\ ``BEFORE ROW``\ 触发器可以修改要被插入的数据, 从而影响安全性策略检查的结果。\ ``WITH CHECK``\ 表达式 在任何其他约束之前被强制。 策略名称是针对每个表的。因此,一个策略名称可以被用于很多个不同的表 并且对于不同的表呈现适合于该表的定义。 策略可以被应用于特定的命令或者特定的角色。除非特别指定,新创建的策略 的默认行为是适用于所有命令和角色。多个策略可以应用于单个命令,更多细节请见下文。\ `表 256 `__\ 总结了不同类型的策略如何应用于特定的命令。 对同时具有\ ``USING``\ 和\ ``WITH CHECK`` 表达式(\ ``ALL``\ 和\ ``UPDATE``\ )的策略, 如果没有定义\ ``WITH CHECK``\ 表达式,那么 ``USING``\ 表达式将被用于决定哪些行可见(普通 ``USING``\ 情况)以及允许哪些新行被增加( ``WITH CHECK``\ 情况)。 如果为一个表启用了行级安全性但是没有适用的策略存在,将假定为一种 “默认否定”策略,这样任何行都不可见也不可更新。 .. container:: refsect1 :name: id-1.9.3.75.6 .. rubric:: 参数 :name: 参数 .. container:: variablelist ``name`` 要创建的策略的名称。这必须和该表上已有的任何其他策略名称相区分。 ``table_name`` 该策略适用的表的名称(可以被模式限定)。 ``PERMISSIVE`` 指定策略被创建为宽容性策略。适用于一个给定查询的所有宽容性策略将被使用布尔“OR”操作符组合在一起。通过创建宽容性策略,管理员可以在能被访问的记录集合中进行增加。策略默认是宽容性的。 ``RESTRICTIVE`` 指定策略被创建为限制性策略。适用于一个给定查询的所有限制性策略将被使用布尔“AND”操作符组合在一起。通过创建限制性策略,管理员可以减少能被访问的记录集合,因为每一条记录都必须通过所有的限制性策略。 注意在限制性策略真正能发挥作用减少访问之前,需要至少一条宽容性策略来授予对记录的访问。如果只有限制性策略存在,则没有记录能被访问。当宽容性和限制性策略混合存在时,只有当一个记录能通过至少一条宽容性策略以及所有的限制性策略时,该记录才是可访问的。 ``command`` 该策略适用的命令。合法的选项是 ``ALL``\ 、\ ``SELECT``\ 、 ``INSERT``\ 、\ ``UPDATE`` 以及\ ``DELETE``\ 。 ``ALL``\ 为默认。有关这些策略如何被应用的 细节见下文。 ``role_name`` 该策略适用的角色。默认是\ ``PUBLIC``\ ,它将把策略应用 到所有的角色。 ``using_expression`` 任意的SQL条件表达式(返回 ``boolean``\ )。该条件表达式不能包含任何聚集或者窗口 函数。如果行级安全性被启用,这个表达式将被增加到引用该表的查询。 让这个表达式返回真的行将可见。让这个表达式返回假或者空的任何行 将对用户不可见(在\ ``SELECT``\ 中)并且将对修改不可用( 在\ ``UPDATE``\ 或\ ``DELETE``\ 中)。这类行 会被悄悄地禁止而不会报告错误。 ``check_expression`` 任意的SQL条件表达式(返回 ``boolean``\ )。该条件表达式不能包含任何聚集或者窗口 函数。如果行级安全性被启用,这个表达式将被用在该表上的 ``INSERT``\ 以及 ``UPDATE``\ 查询中。只有让该表达式计算为真 的行才被允许。如果任何被插入的记录或者跟新后的记录导致该表达式计 算为假或者空,则会抛出一个错误。注意 *``check_expression``* 是根据行的新内容而不是原始内容计算的。 .. container:: refsect2 :name: id-1.9.3.75.6.3 .. rubric:: 针对每种命令的策略 :name: 针对每种命令的策略 .. container:: variablelist ``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``\ 命令将会中止。 一条\ ``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`` 子句。由于\ ``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``\ 表达式,因为它只适用于正在从关系中删除记录的情况, 所以没有新行需要检查。 .. container:: table :name: SQL-CREATEPOLICY-SUMMARY **表 256. 按命令类型应用的策略** .. container:: table-contents +----------+----------+----------+----------+----------+----------+ | 命令 | | | | | | | | SELECT/A | INSERT/A | UPDATE/A | | DELETE/A | | | LL策略 | LL策略 | LL策略 | | LL策略 | +==========+==========+==========+==========+==========+==========+ | | USING | WI | USING | WI | USING | | | 表达式 | TH CHECK | 表达式 | TH CHECK | 表达式 | | | | 表达式 | | 表达式 | | +----------+----------+----------+----------+----------+----------+ | | 现有行 | — | — | — | — | | SELECT | | | | | | +----------+----------+----------+----------+----------+----------+ | S | 现有行 | — | 现有行 | — | — | | ELECT FO | | | | | | | R UPDATE | | | | | | | /SHARE | | | | | | +----------+----------+----------+----------+----------+----------+ | | — | 新行 | — | — | — | | INSERT | | | | | | +----------+----------+----------+----------+----------+----------+ | INSERT | 新行 | 新行 | — | — | — | | ... RET | `[a] < | | | | | | URNING | #ftn.RLS | | | | | | | -SELECT- | | | | | | | PRIV>`__ | | | | | +----------+----------+----------+----------+----------+----------+ | | 现有 & | — | 现有行 | 新行 | — | | UPDATE | 新行 | | | | | | | `[a | | | | | | | ] `__ | | | | | +----------+----------+----------+----------+----------+----------+ | | 现有 | — | — | — | 现有行 | | DELETE | 行\ `[a | | | | | | | ] `__ | | | | | +----------+----------+----------+----------+----------+----------+ | | 现有 & | — | 现有行 | 新行 | — | | ON CONF | 新行 | | | | | | LICT DO | | | | | | | UPDATE | | | | | | +----------+----------+----------+----------+----------+----------+ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `[ | | | | | | | a] <#RLS | | | | | | | -SELECT- | | | | | | | PRIV>`__ | | | | | | | 对 | | | | | | | 于现有行 | | | | | | | 或新行, | | | | | | | 如果需要 | | | | | | | 读访问的 | | | | | | | 话(例如 | | | | | | | 涉及到关 | | | | | | | 系内列的 | | | | | | | WHER | | | | | | | E 或 | | | | | | | RETUR | | | | | | | NING | | | | | | | 子句)。 | | | | | | +----------+----------+----------+----------+----------+----------+ .. container:: refsect2 :name: id-1.9.3.75.6.4 .. rubric:: 多重策略的应用 :name: 多重策略的应用 当多种不同命令类型的策略应用于相同命令(例如\ ``SELECT``\ 和\ ``UPDATE``\ 策略应用于\ ``UPDATE``\ 命令)时,用户就必须同时具有这两种类型的权限(例如从关系中选取行和更新的权限)。这样一种策略类型的表达式就与另一种策略类型的表达式通过使用\ ``AND``\ 操作符组合在一起。 当相同命令类型的多种策略应用于同一命令时,则必须至少有一个\ ``PERMISSIVE``\ 策略授权对该关系的访问,所有的\ ``RESTRICTIVE``\ 策略必须通过。这样,所有的\ ``PERMISSIVE``\ 策略表达式都用\ ``OR``\ 来组合,所有的\ ``RESTRICTIVE``\ 策略表达式都用\ ``AND``\ 来组合,而结果用\ ``AND``\ 来组合。如果没有\ ``PERMISSIVE``\ 策略,则拒绝访问。 要注意的是,出于组合多种策略的目的,将\ ``ALL``\ 策略视为与所应用的任何其他类型的策略具有相同的类型。 例如,在\ ``UPDATE``\ 命令中,\ ``SELECT``\ 和\ ``UPDATE``\ 两种权限都需要,如果每种类型都有多个适用的策略,则将之以下面的方式组合: .. code:: programlisting 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 ... ) .. container:: refsect1 :name: id-1.9.3.75.7 .. rubric:: 注解 :name: 注解 要为一个表创建或者修改策略,你必须是该表的拥有者。 虽然策略将被应用于针对数据库中表的显式查询上,但当系统正在执行 内部引用完整性检查或者验证约束时不会应用它们。这意味着有间接的 方法来决定一个给定的值是否存在。一个例子是向一个作为主键或者拥 有唯一约束的列中尝试插入重复值。如果插入失败则用户可以推导出该 值已经存在(这个例子假设用户被策略允许插入他们看不到的记录)。 另一个例子是一个用户被允许向一个引用了其他表的表中插入,然而另 一个表是隐藏表。通过用户向引用表中插入值可以判断存在性, 成功表示该值存在于被引用表中。为了解决这些问题,应该仔细地制作 策略以完全阻止用户插入、删除或者更新那些可能指示他们不能看到的 值的记录,或者使用生成的值(例如代理键)来代替具有外部含义的键。 通常,系统将在应用用户查询中出现的条件之前先强制由安全性策略施 加的过滤条件,这是为了防止无意中把受保护的数据暴露给可能不可信 的用户定义函数。不过,被系统(或者系统管理员)标记为 ``LEAKPROOF``\ 的函数和操作符可以在策略表达式之前 被计算,因为它们已经被假定为可信。 因为策略表达式会被直接加到用户查询上,它们将使用运行整个查询的用户的 权限运行。因此,使用一条给定策略的用户必须能够访问表达式中引用的任何 表或函数,否则在尝试查询启用了行级安全性的表时,他们将简单地收到一条 没有权限的错误。不过,这不会改变视图的工作方式。就普通查询和视图来说, 权限检查和视图所引用的表的策略将使用视图拥有者的权限以及任何适用于视 图拥有者的策略。 在\ `第 5.8 节 `__\ 中可以找到额外的讨论和实际的例子。 .. container:: refsect1 :name: id-1.9.3.75.8 .. rubric:: 兼容性 :name: 兼容性 ``CREATE POLICY``\ 是一种PostgreSQL扩展。 .. container:: refsect1 :name: id-1.9.3.75.9 .. rubric:: 另见 :name: 另见 `ALTER POLICY `__, `DROP POLICY `__, `ALTER TABLE `__