cancan
I’ve started adding authorization code to the artist portfolio cms I’m working on, using a combination of Authlogic for authentication and CanCan for authorization. I have to say I really like CanCan.
I think what I appreciate most is how accessible it is. Admittedly, I didn’t look too hard at any of the other myriad gems or plugins out there, but my initial impressions were confused ones. CanCan is extremely easy to get going.
Like with anything, however, there are little tricks. My biggest problems were in getting Authlogic to play nicely with Cucumber. Most of those were fixed with the following code in my setup.rb (in the feature/support directory, so it’s not overwritten by changes to env.rb)
1 2 3 4 5 6 7 8 | require 'authlogic/test_case' include Authlogic::TestCase ApplicationController.send(:public, :current_user, :current_user_session) Before do activate_authlogic end |
I was trying awfully hard to abstract the session creation and login code by hooking into the same sort of code I use in my controllers–mostly because I suspect that the tests and features are going to start adding up. If I can cut out the cost of calling the stack to login through a webrat request, I think it’s probably worth it in the long run, especially since that way I would only need one feature to test the login form. Then, I would have the choice in my scenario of logging in through a web request, or just setting up a login and then going straight to the real test. Alas, I was denied this simple pleasure.
Most of my trouble with testing CanCan is in figuring out when the issue is with CanCan and when it’s somewhere else. So far, the issues have all be elsewhere. There were a few stumbling blocks that have taken time to work through, however.
I’m only allowing non-admin users to edit their own user record. In my UsersController, I have this:
1 | unauthorized! if cannot? :manage, @user |
As per the CanCan documentation, I have a rescue_from in my ApplicationController so that I can do something with the exception rather than throw a giant error in front of the user.
1 2 3 4 5 6 | @allow-rescue Scenario: edit user fails when other random user When I am logged in as 'user' with password 'testpass' And I go to the edit user page for 'user2' Then I should be on the home page And I should see "Access denied" |
It took me a couple hours, I admit, before I bothered to check the Cucumber documentation and notice the @allow-rescue tag. You can set this as a system-wide variable in env.rb–that I knew. Being able to tag individual scenarios this way allows you to specify exactly which tests allow Rails to rescue_from exceptions. Without this, you see the exception, rather than the rescue.
My next issue was with my login controller. If my user has the right access, then upon login they should be redirected to an admin controller, rather than going back to the home page. Pretty standard:
1 2 3 4 5 6 7 8 9 10 11 | class UserSessionsController < ApplicationController def create @user_session = UserSession.new(params[:user_session]) if @user_session.save flash[:notice] = "Logged in" redirect_back_or_default can?(:update, Page) ? admin_path : root_url else render :action => :new end end end |
I couldn’t understand why this was not redirecting to the right place. I was logging in with an admin user, but kept being redirected to the root.
I’m still not sure why this is happening, but at a certain point I realized that it’s an issue with my Authlogic helpers. CanCan assumes a method called current_user available to the controller. This should be lazy loading the user based on the current session, which should be lazy loaded using UserSession.find. All pretty standard. For some reason, however, current_user is coming up nil right when the session is logged in.
My solution right now is this:
1 2 3 4 5 | private def current_ability Ability.new(current_user || @user_session.try(:record)) end |
This will probably be refactored when I know more of what’s going on, but it works for now.
I would appreciate more visual materials, to make your blog more attractive, but your writing style really compensates it. But there is always place for improvement