Rails:使用CanCan定义基于单个模型实例的多个角色?

7

我目前卡在了如何根据我们想要的每个条件为CanCan分离角色上。在我们的应用程序中,有许多类别(例如数学、英语、历史等),每个类别中都有许多课程。

每个用户可以在每个类别中拥有许多不同的角色。例如,约翰可以是数学的“读者”,这意味着他可以阅读所有在数学中的课程。约翰还可以成为英语的“作者”,这意味着他可以阅读英语中的所有课程,在英语类别内创建一个课程,并仅编辑/删除他在英语中创建的课程。

如果这些是约翰唯一拥有的角色,则他将无法在导航栏中看到历史类别,并且将被拒绝访问在历史类别中的课程。

以下是关系设置方式:

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

在 model/ability.rb 文件中,我们有以下内容:
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

问题:

  1. 如何正确分离“读者”和“作者”的角色?如何访问我们有权限访问的某个分类下的课程?

  2. 在ability.rb中定义了读者和作者方法后,如何在视图页面中使用它们?当前文档似乎使用类似 "<% if can? :read, @category %> " 的方式,但这并没有使用我们分离和定义的方法。

p.s. 我们将有7种不同的角色:访客、读者、作者、编辑、管理员、超级管理员和应用程序管理员(开发人员)

我已经尝试解决这个问题3天了 - 请理解我还是一个初学者!提前感谢

2个回答

0

今天我遇到了同样的需求,并在CanCan Wiki上找到了一种解决方法。

只需按照以下简单步骤操作:

1)在User类下创建一个常量,用于存储角色名称:

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

2a) 如果你正在使用ActiveRecord,创建并运行一个迁移:
rails generate migration add_roles_mask_to_users roles_mask:integer
rake db:migrate

如果您正在使用Mongoid,请在用户模型上添加这些字段:

field :roles_mask, type: Integer

3) 接下来,您需要将以下代码添加到用户模型中:

# 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) 如果您正在使用没有 strong parameters 的 devise,请不要忘记在用户模型中添加 attr_accessible :roles。如果您正在使用带有 strong_parameters 的 devise,无论是作为 Rails 3 应用程序中的 gem,还是作为内置于 Rails 4 中的 gem,请不要忘记将角色添加到控制器中允许的列表中:

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) 添加以下代码以在视图中生成复选框以设置这些角色:

<% 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) 最后,您可以在 Ability 类中添加一种方便的方式来检查用户的角色:

# in models/user.rb
def is?(role)
  roles.include?(role.to_s)
end

# in models/ability.rb
can :manage, :all if user.is? :admin

就是这样。

希望可以帮到你。


-2

在你的Gemfile中包含以下内容:

  1. gem "cancan"

  2. 安装bundle

  3. rails g cancan:ability

这将在你的models中生成一个ability类。

在那里定义你的权限,如下所示。

但请记住,你已经定义了角色,

例如,你有一个用户模型,

定义了两个角色,即管理员和支持人员。

  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. 如果您想限制用户访问某些资源,可以在他们的控制器中使用以下过滤器。

              load_and_authorize_resource

如果你想限制某些视图不显示的内容。

    <% 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 %>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接