【问题标题】:How do I use cancan to authorize an array of resources?如何使用 cancan 对资源数组进行授权?
【发布时间】:2011-03-27 01:42:25
【问题描述】:

我有一个 non-restful 控制器,我正在尝试使用 cancan 授权!方法来应用权限。

我有一个这样开始的 delete_multiple 操作

def delete_multiple
    @invoices = apparent_user.invoices.find(params[:invoice_ids])

我想在继续之前检查用户是否有权删除所有这些发票。如果我使用

authorize! :delete_multiple, @invoices

权限被拒绝。我的能力.rb 包括以下内容

if user.admin?
  can :manage, :all
elsif user.approved_user?
  can [:read, :update, :destroy, :delete_multiple], Invoice, :user_id => user.id
end

是循环遍历我的数组并单独调用授权还是有更聪明的做事方式?我开始觉得手动进行授权比使用 cancan 来处理复杂的非 RESTful 控制器更容易(尽管我的应用中有很多其他的 RESTful 控制器,它们都很好用)。

【问题讨论】:

    标签: ruby-on-rails authorization cancan


    【解决方案1】:

    这里有点晚,但你可以在你的能力课上写这个

    can :delete_multiple, Array do |arr|
      arr.inject(true){|r, el| r && can?(:delete, el)}
    end
    

    编辑

    这也可以写成:

    can :delete_multiple, Array do |arr|
      arr.all? { |el| can?(:delete, el) }
    end
    

    【讨论】:

    • 我什至不记得我最后是如何处理这个问题的,但是那段代码让我的大脑扭曲得太厉害了,我不得不投票!
    • 所以我定义了一个类似的:read_multiple 能力。在我的控制器顶部,我有load_and_authorize_resource :through => :tickets, :except => :index。在我的索引方法中,我得到了@claims = Claim.where(:claimant_provider_id => current_user.provider.id); authorize!(:read_multiple, @claims) unless @claims.empty?。但是,即使无论如何它应该授权,它也总是无法授权。我尝试在 read_multiple 块中添加debugger 或对raise 的调用,但都没有被触发,所以我不确定是否正在使用该功能。有什么想法吗?
    • 没关系,解决了我自己的问题。上面定义的功能检查 Array 类,但@claims 在技术上是一个 ActiveRecord::Relation 类。将其更改为 @claims.all 使其行为符合预期。
    • 感谢您的更新!支持一个 22 个月大的答案并收到即时编辑给了我一个 SO-ner。如果可以的话,我会再次 +1。
    • @brad 我会争辩说,如果这段代码“扭曲”了你的大脑,那么它就不值得支持。复杂性不应该得到回报。保持简单直接。几个月后我不想再看这段代码,然后试着弄清楚我想要完成什么。
    【解决方案2】:

    似乎authorize! 仅适用于单个实例,而不适用于数组。下面是我使用 Rails 3.2.3 和 CanCan 1.6.7 解决这个问题的方法。

    基本思路是统计用户尝试删除的记录总数,统计accessible_by (current_ability, :destroy)的记录,然后比较计数。

    如果您只想要一个用户有权销毁的记录数组,您可以使用accessible_by (current_ability, :destroy) 返回的数组。但是我使用的是destroy_all,它直接在模型上工作,所以我最终选择了这个计数和比较解决方案。

    值得检查一下开发日志,看看这两条SELECT COUNT 语句的样子:第二条应该为CanCan 施加的授权限制添加WHERE 短语。

    我的示例处理删除多条消息。

    ability.rb

    if user.role_atleast? :standard_user
      # Delete messages that user owns
      can [:destroy, :multidestroy], Message, :owner_id => user.id
    end
    

    messages_controller.rb

    # Suppress load_and_authorize_resource for actions that need special handling:
    load_and_authorize_resource :except => :multidestroy
    # Bypass CanCan's ApplicationController#check_authorization requirement:
    skip_authorization_check :only => :multidestroy
    
    ...
    
    def multidestroy
      # Destroy multiple records (selected via check boxes) with one action.
      @messages = Message.scoped_by_id(params[:message_ids]) # if check box checked
      to_destroy_count =  @messages.size
      @messages = @messages.accessible_by(current_ability, :destroy) # can? destroy
      authorized_count =  @messages.size
    
      if to_destroy_count != authorized_count
        raise CanCan::AccessDenied.new # rescue should redirect and display message
      else # user is authorized to destroy all selected records
        if to_destroy_count > 0
          Message.destroy_all :id => params[:message_ids]
          flash[:success] = "Permanently deleted messages"
        end
        redirect_to :back
      end
    end 
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多