rarbac
What is rarbac?
rarbac
stands for "Rails Role-Based Access Control". I couldn't find a good
gem that did RBAC and integrated with a Rails app, so I made one. Since this is
basically a stripped down Rails project, I created it as a Rails engine
(a.k.a. plugin).
However, there was a UC Berkley project which did well but fell a little short and does not appear to be maintained any more, called rails_access. You will probably see similarities in the data structure between that project and this one.
Installation
You simply add the gem and then mount the engine.
In your Gemfile:
gem 'rarbac'
In config/routes.rb
:
mount Rarbac::Engine, at: "rarbac"
The project includes migrations for non-user data. Your Rails app must have, and
supply, its own user model. To generate the migrations for rarbac
simply run:
rake rarbac:install:migrations
Then run migrations. One more step to ensure everything works properly. Open your user model and add the following to it:
has_many :user_roles, class_name: "Rarbac::UserRole"
has_many :roles, through: :user_roles, class_name: "Rarbac::Role"
has_many :permissions, through: :roles, class_name: "Rarbac::Permission"
has_many :actions, through: :permissions, class_name: "Rarbac::Action"
include Rarbac::UserHelper
That may seem like a lot, but really we're just off-loading a lot of the
querying work to the underlying ActiveRecord
base (or whatever else you are
using). If we didn't do this we would have to do things like:
user.user_roles.roles.permissions.where(...)
By including all those has_many X through: Y
statements, we can go straight to
what we want:
user.permissions.where(...)
With migrations installed, run, and the user model setup, start your application
and enjoy! Read on to make the most out of rarbac
.
How it works
You will have a few new models, under the namespace Rarbac
, called Role
,
Action
, Permission
, and UserRole
.
The Role
model/table holds a collections of roles, with an optional
description. e.g. "guest", "user", "admin".
The Action
model/table holds a collection of actions that a user may perform
in the system. e.g. "new_post", "delete_post", "new_comment".
The Permission
model/table links roles to actions. e.g. "the admin
role has
permission to the delete_post
action".
The UserRole
model/table links users to roles. e.g. "Jill has the user
role".
It is important to note that user ids are not uniquely kept in the UserRole
model, so you may have a single user with more than one role.
Controller Filters
You may use the Rarbac
controller filters/functions by including
Rarbac::ApplicationHelper
. The only assumption made is the presence of a
resource (function or attribute) named current_user
. This is modeled after the
popular authentication gem Devise.
You may add before_filter :ensure_permission!
to create role-based authorization
on the controller and action being executed. For example:
class PostsController < ApplicationController::Base
include Rarbac::ApplicationHelper
before_filter :ensure_permission!
def show
...
end
def destroy
...
end
end
If you had an Action
specified for "posts#destroy", this would ensure that
current_user
had permission to that action. If no such action existed,
however, it is assumed that you don't need permission and anyone, even
un-authenticated users, would be allowed access to that route.
You may also customize your filters using the normal application controller options:
before_filter :ensure_permission!, only: :destroy
Now the filter only runs when the destroy
action is being executed. You may
also use the block method of invoking a filter to further customize your
options:
before_filter only: :destroy do |controller|
controller.ensure_permission!(:delete_post)
end
But wait, there's more! What if you want to check a role rather than an
individual permission to some action? Maybe an entire controller should be
off-limits to everyone but those with the admin
role. That's easy:
before_filter do |controller|
controller.ensure_role!(:admin)
end
Bonus: it accepts arrays of roles.
before_filter do |controller|
controller.ensure_role!(:author, :admin)
# The above uses an "OR" operation; use the plural to perform "AND"
# e.g. controller.ensure_roles!(:author, :admin)
end
Don't forget that you may create your own filter functions and call ensure_*
from there. However, if you create a custom filter and have access to
current_user
, it might be better, and more configurable, to use the model
functions defined in the next section.
Model Functions
With the appropriate permissions added to your user model you may very easily check on a user's roles:
user.has_role?(:admin)
user.has_role?(:admin, :author) # Singular noun implies "OR" operation
# e.g. has_role?(:admin) || has_role?(:author)
user.has_roles?(:admin, :author) # Plural noun implies "AND" operation
# e.g. has_role?(:admin) && has_role?(:author)
And also permissions:
user.has_permission?(:delete_post)
It's important to note that permissions don't accept arrays. Why? Because very
rarely should you be checking for a collection of permissions in a single
"question". If you want to know if a user has permission to multiple actions,
more likely you actually want to know if the user is assigned to one or more
roles instead. That's why the has_role?
and has_roles?
functions have the
argument-array option.
View Helpers
There is no explicit view helper system. The idea is that the controller filters will help control when a view is rendered, and the model functions can further control what is rendered within a template.