How to redirect with InvalidAuthenticityToken error - ruby-on-rails

I've discovered that the handle_unverified_request method can be overridden in the ApplicationController to rewrite how Rails handles InvalidAuthenticityToken errors. By default the handle_unverified_request will raise the InvalidAuthenticityToken error. I've overridden this method like so
def handle_unverified_request
redirect_to '/422'
end
However, I'm using Airbrake which records errors that are raised on my Rails application. According to this answer, Rails can raise the error AND redirect the user to the 404 page. Does the same thing exist for the 422 page? I want to raise the InvalidAuthenticityToken and redirect the user to the 422 page. How do I do that?

ApplicationController.rb
protect_from_forgery with: :exception
rescue_from ActionController::InvalidAuthenticityToken, with: :rescue_422
def handle_unverified_request
raise(ActionController::InvalidAuthenticityToken)
end
def rescue_422
redirect_to '/422'
end

According to the link, you posted, Rails does not redirect the user to the 404 page, it renders 404 page instead. When InvalidAuthenticityToken error, Rails must render 422 page by default. It is done on Rack Middleware level.
If default behaviour is not what you want and you need redirect and Airbrake to log the exception, then you have to handle the exception and do the redirect AFTER! Airbrake logs it. I think that Airbrake logs exceptions on the Rake Middleware level, so you will have to somehow customize Rack Middleware's exceptions handler to get what you want. You will have to find out where Airbrake logs exceptions and make sure that your custom exceptions handler works after the logging.
Are you sure you want redirect, not render?

Related

'Render and/or redirect were called multiple times in this action' on devise

I am using devise and trying to redirect my user after sign up form to a specific template.
I've checked the docs about after_inactive_sign_up_path_for method but I am receiving an exception saying I am redirecting twice.
RegistrationsController < Devise::RegistrationsController
def create
super
UsersCreateJob.perform_later(resource.id) if resource.persisted?
end
protected
def after_inactive_sign_up_path_for(resource)
render template: 'devise/registrations/success'
end
Exception
AbstractController::DoubleRenderError in RegistrationsController#create
Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".
As far as I understand, the after_inactive_sign_up_path_for method overwrites the one from Devise Registration Controller and redirect to my template, right? Where this other template is coming from?
after_inactive_sign_up_path_for is not meant to render a view. It's a path that will be used for redirect. You should just put a path to an action rendering'devise/registrations/success'.
The error you see is cause by the fact that the devise controller calls render/redirect and then you try to call it as well.

How can I re-raise a Ruby exception in a Rails rescue_from statement?

My Rails 4 app uses RocketPants for its JSON API and Pundit for authorization.
I have code in my /app/controllers/api/v1/base_controller.rb file to handle errors from Pundit. Whenever a user isn't authorized to update a resource, Pundit throws a NotAuthorizedError exception and I rescue it with my user_not_authorized method:
class API::V1::BaseController < RocketPants::Base
include Pundit
version 1
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
def user_not_authorized
error! :forbidden
end
end
When I call the error! method that RocketPants provides from my exception handler, I expect to get a JSON response like this:
{
"error": "forbidden",
"error_description": "The requested action was forbidden."
}
Instead, however, calling error! just immediately blows up the request:
Completed 500 Internal Server Error in 143ms
RocketPants::Forbidden - RocketPants::Forbidden:
rocket_pants (1.13.1) lib/rocket_pants/controller/error_handling.rb:44:in `error!'
app/controllers/api/v1/base_controller.rb:61:in `user_not_authorized'
Full stack trace here.
Why isn't the error! method doing what it should when called from my Pundit exception handler?
If I put error! :forbidden in the middle of my controller action, it works as expected.
For context, the controller that inherits from base_controller.rb and calls Pundit's authorize method looks like this:
class API::V1::MealsController < API::V1::BaseController
before_filter :find_entity
def create
meal = #entity.meals.build(meal_params)
authorize(#entity, :update?)
if meal.save
expose meal, status: :created
else
expose meal.errors, status: 422
end
end
end
Apparently raising exceptions in a rescue_from is a bad idea and according to the Rails docs, exceptions raised in a handler are not bubbled up:
Exceptions raised inside exception handlers are not propagated up.
Docs: http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html
Instead of re-raising RocketPants' exception, I'm simply creating and returning the JSON error message myself:
def user_not_authorized
# error! :forbidden
head 403
error = { error: 'Action not allowed.', error_description: 'Sorry, you are not allowed to perform this action.'}
expose error
end
This works!
UPDATE
I've since found an even cleaner solution: just map the Pundit exception to the RocketPants exception. This means that whenever a Pundit::NotAuthorizedError error is raised it'll be treated as a RocketPants::Forbidden error.
Got the entire solution down to a single line of code at the top of base_controller.rb:
map_error! Pundit::NotAuthorizedError, RocketPants::Forbidden
No handler required.

Redirect to 404 when rails app crash

i am new in rails. I want to add redirection to "404 page" whenever my "app crashed" or "page not found" or in case of "exception".
If someone have good tutorials please share with me or provide some simple solution.
I read this Rails_admin redirect to 404 but it did not solved my problem.
The Rails guide has a chapter about exception handling. You can use rescue_from to run a custom method, when an exception is raised. The following example is from that guide and should be added to your application_controller:
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
private
def record_not_found
render plain: "404 Not Found", status: 404
end
You may want to change the exception type to whatever exceptions you want to catch. I guess that you can also catch from every exception by:
rescue_from StandardError, with: :record_not_found
Note: I would consider this a bad practise and would instead just design my 500 error page to look the same then my 404 page.
I have this method in my application_controller.rb
def not_found
raise ActionController::RoutingError.new('Not Found')
end
and then in any controller when I do a find:
#model = Model.find_by(id: params[:id]) or not_found
I'm using Rails 2 at the moment though so it might be slightly different for your version.
For 404 i.e. page not found, we can redirect page to custom route.
For this you need create new route,
match "*path", to: "handle_errors#page_not_found", via: :all
which will redirect to page_not_found action of handle_errors controller.
Obviously you need to create a new controller handle_errors
For any other exception occurred in app, you can handle it using some customer method in application controller as
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found_error_handler
rescue_from ActiveRecord::RecordNotUnique, :with => :default_error_handler
as so on ...
with defination of methods as,
def default_error_handler(e)
render 'handle_errors/page_404', :status => 404
end
def record_not_found_error_handler(e)
render 'handle_errors/record_not_found'
end

Error during failsafe response when raising an exception in ApplicationController

In my API I use Rails' exceptions_app to handle all exceptions and make a standard JSON response. This works fine except when I raise an exception in the ApplicationController. The controller looks like this:
class ApplicationController < ActionController::Base
before_filter :raise_exception
def raise_exception
raise ActiveRecord::RecordNotFound
end
end
When I request any page I would expect to get a 404 status response. Instead of this Rails returns a 500 status. The log shows this error message:
Error during failsafe response: ActiveRecord::RecordNotFound
What does this mean and how can I work around it?
In your config/application.rb, see if the line below is commented out. If it isn't, comment it out and see if the error still persist.
config.exceptions_app = self.routes

How to rescue from a OAuth::Unauthorized exception in a Ruby on Rails application?

How can I rescue from an OAuth::Unauthorized exception as raised from OmniAuth in a Ruby on Rails application?
Obviously this:
rescue_from OAuth::Unauthorized, :with => :unauthorized
won't work as that only catches exception thrown inside Rails and this exception is thrown somewhere else in the rack chain.
In this application the administrators (and not us, the developers) configure the credentials for twitter and facebook, so having the wrong ones is something that can happen and indeed does happen. I'd like to show a better message that "Something went wrong" when that happens.
Update: I also asked on the omniauth google group, so far there are no answers, but if you are reading this question you might want to check it out.
OmniAuth operates from Rack Middleware, so a rescue_from will not affect it because that is a level of abstraction above OmniAuth via ActionController.
This error is usually due to a misconfiguration of your OAuth settings. Basically it is saying that your application is not authorized to authenticate, not that the user's authentication failed.
A configuration error is something you as a developer would want to mitigate, so I'm not sure why you would want to rescue an exception like this.
If you absolutely must rescue this exception, you can override and use middleware that inherits from OmniAuth
module OmniAuth
module Strategies
class FacebookWithExceptionHandling < OmniAuth::Strategies::Facebook
def call
begin
super
raise OmniAuth::Unauthorized => e
#handle appropriately in rack context here
end
end
end
end
end
Rails.application.config.middleware.use OmniAuth::Builder do
provider OmniAuth::Strategies::FacebookWithExceptionHandling,
api_key, #your api key
secret_key, #your secret key
end

Resources