Access control at a glance
Four layers, evaluated independently
| Order | Layer | Decides | A denial looks like |
|---|---|---|---|
| 1 | Tenant isolation | Which rows exist at all | 404 — the row is invisible |
| 2 | Permissions | Whether the action is allowed | 403, with the missing code named |
| 3 | ABAC policies | Whether this record is visible | 403 directly, or dropped from a list |
| 4 | Field masking | Whether this value is visible | The value is masked; the record returns |
None assumes another did its job. The innermost is enforced by the database.
Why the denials differ
A 404 across tenants is not obfuscation — the row is genuinely invisible to the query, so nothing looked and refused. A 403 would confirm it exists somewhere.
A policy denial inside a list drops the row rather than erroring, for the same reason: erroring reveals that something exists and is withheld.
Composing access
permission codes → role → group → user
└───────────→ user (direct)
Effective permissions are the union of every role held directly and through groups. Bind through groups so access follows joining and leaving.
ABAC policies
Deny-overrides on a default-allow base: a denial always wins, and ordering is never a decision input.
A denial with no conditions is refused when you save it — it would match every request and lock the tenant out of the screen needed to remove it. Access-control administration is exempt from ABAC policies for the same reason.
Masking
Applied when the response is generated, not per endpoint. That is what makes it unbypassable: no alternative surface returns the value unmasked, including for the AI layer.
Actor metadata in audit entries is never masked — withhold the audit permission instead.
Verify with the account
Effective permissions are a union across roles and groups. Reading a role definition tells you what one role grants, not what a person can do. Sign in as them.
Next
- Compose roles and bind them to groups
- Restrict access to individual records with ABAC policies
- Mask sensitive fields
- Access control reference
Last verified against commit 1e1de1ad (2026-08-03)