Posted on 2021-07-24 byVlad Călin
Reading time of 1 minutes
A recurrent topic in the world of software development is authorization.
I will talk only about the topic of authorization, not authentication. Authorization is how systems figure out if certain users/actors should be allowed to do certain actions in the system, while authentication is about figuring out who the user/actor is and revolves around identity management.
Authorization is hard to get right, because you would have to take into consideration multiple things when designing a good authorization system:
These are big topics you need to explore when you start designing your permission system. Of course, you can
start with a simple permission system (admin/non-admin - eg. Django's
is_superuser), then migrate
later on to more flexible systems (eg. per-instance permissions, or RBAC - Role Based Access Control).
I did that a few times, and it was a pain. Migrating from a permission system to another is very hard and requires a lot of effort and development time. Suddenly, you need to revisit your whole app, figure out where all the permission checks are done (ideally, permission checking would be done in a centralized class/module), and having long and tiresome sessions where you discuss how the old permission system checks translate to the newer system, without breaking or frustrating the users.
So what is it and what problem does it solve?
In systems, you need granularity for your permission system: certain actors can perform certain actions on certain resources. With each type of resources, you would have at least one permission per CRUD operation (create, read, update, delete). Systems that actually do something useful tend to have a lot of resources available. Combined with at least 4 permissions for each resource type, there would be a lot of permissions in the system to manage and assign to users.
This leads to increased complexity, becomes very error prone, and adding new users will be a nightmare. Adding a new user will require assigning all the permissions they need to get their job done, which more than often would be a lot of them.
As a solution for that, RBAC comes in: grouping multiple permissions in a single role, and then assigning roles to users. This is based on the assumption that users from the same teams will most likely require the same set of permissions on the same set of resources (in corporate environments, teams tend to own their own set of resources and shuld be forbidden from accessing other team's resources).
So how would it be implemented?
If you are starting from scratch, it's actually pretty simple:
The resource can be also defined as a resource selector rather
than just a single resource reference (eg. all instances with a certain tag, all containers that are in a
cluster whose name starts with
"dev-", etc). This offers a lot of flexibility when teams are able to create their
own resources: when creating a new resource, you don't have to extend all the team's roles to also reference this
new resource. Instead, as long as the newly created resource matches the resource selector, the job is already done.
have a way to group permissions under a single easy to remember name, traditionally named roles. This would be in fact just a named list of permissions. Then each user will be able to reference one or more such roles.
have an internal unified way to easily check if a certain user/actor is allowed to do a specific action on
a specific item. (
has_permission(user.id, "read", resource.id)). This will simplify development by having a single
entry point for the permission checking.
Having all these will lead to a robust but flexible system for managing permissions for users (external users / clients and internal users / employees as well).
The only thing left to do is figure out what the sensible defaults would be, because setting up a new account from scratch means creating all the default roles with the default permissions assigned every time. This can be automized, but once you do that, changing the defaults will be a pain: adding/removing permissions from the default roles or adding/removing default roles might require going back to all existent accounts and apply the change retroactively. If not, older clients might end up not having these new permissions/roles, and their experience might get impacted. Of course, it depends on the system, but this is something to keep in mind when changing defaults.
But there are alternatives that might be more suited to your case:
If you need to implement authorization in a system, you can't go wrong with RBAC. It scales with your system, it is flexible enough to support a wide variation of access patterns and requirements from your users, and robust enough to support a huge number of users, permissions and resources.
Most enterprises use it, so it also has an extra added benefit: familiarity. Users already know how to deal with authorization frameworks based on RBAC and they will be able to configure it without guidance and support from your side more often.
Using RBAC will allow your system to scale and offers the flexibility and robustness needed for such a system. After all, authorization is usually the most critical system inside a system. Leaking data must be avoided at all costs and proper authorization can not be an afterthought.