【问题标题】:CanCan check_authorization not throwing AccessDenied Exception for Non RESTful ControllerCanCan check_authorization 不会为非 RESTful 控制器抛出 AccessDenied 异常
【发布时间】:2016-11-10 00:09:02
【问题描述】:

动机

我正在尝试在其中一个控制器未与资源关联的应用程序中使用 CanCan - 非 RESTful 控制器。我希望根据其他资源和会话参数有条件地授权该控制器的操作。

背景

The CanCanCan documentation for Non RESTful controllers 声明

你不应该使用 load_and_authorize_resource 方法,因为那里 没有要加载的资源。相反,您可以致电授权!在每一个动作 分开。

无论如何这不应该是一个问题,因为理论上我应该将其锁定并通过将check_authorization 添加到我的ApplicationController 来确保我的应用程序中的每个操作都发生授权。根据the CanCanCan documentation

如果授权在 行动。

问题

我遇到的问题是 check_authorization 似乎没有锁定非 RESTful 控制器的操作。未声明授权的操作!正在执行而不引发任何 AccessDenied 异常。

下面是代表我正在做的事情的 MCVE。我做错了什么还是我发现了一个错误?这样做的“正确”方法是什么?

实施

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  check_authorization

  def current_user
    Object.new
  end

  protected

  rescue_from CanCan::AccessDenied do |exception|
    puts "CanCan::AccessDenied Exception thrown : message=#{exception.message}"
  end
end

在我的控制器中,我尝试手动授权,但前提是满足某些条件:

class FooController < ApplicationController
  def new
    authorize! :foo, :bar, unless: true
    puts 'FooController#new'
  end

  def index
    authorize! :foo, :bar, if: true
    puts 'FooController#index'
  end

  def show
    puts 'FooController#show'
  end
end

以及启用 :foo :bar 授权的 Ability 类:

class Ability
  include CanCan::Ability

  def initialize(user)
    puts 'CanCan::Ability#initialise'
    can :foo, :bar
  end
end

在 Rails 控制台中生成的输出:

2.3.0 :001 > app.get '/foo/new'
CanCan::Ability#initialise
FooController#new

2.3.0 :002 > app.get '/foo'
CanCan::Ability#initialise
FooController#index

2.3.0 :001 > app.get '/foo/1'
FooController#show

请注意,show 操作甚至没有 authorize! 声明,但仍允许执行而不会引发异常。

另外,我不明白的是,Ability#initialize 根本没有调用 show 操作。让我更加困惑的是——newindex 都调用了 Ability#initialize 方法——你会期望其中一个具有与 show 操作相同的行为,因为 new 操作应该不要调用authorize! 方法。

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-4 cancan cancancan


    【解决方案1】:

    更顺畅的方法是在您的 ApplicationController 中定义 check_authorization,如下所示:

    def check_authorization
      raise CanCan::AccessDenied.new('Some message', params[:action].to_sym, params[:controller].to_sym) if cannot? params[:action].to_sym, params[:controller].to_sym
    end
    

    然后您的 rescue_from 块将完成剩下的工作。

    在您需要检查授权的控制器内部,而不是在每个操作开始时调用authorize!,您实际上可能更喜欢执行以下操作:

    before_action: check_authorization
    

    在开始时,或者使用before_actionexceptonly,如果您需要检查选择性操作的授权,例如:

    before_action: check_authorization, except: [:create, :update]
    

    before_action: check_authorization, only: [:show, :update_permissions]
    

    我不知道它是否能完全解决您的问题,但它确实是一种更具可读性的方式,并且适用于 RESTful 和非 RESTful 控制器。试一试。

    【讨论】:

    • 我很感激你的努力,但这怎么能行呢?你建议而不是授权!我打电话给before_action :check_authorization,然后会引发CanCan::AccessDenied 异常...使我无法授权该操作。
    • ifraise 行的末尾。在ability.rb 中列出您需要的所有权限,使用cancannot,然后,当调用以check_authorization 作为前导的操作时,如果权限不存在,它将引发AccessDeniedability.rb,否则它会进展得很好。此外,我刚刚注意到您在所有操作中都调用了同一对 action 和“类”而不是 authorize!。不同的动作不应该是不同的吗?同样action 是第一部分,class 是所有authorize! 的第二部分。 CanCan docs 清楚地解释了这一切。
    • 啊,我现在完全理解你在做什么了,谢谢你的清晰解释。不幸的是,我认为我不能以这种方式使用这些能力,因为我没有使用资源。对于 RESTful 操作,您可以为有效地使授权有条件的能力提供一些范围 - 例如can :read, Product, organisation_id: @user.organisation_id。这就是为什么我在操作本身内有条件地授权的原因——我可能过于简化了我的 MCVE 并且没有足够清楚地说明这一点。不过,您的意见是有价值的,并且提供了更多的清晰度,所以我会赞成。
    【解决方案2】:

    我找到了解决此问题的方法。

    在这种情况下,由于条件语句,您希望授权在您需要明确authorize! 的操作中失败,您知道不会定义该功能。例如;

    condition ? (authorize! :foo, :bar) : (authorize! nil, nil)
    

    这将导致当 condition 为 false 时,非 RESTful 控制器会引发 AccessDenied 异常,然后您可以像处理其他未授权资源的 RESTful 控制器一样处理该异常。

    但是,这个解决方案并不理想 - 感觉很老套,感觉不像 CanCan 的做事方式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多