2016-11-10 74 views
0

動機慘慘check_authorization不扔存取遭拒例外非REST風格的控制器

我試圖在其中一個控制器不與資源相關的應用程序使用慘慘 - 非REST風格的控制器。我希望該控制器的操作能夠根據其他資源和會話參數進行有條件授權。

背景

The CanCanCan documentation for Non RESTful controllers指出

因爲 沒有資源來加載你不應該使用load_and_authorize_resource方法。相反,您可以致電授權!分別在每個動作 中。

這不應該是一個問題,反正在理論上我應該鎖定下來,並確保授權加入check_authorizationApplicationController發生在我的應用程序的每一個動作。據the CanCanCan documentation

如果授權在 行動執行這將引發異常。

問題

我遇到的問題是,check_authorization似乎並沒有被鎖定非REST風格的控制器的操作。不聲明授權的行爲!正在執行而不會引發任何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:酒吧授權:

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方法 - 考慮到new操作不應調用authorize!方法,您會期望其中一個操作與show操作具有相同的行爲。

回答

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

+0

我很欣賞這種努力,但它如何工作?你建議,而不是授權!我調用'before_action:check_authorization',然後會引發'CanCan :: AccessDenied'異常......使我無法授權該操作。 – David

+0

'raise'行末尾有一個'if'。在'ability.rb'裏面用'can'或'cannot'列出你需要的所有權限,然後,當一個具有'check_authorization'作爲前兆的動作被調用時,如果權限確實會引發'AccessDenied'不存在於'ability.rb'中,否則它會進展得很好。我還注意到,在你的所有行爲中,你都會在'authorize!'上面調用同一對'action'和'class'。不同的行爲不應該是不同的嗎?另外'action'是第一部分,'class'是所有'authorize!'的第二部分。 CanCan'文檔清楚地解釋了所有這些。 –

+0

啊我現在完全理解你在做什麼,謝謝你的明確解釋。不幸的是,我不認爲我可以以這種方式使用這些能力,因爲我沒有使用資源。對於RESTful操作,您可以爲有效地使授權有條件的能力提供一些範圍 - 例如, 'can:read,Product,organisation_id:@ user.organisation_id'。這就是爲什麼我在行動本身內有條件地授權 - 我可能已經過分簡化了我的MCVE,並沒有足夠清楚。你的意見雖然有價值,並提供了更多的清晰度,所以我會upvote。 – David

0

我找到了解決此問題的解決方法。

在這種情況下,由於條件語句,您希望授權在某個操作中失敗,因此您需要明確authorize!您所知道的功能將不會被定義。例如;

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

那麼這將導致AccessDenied拋出異常的非REST風格的控制器,當condition是假的,然後你就可以處理,因爲你會爲這裏的資源還沒有被授權的其他REST風格的控制器。

但是,這種解決方案並不理想 - 它感覺非常黑客,並且感覺不像CanCan的做事方式。