During development of a web application, I came across a problem with users having one role per company.
For example, you have a user who can manage two companies. He created the first company and therefore he is the owner and an admin of that company, but he has been invited to second company, where he haves limited access of content, so he's the moderator of that company.
On data level, I have a User table created by Devise, Companies table and a Role which Rolify created for me.
Because I want to know which user has which role for a company; I need a table to store their IDs, so I created a Unities table.
create_table :unities do |t| t.integer :role_id t.integer :user_id t.integer :company_id t.timestamps end
After that I created a model called Unity, which I will use for easier role manipulation.
class Unity < ActiveRecord::Base attr_accessible :role_id, :user_id, :company_id belongs_to :role belongs_to :company belongs_to :user end
and I also added associtaions to company, role and user models.
class User < ActiveRecord::Base rolify cattr_accessor :current_role has_many :unities has_many :roles, through: :unities has_many :companies, through: :unities end
I added current_role so I can check which role does user haves on a company that he is on but I will get to that later
class Role < ActiveRecord::Base has_many :unities has_many :users, through: :unities has_many :companies, through: :unities end
class Company < ActiveRecord::Base cattr_accessor :current has_many :unities has_many :users, through: :unities has_many :roles, through: :unities end
Here I added a current company, so I can check wich company is active I will use that later.
What is cattr_accestor?
After defining associations between these three tables, we can now define roles and abilities in Ability.rb which is used by CanCan
But before that, we need to find a current User role.
Since one user can manage multiple companies but on different level, first we need to find wich company is he currently on, and we can get that from params.
So in a ApplicationController add two methods.
class ApplicationController < ActionController::Base before_filter :initialize_company, :initialize_user_role def initialize_company if params.has_key?(:company_id) Company.current = Company.find(params[:company_id]) end end def initialize_user_role unless Company.current.nil? User.current_role = Unity.where(company_id: Company.current.id, user_id: current_user.id).first.role.name end end end
And now we are ready to create ability.rb
class Ability include CanCan::Ability def initialize(user) user ||= User.new if User.current_role == 'admin' can :manage, :all else if User.current_role == 'moderator' can :read, Products can :update, Products cannot :destroy, Products cannot :create, Products cannot :manage, Client end can :read, :all end end end
From ApplicationController we can get current_role and check it up against the role we want.
if User.current_role == 'admin'
In this article I explained how things should work, and basic principle of creating a role per company and user. In next article I will describe how to change user permissions and how to add roles to users.
And here is gist for better view
This is just my way of doing things. If you have any suggestions, or you know how to do it better, write a comment.