This is how I do user roles and rights.
I create the following models:
class Role < ApplicationRecord
has_and_belongs_to_many :users
has_and_belongs_to_many :rights
validates :name, presence: true
end
class Right < ApplicationRecord
has_and_belongs_to_many :roles
validates :name, presence: true
end
Make sure you have proper constraints and indexes set in your database:
add_index :roles, :name, unique: true
add_index :rights, :name, unique: true
From there, you will need join tables for roles_users
as well as rights_roles
(because it's a many to many)
Then in seeds I create a few roles and rights:
role_admin = Role.create!(name: 'admin')
role_admin.rights.create!(name: 'full_access')
role_cm = Role.create!(name: 'company_manager')
role_cm.rights.create!(name: 'edit_company')
role_cm.rights.create!(name: 'edit_all_invoices')
role_cm.rights.create!(name: 'edit_all_users')
Then you need to assign one or more roles to your user
current_user.roles << Role.find_by(name: 'company_manager')
Now, I simply check the roles/rights on login, and store it in a session along with the user_id
def session_login(user)
session[:user_id] = user.id
session[:rights] = user.list_rights
end
And you can access roles/rights with several JOIN
sql statements efficiently. You want to store it in a session on login so you dont need to make this query for every request. It does mean however if you change roles/rights in the middle of your users session they will need to log back out and in again to see the updated changes
class User < ApplicationRecord
def list_roles
roles.map(&:name)
end
def list_rights
Right.joins(roles: :roles_users).where('roles_users.user_id = ?', id).map(&:name)
end
end
Final notes
Now, when you do your 'checking', make sure you verify based on RIGHTS (dont check a users' role)
You can make this helper method
def current_user_has_right?(right)
return false unless session[:rights]
session[:rights].include? right
end
You can authorize! an entire controller for instance in this way:
def authorize!
not_found unless current_user_has_right?('full_access')
end