Rails 4: CanCanCan abilities with has_many :through association

Rails 4: CanCanCan abilities with has_many :through association

我有一个 Rails 应用程序,具有以下型号:

class User < ActiveRecord::Base
  has_many :administrations
  has_many :calendars, through: :administrations
end

class Calendar < ActiveRecord::Base
  has_many :administrations
  has_many :users, through: :administrations
end

class Administration < ActiveRecord::Base
  belongs_to :user
  belongs_to :calendar
end

对于给定的 calendaruser 有一个 role,它在 administration 连接模型中定义。

对于每个日历,用户只能拥有以下三种角色之一:OwnerEditorViewer

这些角色目前未存储在字典或常量中,仅通过不同方法以字符串形式("Ower"、"Editor"、"Viewer")分配给管理部门。

User 模型上的身份验证是通过 Devise 处理的,current_user 方法有效。

为了只允许登录用户访问应用内资源,我已经在calendarsadministrations控制器中添加了before_action :authenticate_user!方法

现在,我需要实现一个基于角色的授权系统,所以我只安装了 CanCanCan gem.

这是我想要实现的目标:

为了实现上述内容,我提出了以下 ability.rb 文件:

class Ability
  include CanCan::Ability

  def initialize(user, calendar)
    user ||= User.new
    calendar = Calendar.find(params[:id])
    user can :create, :calendar
    if user.role?(:owner)
      can :manage, :calendar, :user_id => user.id
      can :manage, :administration, :user_id => user.id
      can :manage, :administration, :calendar_id => calendar.id
    elsif user.role?(:editor)
      can [:read, :update], :calendar, :user_id => user.id
      can :destroy, :administration, :user_id => user.id
    elsif user.role?(:viewer)
      can [:read], :calendar, :user_id => user.id
      can :destroy, :administration, :user_id => user.id
    end    
  end
end

由于我对 Rails 的实验并不多,而且这是我第一次使用 CanCanCan,我对我的代码不是很有信心,希望得到一些验证或改进建议.

那么,这段代码是否有效,它是否能让我实现我所需要的?

UPDATE:使用当前代码,当我以用户身份登录并访问另一个用户日历的calendars#show页面时,我实际上可以访问日历,这我不应该。

所以,很明显,我的代码不起作用。

知道我做错了什么吗?

更新 2:我认为我的代码中存在错误,因为我使用 :model 而不是 Model 来允许 user s 对给定的 model.

执行操作

但是,代码仍然无效。

知道这里有什么问题吗?

更新 3:问题可能是由于我使用 if user.role?(:owner) 检查用户的角色是否设置为所有者,而在数据库中角色实际上定义为 "Owner"(作为字符串)?

更新 4:我继续做一些研究,我意识到我犯了两个错误。

  1. 我没有将 load_and_authorize_resource 添加到 calendarsadministrations 控制器。

  2. 我定义了两个属性和两个参数 — initialize(user, calendar) — 而不是我的 initialize 方法中的一个。

因此,更新两个控制器以及 ability.rb 文件如下:

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new
    if user.role?(:owner)
      can :manage, Calendar, :user_id => user.id
      can :manage, Administration, :user_id => user.id
      can :manage, Administration, :calendar_id => calendar.id
    elsif user.role?(:editor)
      can [:read, :update], Calendar, :user_id => user.id
      can :destroy, Administration, :user_id => user.id
    elsif user.role?(:viewer)
      can [:read], Calendar, :user_id => user.id
      can :destroy, Administration, :user_id => user.id
    end    
  end
end

现在,当我尝试访问不属于 current_user 的日历时,出现以下错误:

NoMethodError in CalendarsController#show
undefined method `role?' for #<User:0x007fd003dff860>
def initialize(user)
    user ||= User.new
    if user.role?(:owner)
      can :manage, Calendar, :user_id => user.id
      can :manage, Administration, :user_id => user.id
      can :manage, Administration, :calendar_id => calendar.id

我该如何解决这个问题?

用户模型没有这样的方法role?。 Cancancan 文档错误地假设示例中存在这种方法。

要解决这个问题,您应该这样做:

if user.role == 'Owner'
  ...
elsif user.role == 'Editor'
  ...
elsif user.role == 'Viewer'
  ...