动机
我试图在其中一个控制器与资源非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#initialize
和new
调用了index
方法 - 您会期望其中一个方法与show
操作具有相同的行为new
操作不应该调用authorize!
方法。
答案 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_action
与except
或only
一起使用,例如:
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的做事方式。