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