cancancan授权资源无法达到预期效果

7

我在简单的cancancan授权中遇到了意外行为。

ability.rb

class Ability
  include CanCan::Ability

  def initialize(user)
    # Define abilities for the passed in user here. For example:
    #
    user ||= User.new # guest user (not logged in)
    if user.is_admin?
        can :manage, :all
    elsif user.is_standard?
        can :manage, ServiceOrder, {user_id: user.id}
        can :manage, ServiceOrderDetail, :service_order => { :user_id => user.id }
    end

service_order.rb控制器(部分显示)

class ServiceOrdersController < ApplicationController
  authorize_resource

  def show
    @service_order = ServiceOrder.includes(:service_order_details).find(params[:id])
  end

end

这种方式不可行,因为它让控制器展示任何服务订单记录,而不仅仅是当前用户拥有的订单。

唯一有效的方式是手动授权控制器,添加以下内容:

authorize! :show, @service_order

像这样:

  def show
    @service_order = ServiceOrder.includes(:service_order_details).find(params[:id])
    authorize! :show, @service_order
  end

这没有意义,因为authorize_resource应该做这个。
3个回答

10
发生的情况是在展示操作之前,authorize_resource 发生了,由于 @service_order 还没有设置,因此它会针对类进行检查,用户仅受到约束下才能显示 ServiceOrder

添加 authorize_resource 将安装一个 before_action 回调函数,该回调将调用 authorize!,如果存在资源实例变量,则传递该变量。如果未设置实例变量(例如在索引操作中),它将传递类名。例如,如果我们有一个 ProductsController,它将在每个操作之前执行以下操作:

authorize!(params[:action].to_sym, @product || Product)

来自 Cancancan 文档

你需要做的是像 widjajayd 建议的那样使用 load_and_authorize_resource 或者 (如果您不想使用 cancancan 默认的加载操作) 在调用 authorize_resource 之前手动使用您的自定义方法加载资源的 before_filter


1
我之前没有使用load_and_authorize_resource,因为我在加载资源时进行了预取。如果能指出如何解决这个问题就加一分。 - Augusto Samamé Barrientos
在我的情况下,解决方案就是将 before_action :fetch_userauthorize_resource 交换位置,以便在授权尝试之前先加载用户。 - mike927

2

我的建议是:不要使用authorize_resource,而是使用load_and_authorize_resource。以下是您的控制器示例,请确保您的strong_parameters声明为:service_order_params。

  load_and_authorize_resource param_method: :service_order_params

0

当在控制器的顶部使用authorize_resource时,实际上是设置了一个before_action,该操作将在控制器动作之前完成授权检查,因此必须在控制器动作之前设置实例变量(而不是在其中),即您必须使用一个 before_action 来设置实例变量。

示例

class PostsController < ApplicationController

authorize_resource

# rest of controller

authorize_resource 创建的 before_action 将查找必须在 before_action 中创建的 @post 实例变量(对于索引操作,是 @posts),因为授权检查将在控制器操作运行时已经执行。

资源

这些信息来自于 cancancan源代码/文档authorize_resource 方法:

设置一个前置过滤器,使用实例变量对资源进行授权。例如,如果你有一个 ArticlesController,它将检查 @article 实例变量,并确保用户可以对其执行当前操作。在底层,它会执行类似以下的操作:

authorize!(params[:action].to_sym, @article || Article)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接