An authorization library supporting distributed role-based access control (RBAC)
The open-source project Casbin-ruby provides the solution for decentralized, distributed management of users, roles, and what they can do.
Our KFC project employs a microservice architecture, with components written in various languages and runtime environments, such as .NET, Ruby, and Go. Many of these components need to be able to make decisions based upon the authorization status of a user: who are they, what roles do they belong to, what actions are they able to perform, and to what?
We needed a consistent way to use role-based access control between multiple independent languages, and we also needed a way to manage the permissions and access control lists (ACLs).
When developing applications in common frameworks in languages such as Ruby, Python, PHP, or Go, role-based access control tends to come as part of the framework and readily available for use. But this doesn't help when developing an application as part of a much larger system which doesn't use the same frameworks throughout.
We set about researching our options, and quickly discovered we had a choice. One approach would be using a centralized service, such as Keycloak, which would act as a common authorization broker for all of the microservices. The obvious downside is that the centralized broker becomes a single point of failure and a potential bottleneck. Any failure here would mean that all of the benefits of a distributed microservice architecture would be lost in an instant.
So instead we turned to decentralized approaches, and in particular turned our attention to finding a consistent way of representing and processing access control lists across languages, and we discovered Casbin, an authorization library that does exactly this, and is implemented in a variety of different languages.
We had our RBAC implementation, but there was one snag: we needed a Ruby implementation, and Casbin had not yet been ported to Ruby. So, we decided to do it ourselves, and the result is Casbin-Ruby. Because Casbin has been ported to many different languages before, it has a comprehensive testsuite that we could use to verify that our implementation behaves consistently with its cousins.
How does it work?
At its simplest, each microservice has access to two files, which are distributed across the system. The first contains a list of the users, groups, roles, and so on — in other words, it defines the authorization principles. The second contains the mappings between the principals: in effect, it is an amalgamation of all of the access control lists used by the system.
This provides the solution for the very core of our challenge: decentralization (or distributed) management of users, roles, and what they can do.
What Casbin allows you to do:
- enforce the policy in the classic
{subject, object, action}
form or a customized form as you defined, both allow and deny authorizations are supported. - handle the storage of the access control model and its policy.
- manage the role-user mappings and role-role mappings (aka role hierarchy in RBAC).
- support built-in superuser like
root
oradministrator
. A superuser can do anything without explict permissions. - multiple built-in operators to support the rule matching. For example,
keyMatch
can map a resource key/foo/bar
to the pattern/foo*
.
What Casbin does NOT allow you to do:
- authentication (aka verify
username
andpassword
when a user logs in) - manage the list of users or roles. It's more convenient for the project itself to manage these entities. Users usually have their passwords, and Casbin is not designed as a password container. However, Casbin stores the user-role mapping for the RBAC scenario.
Install Casbin with:
to use latest version:
gem 'casbin-ruby', github: 'CasbinRuby/casbin-ruby'
to use specific version:
gem 'casbin-ruby', '1.0.5'
Future development
Our Ruby implementation of Casbin took about a month to develop, and by using tests from existing implementations in different languages, we started with a deep base of test coverage, and have continued with the TDD-first (test-driven development) approach throughout.
Since developing the core Ruby implementation, we are moving on to writing stores and notifications. We also plan to support role managers, and also intend to add bridges to different Ruby frameworks, such as Ruby on Rails, Hanami, and Roda.
Casbin-Ruby is available on Github and you can learn more about Casbin itself on its website.