Combining conditions
Policies combine individual conditions in two ways: requiring several conditions to hold at the same time (and), and offering independent reasons to grant access (or or multiple rules for the same predicate).
Requiring conditions together with and
Section titled “Requiring conditions together with and”The and operator requires every combined condition to hold at the same time, using compatible values for any shared variables:
same_organization(user: User, document: Document) if belongs_to(document, organization: Organization) and member(user, organization);Both belongs_to and member must match, and the repeated organization variable requires both facts to match the same Organization identity. See Variables and matching for how shared variables join facts.
Offering alternative paths with or and multiple rules
Section titled “Offering alternative paths with or and multiple rules”When your application has more than one way to qualify for access, combine those alternatives inside a single rule with or or write separate rules for the same predicate:
// Using "or" inside one rulecan_read(user: User, document: Document) if is_owner(user, document) or same_organization(user, document);
// Equivalent: separate rules with the same head predicate and aritycan_read_split(user: User, document: Document) if is_owner(user, document);
can_read_split(user: User, document: Document) if same_organization(user, document);Both forms mean the same thing. Unlike an imperative if / else if chain, rules in Verdict are additive alternatives: any matching rule is enough to derive the conclusion. Use or for compact choices inside one rule, and separate rules when each path has its own set of conditions.
Grouping alternatives within shared requirements
Section titled “Grouping alternatives within shared requirements”In Verdict, and binds more tightly than or. When combining an or choice with shared and requirements in the same rule, wrap the or branch in parentheses:
allow(user: User, action: string, document: Document) if (action = "read" or action = "download") and can_read(user, document) and is_published(document);Without parentheses around action = "read" or action = "download", can_read and is_published would attach only to action = "download", leaving action = "read" unrestricted. See the Language Reference for operator precedence.
Naming reusable helper predicates
Section titled “Naming reusable helper predicates”Extracting helper predicates like same_organization and can_read keeps top-level allow rules readable and lets you reuse the same logic across multiple actions. Permissions shows how policies use this technique to build a shared has_permission(actor, action, resource) layer.
Applying a shared restriction to every intended path
Section titled “Applying a shared restriction to every intended path”Because rules for the same predicate are additive, a shared requirement only governs the grant paths that pass through it:
can_read(user: User, document: Document) if is_owner(user, document) or same_organization(user, document);
allow(user: User, action: string, document: Document) if (action = "read" or action = "download") and can_read(user, document) and is_published(document);Because both owners and organization members flow through can_read into a single allow rule that requires is_published(document), neither path can read or download an unpublished document. If you also left an unguarded allow(user, "read", document) if is_owner(user, document); rule in the policy, owners would bypass the is_published check.
Next steps
Section titled “Next steps”- Route permissions and resource-qualified restrictions through
has_permissionin Permissions. - Express absence conditions and understand missing data in Negation and missing facts.
- Verify that shared restrictions cover every access path in Testing & Validating Policies.