Author’s note: This is a complex topic, and some of this is business/strategy-oriented while the rest of it is rather technical. To best serve our readers I split this into two posts. Below is the technical post. For the analysis of API politics that motivated this approach, you should read this post.
This post is an attempt to formalize Function-Based Access Control (FBAC), a framework for a general-purpose Access-Control System (ACS) that can scale in complexity from simple use cases (simple Web/mobile app or CMS) to massive organizational use cases (government agency or Fortune 1000 company) with full lifecycle continuity.
Policy is as Policy does
As explained in a previous post, I don’t believe RBAC and ABAC go far enough to address existing use-cases for access-control. They certainly don’t go far enough to address emerging use cases for access control that become necessary in a world of microservices, Internet of Things, serverless architecture, drones flying overhead, autonomous vehicles… the list goes on.
Extensible access control starts with the admission that organizational requirements vary. A freelancer, small business, NGO, large company or government agency have wildly different needs in terms of (naming just a few factors):
- Regulatory compliance
- Acceptable administrative/maintenance burden
- Rate of policy change
- Integration requirements
The most important difference is the very definition of “Policy.” To a freelancer, a policy (with access-control implications) can be something they thought up in the shower. To a government agency, there will presumably be a more structured process by which policies are proposed, vetted, revised, approved and implemented. This gives us our first functional requirement.
Instance vs. Kind routing
We must distinguish a situational request, such as George requesting the right to print invoices, from a policy-based request, such as George requesting that all sales staff have that right. The ACS need not detect this difference, just give the developer the necessary endpoints to express and act on it.
Function-Based Access Control: Ontology
Our next requirement is to provide a sufficiently flexible definition of Policy that it can handle the range of use-cases mentioned above. We do this by making Roles and Policies dynamic side effects of our core ACS, and putting Actors and Verbs at the center. Policies become the result of processed conditions and business rules, not the starting point for the administrator.
Compliance (e.g., regulatory) is observed and validated after-the-fact, rather than placing the burden on the administrator to express it correctly in the first place, much like a language compiler indicates syntax errors rather than “demanding” that you provide code with 100% perfect syntax.
By “Function” in FBAC we mean a human activity (or “Verb”). The term in this context has nothing to do with mathematics or software source code. That said, FBAC applies just as well to non-human activities or functions (such as device control).
The FBAC ontology is quite simple:
An Actor is any entity that can perform an action/verb. This can be a human user, a device (server, router, drone, smartphone etc.) or a software application.
A Verb or Function is some action an Actor can perform. You can think of this in terms of CRUD (Create, Read, Update, Delete) or in much broader terms: run, dance, swim, reboot, jump, install etc.
A Target is the object upon which a Verb or Function would be exercised. Actors, Verbs, Factories and Constraints are all legitimate Targets. A Target could be something as ordinary as a file, URL or IP address.
A Factory is a process that produces enumerable entities of any type. This concept is familiar to OO developers and Design Patterns aficionados. Quick examples :
- All of my Facebook friends — “my” is a Constraint (described below), and the Factory produces a set of entities that may be used as Actors or Targets
- A Group — a list of Actors (all servers in Ireland, everyone in /etc/sudoers or “All GSuite users” etc.)
A Constraint is a kind of Factory that limits, filters or queries a list of entities of any type. All Constraints are Factories, but not all Factories are Constraints. Constraints can “consume” any list of 0+ entities (including Factories, and other Constraints) and return a non-strict subset of that list. Quick examples :
- Within 100 meters (could apply to any list of Entities with a static or dynamic Location attribute)
- Every other Thursday (could apply to any list of Entities with a Time attribute)
In FBAC, a Privilege is a Verb + Target pair. In English, it is the right to do (Verb) to (Target).
FBAC implements Roles as a Factory of Privileges, and Groups as a Constraint of Roles. Actors are assigned to Groups by a Factory as well. These (optional) constructs allow an FBAC implementation to provide a superset of RBAC.
As Factories or Constraints can implement complex boolean rules, FBAC can provide a superset of ABAC. But things get interesting when we set aside both RBAC and ABAC and explore what only FBAC can do.
This basic framework allows for rapid implementation of trivial use cases. A Factory implementation can be something as ordinary as a file I/O or database query operation. Such Factories could be cloned or subclassed to quickly cover the access-control requirements of a content-management system, for example.
FBAC also simplifies expression of what is possible vs. what is permitted, for instance by developing Constraints based on semantic technology. A “Drone” can “Dance” (maybe), but a “Server” cannot. This can be used to prevent rights-administrators from granting “impossible” Privileges.
The principal design objective of FBAC, however, is to enable an evolutionary process over time that lets an implementation start with simple use cases and grow in complexity (suddenly, or over time) while staying within the same fundamental framework for the duration.
Now that we have this neat-o object-relational model, what can we do with it? Let’s start by keeping the promises made at the beginning of the post regarding Policies. In FBAC, a Policy is the superset of all expressible Privileges (Actors X Verbs X Targets). That is to say, it is observed, not defined. While this definitely requires a mindset change for those used to RBAC and ABAC, it still practically allows the verification of conformance to an arbitrary set of rules (such as Sarbanes-Oxley compliance).
Since Constraints can apply to Actors, Roles, Groups and Privileges, implementation of (static or dynamic) Separation of Duty is also pretty straightforward.
The Turnstile or the Sieve
The critical aspect of any access-control framework is transaction approval, or rights-testing. When Anne tries to print an invoice, the application has to validate her right to do so.
There are two approaches: the “Turnstile,” an explicit call to a function or method that tests the permission; or the “Sieve,” an application layer that tests permissions and returns errors or throws exceptions when an unauthorized action is attempted. Content-management frameworks such as WordPress or Drupal rely on the Turnstile approach, while web servers such as Apache or firewall software like iptables in Linux, favor the Sieve.
Most RBAC/ABAC systems I’ve used have been Turnstile — but that is a bias born of my own experience, as I’ve spent most of my life developing human-facing applications. I prefer the Sieve, because it leads to cleaner code at the application level: developers need not worry about explicitly invoking User->CanDoThis(…) each time a feature is accessed. But that’s not my call to make. The decision lies solely with the architect(s) and developer(s) of the application. If you were wondering why FBAC ontology feels so much like network-firewall ontology, this is the reason. We want a system that lets developers use any combination of explicit and/or implicit permissions tests, as they see fit.
One advantage to the Turnstile in a human-facing application is the opportunity to prompt the user to request a denied privilege, and route such requests to someone empowered to grant it. This would be an example of “Instance routing” mentioned at the beginning of the post. But it requires the “request access” logic to be present in the UI, so there needs to be access-control “awareness” baked into the topmost levels of the application.
Implementing a Sieve in FBAC is a matter of defining a Constraint on a set of Targets that map to application features. In such a use case, the Verb can just be “Execute.” But a more interesting use case for the Sieve is retrofitting an application with existing access-control functionality to use FBAC. Why would one want to do this? Because it opens the door to unified access control across different systems. To be fair, many ABAC systems offer centralized Policy Management; but they cannot extend to use cases where the policies themselves are heterogenous.
FBAC Sieves open the door to “Kind routing,” in which a threshold number of access-denied errors from an identifiable subset of Actors triggers a privilege-request for that subset. In this way, access-control policies can adapt dynamically based on real (human and machine) activity, instead of pre-defined policies constantly playing catch-up with actual needs.
To sum up, we don’t know if our approach to access-control is right or wrong, but we believe it is radical. It proposes three seemingly-outlandish notions:
- Policy can be an after-the-fact observable result of access-control rules, instead of the basis for defining those rules in the first place.
- Access control needs to be generalizable across an entire heterogeneous ecosystem of human-facing and automated systems, so that the stakeholders of access rules need not express the same policy over and over again in different systems and interfaces.
- Access control systems need to express richer use cases than pre-defined policy enforcement, and in order to do this well, permissions-testing needs to behave differently than it does in most existing systems.
In my next post, I’ll describe some of the implementation challenges we face in the development of Hurima™, our RESTful API FBAC solution, and how we’ve overcome them so far. You can read about Hurima™ here.
Meanwhile, I eagerly await your criticism. These posts are not an attempt to prove that we’re right, but to start a conversation so we can figure out together what works best.