Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I redirect a user's home (root) path based on their role using Devise?

I'm working on a project management app, and in the app, I have project_managers and clients. I'm using Devise and CanCan for authentication/authorization.

At what point after login should I be redirecting the user to their own specific controller/layout/views? Is there a way to check for current_user.role in routes.rb and set the root (or redirect) based on whether or not they're a project manager or a client? Is this a change I can make in Devise somewhere?

Thanks in advance for any help! --Mark

like image 311
Mark Avatar asked Jan 20 '11 23:01

Mark


5 Answers

Your routes.rb file won't have any idea what role the user has, so you won't be able to use it to assign specific root routes.

What you can do is set up a controller (for example, passthrough_controller.rb) which in turn can read the role and redirect. Something like this:

# passthrough_controller.rb
class PassthroughController < ApplicationController
  def index
    path = case current_user.role
      when 'project_manager'
        some_path
      when 'client'
        some_other_path
      else
        # If you want to raise an exception or have a default root for users without roles
    end

    redirect_to path     
  end
end

# routes.rb
root :to => 'passthrough#index'

This way, all users will have one point of entry, which in turn redirects them to the appropriate controller/action depending on their role.

like image 55
vonconrad Avatar answered Nov 17 '22 14:11

vonconrad


The simplest solution is to use lambda:

root :to => 'project_managers#index', :constraints => lambda { |request| request.env['warden'].user.role == 'project_manager' }
root :to => 'clients#index'
like image 22
collimarco Avatar answered Nov 17 '22 14:11

collimarco


Another option is to pass a proc to the authenticated method like this (I'm using rolify in this example):

authenticated :user, ->(u) { u.has_role?(:manager) } do
  root to: "managers#index", as: :manager_root
end

authenticated :user, ->(u) { u.has_role?(:employee) } do
  root to: "employees#index", as: :employee_root
end

root to: "landing_page#index"

Note that in Rails 4 you have to specify a unique name for each root route, see this issue for details.

like image 21
HargrimmTheBleak Avatar answered Nov 17 '22 14:11

HargrimmTheBleak


I do this in a Rails 3 app that uses Warden. Since Devise is built on top of Warden, I think it'll work for you, but be sure to experiment with it a bit before relying on it.

class ProjectManagerChecker
  def self.matches?(request)
    request.env['warden'].user.role == 'project_manager'
  end
end

# routes.rb
get  '/' => 'project_managers#index', :constraints => ProjectManagerChecker
get  '/' => 'clients#index'

If the user's role is "project_manager" the ProjectManagersController will be used - if it's not the ClientsController will be used. If they're not logged in at all, env['warden'].user will be nil and you'll get an error, so you'll probably want to work around that, but this will get you started.

like image 8
PreciousBodilyFluids Avatar answered Nov 17 '22 13:11

PreciousBodilyFluids


Blog post with the implementation http://minhajuddin.com/2011/10/24/how-to-change-the-rails-root-url-based-on-the-user-or-role/

like image 3
Khaja Minhajuddin Avatar answered Nov 17 '22 12:11

Khaja Minhajuddin