如何使用cancan来授权一系列资源?

我有一个非宁静的控制器,我正在尝试使用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用于复杂的非静态控制器更容易手动(尽管我的应用程序中有很多其他的安静控制器,它工作得很好)。

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

 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 

似乎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