I'm currently stuck on how to separate roles for CanCan depending on each condition that we want. In our application, there are many categories (such as math, english, history, etc.) and within each are many courses.
Each user can have many different roles on each category. For example, John can be a "reader" for math, which means he can read all the courses that are in math. John can also be a "writer" for english, which means he can read all the courses in english, create a course within category english, and edit/delete only the courses he created within english.
If these were the only roles John had, he would not be able to see the category history in the navbar, and would be denied access to courses that are within history.
These are how relations are set up:
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 correct way? How do we access the courses that are within the categories that we have access to?
After defining the reader and writer methods in the ability.rb, how do we use them in our view pages? It looks like the current documentations use something like "<% if can? :read, @category %> " but that doesn't use the methods we separated and defined.
p.s. We will have 7 different roles: guest, reader, writer, editor, manager, admin, and app_admin(our developers)
I've been trying to solve this for 3 days now - please understand that I'm still fairly a beginner! Thanks in advance
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.
gem "cancan"
install bundle.
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 %>
来源:https://stackoverflow.com/questions/18482747/rails-using-cancan-to-define-multiple-roles-depending-on-instances-of-single-mo