-
Notifications
You must be signed in to change notification settings - Fork 0
13 Real World Recipes
Each recipe follows the same reasoning order: problem, authorization model, policy, trusted backend data, frontend projection, tests, and mistakes.
A user may update a document only while it is a draft and only when the user owns that document.
- permission: broad update capability;
- subject attribute: current user ID;
- resource attributes: owner ID and status;
- action:
update.
- id: document-update-own-draft
resourceType: document
action: update
requirement:
all:
- permission: DOC.UPDATE
- attribute:
source: resource
name: status
operator: equal
valueType: string
value: draft
- attributeComparison:
left: { source: subject, name: userId }
operator: equal
right: { source: resource, name: ownerId }A subject provider supplies userId; a resource provider loads ownerId and
status from the database. The update command must use the same protected
resource version or re-check before commit.
The snapshot may include DOC.UPDATE to show an edit affordance. The frontend
cannot know authoritative ownership or current status; handle backend 403
after a concurrent change.
Test owner/draft allow, other owner deny, submitted status deny, missing owner indeterminate/deny, and a status change between read and update.
Comparing the route ID to the subject ID does not prove ownership. Load the document.
Users can read documents only in their current organization and tenant.
- id: document-read-organization
resourceType: document
action: read
requirement:
all:
- permission: DOC.READ
- attributeComparison:
left: { source: subject, name: tenantId }
operator: equal
right: { source: resource, name: tenantId }
- attributeComparison:
left: { source: subject, name: organizationId }
operator: equal
right: { source: resource, name: organizationId }Resolve the current tenant/organization from a trusted application assignment, not a caller-selected header. Scope repository queries by tenant before authorization. RuleGate provides defense in depth and business policy; it is not a substitute for data-layer isolation.
Create a matrix:
| Subject tenant | Subject org | Resource tenant | Resource org | Result |
|---|---|---|---|---|
| A | records | A | records | allow |
| A | records | A | legal | deny |
| A | records | B | records | deny |
| missing | records | A | records | deny |
Using a global admin role as an implicit tenant bypass. If a reviewed support workflow needs cross-tenant access, model it as a separate policy/action with strong context and audit requirements.
An approver may approve a submitted request within their amount limit, but cannot approve their own request.
- id: purchase-request-approve
resourceType: purchase-request
action: approve
requirement:
all:
- permission: PURCHASE.APPROVE
- role: PURCHASE.APPROVER
- attribute:
source: resource
name: status
operator: equal
valueType: string
value: submitted
- attributeComparison:
left: { source: resource, name: totalAmount }
operator: lessThanOrEqual
right: { source: subject, name: approvalLimit }
- not:
attributeComparison:
left: { source: subject, name: userId }
operator: equal
right: { source: resource, name: requesterId }Load amount, requester, and status from a transactionally appropriate read.
After authorization, change only from submitted using an optimistic
concurrency check or transaction.
Show the approvals area from PURCHASE.APPROVE; show the final Approve button
as tentative UX. Backend resource checks remain authoritative.
Test exact limit, one unit above limit, self-approval, wrong status, missing limit, and two approvers racing.
Confidential content requires sufficient clearance, an internal/VPN network, a trusted device, fresh MFA, and organization-specific operating hours.
For a shared fixed schedule:
- id: confidential-document-read
resourceType: document
action: read-confidential
requirement:
all:
- permission: DOC.CONFIDENTIAL.READ
- attributeComparison:
left: { source: subject, name: clearanceLevel }
operator: greaterThanOrEqual
right: { source: resource, name: classificationLevel }
- context:
property: networkZone
operator: in
valueType: stringCollection
value: [internal, vpn]
- context:
property: trustedDevice
operator: equal
valueType: boolean
value: true
- contextAge:
timestamp: mfa
maximumAge: '00:10:00'
- timeWindow:
days: [monday, tuesday, wednesday, thursday, friday]
start: '08:00'
end: '18:00'
timeZone: Europe/IstanbulFor per-organization schedules, load the schedule from application settings or a trusted directory through a context provider. Then use a reviewed custom requirement/evaluator or separate organization policy snapshots. A request parameter must never choose the allowed hours.
Test clearance equal/less, every network state, trusted false/missing, MFA at the exact age boundary, before/at/after schedule boundaries, weekends, and daylight-saving behavior for zones that observe it.
A list endpoint must return only resources visible to the caller.
- authorize the collection action
document/list; - derive tenant/organization/clearance filters from trusted subject data;
- apply those filters in the repository query;
- optionally authorize returned high-risk items individually;
- never fetch all tenants and hide rows only in Angular.
Collection policy:
- id: documents-list
resourceType: document
action: list
requirement:
all:
- permission: DOC.LIST
- attribute:
source: subject
name: organizationId
operator: existsResource policies cannot automatically rewrite database queries. The application must enforce filtering.
A worker may post an invoice only through the worker channel and only for its assigned organization.
- id: invoice-worker-post
resourceType: invoice
action: post
requirement:
all:
- permission: INVOICE.POST
- context:
property: identityType
operator: equal
valueType: string
value: service
- context:
property: requestChannel
operator: equal
valueType: string
value: worker
- attributeComparison:
left: { source: subject, name: organizationId }
operator: equal
right: { source: resource, name: organizationId }Authenticate the service with an appropriate machine credential. Do not use a
human role as a substitute for a service identity. Construct a direct
AuthorizationRequest or adapt the worker host around the same engine.
An incident team needs temporary read access during a declared emergency.
Use a separate action/policy with explicit capability, context, fixed window,
and application audit—not an if (isAdmin) return true hidden in code:
- id: document-emergency-read
resourceType: document
action: emergency-read
requirement:
all:
- permission: INCIDENT.EMERGENCY.READ
- role: INCIDENT.RESPONDER
- context:
property: authenticationMethod
operator: equal
valueType: string
value: phishing-resistant-mfa
- contextAge:
timestamp: mfa
maximumAge: '00:05:00'
- dateTimeWindow:
startsAt: '2026-08-01T00:00:00Z'
endsAt: '2026-08-02T00:00:00Z'Promote/remove the window through the governed policy lifecycle. Record a domain audit event for every use. Do not log sensitive document content in RuleGate diagnostics.
An Angular 22 application uses signals/functional guards; an Angular 15 application uses the legacy observable adapter; an Angular 10 application uses the framework-independent store. All send operations to the same protected backend.
The UI technology changes how the snapshot is consumed, not the meaning of
DOC.READ, document-read, or the backend's organization/resource/context
rules. Keep generated identifiers and backend policies aligned, but never
duplicate the complete ABAC/CBAC policy in JavaScript.
Use this template for new domains:
Problem:
Subject:
Resource:
Action:
Requirements:
Trusted source for every fact:
Manifest policy:
Backend enforcement point:
Frontend projection (optional):
Allow tests:
Deny/indeterminate tests:
Concurrency and stale-data behavior:
Diagnostics and audit behavior:
Common bypasses to prevent:
Previous: Extensibility · Next: Production checklist
Canonical source: docs/guide · Documentation index · RuleGate 1.0.0
- Home
- 1. Authorization foundations
- 2. Packages and installation
- 3. First protected API
- 4. Policy language
- 5. ASP.NET Core integration
- 6. Trusted attributes and context
- 7. Identity and Keycloak
- 8. Frontend integration
- 9. CLI and policy lifecycle
- 10. Testing and diagnostics
- 11. Policy sources and reload
- 12. Extensibility
- 13. Real-world recipes
- 14. Production checklist
- Glossary