constructive-safegres
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSafegres (Authz* Security Protocol)
Safegres(Authz* 安全协议)
Safegres is the protocol layer behind Constructive authorization.
- Safegres is expressed as a policy type (e.g. ) plus a JSON config (policy
AuthzEntityMembership).data - The system compiles these policy nodes into enforcement mechanisms (most notably PostgreSQL RLS), but Safegres itself is not SQL.
If you are writing automation that provisions security, treat Safegres as the vocabulary of "what access means".
Related skills:
- TypeScript SDK secure provisioning:
constructive-security - Relation provisioning:
constructive-relations - Data modules (field generators):* -- defines each Data* nodeType, what fields it creates, and which Authz* policy it pairs with
constructive-data-modules
Safegres是Constructive授权体系底层的协议层。
- Safegres由策略类型(例如)和JSON配置(策略
AuthzEntityMembership)两部分组成。data - 系统会将这些策略节点编译为权限执行机制(最典型的是PostgreSQL RLS),但Safegres本身不是SQL。
如果你正在编写安全配置相关的自动化逻辑,可以把Safegres当作定义「访问权限含义」的术语表。
相关skill:
- TypeScript SDK 安全配置:
constructive-security - 关系配置:
constructive-relations - Data 模块(字段生成器):* -- 定义了每一种Data节点类型、生成的字段,以及与之配对的Authz策略
constructive-data-modules
Core vocabulary (used in every Safegres policy)
核心术语(所有Safegres策略通用)
Actor
Actor
The actor is the authenticated user performing the query.
- (conceptually) = the actor's user id.
current_user_id() - In membership resolution tables you'll see this represented as .
actor_id
Actor是执行查询的已认证用户。
- (概念上)等于Actor的用户ID。
current_user_id() - 在成员关系解析表中,它会以字段的形式出现。
actor_id
Entity
Entity
An entity is the scope a membership belongs to.
- For org/group memberships: identifies the org/group.
entity_id - For app memberships: membership is global, so there is typically no per-row entity_id binding.
Entity是成员关系所属的作用域。
- 对于组织/群组成员关系:标识对应的组织/群组。
entity_id - 对于应用级成员关系:成员关系是全局的,因此通常没有行级的entity_id绑定。
Membership types (scopes)
成员关系类型(作用域)
Safegres policies commonly take :
membership_type- = App
1 - = Org
2 - = Group
3
This can be provided as an integer or a string name (resolved via the membership types module).
Safegres策略通常会接收参数:
membership_type- = 应用(App)
1 - = 组织(Org)
2 - = 群组(Group)
3
参数可以传整数,也可以传字符串名称(会通过成员关系类型模块解析)。
Users ARE Organizations (personal orgs)
用户即组织(个人组织)
A key identity property:
- Every user also has an "org identity".
- Each user automatically has an org-level membership to themselves ("personal org").
This matters because an org-level membership check against a field like can often unify:
owner_id- "user owns it personally" and
- "org owns it and user is a member"
...under a single policy.
AuthzEntityMembership一个核心身份特性:
- 每个用户都有一个「组织身份」。
- 每个用户自动拥有针对自身的组织级成员关系(即「个人组织」)。
这一特性的意义在于,针对这类字段的组织级成员关系检查可以统一覆盖两种场景:
owner_id- 「用户个人拥有该资源」
- 「组织拥有该资源,且用户是组织成员」
两种场景都可以用同一个策略实现。
AuthzEntityMembershipThe critical distinction: AuthzMembership
vs AuthzEntityMembership
AuthzMembershipAuthzEntityMembership核心差异:AuthzMembership
vs AuthzEntityMembership
AuthzMembershipAuthzEntityMembershipAuthzMembership
(UNBOUND)
AuthzMembershipAuthzMembership
(无绑定)
AuthzMembershipMeaning: "Is the actor a valid member of some scope (app/org/group), optionally with a permission/admin flag?"
- It does not bind to any field on the row being accessed.
- Therefore it is primarily an app-level gate.
Typical correct uses:
- "Is this request coming from any authenticated/approved user?"
- "Is the actor a super app admin?"
- "Can the actor access a global administrative table that is not entity-scoped?"
Typical incorrect uses:
- Using on an entity-scoped table and expecting it to mean "member of this row's org".
AuthzMembership(membership_type=2)- It does not.
- It means "member of any org" (or, more precisely, "has at least one org membership row"), which is almost always too broad.
含义: 「Actor是否是某个作用域(应用/组织/群组)的有效成员,可选校验权限/管理员标识?」
- 它不会与被访问行的任何字段绑定。
- 因此它主要用作应用级权限 gate。
典型适用场景:
- 「该请求是否来自任意已认证/已审核的用户?」
- 「Actor是否是超级应用管理员?」
- 「Actor是否可以访问无实体作用域的全局管理表?」
典型错误用法:
- 在有实体作用域的表上使用,期望它代表「当前行所属组织的成员」。
AuthzMembership(membership_type=2)- 它并不会这么判断。
- 它实际代表「任意组织的成员」(更准确地说,「至少有一条组织成员关系记录」),权限范围通常过于宽泛。
AuthzEntityMembership
(BOUND)
AuthzEntityMembershipAuthzEntityMembership
(有绑定)
AuthzEntityMembershipMeaning: "Does the actor have membership in the specific entity referenced by this row's field?"
- It binds membership evaluation to an on the protected row.
entity_field - It is the default choice for entity-scoped resources.
Rule of thumb:
- If your row has an ,
entity_id, ororganization_idthat should scope access: you almost always want EntityMembership, not Membership.owner_id
含义: 「Actor是否是当前行某个字段关联的特定实体的成员?」
- 它会将成员关系校验绑定到受保护行的字段。
entity_field - 它是有实体作用域资源的默认选择。
经验法则:
- 如果你的行包含、
entity_id或者organization_id这类用来限定访问范围的字段,你几乎都应该选择EntityMembership,而不是Membership。owner_id
Safegres policy node types (leaf types)
Safegres策略节点类型(叶子类型)
Below are the 14 leaf policy node types.
Each policy is described as:
- Intent: what it's for
- Config: JSON shape (keys)
- Semantics: what it authorizes (in words)
- Use when / Avoid when
Note:exists as an advanced meta-node for boolean trees; see the section at the end.AuthzComposite
以下是14种叶子策略节点类型。
每个策略的描述包含:
- 用途: 该策略的作用
- 配置: JSON结构(键名)
- 语义: 它会授权哪些访问(文字描述)
- 适用场景 / 避免场景
注意:是用于构建布尔树的高级元节点,请参考末尾的章节说明。AuthzComposite
1) AuthzDirectOwner
AuthzDirectOwner1) AuthzDirectOwner
AuthzDirectOwnerIntent: Direct personal ownership.
Config:
json
{ "entity_field": "owner_id" }Semantics: Authorize when the row's equals the actor's user id.
{entity_field}Use when:
- The row is owned by exactly one user, and ownership is represented directly on the row.
Avoid when:
- Ownership can be an organization (or user-as-org) and you want "org members can access." Prefer (org scope) instead.
AuthzEntityMembership
用途: 直接个人所有权校验。
配置:
json
{ "entity_field": "owner_id" }语义: 当行的值等于Actor的用户ID时授权。
{entity_field}适用场景:
- 行仅归单个用户所有,且所有权字段直接存储在行上。
避免场景:
- 所有者可以是组织(或作为组织的用户),且你希望「组织成员可以访问」。此时优先使用(组织作用域)。
AuthzEntityMembership
2) AuthzDirectOwnerAny
AuthzDirectOwnerAny2) AuthzDirectOwnerAny
AuthzDirectOwnerAnyIntent: Multi-owner OR logic.
Config:
json
{ "entity_fields": ["sender_id", "receiver_id"] }Semantics: Authorize when the actor id matches any of the fields.
Use when:
- A record has multiple relevant user id columns and any of them confer access.
用途: 多所有者OR逻辑校验。
配置:
json
{ "entity_fields": ["sender_id", "receiver_id"] }语义: 当Actor ID匹配任意一个字段的值时授权。
适用场景:
- 记录有多个关联用户ID字段,任意一个字段匹配即可授予访问权限。
3) AuthzMembership
AuthzMembership3) AuthzMembership
AuthzMembershipIntent: Unbound membership gate (app/org/group), optionally permissioned.
Config (minimal):
json
{ "membership_type": 1 }Config (permissioned):
json
{ "membership_type": 1, "permission": "admin_permissions" }Optional keys (depending on policy needs):
- (string)
permission - (string[])
permissions - (boolean)
is_admin - (boolean)
is_owner
Semantics: "The actor has at least one membership record in the given scope, optionally matching permission/admin flags."
Use when:
- App-level admin checks.
- Global feature gating.
Avoid when:
- Entity-scoped resources (anything that should be constrained by a row's field).
用途: 无绑定的成员关系gate(应用/组织/群组),可选权限校验。
最小配置:
json
{ "membership_type": 1 }带权限配置:
json
{ "membership_type": 1, "permission": "admin_permissions" }可选键(根据策略需求选择):
- (字符串)
permission - (字符串数组)
permissions - (布尔值)
is_admin - (布尔值)
is_owner
语义: 「Actor在指定作用域内至少有一条成员关系记录,可选匹配权限/管理员标识。」
适用场景:
- 应用级管理员校验。
- 全局功能灰度控制。
避免场景:
- 有实体作用域的资源(任何应该被行字段限制访问范围的资源)。
4) AuthzEntityMembership
AuthzEntityMembership4) AuthzEntityMembership
AuthzEntityMembershipIntent: Bound membership-to-row.
Config (minimal):
json
{ "entity_field": "entity_id", "membership_type": 2 }Optional keys:
- /
permissionpermissions - /
is_adminis_owner
Semantics: "The actor is a member of the entity referenced by this row's ."
{entity_field}Use when:
- Org-owned or group-owned resources.
- that may refer to either a user or an org (because users are orgs via personal orgs).
owner_id
用途: 绑定到行的成员关系校验。
最小配置:
json
{ "entity_field": "entity_id", "membership_type": 2 }可选键:
- /
permissionpermissions - /
is_adminis_owner
语义: 「Actor是当前行关联的实体的成员。」
{entity_field}适用场景:
- 组织所有或群组所有的资源。
- 可能指向用户或组织的场景(因为用户通过个人组织也属于组织类型)。
owner_id
5) AuthzRelatedEntityMembership
AuthzRelatedEntityMembership5) AuthzRelatedEntityMembership
AuthzRelatedEntityMembershipIntent: Entity membership where the entity isn't directly on the protected row, but reachable via a join.
Config (typical):
json
{
"entity_field": "post_id",
"membership_type": 2,
"obj_schema": "public",
"obj_table": "posts",
"obj_field": "organization_id"
}Semantics: "Look up the related row and authorize based on membership in the entity referenced there."
Use when:
- Protected rows reference another table (FK), and that related table carries / org id.
entity_id
用途: 实体成员关系校验,其中实体不直接存储在受保护行上,需要通过关联表查询。
典型配置:
json
{
"entity_field": "post_id",
"membership_type": 2,
"obj_schema": "public",
"obj_table": "posts",
"obj_field": "organization_id"
}语义: 「查询关联行,基于关联行上的实体成员关系授权。」
适用场景:
- 受保护行引用了另一个表(外键),且关联表上存储了/组织ID。
entity_id
6) AuthzPeerOwnership
AuthzPeerOwnership6) AuthzPeerOwnership
AuthzPeerOwnershipIntent: Peer visibility via shared entity membership (direct owner field on protected row).
Config (typical):
json
{ "owner_field": "owner_id", "membership_type": 2 }Optional keys:
- /
permissionpermissions - /
is_adminis_owner
Semantics (in words):
- Find the entities (orgs/groups) the actor belongs to.
- Find other users who belong to those same entities.
- Allow access when the row's is one of those peer user ids.
{owner_field}
Use when:
- "People in the same org can see each other's user-owned objects."
Avoid when:
- The owner is not directly on the protected row (then use ).
AuthzRelatedPeerOwnership
用途: 通过共享实体成员关系实现同行可见性(所有者字段直接存储在受保护行上)。
典型配置:
json
{ "owner_field": "owner_id", "membership_type": 2 }可选键:
- /
permissionpermissions - /
is_adminis_owner
语义(文字描述):
- 查询Actor所属的实体(组织/群组)。
- 查询属于这些相同实体的其他用户。
- 当行的是这些同行用户ID之一时授权。
{owner_field}
适用场景:
- 「同一组织内的用户可以查看彼此的个人所有资源」。
避免场景:
- 所有者没有直接存储在受保护行上(此时使用)。
AuthzRelatedPeerOwnership
7) AuthzRelatedPeerOwnership
AuthzRelatedPeerOwnership7) AuthzRelatedPeerOwnership
AuthzRelatedPeerOwnershipIntent: Peer visibility via shared entity membership through a related table.
Config (typical):
json
{
"entity_field": "message_id",
"membership_type": 2,
"obj_schema": "public",
"obj_table": "messages",
"obj_field": "sender_id"
}Optional keys:
- (defaults to
obj_ref_field)id - /
permissionpermissions - /
is_adminis_owner
Semantics (in words):
- Find peers of the actor (as in ).
AuthzPeerOwnership - Join the related table where each peer is the related row's owner ().
obj_field - Allow access when the protected row's matches those related rows.
{entity_field}
Use when:
- Protected row points at another object, and that object's owner is what should be peer-visible.
用途: 通过关联表的共享实体成员关系实现同行可见性。
典型配置:
json
{
"entity_field": "message_id",
"membership_type": 2,
"obj_schema": "public",
"obj_table": "messages",
"obj_field": "sender_id"
}可选键:
- (默认值为
obj_ref_field)id - /
permissionpermissions - /
is_adminis_owner
语义(文字描述):
- 查询Actor的同行用户(和逻辑一致)。
AuthzPeerOwnership - 关联查询同行用户作为所有者()的关联表行。
obj_field - 当受保护行的匹配这些关联行时授权。
{entity_field}
适用场景:
- 受保护行指向另一个对象,且该对象的所有者需要支持同行可见。
8) AuthzOrgHierarchy
AuthzOrgHierarchy8) AuthzOrgHierarchy
AuthzOrgHierarchyIntent: Visibility via org hierarchy (manager/subordinate relationships).
Config (typical):
json
{ "direction": "down", "anchor_field": "owner_id", "entity_field": "entity_id" }Semantics: Authorize based on hierarchy closure relationships anchored at a user field (often ).
owner_idUse when:
- Manager sees subordinate-owned records.
- Subordinate sees manager-owned records.
用途: 通过组织层级(经理/下属关系)实现可见性。
典型配置:
json
{ "direction": "down", "anchor_field": "owner_id", "entity_field": "entity_id" }语义: 基于锚定在用户字段(通常是)的层级闭包关系授权。
owner_id适用场景:
- 经理可以查看下属所有的记录。
- 下属可以查看经理所有的记录。
9) AuthzTemporal
AuthzTemporal9) AuthzTemporal
AuthzTemporalIntent: Time-window constraints.
Config (typical):
json
{ "valid_from_field": "valid_from", "valid_until_field": "valid_until" }Either field can be omitted (at least one is required):
- only -> "accessible from this time onward" (open-ended future)
valid_from_field - only -> "accessible until this time" (open-ended past)
valid_until_field - Both -> classic time window
Additionally, a NULL column value in is treated as "no expiry" (), making the window dynamic per row.
valid_untilvalid_until IS NULL OR valid_until > now()Optional keys:
- (default
valid_from_inclusive)true - (default
valid_until_inclusive)false
Semantics: Authorize only when "now" is within the configured time window. Omitting a field removes that boundary.
Use when:
- Scheduled content.
- Expiring invites.
- Open-ended "accessible after publish date" (use only).
valid_from_field
Combination guidance:answers when access is valid, not who has access. On its own it means "anyone can access within the time window." In practice, always combine it with an identity-based policy — either as a restrictive top-level policy (ANDed with a permissive identity policy) or inside anAuthzTemporalAuthzComposite.BoolExpr
Overlap with: You could approximate published-content gating withAuthzPublishable(e.g.AuthzTemporalwith novalid_from_field: "published_at"). However,valid_until_fieldadditionally provides theAuthzPublishableboolean toggle, which lets authors unpublish content independently of time. Useis_publishedwhen you need an explicit on/off switch; useAuthzPublishablewhen access is purely time-driven.AuthzTemporal
用途: 时间窗口约束。
典型配置:
json
{ "valid_from_field": "valid_from", "valid_until_field": "valid_until" }两个字段可以省略任意一个(至少需要保留一个):
- 仅保留-> 「从该时间点起可访问」(未来无限制)
valid_from_field - 仅保留-> 「到该时间点为止可访问」(过去无限制)
valid_until_field - 同时保留 -> 经典时间窗口
此外,字段为NULL时会被视为「无过期时间」(),实现行级动态时间窗口。
valid_untilvalid_until IS NULL OR valid_until > now()可选键:
- (默认
valid_from_inclusive)true - (默认
valid_until_inclusive)false
语义: 仅当「当前时间」在配置的时间窗口内时授权。省略字段会移除对应的边界限制。
适用场景:
- 定时发布的内容。
- 过期的邀请。
- 无上限的「发布日期后可访问」(仅使用)。
valid_from_field
组合使用指引:解决的是访问何时有效的问题,而不是谁有权访问的问题。单独使用时它意味着「时间窗口内任何人都可以访问」。实际使用时,始终要和基于身份的策略组合使用——要么作为限制性顶层策略(和宽松的身份策略做AND逻辑),要么放在AuthzTemporal的AuthzComposite中。BoolExpr
与的重叠: 你可以用AuthzPublishable近似实现已发布内容的gate(例如AuthzTemporal且不设置valid_from_field: "published_at")。但valid_until_field额外提供了AuthzPublishable布尔开关,允许作者独立于时间下线内容。需要显式开/关切换时使用is_published;访问完全由时间驱动时使用AuthzPublishable。AuthzTemporal
10) AuthzPublishable
AuthzPublishable10) AuthzPublishable
AuthzPublishableIntent: Draft/published gating.
Config (default fields):
json
{}Optional keys:
- (default
is_published_field)"is_published" - (default
published_at_field)"published_at" - (default
require_published_at)true
Semantics: Authorize when a record is published (and, if , when ).
require_published_at=truepublished_at <= nowUse when:
- Public content that is only visible after publishing.
Combination guidance:answers whether content is published, not who can see it. On its own it means "anyone can see published content." In practice, always combine it with an identity-based policy — either as a restrictive top-level policy (ANDed with a permissive identity policy likeAuthzPublishable) or inside anAuthzEntityMembershipAuthzComposite. See the "Permissive vs Restrictive policies in RLS" section for examples.BoolExpr
Overlap with: The time component ofAuthzTemporal(AuthzPublishable) is a subset of whatpublished_at <= nowcan express. The key difference is theAuthzTemporalboolean -- a deliberate on/off toggle thatis_publisheddoes not provide. If you only need time-window access with no manual toggle,AuthzTemporalis sufficient.AuthzTemporal
用途: 草稿/发布状态 gate。
默认字段配置:
json
{}可选键:
- (默认
is_published_field)"is_published" - (默认
published_at_field)"published_at" - (默认
require_published_at)true
语义: 当记录已发布时授权(如果,还需要满足)。
require_published_at=truepublished_at <= now适用场景:
- 仅发布后可见的公开内容。
组合使用指引:解决的是内容是否已发布的问题,而不是谁可以查看的问题。单独使用时它意味着「任何人都可以查看已发布内容」。实际使用时,始终要和基于身份的策略组合使用——要么作为限制性顶层策略(和AuthzPublishable这类宽松的身份策略做AND逻辑),要么放在AuthzEntityMembership的AuthzComposite中。示例可参考「RLS中的宽松策略 vs 限制性策略」章节。BoolExpr
与的重叠:AuthzTemporal的时间组件(AuthzPublishable)是published_at <= now能力的子集。核心差异是AuthzTemporal提供了AuthzPublishable布尔值——这是is_published没有的手动开/关切换能力。如果你只需要时间窗口访问,不需要手动开关,使用AuthzTemporal即可。AuthzTemporal
11) AuthzMemberList
AuthzMemberList11) AuthzMemberList
AuthzMemberListNot recommended. This policy relies on a UUID array column rather than a proper foreign-key relationship. It does not scale well and bypasses normal relational integrity. PreferorAuthzEntityMembershipwith proper FK-based membership tables when possible.AuthzPeerOwnership
Intent: Actor is present in a UUID array column on the same row.
Config:
json
{ "array_field": "member_ids" }Semantics: Authorize when the actor id appears in .
{array_field}Use when:
- Legacy share lists stored as arrays (supported but not recommended for new designs).
不推荐使用。 该策略依赖UUID数组字段而非正规的外键关系,扩展性差,且会绕过常规的关系完整性校验。尽可能优先使用基于外键成员表的或AuthzEntityMembership。AuthzPeerOwnership
用途: Actor存在于当前行的UUID数组字段中。
配置:
json
{ "array_field": "member_ids" }语义: 当Actor ID出现在中时授权。
{array_field}适用场景:
- 存储为数组的 legacy 共享列表(支持但新设计不推荐使用)。
12) AuthzRelatedMemberList
AuthzRelatedMemberList12) AuthzRelatedMemberList
AuthzRelatedMemberListNot recommended. Same concern as-- relies on a UUID array column in a related table rather than proper FK-based membership. Prefer FK-based policies when possible.AuthzMemberList
Intent: Actor is present in a UUID array column in a related table.
Config (conceptual):
json
{
"owned_schema": "public",
"owned_table": "documents",
"owned_table_key": "member_ids",
"owned_table_ref_key": "document_id",
"this_object_key": "id"
}Semantics: "Follow a reference to a related row that contains an array of member ids."
Use when:
- Legacy membership lists stored as arrays in a related table (supported but not recommended for new designs).
不推荐使用。 和有相同的问题——依赖关联表中的UUID数组字段而非正规的基于外键的成员关系。尽可能优先使用基于外键的策略。AuthzMemberList
用途: Actor存在于关联表的UUID数组字段中。
概念配置:
json
{
"owned_schema": "public",
"owned_table": "documents",
"owned_table_key": "member_ids",
"owned_table_ref_key": "document_id",
"this_object_key": "id"
}语义: 「查询关联行,基于关联行中的成员ID数组做校验。」
适用场景:
- 存储为关联表数组的 legacy 成员列表(支持但新设计不推荐使用)。
13) AuthzAllowAll
AuthzAllowAll13) AuthzAllowAll
AuthzAllowAllWARNING:is almost never what you want. It grants unconditional access to every authenticated user for the specified privilege. Before using it, ask yourself: "Should literally every authenticated user be able to read/write this data?" If the answer is no (and it usually is), use a scoped policy likeAuthzAllowAllorAuthzDirectOwnerinstead.AuthzEntityMembershipEspecially avoidon junction tables. When creating ManyToMany relations with security, match the junction table's policy to the parent tables' policies. If parents useAuthzAllowAll, the junction should too. UsingAuthzDirectOwneron a junction table means any authenticated user can create/delete links between rows they don't own. See theAuthzAllowAllskill for junction table security patterns.constructive-relations
Intent: Unconditional allow.
Config:
json
{}Semantics: Always authorizes.
Legitimate use cases (rare):
- Truly public reference data (e.g., a lookup table that any user should read)
countries - Public read-only access (combine with restrictive write policies)
Common misuses:
- Using as a "just make it work" default -- this bypasses all access control
AuthzAllowAll - Using on junction tables when parent tables have scoped policies -- the junction should match the parents
AuthzAllowAll - Using for both read AND write on any table with user-generated content
AuthzAllowAll
警告:几乎永远不是正确选择。 它会为所有已认证用户无条件授予指定权限的访问权。使用前请问自己:「真的要让所有已认证用户都能读/写这个数据吗?」如果答案是否(通常都是如此),请改用AuthzAllowAll或AuthzDirectOwner这类有范围的策略。AuthzEntityMembership尤其不要在关联表上使用。 创建带安全控制的多对多关系时,关联表的策略要和父表的策略匹配。如果父表使用AuthzAllowAll,关联表也应该使用相同策略。在关联表上使用AuthzDirectOwner意味着任何已认证用户都可以在不属于自己的行之间创建/删除关联。关联表安全模式可参考AuthzAllowAllskill。constructive-relations
用途: 无条件允许访问。
配置:
json
{}语义: 始终授权。
合理使用场景(非常少):
- 真正公开的参考数据(例如所有用户都可以读取的lookup表)
countries - 公开只读访问(和限制性的写策略组合使用)
常见误用:
- 把当作「先跑通再说」的默认配置——这会绕过所有访问控制
AuthzAllowAll - 父表有范围策略时,在关联表上使用——关联表策略应该和父表匹配
AuthzAllowAll - 对任何包含用户生成内容的表同时授予读和写的权限
AuthzAllowAll
14) AuthzDenyAll
AuthzDenyAll14) AuthzDenyAll
AuthzDenyAllIntent: Unconditional deny.
Config:
json
{}Semantics: Never authorizes.
Use when:
- Explicitly blocking a privilege.
用途: 无条件拒绝访问。
配置:
json
{}语义: 永远不授权。
适用场景:
- 显式禁用某个权限。
Advanced: AuthzComposite
(meta-node, not a leaf type)
AuthzComposite高级特性:AuthzComposite
(元节点,非叶子类型)
AuthzCompositeAuthzCompositeThe for an is itself an AST node that the system recursively evaluates. It can be either a single Authz* leaf node or a combining multiple nodes.
dataAuthzCompositeBoolExprSingle leaf node wrap — delegates to one Authz* node:
json
{
"AuthzEntityMembership": {
"entity_field": "owner_id",
"membership_type": "Organization Member"
}
}BoolExprjson
{
"BoolExpr": {
"boolop": "AND_EXPR",
"args": [
{ "AuthzTemporal": { "valid_from_field": "publish_at" } },
{ "AuthzDirectOwner": { "entity_field": "owner_id" } }
]
}
}BoolExprjson
{
"BoolExpr": {
"boolop": "OR_EXPR",
"args": [
{
"AuthzEntityMembership": {
"entity_field": "owner_id",
"membership_type": "Organization Member"
}
},
{
"AuthzMembership": {
"membership_type": "App Member",
"permission": "create_invites"
}
}
]
}
}When to use :
AuthzComposite- Genuinely nested boolean logic that cannot be expressed with separate top-level policies.
- Mixing AND/OR at different levels (e.g., ).
(A OR B) AND (C OR D) - NOT expressions.
- Non-authz conditions in the same expression tree (e.g., column value checks combined with auth checks).
AuthzCompositeAuthzCompositedataBoolExpr单个叶子节点包装——委托给单个Authz*节点:
json
{
"AuthzEntityMembership": {
"entity_field": "owner_id",
"membership_type": "Organization Member"
}
}BoolExprjson
{
"BoolExpr": {
"boolop": "AND_EXPR",
"args": [
{ "AuthzTemporal": { "valid_from_field": "publish_at" } },
{ "AuthzDirectOwner": { "entity_field": "owner_id" } }
]
}
}BoolExprjson
{
"BoolExpr": {
"boolop": "OR_EXPR",
"args": [
{
"AuthzEntityMembership": {
"entity_field": "owner_id",
"membership_type": "Organization Member"
}
},
{
"AuthzMembership": {
"membership_type": "App Member",
"permission": "create_invites"
}
}
]
}
}AuthzComposite- 无法通过独立顶层策略表达的嵌套布尔逻辑。
- 不同层级混合AND/OR的场景(例如)。
(A OR B) AND (C OR D) - NOT表达式。
- 同一个表达式树中包含非授权条件的场景(例如列值检查和授权检查组合)。
Permissive vs Restrictive policies in RLS
RLS中的宽松策略 vs 限制性策略
When Safegres policies compile to PostgreSQL RLS, their interaction depends on whether they are permissive or restrictive:
- Permissive (default): Multiple permissive policies on the same table and privilege are ORed together. If any permissive policy passes, the row is accessible.
- Restrictive (): Restrictive policies are ANDed with the result of permissive policies. All restrictive policies must pass in addition to at least one permissive policy.
permissive := false
OR composition (permissive + permissive):
"Owner OR org admin can see" — add two separate permissive policies. PostgreSQL automatically ORs them:
Policy 1 (permissive): AuthzDirectOwner { entity_field: "owner_id" }
Policy 2 (permissive): AuthzEntityMembership { entity_field: "organization_id", membership_type: 2, is_admin: true }
Effective rule: row.owner_id = actor OR actor is admin of row.organization_idAND composition (permissive + restrictive):
"Org members can access, but only while the row's time window is active" — add membership as permissive and the time constraint as restrictive:
Policy 1 (permissive): AuthzEntityMembership { entity_field: "entity_id", membership_type: 2 }
Policy 2 (restrictive): AuthzTemporal { valid_from_field: "starts_at", valid_until_field: "ends_at" }
Effective rule: actor is member of row.entity_id AND now() is within [starts_at, ends_at)3 policies (2 permissive + 1 restrictive):
"Owner OR org member can access, but only if the row is published":
Policy 1 (permissive): AuthzDirectOwner { entity_field: "owner_id" }
Policy 2 (permissive): AuthzEntityMembership { entity_field: "organization_id", membership_type: 2 }
Policy 3 (restrictive): AuthzPublishable {}
Effective rule: (row.owner_id = actor OR actor is member of row.organization_id) AND row.is_published = true4 policies (2 permissive + 2 restrictive):
"Owner OR org member can access, but only if published AND within the time window":
Policy 1 (permissive): AuthzDirectOwner { entity_field: "owner_id" }
Policy 2 (permissive): AuthzEntityMembership { entity_field: "organization_id", membership_type: 2 }
Policy 3 (restrictive): AuthzPublishable {}
Policy 4 (restrictive): AuthzTemporal { valid_from_field: "available_from", valid_until_field: "available_until" }
Effective rule: (P1 OR P2) AND R3 AND R4
= (owner OR org member) AND is_published AND now() in time windowNotice the pattern: permissive/restrictive composition always produces . This is powerful but limited to a single grouping shape.
(P1 OR P2 OR ... Pn) AND R1 AND R2 AND ... Rm当Safegres策略编译为PostgreSQL RLS时,它们的交互逻辑取决于策略是宽松(permissive)还是限制性(restrictive):
- 宽松策略(默认):同一张表、同一个权限下的多个宽松策略是OR关系。只要任意一个宽松策略通过,该行就可以访问。
- 限制性策略():限制性策略会和宽松策略的结果做AND逻辑。除了至少通过一个宽松策略外,还必须满足所有限制性策略才能访问。
permissive := false
OR组合(宽松+宽松):
「所有者或组织管理员可以查看」——添加两个独立的宽松策略即可,PostgreSQL会自动做OR逻辑:
Policy 1(宽松): AuthzDirectOwner { entity_field: "owner_id" }
Policy 2(宽松): AuthzEntityMembership { entity_field: "organization_id", membership_type: 2, is_admin: true }
生效规则: row.owner_id = actor OR actor is admin of row.organization_idAND组合(宽松+限制性):
「组织成员可以访问,但仅在行的时间窗口生效期间」——将成员关系设为宽松策略,时间约束设为限制性策略:
Policy 1(宽松): AuthzEntityMembership { entity_field: "entity_id", membership_type: 2 }
Policy 2(限制性): AuthzTemporal { valid_from_field: "starts_at", valid_until_field: "ends_at" }
生效规则: actor is member of row.entity_id AND now() is within [starts_at, ends_at)3个策略(2个宽松+1个限制性):
「所有者或组织成员可以访问,但仅当行已发布」:
Policy 1(宽松): AuthzDirectOwner { entity_field: "owner_id" }
Policy 2(宽松): AuthzEntityMembership { entity_field: "organization_id", membership_type: 2 }
Policy 3(限制性): AuthzPublishable {}
生效规则: (row.owner_id = actor OR actor is member of row.organization_id) AND row.is_published = true4个策略(2个宽松+2个限制性):
「所有者或组织成员可以访问,但必须已发布且在时间窗口内」:
Policy 1(宽松): AuthzDirectOwner { entity_field: "owner_id" }
Policy 2(宽松): AuthzEntityMembership { entity_field: "organization_id", membership_type: 2 }
Policy 3(限制性): AuthzPublishable {}
Policy 4(限制性): AuthzTemporal { valid_from_field: "available_from", valid_until_field: "available_until" }
生效规则: (P1 OR P2) AND R3 AND R4
= (owner OR org member) AND is_published AND now() in time window可以看出规律:宽松/限制性组合的结果永远是。这种模式很强大,但仅支持单一组合结构。
(P1 OR P2 OR ... Pn) AND R1 AND R2 AND ... RmWhen AuthzComposite
is necessary
AuthzComposite需要使用AuthzComposite
的场景
AuthzCompositePermissive/restrictive composition cannot express arbitrary boolean groupings. Consider:
"Access is allowed if (org member AND published) OR (direct owner AND within time window)":
Desired: (AuthzEntityMembership AND AuthzPublishable) OR (AuthzDirectOwner AND AuthzTemporal)This requires OR-ing two AND-groups — impossible with flat permissive/restrictive policies (which always produce a single shape). Use :
(any P) AND (all R)AuthzCompositejson
{
"BoolExpr": {
"boolop": "OR_EXPR",
"args": [
{
"BoolExpr": {
"boolop": "AND_EXPR",
"args": [
{ "AuthzEntityMembership": { "entity_field": "organization_id", "membership_type": 2 } },
{ "AuthzPublishable": {} }
]
}
},
{
"BoolExpr": {
"boolop": "AND_EXPR",
"args": [
{ "AuthzDirectOwner": { "entity_field": "owner_id" } },
{ "AuthzTemporal": { "valid_from_field": "starts_at", "valid_until_field": "ends_at" } }
]
}
}
]
}
}Prefer multiple top-level policies over whenever possible. They are simpler, easier to read, and easier to maintain. Reserve for cases that genuinely require nested boolean trees like the one above.
AuthzCompositeAuthzComposite宽松/限制性组合无法表达任意布尔分组。例如:
「满足(组织成员且已发布)或者(直接所有者且在时间窗口内)时允许访问」:
期望逻辑: (AuthzEntityMembership AND AuthzPublishable) OR (AuthzDirectOwner AND AuthzTemporal)这种场景需要对两个AND分组做OR逻辑——扁平的宽松/限制性策略无法实现(它们永远只能生成的结构)。此时使用:
(任意P) AND (所有R)AuthzCompositejson
{
"BoolExpr": {
"boolop": "OR_EXPR",
"args": [
{
"BoolExpr": {
"boolop": "AND_EXPR",
"args": [
{ "AuthzEntityMembership": { "entity_field": "organization_id", "membership_type": 2 } },
{ "AuthzPublishable": {} }
]
}
},
{
"BoolExpr": {
"boolop": "AND_EXPR",
"args": [
{ "AuthzDirectOwner": { "entity_field": "owner_id" } },
{ "AuthzTemporal": { "valid_from_field": "starts_at", "valid_until_field": "ends_at" } }
]
}
}
]
}
}尽可能优先使用多个顶层策略,而非。 顶层策略更简单、易读、易维护。仅当确实需要上述这类嵌套布尔树的场景时再使用。
AuthzCompositeAuthzComposite