Rails: using CanCan to define multiple roles depending on instances of the same model?

Currently, I am sticking to how to split roles for CanCan depending on each condition that we want. In our application there are many categories (for example, mathematics, English, history, etc.), and in each of them there are many courses.

Each user can have different roles in each category. For example, John can be a “reader” for mathematics, which means that he can read all the courses that are in mathematics. John can also be a "writer" for English, which means that he can read all courses in English, create courses within the English category and edit / delete only those courses that he created in English.

If these were the only roles that John had, he would not be able to see the history of the categories in the navigation bar and would be denied access to the courses that are in the history.

Here's how the relationship is established:

class User < ActiveRecord::Base has_many :roles def has_role?(role_sym) roles.any? { |r| r.level.underscore.to_sym == role_sym } end end class Category < ActiveRecord::Base has_many :roles has_many :courses end class Role < ActiveRecord::Base belongs_to :user belongs_to :category attr_accessible :level, :category_id, :user_id end 

in model / ability .rb we have

 class Ability include CanCan::Ability def initialize(user) user ||= User.new # guest user (not logged in) #guest if user.has_role? :reader reader(user) end if user.has_role? :writer writer(user) end end #BE ABLE TO SEE COURSES AND CATS FOR PERMITTED CATS. def reader(user) can :read, Category, :roles => { :user_id => user.id, :level => "reader" } ## how would we be able to limit reading of courses that are within permitted categories? something like category.courses ~~ end def writer(user) reader(user) #inheriting from reader? this doesnt work because level is hardcoded into reader can :read, Category, :roles => { :user_id => user.id, :level => "writer"} # 1.you can read all courses in category that you are given permission to # 2.you can write courses in permitted category # 3.you can edit, delete courses that only youve created within permitted category end end 

Questions:

  • How do we separate the roles of “reader” and “writer” in the right way? How do we access courses that fall into the categories that we have access to?

  • After defining the read and write methods in the .rb ability, how do we use them on our watch pages? It appears that the current documentation uses something like "<% if can ?: Read, @ category%>" but it does not use the methods that we have split and defined.

ps We will have 7 different roles: guest, reader, writer, editor, manager, administrator and app_admin (our developers)

I have been trying to solve this for 3 days - please understand that I'm still pretty new! thanks in advance

+7
ruby ruby-on-rails authorization cancan
source share
2 answers

Today I faced the same needs and found a way to do this on the CanCan Wiki .

Just follow these simple steps:

1) Create a constant under the User class with role names:

 class User < ActiveRecord::Base ROLES = %w[admin moderator author banned] end 

2a) Create and run the migration if you are using ActiveRecord:

 rails generate migration add_roles_mask_to_users roles_mask:integer rake db:migrate 

2b) Add this field to the user model if you are using Mongoid:

 field :roles_mask, type: Integer 

3) Then you need to add the following code to the user model:

 # in models/user.rb def roles=(roles) self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.inject(0, :+) end def roles ROLES.reject do |r| ((roles_mask.to_i || 0) & 2**ROLES.index(r)).zero? end end 

4) If you use the application without strong parameters, be sure to add attr_accessible: roles to the user model. If you use devise with strong_parameters, either as a stone in a Rails 3 application or as a built-in in Rails 4, be sure to add roles to the allowed list in the controller:

 class ApplicationController < ActionController::Base before_filter :configure_permitted_parameters, if: :devise_controller? protected def configure_permitted_parameters devise_parameter_sanitizer.for(:sign_up) {|u| u.permit(:email, :password, :password_confirmation, roles: [])} end end 

5) Add the code below to create checkboxes in the view to set these roles:

 <% for role in User::ROLES %> <%= check_box_tag "user[roles][#{role}]", role, @user.roles.include?(role), {:name => "user[roles][]"}%> <%= label_tag "user_roles_#{role}", role.humanize %><br /> <% end %> <%= hidden_field_tag "user[roles][]", "" %> 

6) Finally, you can add a convenient way to test user roles in the Ability class:

 # in models/user.rb def is?(role) roles.include?(role.to_s) end # in models/ability.rb can :manage, :all if user.is? :admin 

What is it.

Hope this helps.

0
source share

In your gemfile Include.

  • gem "cancan"

  • install the package.

  • g cancan rails: ability

this will create a class of abilities in your models.

Define your abilities there as shown below.

but remember that you have already defined the roles,

for example, you have a user model,

having two roles defined by ie admin and support.

  class Ability include CanCan::Ability def initialize(user) user||= User.new can :read, :all if user.role == 'admin' can :manage, :all else can :read, :all end end end 

4. The resource you want to limit the user to, use the following filter in your controller.

  load_and_authorize_resource 

5. if you want to limit something in the views, so as not to show.

  <% if can? :manage, @flower %> <td><%= link_to 'Edit', edit_flower_path(flower) %></td> <% end %> <% if can? :manage, @flower %> <td><%= link_to 'Destroy', flower_path(flower), method: :delete, data: { confirm: 'Are you sure?' } %></td> <% end %> 
-2
source share

All Articles