Authorization: ACL, RBAC, ABAC & ReBAC
How enterprise distributed systems enforce granular permissions: the progression from simple Access Control Lists (ACL) to Role-Based (RBAC), Attribute-Based (ABAC), Google Zanzibar Relationship-Based Access Control (ReBAC), and Open Policy Agent (OPA) policy decoupling.
1. The Evolution of Authorization: A Hospital Analogy
Authentication proves who you are (Alice, an employee). Authorization answers what you are allowed to touch right now. To understand how authorization models evolved, imagine designing the physical security system for a major metropolitan hospital:
1. Access Control List (ACL)
Analogy: A physical paper clipboard taped to the door of Room 402 with names: [Alice: Read, Bob: Write].
โ ๏ธ Fails at scale: When Alice leaves the hospital, you must walk to 10,000 doors to erase her name from 10,000 paper clipboards.
2. Role-Based Access Control (RBAC)
Analogy: Color-coded badges. Anyone with a Doctor badge can enter any examination room; Nurses can enter supply closets.
โ ๏ธ Fails on context: Cannot express “Dr. Alice can only access Room 402 if she is currently on shift AND assigned to Patient Charlie”. Leads to Role Explosion!
3. Attribute-Based Access Control (ABAC)
Analogy: Smart biometric locks evaluating dynamic rules: IF (User.Role == "Doctor" AND User.Dept == Room.Dept AND Time BETWEEN 08:00-20:00 AND IP IN HospitalWiFi) THEN UNLOCK.
โ Highly expressive: Context-aware security without creating thousands of static roles.
4. Relationship-Based (ReBAC / Zanzibar)
Analogy: Google Drive sharing graph: Alice → MemberOf(Cardiology Team) → ViewerOf(Folder Patient 402) → InheritsAccess(LabResults.pdf).
โ Gold standard for SaaS: Handles deep nested folder sharing, user-to-user delegation, and group inheritance.
2. Architecture Comparison Matrix
| Model | Core Data Structure | Best Suited For | Primary Architectural Trade-off |
|---|---|---|---|
| ACL | (ResourceID, UserID, Permissions) | Simple file systems, single-tenant tools | O(N) management cost; impossible reverse lookup ("What can Alice see?") |
| RBAC | User → Role → Permissions | Internal admin tools, standard SaaS tiers | Role Explosion: Creating thousands of hyper-specific roles for minor exceptions |
| ABAC | Boolean Policy (Subject, Object, Action, Env) | Fintech, Healthcare, Zero-Trust Enterprise | Higher evaluation latency (CPU rule evaluation per request) |
| ReBAC | (Object#Relation@Subject) Graph Tuples | Google Docs/Drive, Figma, GitHub Organizations | Requires specialized graph database / Zanzibar index cluster |
3. Policy Decoupling & Open Policy Agent (OPA)
A critical anti-pattern in microservices is hardcoding authorization rules directly into controller business logic:
โ Hardcoded Anti-Pattern
if user.role == "doctor" and user.dept == patient.dept:
if request.is_emergency or (time.now().hour >= 8 and time.now().hour <= 20):
return allow_access()
Modifying a security policy requires recompiling, testing, and redeploying 25 different microservices across the company.
โ Decoupled Policy Engine (OPA)
# Open Policy Agent (Rego Policy)
default allow = false
allow {
input.user.role == "doctor"
input.user.department == input.patient.department
is_valid_shift(input.time)
}
Security teams update policies declaratively in Git. Microservices query OPA sidecars in <1ms via local UNIX sockets.
4. Google Zanzibar & Relationship-Based Access Control (ReBAC)
How does Google determine if you can view a Google Doc shared with an email group inside a subfolder owned by an organization? They built Zanzibar: a globally distributed authorization system storing trillions of relation tuples.
doc:design_spec#owner@aliceAlice is the owner of design_spec.
folder:engineering#viewer@group:devsDevs group can view folder.
doc:design_spec#parent@folder:engineeringDoc inherits folder permissions.
5. Interactive Multi-Model Authorization Simulator
Configure request attributes and switch between authorization models (ACL, RBAC, ABAC, ReBAC) to see how different authorization paradigms evaluate access in real-time.
6. Policy Decision Engine in 5 Languages
Below is an implementation of a dynamic Attribute-Based Access Control (ABAC) evaluation engine enforcing context rules.
from typing import Dict, Any
class AbacPolicyEngine:
@staticmethod
def evaluate(subject: Dict[str, Any], resource: Dict[str, Any], action: str, env: Dict[str, Any]) -> bool:
# Rule 1: Superadmins always have full access
if subject.get("role") == "superadmin":
return True
# Rule 2: Doctors can view records in their own department during working hours
if action == "read" and subject.get("role") == "doctor":
dept_match = subject.get("department") == resource.get("department")
time_valid = 8 <= env.get("hour", 0) <= 20
ip_valid = env.get("is_trusted_network", False)
if dept_match and time_valid and ip_valid:
return True
# Rule 3: Emergency override mode
if env.get("is_emergency", False) and subject.get("role") in ["doctor", "nurse"]:
return True
return False
interface Subject {
id: string;
role: string;
department: string;
}
interface Resource {
id: string;
department: string;
sensitivity: 'public' | 'confidential' | 'restricted';
}
interface Environment {
hour: number;
isTrustedNetwork: boolean;
isEmergency?: boolean;
}
export class AbacEngine {
static evaluate(sub: Subject, res: Resource, action: string, env: Environment): boolean {
if (sub.role === 'superadmin') return true;
if (action === 'read' && sub.role === 'doctor') {
const isDeptMatch = sub.department === res.department;
const isDayShift = env.hour >= 8 && env.hour <= 20;
return isDeptMatch && isDayShift && env.isTrustedNetwork;
}
if (env.isEmergency && ['doctor', 'nurse'].includes(sub.role)) {
return true;
}
return false;
}
}
package main
type Subject struct {
ID string
Role string
Department string
}
type Resource struct {
ID string
Department string
}
type Environment struct {
Hour int
IsTrustedNetwork bool
IsEmergency bool
}
func EvaluateABAC(sub Subject, res Resource, action string, env Environment) bool {
if sub.Role == "superadmin" {
return true
}
if action == "read" && sub.Role == "doctor" {
if sub.Department == res.Department && env.Hour >= 8 && env.Hour <= 20 && env.IsTrustedNetwork {
return true
}
}
if env.IsEmergency && (sub.Role == "doctor" || sub.Role == "nurse") {
return true
}
return false
}
public class AbacEngine {
public record Subject(String id, String role, String department) {}
public record Resource(String id, String department) {}
public record Environment(int hour, boolean isTrustedNetwork, boolean isEmergency) {}
public static boolean evaluate(Subject sub, Resource res, String action, Environment env) {
if ("superadmin".equals(sub.role())) return true;
if ("read".equals(action) && "doctor".equals(sub.role())) {
boolean deptMatch = sub.department().equals(res.department());
boolean timeValid = env.hour() >= 8 && env.hour() <= 20;
return deptMatch && timeValid && env.isTrustedNetwork();
}
if (env.isEmergency() && ("doctor".equals(sub.role()) || "nurse".equals(sub.role()))) {
return true;
}
return false;
}
}
#include <string>
struct Subject {
std::string id;
std::string role;
std::string department;
};
struct Resource {
std::string id;
std::string department;
};
struct Environment {
int hour;
bool is_trusted_network;
bool is_emergency;
};
bool evaluate_abac(const Subject& sub, const Resource& res, const std::string& action, const Environment& env) {
if (sub.role == "superadmin") return true;
if (action == "read" && sub.role == "doctor") {
bool dept_match = (sub.department == res.department);
bool time_valid = (env.hour >= 8 && env.hour <= 20);
return dept_match && time_valid && env.is_trusted_network;
}
if (env.is_emergency && (sub.role == "doctor" || sub.role == "nurse")) {
return true;
}
return false;
}