使用设计和cancan进行嵌套资源授权

时间:2013-04-22 22:19:01

标签: ruby-on-rails ruby devise ruby-on-rails-3.2 cancan

如何正确授权使用devise和cancan嵌套的资源?我已经从文档中实现了建议的过程但没有成功。

此问题涉及能力模型使用设计和cancan gems无法从嵌套资源的第三层层获取用户ID。但是,我可以从 secondary 层获取用户ID。非常感谢任何帮助!

我有一个像这样的嵌套资源:

  resources :users do
    resources :clients do
      resources :positions
    end
  end

  resources :clients do
    resources :positions
  end

  resources :users do
    resources :positions
  end

  resources :users
  resources :clients
  resources :positions 

使用position模型控制器使用以下内容:

class PositionsController < ApplicationController
  before_filter :grab_client_from_client_id
  load_and_authorize_resource :user
  load_and_authorize_resource :client, through: :user, shallow: true
  load_and_authorize_resource :position, through: :client, except: [:index], shallow: true
  ...
end

ability.rb文件:

class Ability
  include CanCan::Ability

  def initialize(user)

    user ||= User.new # guest user (not logged in)

    if user.has_role? :admin
      can :manage, :all
    elsif user.has_role? :management
      can [:create, :read, :update], :all
    else
      can :read, :all, user_id: user.id
    end

  end
end

这会导致非管理员/非管理员用户收到以下错误:

undefined method 'user_id' for #<User:0x5227d40>

显然,某些设置未正确设置。我已经遍历了每个宝石的文档,以及在各处搜索寻找解决方案。

我还将在下面提供我的模型关系。


class User < ActiveRecord::Base
  has_many :clients
  has_many :positions, through: :clients
  resourcify
  ...
end


class Client < ActiveRecord::Base
  resourcify
  has_many :checklogs
  has_many :positions
  belongs_to :user
end


class Position < ActiveRecord::Base
  resourcify
  belongs_to :client
  delegate :user, to: :client, allow_nil: true
end

1 个答案:

答案 0 :(得分:4)

问题出在这一行:

can :read, :all, user_id: user.id

当您检查用户是否可以阅读某些内容时,它会检查您要阅读的内容。

由于你的控制器中有这一行:

load_and_authorize_resource :user

您尝试授权的资源是用户。

您的能力会将user.user_idcurrent_user.id进行比较。用户没有user_id,因此这就是错误的来源。

根据您的代码,我怀疑您希望用户只能阅读他的内容,除非他是经理或管理员。

您可以通过以下方式实现此目的:

if user.has_role? :admin
  can :manage, :all
elsif user.has_role? :management
  can [:create, :read, :update], :all
else
  can :read, User, id: user.id
  can :read, Client, user_id: client.id
  can :read, Position, client: { user_id: user.id }
end

这样,用户只能访问与他有关系的那些模型。