May 7
Rails Gotcha #9: Disabled Sessions
I was recently working on a Rails application where sessions were disabled in the application controller (affecting all other controllers), and needed to be turned back on in certain other controllers. Basically, sessions were only needed on the admin controllers.
While testing this application I ran into a few surprises, leading to this 2 in 1 Rails Gotcha, and two patches to Rails edge.
Turning Sessions Back On
As you know, turning off all sessions is simply the matter of using a
class method in your ApplicationController:
session(:off)
Intuitively you would think you could turn the session back on in one of your other controllers using the opposite:
session(:on)
This doesn't work, however. It also doesn't raise any exception or
produce any warning whatsoever. As a matter of fact, if you look at
the source code, using :on has the exact same effect as using :off.
The correct way to enable sessions after they have been turned off is
to pass a hash with the key :disabled with the value false:
session(:disabled => false)
If you're like me, and you'd like to see the session(:on) trick work,
please go vote a +1 for my ticket.
Sessions and Forgery Protection
Turning sessions off in the application controller has an interesting side effect if you also have forgery protection enabled:
protect_from_forgery(:secret => 'foobar') session(:off)
In order for the forgery protection to work, you need to have sessions enabled (it probably doesn't even make sense to guard against forgery if you don't have them enabled). So, if you have sessions disabled, forgery protection enabled, and post a form, you get this helpful error message:
undefined method `session_id' for {}:Hash
This is the result of some checks in the forgery code for a valid session happening too late. I've created a patch that moves the session checking to the correct location, producing this error message instead:
InvalidAuthenticityToken: "Request Forgery Protection requires a valid session. Use #allow_forgery_protection to disable it, or use a valid session."
Without the patch, however, the workaround is simple, either turn off forgery protection when you turn off sessions, or use something like the following in your application controller:
prepend_before_filter do |controller| controller.class.allow_forgery_protection = controller.session_enabled? end
Don't Forget to Vote
Please vote a +1 for the following patches:
- Ticket 136: Session On Ticket
- Ticket 139: Sessions and Forger Protection Ticket
Updates
Sunday, May 11, 2008: Both of these patches have been accepted into Rails, and the tickets were closed. Thanks for the votes!