When tasked with adding authorization & access control to an application, one of the first decisions many developers make is whether to store their application's access control policies in policy files (policy as code) or in a database (policy as data). This decision is dictated by the business & operational needs of the application and is often made indirectly when choosing to use a library or implement a custom access control system from scratch. In this post, we'll cover the pros and cons of both approaches and discuss ideal use-cases for each.
Storing Access Policies in Policy Files
In the policy file approach, sometimes referred to as policy-as-code, an application's access policies are represented in a standardized notation and stored in a structured file format (yaml, json, a custom format). The application can then read files of this format and make authorization decisions at runtime based on the defined policies. More modern implementations of this approach (like Casbin or OPA) have implemented a custom file format which supports lightweight code blocks that can be executed at runtime to make attribute-based authorization decisions (ex: user has access until 9PM, user with IP address X.X.X.X has access, etc).
Simple & Lightweight
The policy file approach is simple & lightweight from the developer's perspective. Many authorization libraries take this approach so they're easy to drop into any application. The developer simply needs to define their application's authorization policies in a file and add the appropriate authorization checks to their code. Then they're off to the races.
Policy files are lightweight, so an application can read these files into memory and make authorization decisions quickly at runtime. No database or network calls are required to perform an authorization check.
Easily Version Controlled
Since access policies are stored as files, like the application's source code, they can be checked into a version control system (like Git), making it easy to version updates to access policies as they change over time.
One-off cases for certain customers/users are a fact of life when operating a live system. While it's possible to define one-off rules in policy files, this can very quickly grow out of control, making policy files harder to manage. Policy files are better suited for applications whose access model can be defined by a finite set of generalized access policies without many one-off cases.
Difficult to Support User-Defined Policies
Many modern applications (Google Docs, Dropbox, Asana, GitHub, etc.) allow users to self-manage access policies on resources they create (ex: email@example.com can make edits to my document). By nature, these types of access policies are dynamic at runtime and difficult to support when storing access policies in files.
Policy Updates Require a Developer
With the policy file approach, a developer typically needs to be involved when an application's access policies need to be updated. This is because the policy files are usually maintained using the same process as the application's source code (for simplicity). Additionally, the format & structure of the policy files isn't as accessible to non-technical team members. This can quickly become a drag on engineering teams that don't want to deal with the day-to-day management of an application's access policies.
Storing Access Policies in a Database
In the database approach, sometimes referred to as policy-as-data, the application's access policies for each user, resource, etc. are represented in a standardized format and stored in a database. The application can then query the database at runtime to perform authorization checks or fetch the access policies for a particular user or resource.
The structure of both the access policies and the underlying database tables storing those access policies is dependent on the type of access model required by the application (RBAC, ABAC, etc). For example, in a previous post we detailed how to structure your database for implementing Role Based Access Control. More modern approaches, such as Google Zanzibar, define a way to structure database tables to allow specifying arbitrary rules to enforce fine-grained access control on any resource in an application.
Since individual access policies for each user and resource in the application are stored in the database, handling one-off cases is as simple as adding a new row to the policy database.
Support for User-Defined Policies
Storing access policies in a database is a natural fit for building applications that allow users to self-manage access policies on resources they created. Granting another user access to a resource is simply a new row in the policy database, and each new policy is immediately enforceable in the application.
Accessible to Non-Technical Roles
One of the biggest advantages of storing access policies in a database is that it's easy to build a user interface on top of the data. This allows even non-technical users to easily manage an application's access policies in real time, removing their dependence on engineering teams. This is especially valuable for product managers, customer support, and other non-technical roles that support software products with complex access control models.
Complicated to Implement & Manage
Effectively storing access policies in a database at scale requires the design, management, and hosting of the underlying policy database. As a result, implementing an access control system that stores access policies in a database is more complicated than storing access policies in files and requires greater effort to maintain over time.
Not Performant by Default
Because access policies are stored in a database, performing authorization checks isn't a fast operation by default. Querying the policy database to check if an access policy exists can take orders of magnitude longer than it takes to check against a policy file loaded into memory. This operation can be made fast by pre-fetching access policies or employing a good caching strategy, neither of which is trivial to implement.
Not Version Controlled by Default
Access policies stored in a database are not easily version controlled like policies stored in a policy file are, so more work is required to ensure policy updates are rolled out carefully. Version control can be added on top of the policy database, but this isn't trivial to implement either.
Because both approaches to access policy management offer a different set of trade-offs, they both tend to be more useful in some use-cases than in others. For example, the policy file approach is often used for managing access to cloud infrastructure (a heavy focus of frameworks like OPA) because the access model in such use-cases tends to be fairly static, well-defined, and uniform, without many one-off cases. On the other hand, the policy database approach is often used for application access control, particularly in Enterprise/B2B SaaS applications because these applications tend to have complex, dynamic access control models with many one-off cases and customizations requested by each customer. Deciding which approach to take for your use-case involves weighing the trade-offs of both approaches and choosing the approach that offers the greatest benefit to your application and team.
Authorization as a Service
Warrant is a fully managed authorization service that helps you add access control to your application and manage its access policies over time. Built from the ground up to solve the challenges of building and maintaining authorization in consumer & enterprise SaaS products, Warrant makes it dead simple to implement role based access control, fine grained access control, and other authorization schemes. Add enterprise grade access control to your application in minutes — try Warrant for free.