Reputation: 3174
This may be more of a conceptual question. I've got a user table and I need to provide roles for each of the users. For example I would like for some users to create customers and some to only view customers. I figure that I could create an association like this:
User has_one :role
Role belongs_to :user
With this structure, I could create boolean
fields on the Role
model such as can_create_customer
. If I did, then I could use the following code to check for permissions based on every user:
if role.can_create_customer?
Customer.create(name: "Test")
end
Is there a better way of accomplishing the same thing?
Upvotes: 1
Views: 2211
Reputation: 13467
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
Upvotes: 6