CanCan check_authorization不会为非RESTful控制器

时间:2016-11-10 00:09:02

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

动机

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

背景

The CanCanCan documentation for Non RESTful controllers表示

  

你不应该使用load_and_authorize_resource方法   没有资源加载。相反,你可以调用授权!在每个行动中   分开。

理论上我不应该成为一个问题我应该将其锁定,并通过向check_authorization添加ApplicationController来确保我的应用程序中的每个操作都能进行授权。根据{{​​3}}:

  

如果授权在中执行,则会引发异常   动作。

问题

我遇到的问题是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根本没有被调用。为了增加我的困惑 - 为Ability#initializenew调用了index方法 - 您会期望其中一个方法与show操作具有相同的行为new操作不应该调用authorize!方法。

2 个答案:

答案 0 :(得分:1)

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

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控制器。试一试。

答案 1 :(得分:0)

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

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

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

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

然而,这个解决方案并不理想 - 它感觉非常hacky并且感觉不像CanCan的做事方式。