Ruby on rails relies on cookies. Perhaps ironically, it doesn’t actually check that a user has cookies enabled. This, as you might imagine, can lead to problems.
I was charged with implementing some sort of cookies test for a project at work. Letting google be my co-pilot, I ran across a blog post that mentioned a way to do it. The method is simple: given a request, check the session for the _session_id variable. If it isn’t there, set it, and redirect the user to the same URL, but with a GET parameter appended to it. The _session_id variable should be set automatically by rails. The GET parameter will indicate that cookie detection is in progress. When the browser follows the redirect, the GET parameter is detected, and if the _session_id still doesn’t exist, then the user has cookies disabled.
Why is all of this required you ask? Because on the first visit to a site, the user can’t possibly have any cookies nor session variables (which are accessed via cookies). Hence the need to set a session variable and reload the page.
I implemented the methodology laid out in the blog post, and while effective, it proved awkward for my company’s use. Because it involved redirecting to a page with a GET argument, e.g. blah.com/my_page?cookies_enabled=testing.
This in and of itself was fine, except that if one restarted the server or deleted the sessions, then reloaded that particular page via its URL, the cookie test would see the GET argument, but not the _session_id session variable that was supposed to be set, and fail. As URLs get cut and pasted around a lot at my work—especially by QA—this didn’t go over well. I needed something different. Here’s what I came up with:
in application.rb:
before_filter :cookies_required, :except => ["cookies_test"]def cookies_testif request.cookies["_session_id"].to_s.blank?logger.warn("=== cookies are disabled")render :template => "shared/cookies_required"elseredirect_back_or_default(:controller => "foo")endend
def cookies_requiredreturn unless request.cookies["_session_id"].to_s.blank?session[:return_to] = request.request_uriredirect_to(:controller => "foo",:action => "cookies_test")return falseend
Don’t forget to write up some scathing admonishment for the shared/cookies_required view, and you’re half-way there.
I say half-way, because no changes can be made to a rails installation without two things; passing your current tests, and writing some new ones. When I ran my tests, all my functional tests failed. Why? Say it with me class, “the cookie test was redirecting all the functional tests to the page with the scathing admonishment for disabling cookies.” That’s right. Fortunately, this is easily worked around, by adding the following cookie line to your functional tests:
in foo_controller_test.rb:
def setup@controller = FooController.new@request = ActionController::TestRequest.new@response = ActionController::TestResponse.new@request.cookies["_session_id"] = "fake cookie bypasses filter"end
This way, all your functional tests’ requests have a fake session id, which fakes out the cookie detection test. Voile! All the tests pass. Now, don’t forget to take the time to write a few that don’t set the session id, and make sure that they do get redirected like they’re supposed to.
The advantage of this method, is that there are never any GET parameters passed around, so it’s safe to load any page at any time. Both methods make some assumptions about how rails handles sessions, but I think this is a limited risk, as the session information is integral to rails, so they can’t go changing it willy nilly without upsetting a lot of people anyway.
As an added bonus, the redirects are all done behind the scenes, so the user is ne’er the wiser. A slight delay on their first page load is all they could ever notice.
Comments
Leave a comment Trackback