Skip to content
This repository has been archived by the owner on Dec 12, 2021. It is now read-only.

Separate Role Model

haniqassim edited this page Apr 26, 2012 · 6 revisions

This approach uses a separate role and shows how to setup a many-to-many association, Assignment, between User and Role. Alternatively, Role Based Authorization describes a simple ruby based approach that defines the roles within ruby.

class User < ActiveRecord::Base
  has_many :assignments
  has_many :roles, :through => :assignments
end

class Assignment < ActiveRecord::Base
  belongs_to :user
  belongs_to :role
end

class Role < ActiveRecord::Base
  has_many :assignments
  has_many :users, :through => :assignments
end

You can assign roles using checkboxes when creating or updating a user model.

<% for role in Role.all %>
<div>
  <%= check_box_tag "user[role_ids][]", role.id, @user.roles.include?(role) %>
  <%=h role.name %>
</div>
<% end %>
<%= hidden_field_tag "user[role_ids][]", "" %>

Or you may want to use Formtastic for this.

Next you need to determine if a user is in a specific role. You can create a method in the User model for this.

# in models/user.rb
def has_role?(role_sym)
  roles.any? { |r| r.name.underscore.to_sym == role_sym }
end

And then you can use this in your Ability.

# in models/ability.rb
def initialize(user)
  user ||= User.new # in case of guest
  if user.has_role? :admin
    can :manage, :all
  else
    can :read, :all
  end
end

That's it!

Role Inheritance Within Ability.rb

You can use the Alternative Role Inheritance strategy described in Role Based Authorization with one minor modification: change "send(role)" to "send(role.name.downcase)" assuming name is the column describing the role's name in the database.

class Ability
  include CanCan::Ability

  def initialize(user)
    @user = user || User.new # for guest
    @user.roles.each { |role| send(role.name.downcase) }

    if @user.roles.size == 0
      can :read, :all #for guest without roles
    end
  end

  def manager
    can :manage, Employee
  end

  def admin
    manager
    can :manage, Bill
  end
end

Here each role is a separate method which is called. You can call one role inside another to define inheritance. This assumes you have a User#roles method which returns an array of all roles for that user.