Does Rails come with a “not authorized” exception?

巧了我就是萌 提交于 2019-12-20 11:03:32

问题


I am writing an application that uses plain old Ruby objects (POROs) to abstract authorization logic out of controllers.

Currently, I have a custom exception class called NotAuthorized that I rescue_from at the controller level, but I was curious to know: Does Rails 4 already come with an exception to indicate that an action was not authorized? Am I reinventing the wheel by implementing this exception?

Clarification: The raise AuthorizationException is not happening anywhere inside of a controller, it is happening inside of a completely decoupled PORO outside of the controller. The object has no knowledge of HTTP, routes or controllers.


回答1:


Rails doesn't seem to map an exception to :unauthorized.

The default mappings are defined 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
)

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

You could add a custom exception from within your application's configuration (or a custom Railtie):

Your::Application.configure do

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

  # ...

end

Or simply use rescue_from.




回答2:


I'm guessing the reason Rails didn't introduce this exception is because Authorisation and Authentication is not Rails native behavior (not considering basicauth of course).

Usually these are responsibilities of other libraries Devise for NotAuthenticated; Pundit, CanCanCan, Rollify for NotAuthorized) I would actually argue it may be a bad thing to extend ActionController with custom exceptions like ActionController::NotAuthorized (because like I said it's not it's responsibility)

So Way how I usually tackled this problem is that I've introduced custom exceptions on 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

Therefore in my controllers I can do

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

This clearly defines that the layer your expecting this exception to be raised and caught is your application layer, not 3rd party lib.

The thing is that libraries can change (and yes this means Rails too) defining exception on a 3rd party lib classes and rescuing them in your application layer is really dangerous as if the meaning of exception class changes it brakes your rescue_from

You can read lot of articles where people are Waring about Rails raise - rescue_from being the modern goto (now considering anti-pattern amongst some experts) and in certain extend it is true, but only if you are rescuing Exceptions that you don't have full control off !!

That means 3rd party exceptions (including Devise and Rails to certain point). If you define the exceptions classes in your application, you are not relaying on 3rd party lib => you have full control => you can rescue_from without this being an anti-pattern.



来源:https://stackoverflow.com/questions/25892194/does-rails-come-with-a-not-authorized-exception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!