2006-12-19

Ruby On Rails Gotcha

This may be resolved in the next version of Ruby on Rails, but I found a little quirk in the Rails testing mechanism. Let's say you have a login page, and a bunch of admin pages. If you try to access the admin page, you get redirected to the login page. After you login, you get redirected back to the original page you were trying to get to in the first place. If you enter the wrong password, it will reload the login page (i.e. it will redirect back to itself). To keep things clear, you are trying to get to:
http://www......com/admin/index
and it takes you to:
http://www......com/login/index
and depending on whether you log in correctly or not, it will take you to the first or second link, respectively.

Quick terminology note: A set of pages is called a "controller" - in this case, pages are either part of the admin controller or the login controller. The specific page is called an "action". So for http://www......com/admin/index, the controller is "admin", and the action is "index".

Now, Rails has an excellent testing framework that will allow you to make sure your website is behaving the way it's supposed to (i.e. as described above). Testing is a great way to catch if someone made a mistake somewhere along the line. One test you can do is something like this:

(code to pretend you're logging in correctly)
assert_redirected_to :controller => 'admin', :action => 'index'

This means "make sure that, after a successful login, I was redirected to the main admin page, and report an error if not".

Conversely:

(code to pretend you're logging in incorrectly)
assert_redirected_to :controller => 'login', :action => 'index'

This means "make sure that, after a unsuccessful login, I was redirected back to the login page".

But what about this?

assert_redirected_to :action => 'index'

If we don't specify the controller, what are we asserting? I always assumed that, if this is being called from a test for the login controller (Rails testing is organized by controller*), that it would assume I meant login/index. But it turns out that it will match whatever controller I actually redirected to. In other words, I expected the test to fail if I was redirected to admin/index, but it didn't. So if you write this:

(code to pretend you're logging in incorrectly)
assert_redirected_to :action => 'index'

it will pass even if you are redirected to the wrong place. That is, the test will not properly catch a mistake that lets you access the administration section of the website without logging in! This is a potential security problem - while it doesn't necessarily mean the admin section is open, it does mean that the testing system won't properly alert you if it is.

Anyway, for those Rails testers out there, watch out - always specify the controller!

--YY

* Rails also has "model testing", and more, but that's beyond the scope of this post.

No comments: