protect_from_forgery - does order matter? - ruby-on-rails

I have read this post https://nvisium.com/blog/2014/09/10/understanding-protectfromforgery/ and if I understood correctly, by default in Rails 3 if we have a controller that looks like:
class ApplicationController < ActionController:Base
protect_from_forgery
end
What will end up happening is that (in case of an attacker) the session will be destroyed. That would mean that if we are doing something like checking if the user is authenticated, since there would be no session, it will stop the request.
So, my question is, I believe that this:
class ApplicationController < ActionController:Base
protect_from_forgery
before_action :authenticate_user
private
def authenticate_user
raise NotAuthenticated unless session.key?(:user)
end
end
Is the proper way of doing it, instead of
class ApplicationController < ActionController:Base
before_action :authenticate_user
protect_from_forgery
private
def authenticate_user
raise NotAuthenticated unless session.key?(:user)
end
end
Or in other words, protect_from_forgery should be the first thing that we do in a controller.
Are my assumptions correct, or I am missing something on how the order of operations work in the controller?

The order of those two methods is not important, no.
The reason has to do with when those methods get executed: those are Class-level methods that are executed in the context of the class itself when Ruby loads the file.
Look at the source for protect_from_forgery:
def protect_from_forgery(options = {})
options = options.reverse_merge(prepend: false)
self.forgery_protection_strategy = protection_method_class(options[:with] || :null_session)
self.request_forgery_protection_token ||= :authenticity_token
before_action :verify_authenticity_token, options
append_after_action :verify_same_origin_request
end
These are basically macros that add code to your class when they are invoked, a form of metaprogramming. You could replace the method call in the class with setting those things manually and it would be the same.
This all happens when your code first gets loaded, before the application has booted up.

Related

A before_filter does not see value assigned in another before_filter

I am trying to access one variable set in a method from another method. This is in Rails 1.9 code below.
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :method1
before_filter :method2
def method1
#remote_user = 'dev_user'
end
def method2
unless #remote_user.present?
render :status => 401, :text => "Authentication failed"
false
end
end
When attempting to access it in the second method, it is always returning blank. The 401 is always returning with the text "Authentication failed". Could someone advise on to what I am doing wrong?
if you need to guarantee that a method is called before another on before_filter, do something like this:
before_filter :fn3
def fn3
fn1
fn2
end
from -> How can I specify the order that before_filters are executed?
Hope it helps
EDITED
Better yet, use prepend_before_filter for anything you needs to be resolved before the before_filter method.
in your case:
class ApplicationController < ActionController::Base
protect_from_forgery
prepend_before_filter :method1
before_filter :method2
def method1
#remote_user = 'dev_user'
end
def method2
unless #remote_user.present?
render :status => 401, :text => "Authentication failed"
false
end
end
To ensure correct order of before_filter you can use prepend_before_filter or append_before_filter. Imho prepend is the default behaviour, so your filters are executed in reverse order.
So to fix this you will have to write:
class ApplicationController < ActionController::Base
before_filter :method2
prepend_before_filter :method1
You could just write before_filter twice (in this order) and it would be fine, but this is imho more expresssive. First write all the before_filters for which the order does not matter, and then prepend the one that needs to be first.
Alternatively you could write
class ApplicationController < ActionController::Base
before_filter :method1
append_before_filter :method2
which does exactly the same, but makes sure method2 is executed last. Whichever you prefer :)
Also note that filters defined in other controllers (deriving from ApplicationController) will normally be executed first!

Skip before_filter defined with block

Is it possible to skip a before filter in using skip_before_filter when the before_filter is defined with a block. For example:
class ApplicationController < ActionController::Base
before_filter do |controller|
# filter stuff
end
end
I know, that is it possible using the "normal" way of defining the filter with a method name.
I actually found a way around this using an if option with a method. Use
class ApplicationController < ActionController::Base
before_filter(:if => :use_filter?) do |controller|
# filter stuff
end
private
def use_filter?
true
end
end
class OtherController < ApplicationController
private
def use_filter?
false
end
end
:D
Its most definitely not possible. From the documentation:
Note that skipping uses Ruby equality, so it’s impossible to skip a callback defined using an anonymous proc using #skip_filter
http://apidock.com/rails/AbstractController/Callbacks/ClassMethods/skip_action_callback

How do I initialise objects in a partial view for use by multiple controllers?

I hope this is something obvious that I've just consistently overlooked and the community can set me on the right path.
I have a news article controller, but I want to be able to use a "common" ticker list on different views. How do I initialise this "#article_list" if I'm using the partial in a few controllers? Apparently it is of the opinion that using a helper is not the solution, since helpers are just for view logic. So where do I put this initialiser that would be available to every controller as required? I shouldn't put them in application controller should I?
You can use before_filter method, i.e. something like this:
class ApplicationController < ActionController::Base
def set_article_list
#article_list = ArticleList.all # or any onther selection
end
end
class NewsArticleController < ApplicationController
before_filter :set_article_list, only: :action1
def action1
end
end
class AnotherNewsArticleController < ApplicationController
before_filter :set_article_list, only: :another_action1
def another_action1
end
end
UPDATE:
Indeed, there will be problem with a fat ApplicationController. To avoid it it's possible to use module (almost #carolclarinet describe it below):
module ArticleList
def set_article_list
#article_list = ArticleList.all # or any onther selection
end
end
class NewsArticleController < ApplicationController
include ArticleList
before_filter :set_article_list, only: :action1
def action1
end
end
class AnotherNewsArticleController < ApplicationController
include ArticleList
before_filter :set_article_list, only: :another_action1
def another_action1
end
end
And
You can create, essentially, a query object that is only responsible for returning what you need for #article_list, for example, building off of Psylone's answer:
class ArticleList
def ticker_articles
ArticleList.all # or any onther selection
end
end
This class could go in lib, app/models, app/query_objects, app/models/query_objects, wherever it makes sense for you. This is a bit outside The Rails Way so there's no convention about where these types of objects should live.
Then in whatever controller you need this, do:
#article_list = ArticleList.new.ticker_articles
For more explanation of query objects, see this codeclimate article #4. Depending on what you're doing to set #article_list, this might also be called a service object (#2) or something else entirely. No matter what you call it though, its responsibility would be to return the value you need for #article_list and that's it.

adding a method to application_controller so it is visible to all controllers and views

I want to create a method that will be available in all controllers, and views.
This method actually makes a db call to get data back, so since it won't be used all the time I want it be be lazily loaded.
class ApplicationController < ActionController::Base
def some_method
#object = customdb.call_method(....)
end
end
To make it lazily loaded do I just do?
#object ||= ....
And how do I propagate this to all controllers and view pages?
Use helper_method in ApplicationController to make some_method available in any controller or view:
class ApplicationController < ActionController::Base
helper_method :some_method
def some_method
#object ||= customdb.call_method(....)
end
end
The ||= does per request caching, not lazy loading. Lazy loading is the deferred initialization pattern.
Big scopes and controller methods in views are code smells. Best to minimize object scope and view logic.
You could use the memoize feature provided by ActiveSupport
http://api.rubyonrails.org/classes/ActiveSupport/Memoizable.html
http://ryandaigle.com/articles/2008/7/16/what-s-new-in-edge-rails-memoization
So possibly something like:
class ApplicationController < ActionController::Base
def some_method
#object = customdb.call_method(....)
end
memoize :some_method
end
If you want the result to be accessible across accross requests, you have to store the results in a class variable:
class ApplicationController < ActionController::Base
helper_method :city_list
def city_list
##city_list =|| City.order(:name)
end
end
The result is lazy loaded and cached.
PS: The variable ##city_list is a class variable.
You should use a before_filter
class ApplicationController < ActionController::Base
before_filter :some_method
def some_method
#object = customdb.call_method(....)
end
end
It will be triggered in all your controllers.
Note that you can skip this before filter using skip_before_filter :some_method wherever you want. You can even prevent only some actions to be preceded by this filter.
Because this will be triggered before all decided controller actions, you won't need your line:
#object ||= ....
Since #object will be instantiated.

How do I limit the accessing of a method across an app?

So I have a method and corresponding partial for including a set of random photos in the sidebar of certain areas of our site.
Right now I have a random_photos method in ApplicationController set with a before_filter.
That works in the sense that it makes the contents of the random_photos method available wherever I need it, but it also unnecessarily executes some complex SQL queries when I don't know it too (ie, when I don't need to access those random photos).
So, how can I limit the accessing of the random_photos method to only when I really need it?
You can add an :if condition to the before_filter call, like so:
class ApplicationController < ActiveController::Base
before_filter :random_photos, :if => is_it_the_right_time?
Yet another option is to use skip_before_filter. It just depends in how many controllers you want to be different. Use skip_before_filter if there are only a handful of controllers you want to be the exception. Use one of the other suggestions if there are many controllers where you want to bypass the filter.
class ApplicationController < ActiveController::Base
before_filter :random_photos
def random_photos
#photos = Photo.random
end
end
class OtherController < ApplicationController
skip_before_filter :random_photos
...
end
You can keep the random_photos method in ApplicationController, and put the before_filters in your other controllers.
class ApplicationController < ActiveController::Base
...
def random_photos
#photos = Photo.random
end
end
class OtherController < ApplicationController
before_filter :random_photos, :only => 'show'
...
end
It depends on how many functions are making use of random_photos...
If a handful then use vrish88's approach but with an after_filter:
class ApplicationController < ActiveController::Base
after_filter :random_photos, :if => is_it_the_right_time?
...
private
def is_it_the_right_time?
return #get_random_photos
end
end
class SomeController < ApplicationController
def show
#get_random_photos = true
...
end
end
If every function in a controller will make use of it then use the skip_before_filter or move the before_filter in the controller and out of the application controller.
Many ways to get it done, and none is more correct then the next. Just try to keep it as simple and transparent as possible so you don't recreate the functionality months down the road because you forgot where all the pieces are located.

Resources