Why Bother with Access Control?
Imagine your app's front door. No, not the login page—the back rooms where data lives. Access control is what decides who gets past the velvet rope and what they can do inside. Get it wrong, and you'll either frustrate users or leave a data breach waiting to happen.
I've seen startups hand out admin like candy because it was easier than setting up proper permissions. That works until someone accidentally deletes the production database. So yeah, it matters.
The four big models are DAC, MAC, RBAC, and ABAC. Each has a different philosophy, and knowing them helps you choose the right tool for your project—or at least understand why your security team keeps pushing for one over another.
The Vocabulary: Subjects, Objects, and Policies
Before we get into the models, let's clear up some terms. Every access control system has three pieces:
- Subject – who or what is acting. Usually a user or a process.
- Object – the thing being accessed. Could be a file, a database row, an API endpoint.
- Policy – the rule that says what a subject can do with an object.
That's it. Every model is just a different way of organizing those rules.
I remember when I first learned this, it clicked: it's like a club. The subject is the patron, the object is the VIP lounge, and the policy is the bouncer's checklist.
Three Principles That Keep You Out of Trouble
There are a few unwritten rules that good access control follows. The first is least privilege: give users only the permissions they absolutely need. It's tempting to give everyone full access because it's simpler, but then one mistake can be catastrophic.
The second is least leakage: sensitive info should be on a need-to-know basis. Fewer people with access means less chance of accidental leaks.
The third is multi-level security, where data is classified into levels like Top Secret, Secret, Confidential, and so on. Higher clearance means more access. That's mostly for military, but you can see similar ideas in finance or healthcare.
These principles sound obvious, but I've seen teams ignore them and pay the price.
DAC: The Owner Decides
Discretionary Access Control is the oldest and simplest. The owner of a resource gets to decide who else can access it. Think of a file on your computer: you created it, so you can share it with anyone. In Linux or Windows, that's the Access Control List (ACL). For a document, an ACL might look like:
Alice: read, write
Bob: read
Alice can read and edit, Bob can only read. That's it.
DAC is easy to implement and works fine for small systems. But it has a flaw: permissions are scattered all over the place. If you want to give a whole team read access to a folder, you have to change each file's ACL one by one. And because owners can grant permissions, a careless user might share something they shouldn't. I once saw a developer share a folder with an intern and forget to revoke it—six months later, the intern still had access to client contracts. Yikes.
MAC: The System Won't Budge
Mandatory Access Control takes the opposite approach. The system, not the owner, makes all the rules. Each subject and object gets a security label, and access is only allowed if the labels match certain rules. For example, a user with Secret clearance can read Secret documents but not Top Secret ones. And they can't change their own label.
MAC is great for government and military where confidentiality is everything. But it's rigid. There's little room for exceptions, and managing labels across a large system is a nightmare. That's why MAC is usually combined with DAC for flexibility. In the real world, you rarely see pure MAC outside of high-security settings.
RBAC: The Role Middleman
By the 1990s, managing individual permissions was getting old. Role-Based Access Control (RBAC) introduced the idea of roles. Instead of giving permissions directly to users, you assign users to roles, and roles get permissions. A user might have the role 'Editor', which includes read and write on documents but not user management.
RBAC became the go-to because it's simple and scales well. But it's not just one thing. The NIST standard defines a family:
- RBAC0 – the base: users, roles, permissions, all many-to-many.
- RBAC1 – adds role hierarchy. A senior role inherits permissions from junior ones, so 'Manager' includes everything 'Employee' can do.
- RBAC2 – adds constraints. Things like separation of duty (one person can't be both 'Cashier' and 'Auditor') or cardinality (only one 'CEO' role allowed).
- RBAC3 – combines all of the above.
RBAC works well for most business apps. But it can get messy when you need fine-grained control, like allowing someone to view a report only during business hours. I've seen teams end up creating a dozen roles just to handle edge cases, and then nobody knows which role does what.
ABAC: The Rule Engine
Attribute-Based Access Control (ABAC) is the newest kid on the block. Instead of roles, it uses attributes—user attributes (age, department), resource attributes (classification, owner), action attributes (read, write), and environmental ones (time of day, location). Access is granted based on rules that evaluate these attributes.
For example, a rule might say: allow access if user.department == 'HR' AND resource.type == 'payroll' AND time between 9am and 5pm. That's a level of nuance RBAC can't easily match.
ABAC is flexible and granular, but it's also more complex. Writing and debugging policies takes effort, and if your rules are a tangle of conditions, it's hard to see who can access what. Many systems end up combining RBAC for broad role assignments and ABAC for fine-grained exceptions. In my experience, ABAC is worth it when you have complex requirements, like multi-tenant apps or compliance-heavy industries.
Putting It Into Practice: A Casbin Example
To see how ABAC works in the real world, here's a simple Go snippet using the Casbin library. It restricts user 'john' to visiting 'http_api' no more than 500 times:
const modelText = `
[request_definition]
r = sub, obj, act, count
[policy_definition]
p = sub, obj, act, limit
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub==p.sub &&& r.obj==p.obj && r.act==p.act && r.count.IsBelow(p.limit)
`
Here, the request carries a 'count' attribute. The policy defines a 'limit' for each subject-object-action. The matcher checks if the count is below the limit. It's a neat way to add rate limiting without hard-coding it. I've used this pattern to throttle API calls per user—it saved my bacon when a client's app started hammering our endpoints.
Which One Should You Use?
Honestly, there's no silver bullet. DAC is fine for personal files. MAC is for high-security environments. RBAC is the workhorse for most enterprise apps. ABAC shines when you need dynamic, context-aware decisions. In practice, you'll often combine them—RBAC for the big picture, ABAC for the edge cases.
The key is to understand what you're protecting and how much flexibility you need. Start simple, then add complexity only when you truly need it. And for heaven's sake, don't give everyone admin access just to avoid a meeting.
Comments (0)
Please sign in to post a comment.
Don't have an account? Create one
No comments yet. Be the first to comment!