2014-09-17 15 views
25

Sto scrivendo un'applicazione che utilizza semplici vecchi oggetti Ruby (PORO) per astrarre la logica di autorizzazione dai controller.Le rotaie hanno un'eccezione "non autorizzata"?

Attualmente, ho una classe di eccezione personalizzata denominata NotAuthorized che ho rescue_from a livello di controller, ma ero curioso di sapere: fa Rails 4 già venuto con un'eccezione per indicare che un'azione non è stato autorizzato? Sto reinventando la ruota implementando questa eccezione?

Chiarimento: Lo raise AuthorizationException non sta accadendo da nessuna parte all'interno di un controller, sta accadendo all'interno di un PORO completamente disaccoppiato al di fuori del controller. L'oggetto non ha conoscenza di HTTP, rotte o controller.

+0

il codice di errore http 401 significa non autorizzato, è possibile dire ai binari di restituire un codice di stato 401 e rendere qualsiasi visualizzazione desiderata –

+1

@MohammadAbuShady - Credo che stia cercando 'raise ActionController :: RoutingError.new (' Not Found ') 'che forza automaticamente l'applicazione a renderizzare 404 senza alcun statmenet di salvataggio. – BroiSatse

+2

Di solito uso solo devise + cancan, una bella combinazione per l'autenticazione e l'autorizzazione –

risposta

22

Rails non sembra mappare un'eccezione a :unauthorized.

Le mappature di default sono definiti in activerecord/lib/active_record/railtie.rb:

config.action_dispatch.rescue_responses.merge!(
    'ActiveRecord::RecordNotFound' => :not_found, 
    'ActiveRecord::StaleObjectError' => :conflict, 
    'ActiveRecord::RecordInvalid' => :unprocessable_entity, 
    'ActiveRecord::RecordNotSaved' => :unprocessable_entity 
) 

e actionpack/lib/action_dispatch/middleware/exception_wrapper.rb:

@@rescue_responses.merge!(
    'ActionController::RoutingError'    => :not_found, 
    'AbstractController::ActionNotFound'   => :not_found, 
    'ActionController::MethodNotAllowed'   => :method_not_allowed, 
    'ActionController::UnknownHttpMethod'  => :method_not_allowed, 
    'ActionController::NotImplemented'   => :not_implemented, 
    'ActionController::UnknownFormat'   => :not_acceptable, 
    'ActionController::InvalidAuthenticityToken' => :unprocessable_entity, 
    'ActionDispatch::ParamsParser::ParseError' => :bad_request, 
    'ActionController::BadRequest'    => :bad_request, 
    'ActionController::ParameterMissing'   => :bad_request 
) 

Si potrebbe aggiungere un'eccezione personalizzato dall'interno di configurazione dell'applicazione (o una consuetudine Railtie):

Your::Application.configure do 

    config.action_dispatch.rescue_responses.merge!(
    'AuthorizationException' => :unauthorized 
) 

    # ... 

end 

oppure utilizzare semplicemente rescue_from.

+1

Grazie per aver risposto alla mia domanda. Sono curioso di sapere come sono state mappate le eccezioni con Mongoid come ORM. – Rick

+2

@Rick https://github.com/mongoid/mongoid/blob/master/lib/mongoid/railtie.rb#L37 – Stefan

+1

@ Il link di Stefan fa riferimento a un numero di riga specifico in 'master' che cambia nel tempo. Ecco un link più "perma" a quel codice: https://github.com/mongoid/mongoid/blob/v4.0.2/lib/mongoid/railtie.rb#L24 –

13

Sto indovinando il motivo per cui Rails non ha introdotto questa eccezione perché Autorizzazione e Autenticazione non sono comportamenti nativi di Rails (non considerando ovviamente Basicauth).

Di solito queste sono le responsabilità di altre librerie Devise per NotAuthenticated; Pundit, CanCanCan, Rollify per NotAuthorized) vorrei davvero sostenere, può essere una cattiva cosa per estendere ActionController con eccezioni personalizzate come ActionController::NotAuthorized (perché come ho detto che non è che sia responsabilità)

Così modo come io di solito affrontato questo problema è che ho introdotto eccezioni personalizzate su ApplicationController

class ApplicationController < ActionController::Base 
    NotAuthorized = Class.new(StandardError) 
    # ...or if you really want it to be ActionController 
    # NotAuthorized = Class.new(ActionController::RoutingError) 

    rescue_from ActiveRecord::RecordNotFound do |exception| 
    render_error_page(status: 404, text: 'Not found') 
    end 

    rescue_from ApplicationController::NotAuthorized do |exception| 
    render_error_page(status: 403, text: 'Forbidden') 
    end 

    private 

    def render_error_page(status:, text:, template: 'errors/routing') 
    respond_to do |format| 
     format.json { render json: {errors: [message: "#{status} #{text}"]}, status: status } 
     format.html { render template: template, status: status, layout: false } 
     format.any { head status } 
    end 
    end 
end 

Pertanto nei miei controllori che posso fare

class MyStuff < ApplicationController 
    def index 
    if current_user.admin? 
     # .... 
    else 
     raise ApplicationController::NotAuthorized 
    end 
    end 
end 

Questo definisce chiaramente che lo strato tua exp zionare questa eccezione da sollevare e catturare è il livello dell'applicazione, non la lib di terze parti.

Il fatto è che le biblioteche possono cambiare (e sì, questo significa Rails troppo) definendo eccezione su un 3rd party classi lib e salvataggio nel livello di applicazione è davvero pericoloso come se il significato di classe di eccezione cambia frena la tua rescue_from

È possibile leggere molti articoli in cui le persone sono in guardia su Rails raise - rescue_from è il moderno goto (che ora considera anti-pattern tra alcuni esperti) e in certi casi è vero, ma solo se si stanno salvando eccezioni che non si non avere il pieno controllo !!

Ciò significa eccezioni di terze parti (compresi Devise e Rails fino a un certo punto). Se definisci le classi di eccezioni nella tua applicazione, non stai inoltrando su lib di terze parti => hai il pieno controllo => puoi rescue_from senza che questo sia un anti-pattern.

Problemi correlati