-
Notifications
You must be signed in to change notification settings - Fork 4
Role Based Access Control
The access control pattern uses three pieces:
- Users
- Roles
- Permissions
Where the following is true:
- A User may have many associated Roles
- A Role may have many associated Permissions
- A User may have many associated Permissions through their associated Roles
The key to the pattern is:
- Access control is done by checking the collective Permissions of the User
- Roles are only a semantic way to track logical groupings of Permissions, but do not have meaning themselves
In other words, when authorizing an action the users's permissions are checked, never the user's roles.
Let's visualize this with a concrete example:
1. The `Staff` Role has the permissions:
- clouds:list
- customers:list
2. The `Customer Success Manager` Role has the permissions:
- customers:list
- customers:show
- customers:create
- customers:update
- customers:delete
If a User had both roles, then the associated permissions would be the combination of both:
- clouds:list
- customers:list
- customers:show
- customers:create
- customers:update
- customers:delete
From an access control standpoint, when the User tries to access the Customer List page it verifies if the user has a customers:list Permission. It does not matter which Role(s) granted the permission.
The value of this access control pattern is:
-
Access levels constantly evolve over the lifespan of an application. By tying access control to permissions, it allows administrators to define semantically meaningful Roles and keep them up-to-date as the use-cases change
-
It keeps permissions flat. No more having to remember what the difference is between an "admin" vs. "site admin" vs. "super admin". Users can be assigned many Roles, and each Role has a clear set of permissions.
-
Permissions can be simple to start. As the application's complexity grows, permissions can scale with it to become increasingly granular. It's important to be able to use the same access control pattern as the application grows without becoming limited by it or having to rewrite it from the ground up.
The downsides of this pattern is:
- There is more to manage. The overhead of managing users, roles, and permissions is higher than in other access control patterns. However, this can be mitigated in part by smart Web and API patterns to make managing changes faster and more intuitive.
- User Access library in
Artemis
In the web application, authorization is done in the controller for each action. Given a standard controller index action:
def index(conn, params) do
render(conn, "index.html")
endAuthorization is added by wrapping the existing logic in an authorize function. It takes three arguments:
-
connwhich is used to pull out the current user and its permissions - required permission(s)
- a function to execute if the permission(s) check passes
An implementation of the pattern looks like:
def index(conn, params) do
+ authorize(conn, "users:list", fn () ->
render(conn, "index.html")
+ end)
endIf the permission check succeeds, the function passed in as the third argument is executed and the page is rendered.
If the permission check fails, the render_forbidden function is executed, which sets a 403 status code header and renders the 403.html error page.
Note: In addition to authorize which checks for one permissions, there is also authorize_any and authorize_all which check for multiple permissions.