Recipe
ABAC Primer
Attribute-Based Access Control (ABAC) lets you express authorization as policies over attributes of the subject, resource, action, and environment. Unlike RBAC, where access hinges on a static role, ABAC composes fine-grained predicates that scale to multi-tenant, multi-region products. This primer walks through the mental model, a minimal policy shape, and a Meridian-flavored evaluation loop.
1. Model the four attribute buckets
Every ABAC decision is a function of four inputs: the subject (who is asking), the resource (what they want), the action (what they want to do), and the environment (time, IP, risk score). Start by listing the attributes you already have for each bucket before writing a single rule.
2. Write rules as data, not code
Encode each rule as a JSON-shaped object with effect, subject, resource, actions, and environment. Storing rules as data means you can audit, diff, and hot-reload policy without redeploying the service. Default to deny when no rule matches.
3. Evaluate with a small, boring loop
The evaluator stays under 50 lines: load the policy for the resource type, iterate rules in declared order, return the first matching effect, otherwise deny. Keep the loop boring so the interesting work lives in the policy data, not the engine.
// ABAC policy evaluation pseudocode
function evaluate(subject, resource, action, env) {
const policy = loadPolicy(resource.type);
for (const rule of policy.rules) {
const match =
matchAttrs(rule.subject, subject) &&
matchAttrs(rule.resource, resource) &&
rule.actions.includes(action) &&
matchAttrs(rule.environment, env);
if (match) return rule.effect; // "allow" | "deny"
}
return "deny"; // default-deny posture
}
// Example: only senior engineers in EU can read EU customer PII
{
effect: "allow",
subject: { role: "engineer", level: ">=senior", region: "EU" },
resource: { type: "customer", dataClass: "pii", region: "EU" },
actions: ["read"],
environment: { timeOfDay: "business-hours" }
}