The Iron RBAC
Permissions as data, evaluated in context — changed by policy, not by deployment.
The incident
The org chart grew faster than the if-statements. 'Regional manager, north region, read-only, except during audits' became nested conditionals in four services. One service missed the update. That's where the breach report says the contractor saw payroll data.
Why the obvious approach fails
if (user.role === 'admin') is a permission system with three fatal properties: changing policy requires a deployment, policy lives in N services that drift apart, and roles explode combinatorially as real organizations demand granularity. Enterprise access control is not a set of booleans — it's a policy evaluation: who (identity and attributes), what (resource and its attributes), and context (time, device, network). That evaluation has to live in one engine, fed by data, or it lives inconsistently everywhere.
The standard
- Authorization decisions go through one policy engine; inline role checks in business logic are banned.
- Policies are data (structured, versioned, stored) — a policy change is a data change with an audit entry, not a release.
- Evaluation is attribute-based: role, resource ownership, tenant boundary, and context (time, device trust, network) are all inputs.
- Tenant isolation is a non-overridable policy layer — no role, however senior, crosses tenant boundaries implicitly.
- Every denial and every sensitive grant is logged with the policy version that decided it.
How Cortex validates it
At plan time
The blueprint declares the resource model, policy schema, and tenant-isolation rules before any endpoint is built.
At validation
assert: authz_decisions.source == policy_engineassert: cross_tenant_access == 0 across permutation suiteassert: inline_role_checks == 0Assertion syntax is illustrative.
At review
The approver sees the permission matrix test: role × resource × context permutations, with every deny/allow justified by a policy version.