refactor login as to be impersonation with better login/logout

Modifies the existing "login as" feature to be called impersonation, as
well as keeping track of who is impersonating to revert back to that
user without having to log out.
This commit is contained in:
James Newton 2015-10-28 16:39:23 +01:00
parent 98cc695afb
commit 3bb626f91c
9 changed files with 88 additions and 35 deletions

View file

@ -118,6 +118,10 @@ header {
}
}
}
.impersonation i {
color: $red-normal;
}
}
@mixin collapsed-header {

View file

@ -8,4 +8,10 @@ class Admin::ApplicationController < ApplicationController
def authenticate_admin!
return render_404 unless current_user.is_admin?
end
def authorize_impersonator!
if session[:impersonator_id]
User.find_by!(username: session[:impersonator_id]).admin?
end
end
end

View file

@ -0,0 +1,32 @@
class Admin::ImpersonationController < Admin::ApplicationController
skip_before_action :authenticate_admin!, only: :destroy
before_action :user
before_action :authorize_impersonator!
def create
session[:impersonator_id] = current_user.username
session[:impersonator_return_to] = request.env['HTTP_REFERER']
warden.set_user(user, scope: 'user')
flash[:alert] = "You are impersonating #{user.username}."
redirect_to root_path
end
def destroy
redirect = session[:impersonator_return_to]
warden.set_user(user, scope: 'user')
session[:impersonator_return_to] = nil
session[:impersonator_id] = nil
redirect_to redirect || root_path
end
def user
@user ||= User.find_by!(username: params[:id] || session[:impersonator_id])
end
end

View file

@ -63,12 +63,6 @@ class Admin::UsersController < Admin::ApplicationController
end
end
def login_as
sign_in(user)
flash[:alert] = "Logged in as #{user.username}"
redirect_to root_path
end
def disable_two_factor
user.disable_two_factor!
redirect_to admin_user_path(user),

View file

@ -7,7 +7,7 @@
.pull-right
- unless @user == current_user
= link_to 'Log in as this user', login_as_admin_user_path(@user), method: :post, class: "btn btn-grouped btn-info"
= link_to 'Impersonate', impersonate_admin_user_path(@user), method: :post, class: "btn btn-grouped btn-info"
= link_to edit_admin_user_path(@user), class: "btn btn-grouped" do
%i.fa.fa-pencil-square-o
Edit

View file

@ -13,6 +13,10 @@
%li.visible-sm.visible-xs
= link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('search')
- if session[:impersonator_id]
%li.impersonation
= link_to stop_impersonation_admin_users_path, method: :delete, title: 'Stop impersonation', data: { toggle: 'tooltip', placement: 'bottom' } do
= icon('user-secret fw')
- if current_user.is_admin?
%li
= link_to admin_root_path, title: 'Admin area', data: {toggle: 'tooltip', placement: 'bottom'} do

View file

@ -212,6 +212,8 @@ Gitlab::Application.routes.draw do
resources :keys, only: [:show, :destroy]
resources :identities, only: [:index, :edit, :update, :destroy]
delete 'stop_impersonation' => 'impersonation#destroy', on: :collection
member do
get :projects
get :keys
@ -221,7 +223,7 @@ Gitlab::Application.routes.draw do
put :unblock
put :unlock
put :confirm
post :login_as
post 'impersonate' => 'impersonation#create'
patch :disable_two_factor
delete 'remove/:email_id', action: 'remove_email', as: 'remove_email'
end

View file

@ -7,21 +7,6 @@ describe Admin::UsersController do
sign_in(admin)
end
describe 'POST login_as' do
let(:user) { create(:user) }
it 'logs admin as another user' do
expect(warden.authenticate(scope: :user)).not_to eq(user)
post :login_as, id: user.username
expect(warden.authenticate(scope: :user)).to eq(user)
end
it 'redirects user to homepage' do
post :login_as, id: user.username
expect(response).to redirect_to(root_path)
end
end
describe 'DELETE #user with projects' do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }

View file

@ -111,24 +111,50 @@ describe "Admin::Users", feature: true do
expect(page).to have_content(@user.name)
end
describe 'Login as another user' do
it 'should show login button for other users and check that it works' do
another_user = create(:user)
describe 'Impersonation' do
let(:another_user) { create(:user) }
before { visit admin_user_path(another_user) }
visit admin_user_path(another_user)
context 'before impersonating' do
it 'shows impersonate button for other users' do
expect(page).to have_content('Impersonate')
end
click_link 'Log in as this user'
it 'should not show impersonate button for admin itself' do
visit admin_user_path(@user)
expect(page).to have_content("Logged in as #{another_user.username}")
page.within '.sidebar-user .username' do
expect(page).to have_content(another_user.username)
expect(page).not_to have_content('Impersonate')
end
end
it 'should not show login button for admin itself' do
visit admin_user_path(@user)
expect(page).not_to have_content('Log in as this user')
context 'when impersonating' do
before { click_link 'Impersonate' }
it 'logs in as the user when impersonate is clicked' do
page.within '.sidebar-user .username' do
expect(page).to have_content(another_user.username)
end
end
it 'sees impersonation log out icon' do
icon = first('.fa.fa-user-secret')
expect(icon).to_not eql nil
end
it 'can log out of impersonated user back to original user' do
find(:css, 'li.impersonation a').click
page.within '.sidebar-user .username' do
expect(page).to have_content(@user.username)
end
end
it 'is redirected back to the impersonated users page in the admin after stopping' do
find(:css, 'li.impersonation a').click
expect(current_path).to eql "/admin/users/#{another_user.username}"
end
end
end