如何使用非标准关系名称指定嵌套的CanCan授权?

时间:2012-04-10 17:42:47

标签: ruby-on-rails-3 cancan nested-resources

我正在使用Devise进行身份验证,使用CanCan进行授权。

目标

我有两种模式:用户和赞助,其中赞助提供了一个has_many:通过用户赞助商和用户即客户之间的关系。

我想配置CanCan,以便拥有sponsor?权限的用户可以管理自己的赞助商,即只有Sponsorship#client_id == user.id的赞助商。用户还可以拥有admin?个权限,在这种情况下,他或她可以管理任何赞助。

模型

class User < ActiveRecord::Base
  has_many :sponsor_links, :class_name => 'Sponsorship', :foreign_key => 'client_id'
  has_many :sponsors, :through => :sponsor_links, :class_name => 'User'

  has_many :client_links, :class_name => 'Sponsorship', :foreign_key => 'sponsor_id'
  has_many :clients, :through => :client_links, :class_name => 'User'

  def has_role?(role)
    ... return true if this user has <role> privileges
  end
end

class Sponsorship
  belongs_to :sponsor, :class_name => 'User'
  belongs_to :client, :class_name => 'User'
end

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new  # handle guest user (not logged in)
    if user.has_role?(:admin)
      can :manage, :all
    elsif user.has_role?(:sponsor)
      # not sure if the third argument is correct...
      can :manage, Sponsorship, :sponsor => {:user_id => user.id}
    end
  end
end

路线

我已设置嵌套路线以反映赞助用户拥有其客户的事实:

resource :users, :only => [:index]
  resource :sponsorships
end

问题

在我的SponsorshipsController中加载和授权用户和赞助资源的正确方法是什么?

我尝试了什么

这类似于普通的嵌套资源,CanCan可以轻松处理。但这些关系有非标准的名称(例如:sponsor_links而不是赞助商),我还没想出如何在我的SponsorshipsController中配置load_and_authorize_resource声明。

在我尝试过的许多不起作用的东西中;),这是一个更简单的版本。 (另请注意,我的能力可能无法正确设置 - 见上文):

class SponsorshipsController < ApplicationController
  load_and_authorize_resource :sponsor_links, :class_name => "User"
  load_and_authorize_resource :sponsorships, :through => :sponsor_links
  respond_to :json

  # GET /users/:user_id/sponsorships.json
  def index
    respond_to @sponsorships
  end

  # GET /users/:user_id/sponsorships/:id.json
  def show
    respond_to @sponsorship
  end
end

通过拯救CanCan :: AccessDenied错误,我知道:

  • index用户:sponsor中,用户身份验证失败。
  • 在使用index用户的:admin中,赞助的身份验证失败。
  • show中,无论角色如何,赞助都会失败。

1 个答案:

答案 0 :(得分:1)

部分答案

第一个问题出现在能力规范中,其中包括:

...if user.has_role?(:sponsor)
  can :manage, Sponsorship, :sponsor => {:user_id => user.id}
end

但应该是

...if user.has_role?(:sponsor)
  can :manage, Sponsorship, :user_id => user.id
end

(记住,孩子们,unit tests是你的朋友!不知怎的,我忘记了这一课。)

在控制器中,我也改变了:

  load_and_authorize_resource :sponsor_links, :class_name => "User"
  load_and_authorize_resource :sponsorships, :through => :sponsor_links

  load_and_authorize_resource :user
  load_and_authorize_resource :sponsorship

主要是有效:它设置@user和@sponsorship并授权访问它们。但索引函数会加载current_user可访问的所有赞助商,而不仅仅是由user_id拥有的赞助商。我的修复 - 可能不是最优的 - 是从

重写索引函数
  def index
    respond_with(@user, @sponsorships)
  end

  def index
    @sponsorships = @sponsorships.where(:sponsor_id => @user.id)
    respond_with(@user, @sponsorships)
  end

通过这些更改,一切正常。

如果某人有更恰当的方式来表达这一点,我想知道。