2014-09-17 1 views
25

Я пишу приложение, которое использует простые старые объекты Ruby (POROs) для абстрактной логики авторизации из контроллеров.Есть ли Rails с «неавторизованным» исключением?

В настоящее время у меня есть пользовательский класс исключений под названием NotAuthorizedrescue_from, что я на уровне контроллера, но мне было интересно узнать: ли Rails 4 уже поставляются с исключением для указания того, что действие не было разрешено? Я изобретаю колесо, выполняя это исключение?

Разъяснение: raise AuthorizationException не происходит в любом месте внутри контроллера, это происходит внутри полностью отсоединен PORO вне контроллера. Объект не знает HTTP, маршрутов или контроллеров.

+0

код ошибки http 401 означает несанкционированный доступ, вы можете сказать, что рельсы возвращают код состояния 401 и отображают любой вид, который вы хотите –

+1

@MohammadAbuShady - я считаю, что он ищет sth, как 'raise ActionController :: RoutingError.new (' Not Found ') ', который автоматически заставляет приложение отображать 404 без каких-либо аварийных статусов. – BroiSatse

+2

Я обычно использую devise + cancan, приятную комбинацию для аутентификации и авторизации. –

ответ

22

Рельсы, похоже, не являются исключением из :unauthorized.

по умолчанию отображений определены в 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 
) 

и 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 
) 

Вы можете добавить пользовательское исключение из в конфигурации вашего приложения (или пользовательские Railtie):

Your::Application.configure do 

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

    # ... 

end 

Или просто используйте rescue_from.

+1

Спасибо, что ответили на мой вопрос. Мне любопытно, как исключения сопоставляются с Mongoid как ORM. – Rick

+2

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

+1

@ Ссылка Стефана ссылается на конкретный номер строки в 'master', который изменяется со временем. Вот ссылка «perma» на этот код: https://github.com/mongoid/mongoid/blob/v4.0.2/lib/mongoid/railtie.rb#L24 –

13

Я догадываюсь, почему Rails не представила это исключение, потому что авторизация и аутентификация не являются поведением Rails (не считая basicauth, конечно).

Обычно это обязанности других библиотек Devise для NotAuthenticated; Pundit, CanCanCan, Rollify для NotAuthorized) Я бы на самом деле утверждают, что это может быть плохая вещь, чтобы расширить ActionController с пользовательскими исключениями, как ActionController::NotAuthorized (потому что, как я сказал, что это не будет ответственность)

Так путь, как я обычно решать эту проблему в том, что Я представил пользовательские исключения на 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 

Поэтому в моих контроллеров я могу сделать

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

Это четко определяет, что слой вы Ожидая, что это исключение будет поднято и поймано, это ваш прикладной уровень, а не сторонний lib.

Дело в том, что библиотеки могут меняться (и да, это означает, что Rails тоже) определение исключений на 3-й партии Lib классов и спасая их в прикладном уровне действительно опасно, как будто смысл класса исключения изменяет это тормозит ваш rescue_from

вы можете прочитать много статей, где люди Уоринг о Rails raise - rescue_from будучи современным goto (в настоящее время рассматривает антишаблон среди некоторых экспертов), а также в определенной степени это верно, но только если вы спасая исключения, которые вы не У меня полный контроль!

Это означает, что сторонние исключения (включая Devise и Rails до определенной точки). Если вы определяете классы исключений в своем приложении, вы не ретранслируете на третьей стороне lib => у вас есть полный контроль => вы можете rescue_from, не считая это анти-шаблоном.