Role based access (RBAC) for Rails

rails ruby datamapper

The basics of a role based access control system using Ruby on Rails, CanCan and Devise.


Common requirements/desires for a authentication/authorisation system:

RBAC authorisation is reasonably common in larger projects and is well described in the Wikipedia article

We would like users to be assigned roles and roles enabling a number of permissions. A user may have a number of different roles meaning the permissions available for each of their roles is combined.

So we will be able to access a user’s permissions (using DataMapper) like so:

u = User.get(1)
u.roles.permissions.each {|perm|


First of all, get devise and cancan installed and basically configured within your application. This is not covered here.

These are the models that we will eventually have and their corresponding relationships. These examples use DataMapper.

class User
  include DataMapper::Resource

  # devise setup
  devise :database_authenticatable, :registerable,
    :recoverable, :rememberable, :trackable, :validatable

  property :id, Serial
  property :email, String, :required => true, :unique => true, :format => :email_address
  property :active, Boolean
  # ...

  has n, :roles, :through => Resource
  # ...

  # for assignment of roles
  def role_ids=(ids)
    ids.delete_if{|i| i.empty?}.each do |id|
      self.roles << Role.get(id)

  def has_role?(role_sym)
    roles.any? { |r| == role_sym }

  def role?(role)
    return !!self.roles.first(:name => role.to_s.camelize)

class Role
  include DataMapper::Resource

  property :id, Serial
  property :name, String
  property :description, Text
  # ...

  has n, :users, :through => Resource
  has n, :permissions, :through => Resource

We are using the anonymous join model Resource to link users and roles. We could quite easily make an actual model to hold extra information (for a history, perhaps).

There are a few extra methods in the User model to help with the assignment of roles. This is DataMapper’s way of overriding accessors and enables specific implementations.

CanCan setup

The permissions for many applications are closely tied to the application’s code. The checks are hard coded into the application. If this is the case there is no need to have the permissions taken from the database. The roles, yes, but the defining permissions will only ever change with the application code.

We can then put the permissions in a static file (YAML) in this case and have it read by the app when needed. CanCan’s ‘ability’ model is a good place to do this:

class Ability
  include CanCan::Ability

  @@permissions = nil

  def initialize(user)

    alias_action :index, :show, :to => :read
    alias_action :new,          :to => :create
    alias_action :edit,         :to => :update
    alias_action :destroy,      :to => :delete

    user ||=

    # super user can do everything
    if user.role? :super
      can :manage, :all
      # edit update self
      can :read, User do |resource|
        resource == user
      can :update, User do |resource|
        resource == user
      # enables signup
      can :create, User

      user.roles.each do |role|
        if role.permissions
          role.permissions.each do |perm_name|
            unless Ability.permissions[perm_name].nil?
              can(Ability.permissions[perm_name]['action'].to_sym, Ability.permissions[perm_name]['subject_class'].constantize) do |subject|
                Ability.permissions[perm_name]['subject_id'].nil? ||
                  Ability.permissions[perm_name]['subject_id'] ==

  def self.permissions
    @@permissions ||= Ability.load_permissions

  def self.load_permissions(file='permissions.yml')

This logic is up to you and your project but the above basic implementation does the following:

You can then start using the action, subject and optional object paramaters with CanCan to check for permissions for which the CanCan documentation has many examples.

Initial Setup

We can then define a few roles to seed the database with for use later. Put this in db/seed.rb:

super_user = User.create(:email => '',
                         :firstname => 'Super',
                         :surname => 'User',
                         :password => 'password',
                         :password_confirmation => 'password',
                         :active => true,
                         :date =>
admin_user = User.create(:email => '',
                         :firstname => 'Admin',
                         :surname => 'User',
                         :password => 'password',
                         :password_confirmation => 'password',
                         :active => true,
                         :date =>
user = User.create(:email => '',
                   :firstname => 'User',
                   :surname => 'User',
                   :password => 'password',
                   :password_confirmation => 'password',
                   :active => true,
                   :date =>

# create roles
super_role = Role.create(:name => 'super', :description => 'Super user')
admin_role = Role.create(:name => 'admin', :description => 'Admin user')
user_role  = Role.create(:name => 'user', :description => 'Normal user')

# get our permissions
permissions = YAML.load_file("#{::Rails.root.to_s}/config/permissions.yml")

# assign permissions
admin_role.permissions = permissions.collect{|n,p| n}

# assign roles
super_user.roles << super_role
admin_user.roles << admin_role
user.roles << user_role

The actual permissions seeded will be entirely up to you and your application but this sets up a few basics for one model.


This also enables you to have a nice report of the roles and their permissions much like the following: RBAC report screenshot

This can be created in the view something like this:

= form_tag({:controller => 'roles', :action => 'report'}, :method => 'post') do
      %th= Role.human_attribute_name('permissions')
      - @roles.sort.each do |role|
          = hidden_field_tag "permissions[#{}][]", ""
    - Ability.permissions.each do |pname, pdetails|
        %td= pdetails['description']
        - @roles.sort.each do |role|
            = check_box_tag "permissions[#{}][]", pname, role.permissions.include?(pname)
    = submit_tag 'Save'

Dynamic Permissions

It is quite possible that you may want to have dynamic permissions. For example, to assign permissions to specific object instances or perhaps in an application that accesses another service which changes.

If this is the case, you will need to have the permissions based in a dynamic datastore, most likely the same database. You would need to define a new model to store them and update the associations and CanCan logic to do authorisation checks.

[RBAC]: Role Based Access Control