Commit 3bb626f9 authored by James Newton's avatar James Newton

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.
parent 98cc695a
...@@ -118,6 +118,10 @@ header { ...@@ -118,6 +118,10 @@ header {
} }
} }
} }
.impersonation i {
color: $red-normal;
}
} }
@mixin collapsed-header { @mixin collapsed-header {
......
...@@ -8,4 +8,10 @@ class Admin::ApplicationController < ApplicationController ...@@ -8,4 +8,10 @@ class Admin::ApplicationController < ApplicationController
def authenticate_admin! def authenticate_admin!
return render_404 unless current_user.is_admin? return render_404 unless current_user.is_admin?
end end
def authorize_impersonator!
if session[:impersonator_id]
User.find_by!(username: session[:impersonator_id]).admin?
end
end
end end
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
...@@ -63,12 +63,6 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -63,12 +63,6 @@ class Admin::UsersController < Admin::ApplicationController
end end
end end
def login_as
sign_in(user)
flash[:alert] = "Logged in as #{user.username}"
redirect_to root_path
end
def disable_two_factor def disable_two_factor
user.disable_two_factor! user.disable_two_factor!
redirect_to admin_user_path(user), redirect_to admin_user_path(user),
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
.pull-right .pull-right
- unless @user == current_user - 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 = link_to edit_admin_user_path(@user), class: "btn btn-grouped" do
%i.fa.fa-pencil-square-o %i.fa.fa-pencil-square-o
Edit Edit
......
...@@ -13,6 +13,10 @@ ...@@ -13,6 +13,10 @@
%li.visible-sm.visible-xs %li.visible-sm.visible-xs
= link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom'} do = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('search') = 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? - if current_user.is_admin?
%li %li
= link_to admin_root_path, title: 'Admin area', data: {toggle: 'tooltip', placement: 'bottom'} do = link_to admin_root_path, title: 'Admin area', data: {toggle: 'tooltip', placement: 'bottom'} do
......
...@@ -212,6 +212,8 @@ Gitlab::Application.routes.draw do ...@@ -212,6 +212,8 @@ Gitlab::Application.routes.draw do
resources :keys, only: [:show, :destroy] resources :keys, only: [:show, :destroy]
resources :identities, only: [:index, :edit, :update, :destroy] resources :identities, only: [:index, :edit, :update, :destroy]
delete 'stop_impersonation' => 'impersonation#destroy', on: :collection
member do member do
get :projects get :projects
get :keys get :keys
...@@ -221,7 +223,7 @@ Gitlab::Application.routes.draw do ...@@ -221,7 +223,7 @@ Gitlab::Application.routes.draw do
put :unblock put :unblock
put :unlock put :unlock
put :confirm put :confirm
post :login_as post 'impersonate' => 'impersonation#create'
patch :disable_two_factor patch :disable_two_factor
delete 'remove/:email_id', action: 'remove_email', as: 'remove_email' delete 'remove/:email_id', action: 'remove_email', as: 'remove_email'
end end
......
...@@ -7,21 +7,6 @@ describe Admin::UsersController do ...@@ -7,21 +7,6 @@ describe Admin::UsersController do
sign_in(admin) sign_in(admin)
end 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 describe 'DELETE #user with projects' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) } let(:project) { create(:project, namespace: user.namespace) }
......
...@@ -111,24 +111,50 @@ describe "Admin::Users", feature: true do ...@@ -111,24 +111,50 @@ describe "Admin::Users", feature: true do
expect(page).to have_content(@user.name) expect(page).to have_content(@user.name)
end end
describe 'Login as another user' do describe 'Impersonation' do
it 'should show login button for other users and check that it works' do let(:another_user) { create(:user) }
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
it 'should not show impersonate button for admin itself' do
visit admin_user_path(@user)
click_link 'Log in as this user' expect(page).not_to have_content('Impersonate')
end
end
expect(page).to have_content("Logged in as #{another_user.username}") 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 page.within '.sidebar-user .username' do
expect(page).to have_content(another_user.username) expect(page).to have_content(another_user.username)
end end
end end
it 'should not show login button for admin itself' do it 'sees impersonation log out icon' do
visit admin_user_path(@user) icon = first('.fa.fa-user-secret')
expect(page).not_to have_content('Log in as this user')
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
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment