Devise: Fast User Switching using a custom Authentication Strategy
Recently, my company needed a way to allow for users with a certain Access Control to log in (or impersonate) another user.
To be able to keep up with the original user, checking to see if the original user has access to the given user they would like to impersonate and signing in the user for impersonation, we came up with the following strategy for Warden.
# lib/sign_in_as.rb
require 'devise/strategies/base'
module SignInAs
module RememberContributor
extend ActiveSupport::Concern
private
def remember_contributor_id
request.env['rack.session']['devise.remember_contributor_id']
end
def remember_contributor_id=(id)
request.env['rack.session']['devise.remember_contributor_id'] = id
end
def remember_contributor_id?
request.env['rack.session'] && request.env['rack.session']['devise.remember_contributor_id'].present?
end
def clear_remembered_contributor_id
request.env['rack.session']['devise.remember_contributor_id'] = nil
end
end
end
# config/initializers/sign_in_as.rb
require 'devise/strategies/authenticatable'
module Devise
module Strategies
class SignInAs < Authenticatable
include ::SignInAs::RememberContributor
def valid?
user = User.find_by_id(params[:id])
if user.athlete?
contributor_has_access? && (ability.can?(:create, user) || ability.can?(:update, user))
else
clear_remembered_contributor_id
true
end
end
def authenticate!
resource = User.find_by_id params[:id]
if resource
success!(resource)
else
fail!("You do not have sufficient access to this account")
end
end
private
def contributor_has_access?
contributor_user.school_admin? || contributor_user.athlete_contributor? || contributor_user.class.name.eql?("HighSchoolCoach")
end
def contributor_user
User.find(remember_contributor_id)
end
def ability
@ability ||= "::Abilities::#{contributor_user.class.name}Ability".constantize.new(contributor_user)
end
end
end
end
Warden::Strategies.add(:sign_in_as, Devise::Strategies::SignInAs)
class SignInAsController < ApplicationController
before_filter :authenticate_user!
include SignInAs::RememberContributor
def create
# Let's remember the contributor ID for use in the Warden::Strategy
self.remember_contributor_id = original_user.try(:id) || current_user.id
# Sign out current user
sign_out(current_user)
# If original_user and original_user ID eql params[:id].to_i, log original_user back in
if original_user && original_user.id == params[:id].to_i
sign_in(:user, original_user)
redirect_to user_root_path
else
# Else pass off request to custom Warden::Strategy
handle_request
end
end
private
def handle_request
# IF Warden autheticates using Devise::Strategies::SignInAs, redirect them to the correct path
if env['warden'].authenticate(:sign_in_as)
redirect_to user_root_path
else
# ELSE sign the contributor back into their account, and tell them they no have rights, they go home
sign_in(:user, User.find(remember_contributor_id))
redirect_to user_root_path, notice: "You do not have sufficient rights"
end
end
end
Written by dennis monsewicz
Related protips
Have a fresh tip? Share with Coderwall community!
Post
Post a tip
Best
#Rails
Authors
Sponsored by #native_company# — Learn More
#native_title#
#native_desc#