CanCan check_authorization 不会针对非 RESTful 控制器引发 AccessDenied 异常

问题描述 投票:0回答:2

动机

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

背景

非 RESTful 控制器的 CanCanCan 文档指出

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

这不应该是一个问题,因为理论上我应该锁定它,并通过将

check_authorization
添加到我的
ApplicationController
来确保我的应用程序中的每个操作都发生授权。根据 CanCanCan 文档

如果在某个权限中执行授权,这将引发异常 行动。

问题

我遇到的问题是 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 授权的能力类:

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
动作调用。让我更加困惑的是 -
Ability#initialize
new
都调用
index
方法 - 你会期望其中一个与
show
操作具有相同的行为,因为
new
操作应该'不要调用
authorize!
方法。

ruby-on-rails ruby-on-rails-4 cancan cancancan
2个回答
1
投票

更顺畅的方法是在

check_authorization
中定义
ApplicationController
,如下所示:

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

您的

rescue_from
块将完成剩下的工作。

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

authorize!

before_action: check_authorization

在开始时,或者将

before_action
except
only
一起使用,如果您需要检查选择性操作的授权,例如:

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

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

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


0
投票

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

在这种情况下,由于条件语句的原因,您希望授权在某个操作中失败,您需要显式

authorize!
您知道不会定义的能力。例如;

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

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

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

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.