Rails: Test against and handle ActionController::InvalidAuthenticityToken - ruby-on-rails

I have a Rails web app with some forms users can use to comment etc. They are frequently being used by spammers trying to create spam comments. Nothing new about that.
Sometimes I get CSRF-hack attempts which causes an ActionController::InvalidAuthenticityToken exception. This happens quite a lot and I would like to rescue and send the user/bot to a "You failed to comment"-page.
This has turned out to be a tricky exception to catch though since I can't recreate the error myself. I first put a rescue in the #create when the model (created by the form) was to be saved but it doesn't catch the exception. In order to do so I consider having it cover the entire controller section but that seems over the top.
My two questions:
Is there a way to re-create an ActionController::InvalidAuthenticityToken error by myself, so I can test it?
When is the exception raised? During .save? On Comment.new? Basically, where should I put my begin/rescue?
Thanks!

You can rescue from this exception in your controller. One way you can set this up is to add a rescue in your ApplicationController.
class ApplicationController < ActionController::Base
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_auth_token
private
def record_not_found
render :text => "You failed to comment", :status => 422
end
end
You could also just capture the exception locally in your controller action.
def create
begin
# Create your comment
rescue ActionController::InvalidAuthenticityToken
# Render your last view with some error text.
end
end
You can add raise ActionController::InvalidAuthenticityToken inside your action if you want to test it out.

Related

Rails default error page doesn't show on development

I built an error handler that, when there's an error in any controller method in production, reroutes the user to an error page and sends me, the developer, a notification email. This works, but I want the normal error screen to appear when I'm in development. I assumed the code to produce this was just raise e, but instead in development I'm now getting the default production error page (the one that says "We're sorry, but something went wrong".), instead of the detailed error message and trace that used to appear.
class ApplicationController < ActionController::Base
rescue_from StandardError, with: :handle_error
#error handler
def handle_error(e)
if Rails.env.production?
#code to send email and redirect to error page
else
raise e
end
end
end
I also tried the following:
raise
raise StandardError.new e
raise e, e.message
raise e.message
and if I run any of those in a binding.pry console, they produce the error message I'm looking for, but the error page still just says "We're sorry, but something went wrong."
Anyone know how I can just show the default development error page?
UPDATE
This is insane...so the code to display an error normally should definitely work, but something somewhere is preventing that. If I change config.consider_all_requests_local = true on production, the errors show up on production, but then even if I copy and paste my config/environments/production.rb file into my config/environments/development.rb, the errors still don't show on development. If I enter a pry console, request.local? returns "0", signifying true, and Rails.env returns "development". I have no idea what is going on.
UPDATE 2
Apparently I'm not supposed to be rescuing exceptions on development, but even if I delete every bit of custom error handling code so my Application Controller is just empty, my errors still don't show on development. Further, I have a different app with the same exact error handling code, and for that the errors do show.
Search your code for consider_all_requests_local, it's this configuration that show the full error log.
It must be set as true on your development.rb config file. It's either missing from your configs, or other config is overwriting it
This is not "insane", this is completely expected behavior. You cannot raise from within a rescue_from handler. That would cause an infinite loop.
You also cannot rescue_from StandardError as stated specifically in the documentation:
Using rescue_from with Exception or StandardError would cause serious side-effects as it prevents Rails from handling exceptions properly. As such, it is not recommended to do so unless there is a strong reason.
Instead of conditionally handling the exception inside your rescue_fromhandler, you should conditionally bind the handler, and pick a more specific exception class to handle.
class ApplicationController < ActionController::Base
rescue_from StandardError, with: :handle_error if Rails.env.production?
#error handler
def handle_error(e)
#code to send email and redirect to error page
end
end
create ErrorsController like below
class ErrorsController < ApplicationController
skip_before_action :login
def not_found
respond_to do |format|
format.html { render status: 404 }
format.json { render json: { error: "Not found" }, status: 404 }
end
end
end

Rails 5: How to declare HEAD 200 in Rescue

I have a webhook sent to a page on my app, and I am catching SOAP errors from processing the data, but I want to send to the webhook a 200 status. Is this possible in the controller?
controller
def example_method
...
rescue Savon::SOAPFault => error
...
# Respond to the HTTP POST request by giving the 'all clear'
head 200
raise
end
First off don't place soap calls directly into your controller - instead create a client class or service object that handles it.
Your controller should only really know that it calls some kind of service and that it may fail or succeed - if you are catching library specific errors in your controller you are on your way down the rabbit hole.
So how do you rescue exceptions in your controller?
You can rescue them inline:
class SomethingController
def do_something
begin
# Something.this_may_blow_up!
rescue SomeError => error
head :ok and return
end
end
end
The key here is and return which halts the controller flow and prevents double render errors.
The other method is by using rescue_from.
class ThingsController
rescue_from ActiveRecord::RecordNotFound do
render :not_found and return
end
def show
#thing = Thing.find(params[:id])
end
end
The above is an example of how you can create a custom 404 page per controller. What happens is that when the action on the controller is invoked it is wrapped in a begin..rescue block that looks for rescue_from handlers in the controller.

Can I ignore a `Filter chain halted as :set_user rendered or redirected`?

Since my tests (and dev output) were getting a bit out of hand, I modified the plain old set_user filter of my UsersController like
def set_user
#user = User.find(params[:id]) rescue render(json: { message: 'User not found' }, status: 404)
end
which (crudely) gets rid of the ActiveRecord::RecordNotFound exception and proceeds to render a plain old 404 message. Now, except for the warning I get regarding the filter halting processing, there seem to be no visible side effects, and I can avoid rescuing the AR exception in all my spec examples.
I'm wondering if this is an acceptable practice, and, if not, what a better solution might look like.
As you already know, this is quite crude. It will catch any exception and return the JSON you specified, although it might not be a ActiveRecord::RecordNotFound error.
I would recommend using rescue_from ActiveRecord::RecordNotFound, with: :method_name in your ApplicationController, which will rescue just that type of error and respond with the appropriate JSON.

Get exception content in Rails Router

So I handle exceptions with an error controller to display dynamic content to my users in production. I have it in my route file to do:
# Errors
%w( 404 422 500 ).each do |code|
get code, :to => "errors#show", :code => code
end
The only problem is now that I'm routing on errors such as that I lose information in my controller when I want to notify Airbrake. How can I maintain the exception information and send it to Airbrake on a 500? Right now all I get is the env that was occurring at the time of the exception which is less helpful for debugging purposes.
class ErrorsController < ApplicationController
def show
notify_airbrake(env)
render status_code.to_s, :status => status_code
end
protected
def status_code
params[:code] || 500
end
end
Are you handling an error by redirecting to a URL like http://your.site/500? That will be just an HTTP request like any other, losing the exception context you're after. Instead, you probably want to be using ActionController's Rescue functionality. It would look like this:
class ApplicationController < ActionController::Base
rescue_from StandardError, with: :render_error
private
def render_error(error)
notify_airbrake(error)
render text: 500, status: 500
end
end
You can add multiple rescue_from declarations to handle different kinds of error, like the ActiveRecord::RecordNotFound from the Rails guide's example.

Exceptions in Ruby on Rails

simple index action in my app
def index
#reports = CustomReport.all
end
when the application was to ready I was told to add exceptions, I copied it from other application as I did not have any idea,
def index
begin
#reports = CustomReport.all
rescue Exception => e
Rails.logger.info "Exception in design_custom_reports controller, index action"
Rails.logger.info e
flash[:notice] = "Sorry, something went wrong. Please inform administration"
redirect_to :controller => :user, :action => :dashboard
end
end
and now it looks clumsy isn't it?
Best way to handle the scenario in RoR?
Short answer
That rescue clause doesn't really make sense. Remove it.
Long answer
That's a very bad code. First of all, the question is: who told you to "add exceptions" and for which purpose?
The code
CustomReport.all
is known to raise an exception only in very special cases. Generally speaking, when the application can't connect to the database. In fact, there are no external user inputs or conditions that may cause the code to naturally fail.
Conversely, this code can fail more frequently
CustomReport.find(params[:id])
because find raises an error if the object is not found, which is definitely a more common case.
If your method crashes for a database error, it's likely the entire application is affected so the rescue management probably makes sense in your global application, not really in that method. There's not that much you can do there to rescue it.
Last but not least, rescuing an exception of class Exception
rescue Exception => e
is considered a code smell in Ruby. In fact, you should rescue only StandardErrors or greater. If you rescue an Exception class you must be very aware of what it means. System level errors and syntax errors inherits from Exception, so if you rescue an Exception it's likely that you will hide a lot of potential errors in your code.
But again, even rescuing StandardError does not make a lot of sense. In fact, we said before that CustomReport.all could only fail in case of database connection errors. This means that, if you really want to rescue something, you should rescue only database failures there.
you can put that code in Application controller . and it will capture all error in your whole application. and you do not need to write any more thing in any other controller as they are already inherited by application controller
rescue_from Exception do |exception|
#error = exception
exception_logger
end
private
def exception_logger
Rails.logger.error "Exception in design_custom_reports controller, index action"
Rails.logger.error #error
flash[:notice] = "Sorry, something went wrong. Please inform administration"
redirect_to :controller => :user, :action => :dashboard
end

Resources