6

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

我有一个这样开始的 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 用于复杂的 non-restful 控制器更容易(尽管我的应用程序中有很多其他的 restful 控制器,它们都很好用)。

4

2 回答 2

13

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

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
于 2011-09-15T10:10:10.650 回答
1

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

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

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

值得检查开发日志以了解这两个SELECT COUNT语句的外观:第二个语句应添加WHERE有关 CanCan 施加的授权限制的短语。

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

能力.rb

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

消息控制器.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 
于 2012-08-17T02:17:09.693 回答