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:
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.
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:
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.
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:
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.

CSS Based on Twitters Bootstrap

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




