# Pathrule Pattern: Authorization with RBAC and ReBAC (1.0.0)
# ::pathrule:package:authorization-rbac-rebac

### [RULE] Every protected operation asks one deny-first decision API  (path: /src/authorization)
<!-- scope: folder | priority: high | strict -->

Authorization is a server-side precondition for every protected read and write, not a UI feature or a role-name convention. Route handlers, jobs, GraphQL resolvers, batch actions, and internal tools call one decision interface with the authenticated principal, canonical action, loaded resource, trusted tenant, and current policy context. Any missing input, evaluation error, or unknown action denies.

- Resolve principal and tenant from authenticated server state, never from an unverified request field.
- Name actions from business capabilities such as invoice.approve rather than HTTP verbs or screen names.
- Return a stable allow or deny result with policy version and reason code, without leaking sensitive relationship data.
- Complete authorization before writes, external calls, queues, or protected response bytes.

Verification: Remove each input, use an unknown action, inject an evaluator failure, and call the same operation through every transport; confirm all paths deny consistently.

---

### [RULE] Authorize the loaded object and its current relationships  (path: /src/domain)
<!-- scope: folder | priority: high | strict -->

Checking permission to access a collection, route, or resource type does not authorize a specific object. Load the object through a tenant-safe lookup, derive its immutable identifiers and relevant parents, then evaluate the action against that exact state. Recheck inside the same consistency boundary when relationships or ownership can change concurrently.

- Reject identifiers that resolve outside the authenticated tenant without revealing whether the object exists.
- Apply checks to nested resources, exports, bulk selection, indirect references, and side-channel metadata such as counts.
- Avoid post-filtering unauthorized rows after an unbounded query; constrain reads at the data or policy boundary.
- Treat create operations as authorization against the intended parent and attributes, not a resource that does not exist yet.

Verification: Swap object, parent, tenant, group, and owner ids across otherwise valid requests; confirm no content, timing detail, count, or mutation crosses the boundary.

---

### [RULE] Policy changes invalidate affected authorization decisions  (path: /src/authorization)
<!-- scope: folder | priority: high | strict -->

Authorization caches can turn revoked membership, changed ownership, disabled accounts, or new policy into continuing access. Cache only after proving the complete decision key, and include every input that can change the result: principal, action, resource, tenant, relevant context, policy version, and relationship or data version. Prefer short request-scoped memoization for complex decisions.

- Invalidate or version-bust on membership, role, ownership, policy, account, and tenant status changes.
- Never share a cached allow across principals or resources because the public response looks identical.
- Define failure behavior and maximum stale access explicitly; evaluator unavailability must not silently allow.
- Measure hit rate, decision age, invalidations, and stale-deny or stale-allow test outcomes without logging sensitive tuples.

Verification: Cache an allow, revoke each underlying relationship, and race reads with the change; confirm access ends within the documented bound.

---

### [RULE] Authorization tests prove denials and relationship edges  (path: /tests/authorization)
<!-- scope: folder | priority: medium | strict -->

Happy-path role tests do not expose broken object-level authorization. Build a policy matrix from principal types, actions, resource states, ownership, group and hierarchy relationships, tenant boundaries, and exceptional attributes. For every allow, include the nearest denial created by changing one fact.

- Test direct endpoints, nested routes, batch operations, exports, background jobs, and alternate transports that reach the same capability.
- Include deleted, disabled, suspended, unowned, transferred, shared, inherited, and cyclic relationship cases.
- Assert both the decision and the absence of protected fields, counts, timing clues, and side effects.
- Run policy-model tests and integration tests against the real data loader and evaluator configuration.

Verification: Generate the action-resource matrix from registered capabilities and fail CI when a new protected action has no explicit allow and deny cases.

---

### [MEMORY] Roles, attributes, and relationships solve different dimensions  (path: /src/authorization)

Use roles for stable bundles of capabilities, not for every team, customer, document, or exception. Use trusted attributes for contextual conditions such as account status, region, data classification, or approved time window. Use relationships when access depends on how a principal connects to a resource through ownership, membership, hierarchy, delegation, or sharing. Complex applications usually combine these dimensions behind one decision API.

Do not copy relationship facts into ever-growing role names or accept authorization attributes from the client. Define tuple or relation ownership, cardinality, inheritance, cycle behavior, and deletion semantics. Keep policy readable enough that product and security owners can review the intended matrix. See /src/domain for canonical resource loading and /tests/authorization for edge coverage.

---

### [MEMORY] Authorization decisions are explainable without exposing the graph  (path: /src/authorization)

Support, incident response, and audit need to know why access was allowed or denied, but returning raw policy traces can reveal group membership, hidden resources, tenant structure, or security rules. Define stable internal reason codes and a privileged explanation path separate from the ordinary application response.

Record principal, canonical action, protected resource reference, tenant, decision, policy version, evaluator status, and safe reason with correlation identifiers. Minimize attributes and relationship data in logs, apply retention and access controls, and never make the explanation record itself a bypass path. Aggregate denial categories for monitoring without high-cardinality sensitive labels. See the Security Audit Logging pattern for evidence integrity and access review.

---

### [SKILL] review-authorization-model  (path: /tests/authorization)

---
name: review-authorization-model
description: Review an application authorization model and its enforcement paths for bypass and drift.
---

# Review Authorization Model

1. Inventory protected actions and every transport, job, export, batch path, and internal tool that can invoke them.
2. Map each action to canonical resource loading, tenant derivation, required roles, trusted attributes, relationships, exceptional states, and default denial.
3. Trace one allow and the nearest deny through handler, policy decision, data query, side effects, cache, and audit evidence.
4. Test cross-tenant ids, indirect references, nested and bulk operations, stale relationships, evaluator failure, policy migration, and concurrent ownership change.
5. Record uncovered capabilities, ambiguous policy, sensitive explanations, cache risks, owner, and the evidence from a clean rerun.

The review is complete only when every protected capability has one enforceable decision boundary and explicit negative tests.
