How does protect_from_forgery with: :exception work?
I'd like to edit the code to look at it and learn from it. However, I cannot find where it is placed as in a higher level of abstraction.
You can find it here on Github : https://github.com/rails/rails/blob/c60be72c5243c21303b067c9c5cc398111cf48c8/actionpack/lib/action_controller/metal/request_forgery_protection.rb#L88
def protect_from_forgery(options = {})
self.forgery_protection_strategy = protection_method_class(options[:with] || :null_session)
self.request_forgery_protection_token ||= :authenticity_token
prepend_before_action :verify_authenticity_token, options
end
The with: :exception is passed to protection_method_class(:exception). Which does :
def protection_method_class(name)
ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify)
rescue NameError
raise ArgumentError, 'Invalid request forgery protection method, use :null_session, :exception, or :reset_session'
end
Then this ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify). name.to_s.classify here will be Exception.
Then you can find:
module ProtectionMethods
class Exception
def initialize(controller)
#controller = controller
end
def handle_unverified_request
raise ActionController::InvalidAuthenticityToken
end
end
end
All of this sets the way the invalid authenticity will be handled.
Then it sets a before_action: :verify_authenticity_token.
def verify_authenticity_token
unless verified_request?
logger.warn "Can't verify CSRF token authenticity" if logger
handle_unverified_request
end
end
Which uses the previously defined strategy:
def handle_unverified_request
forgery_protection_strategy.new(self).handle_unverified_request
end
To raise the exception as defined in Exception.
Related
I have a simple rails app i had added Active Admin onit, and it worked fine (except that my css is having some problem) but suddenly i started reciving this error just after i tried doing some changes to the dashboard.rb file:
This is the error:
[2016-02-16T18:56:55.902056 #5260] FATAL -- :
ArgumentError (A copy of ApplicationController has been removed from the module
tree but is still active!):
app/controllers/application_controller.rb:26:in `notification'
my application_controller.rb
class ApplicationController < ActionController::Base
include PublicActivity::StoreController
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
skip_before_filter :verify_authenticity_token
helper_method :mailbox, :conversation
before_action :set_last_seen_at, if: proc { user_signed_in? }
before_action :notification, if: proc { user_signed_in? }
private
def mailbox
#mailbox ||= current_user.mailbox
end
def conversation
#conversation ||= mailbox.conversations.find(params[:id])
end
def set_last_seen_at
current_user.update_attribute(:last_seen_at, Time.now)
end
def notification
#shipment_ids = Shipment.where(:user_id => current_user.id).pluck(:id)
#comment = Comment.where("shipment_id IN (?)", #shipment_ids).where(:is_read => false).where.not(:user_id => current_user.id)
end
end
Rails's :rescue_from takes in a specific exception type and a method as parameter as follow:
class ApplicationController < ActionController::Base
rescue_from User::NotAuthorized, with: :deny_access # self defined exception
rescue_from ActiveRecord::RecordInvalid, with: :show_errors
rescue_from 'MyAppError::Base' do |exception|
render xml: exception, status: 500
end
protected
def deny_access
...
end
def show_errors(exception)
exception.record.new_record? ? ...
end
end
but this implies that it will deal with the specified exception in the same way ALL ACROSS the controller.
What if I want to handle an exception type differently based on what method the exception is raised from, Example:
class MyController < ActionController::Base
def method_1
# Do Something
rescue MyCustomError => e
handle_exception_for_method_1(e)
end
def method_2
# Do Something
rescue MyCustomError => e
handle_exception_for_method2(e)
end
protected
def handle_exception_for_method_1(exception)
# Do Something
end
def handle_exception_for_method_2(exception)
# Do Something
end
end
I have the following questions:
Can this be done by using :rescue_from as well (with any sort of options to pass in)?
If not, is there any better solution of dealing with this kind of situations?
(Kind of off topic but) Is it a bad practice to handle the same type of error differently in different methods in general?
Rails provides access to the controller and action names through controller_name and action_name methods. You could use this to handle exceptions differently based on the what method the exception was raised.
Example:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordInvalid, with: :show_errors
protected
def show_errors
if action_name == "create"
...
elsif action_name == ...
...
end
end
I have a problem with authenticating requests made to my Rails API
In my application controller I hvae the following code
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
# protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }
before_filter :authenticate_user_from_token!
private
def authenticate_user_from_token!
Rails.logger.debug "Authenticating..." <---------- (1)
authenticate_with_http_token do |token, options|
Rails.logger.debug "options: #{options.inspect}" <-----------------------(2)
user_email = options[:user_email].presence
user = user_email && User.find_by_email(user_email)
if user && Devise.secure_compare(user.authentication_token, token)
sign_in user, store: false
end
end
end
end
But the problem is that while (1) gets executed and logged, (2) is never reached for some reason. Anyone have a clue as to why this is?
Seems like your ApplicationController needs:
include ActionController::HttpAuthentication::Token::ControllerMethods
I'm trying to handle 404 errors when using a cms gem. In my past application this 404 method seemed to work but now its not rescuing the error. Any ideas?
# application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
rescue_from ActionController::RoutingError, :with => :render_404
def after_sign_in_path_for(resource)
admin_cms_path
end
private
def render_404(exception = nil)
if exception
logger.info "Rendering 404: #{exception.message}"
end
flash[:error] = 'The page you entered is unavailble, please send us an email if this is unexpected'
redirect_to root_path
end
end
and the routes
# routes.rb
Amskier::Application.routes.draw do
# ...
comfy_route :blog_admin, path: '/admin'
comfy_route :cms_admin, path: '/admin'
root to: "cms/content#show"
comfy_route :cms, path: '/', sitemap: false
end
Add the route to this action of controller:
match 'path' to: 'errors#catch_404'
and create errors controller like this
class ErrorsController < ApplicationController
def catch_404
raise ActionController::RoutingError.new(params[:path])
end
end
after this it should start working.
I tried almost everything on web, all I want is to call a method whenever an exception like "ActiveRecord::RecordNotFound" or "No route matches" appears.
Rescues from ApplicationController does not work, but why?
class ApplicationController < ActionController::Base
protect_from_forgery
private
def self.send_report_error(message)
Notifier.page_failure(message).deliver
end
rescue ActiveRecord::RecordNotFound
# handle not found error
send_report_error ActiveRecord::RecordNotFound.to_s
rescue ActiveRecord::ActiveRecordError
# handle other ActiveRecord errors
send_report_error ActiveRecord::ActiveRecordError.to_s
rescue # StandardError
# handle most other errors
send_report_error "common error"
rescue Exception
# handle everything else
send_report_error "common exception"
end
Use rescue_from. For example:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, :with => :send_report_error
end
http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html