Rails: Using CanCan to define multiple roles depending on instances of single Model?

杀马特。学长 韩版系。学妹 提交于 2019-12-05 05:24:29

I ran into a same needs today and found a way to do this on CanCan Wiki.

Simple follow these simple steps:

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

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

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

rails generate migration add_roles_mask_to_users roles_mask:integer
rake db:migrate

2b) Add these field on User model if you are using Mongoid:

field :roles_mask, type: Integer

3) Next you'll 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're using devise without strong parameters, don't forget to add attr_accessible :roles to you user model. If you're using devise with strong_parameters, either as a gem in a Rails 3 app, or as is built-in in Rails 4, dont forget to add the roles to the permitted 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 bellow to generate checkboxes in the view for setting 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 then add a convenient way to check the user's 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

That's it.

I hope this can help.

In your gemfile Include.

  1. gem "cancan"

  2. install bundle.

  3. rails g cancan:ability

this will generate an ability class in your models.

define your Abilities there like below.

but keep remember that you have already defined roles,

such as you have a User model,

having two roles defined i.e 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 on which you want to restrict a user, use the following filter in their controller.

              load_and_authorize_resource

5. if you want restrict something in the views 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 %>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!