Desenvolva: troca rápida de usuário usando uma estratégia de autenticação personalizada

Recentemente, minha empresa precisava de uma maneira de permitir que usuários com um determinado Controle de Acesso façam login (ou representem) outro usuário.

Para poder acompanhar o usuário original, verificando se o usuário original tem acesso a determinado usuário que gostaria de personificar e registrando o usuário para personificação, criamos a seguinte estratégia para o 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