21
June
2011

Controlling access to Resque with CanCan

Tagged: CanCan Resque Rails
Comments
Facebook_share Twitter_share Reddit_share
This weeks Railscast was on setting up and using Resque for you rails application and whilst the screen cast was great theres a bit of an extension I wanted.

In the screencast ryanb demonstrates how to use Devise and HTTP basic to restrict access to the Resque web interface. Obviously HTTP basic only allows for one user and Devise will only be checking if the user is logged in, so it makes a lot more sense to get your authorization system to check if the user can access Resque. I'm using CanCan on my application so thats what I'm going to demonstrate here.

You are limited to only being able to run the checks within the rails router as Resque is mounted so runs outside your application. Fortunately the rails router allows you to write constraints which can prevent a block from being executed if certain conditions aren't met. So the first step is to wrap your Resque mount point inside a constraint like this:


1constraints(CanAccessRoute) do
2 mount Resque::Server, :at => "/resque"
3end




This will run CanAccessRoute.matches? for your request and then create the Resque routes if that returns true, if it returns false Resque wont be mounted at all and I found that I got redirect loops. To fix this I needed to add these two routes below the constraints block so that unauthroized requests to Resque got sent back to the login page.


1match "/resque", :to => redirect { |_, request| "http://" + request.host }
2match "/resque/*path", :to => redirect { |_, request| "http://" + request.host }




You now need to write that CanAccessRoute class which needs to be placed in config/initializers/can_access_route.rb and look something like this:


1class CanAccessRoute  
2 def self.matches?(request)
3 current_user = User.find(request.env["rack.session"]["user_id"]) if request.env["rack.session"]["user_id"]
4 current_ability = Ability.new(current_user)
5 current_ability.can? :route, :resque
6 end
7end




The contents of the matches? method may need changing as this will only work if you store the users id in a session variable of user_id (set in a controller using session[:user_id]) but all you should need to do is change the current_user definition to match that of your ApplicationController. current_ability is defined in the same way as it is in CanCan when checking an ability and the Ability class is where you defined all your abilities (usually in your app/models folder). The check done is the same as if your did can? :route, :resque in a view or controller except inside CanAccessRoute you don't have access to the handy can? helper method.

To give users access to Resque simply add can :route, :resque to your abilities class like you would for any controller or action.
blog comments powered by Disqus
Html5-badge-h-css3-graphics-semantics
CSS Based on Twitters Bootstrap

88x31
All Content is written by Adam Laycock (Arcath) unless otherwise stated.

A little bit About Me

I am an IT Technician working in Primary Schools, I spend my time outside work writing programs, normally they end up bieng bots and other web based dodads