Policies overview
When ABAC policies are needed beyond roles, how targets, rules, conditions, and combining algorithms layer, and where each topic is documented
A policy is a named list of rules with a combining algorithm. Roles answer "which subject may do which action on which resource"; policies answer everything that depends on attributes, time, network, or relationships between the subject and the resource.
When to use / When not to use
Roles cover the common case: "editors can update posts." The diagram shows which requirements fit roles and which need a policy.
Use a policy for:
- Time-based restrictions - deny writes on weekends or outside business hours (
A1). - IP / geo-fencing - allow access only from trusted networks (
A2). - Cross-attribute checks - allow updates only when subject and resource share a department (
A3, see$-variable references). - Dynamic deny rules - block specific subjects or suspicious behaviour without changing role assignments (
A4). - Maintenance mode - deny all writes globally when a feature flag is on (
A5). - Attribute-gated grants - only senior staff may publish (
A6).
Do not use a standalone policy when the condition belongs to a single role's permission. Use grantWhen() on the role instead; it compiles into the same rule shape.
Roles and policies share one pipeline: rolesToPolicy() turns roles into a synthetic policy (id: '__rbac__', algorithm: 'allow-overrides') that the engine evaluates next to yours. By default the engine combines policies with policyCombine: 'and' - a deny from any applicable policy is final. See cross-policy combining.
How the layers filter a request
A request passes through concentric filters. Each layer decides whether the next one runs.
Tis the target check: actions, resources, roles.SHAPEis the rule action/resource match described in rule matching. A policy with no rule for this action/resource pair is NotApplicable, exactly like a target miss.CONDevaluates conditions and nested groups.COMBis the policy's combining algorithm. When the policy applied but no rule matched,defaultEffectis folded in (DEF) - this is the one place a policy "votes" without a matching rule.CROSSis the engine-level merge described in evaluation pipeline and cross-policy combining.
Put broad preconditions in targets, rule-local matching in actions/resources, and contextual logic in conditions.
Reading order
| Page | Covers |
|---|---|
| Building policies | definePolicy(), defineRule(), when(), what build() validates and throws |
| Rules | Rule anatomy: effect, actions, resources, priority, forScope, metadata |
| Targets | Pre-filter a policy by action, resource, or role; NotApplicable semantics |
| Conditions | The When builder, every operator with edge semantics, field resolution |
| Nesting and/or/not | and(), or(), not(), whenAny(), depth limit, empty groups |
| Combining algorithms | deny-overrides, allow-overrides, first-match, highest-priority with decision diagrams |
$-variable references | Compare two fields on the same request; resolution rules |
| Layered example | Roles plus three policies, wired to an engine, with a walkthrough |
Quick example
import { definePolicy } from '@gentleduck/iam'
const weekendDeny = definePolicy('deny-weekends')
.name('Deny on Weekends')
.desc('Block all write operations on weekends')
.version(1)
.algorithm('deny-overrides')
.rule('r-deny-weekends', (r) =>
r
.deny()
.on('create', 'update', 'delete')
.of('*')
.when((w) => w.env('dayOfWeek', 'in', [0, 6])),
)
.build()
This policy:
- Uses
deny-overrides- any matching deny rule makes the policy deny. - Only has a rule for writes, so a
readrequest finds no rule shape and the policy is NotApplicable for it. - Denies when
environment.dayOfWeekis Sunday (0) or Saturday (6). The value[0, 6]is a literal array; see conditions for howintreats arrays.
FAQ
Why use policy targets instead of only rule conditions?
Targets are a fast pre-filter. They let the engine skip a whole policy before it inspects rules or conditions, and they make a policy NotApplicable rather than a defaultEffect vote when they miss. See targets.
Can a policy deny something that a role allows?
Yes. Roles compile into an allow-only policy (allow-overrides), but your policies are evaluated alongside it. Under the default policyCombine: 'and', a deny from any applicable policy wins. See cross-policy combining.
grantWhen() on a role or a standalone policy rule?
Use grantWhen() when the condition belongs to a single role's permission. Use a standalone policy when the rule spans many roles, acts as a global deny layer, or needs its own combining algorithm and lifecycle. See conditional permissions.
How do targets, rule action/resource filters, and conditions layer?
Concentric filters, as in the diagram above: targets decide whether the policy runs, rule actions/resources decide which rules are candidates, conditions are the final contextual check on those candidates.
See also
- Rule matching - wildcard and hierarchical action/resource matching
- Evaluation pipeline - the full request lifecycle
- Cross-policy combining -
policyCombinemodes - Roles overview - the RBAC side