diff options
author | Robert Speicher <robert@gitlab.com> | 2016-04-25 20:50:38 +0000 |
---|---|---|
committer | Robert Speicher <rspeicher@gmail.com> | 2016-04-25 16:58:52 -0400 |
commit | 806cc20803982dc1315a4b387c740e68b29f23bd (patch) | |
tree | 4dcf10928b433941bb05b3a57c00d81eb154247e | |
parent | 6233e8be5c39c0c700fbecc384745066dde3551e (diff) | |
download | gitlab-ce-806cc20803982dc1315a4b387c740e68b29f23bd.tar.gz |
Merge branch 'fix-impersonation-issue' into 'master'
Prevent privilege escalation via "impersonate" feature
Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/15548
See merge request !1956
-rw-r--r-- | app/controllers/admin/application_controller.rb | 8 | ||||
-rw-r--r-- | app/controllers/admin/impersonation_controller.rb | 38 | ||||
-rw-r--r-- | app/controllers/admin/impersonations_controller.rb | 24 | ||||
-rw-r--r-- | app/controllers/admin/users_controller.rb | 16 | ||||
-rw-r--r-- | app/views/layouts/header/_default.html.haml | 2 | ||||
-rw-r--r-- | config/routes.rb | 6 | ||||
-rw-r--r-- | spec/controllers/admin/impersonation_controller_spec.rb | 19 | ||||
-rw-r--r-- | spec/controllers/admin/impersonations_controller_spec.rb | 95 | ||||
-rw-r--r-- | spec/controllers/admin/users_controller_spec.rb | 49 |
9 files changed, 188 insertions, 69 deletions
diff --git a/app/controllers/admin/application_controller.rb b/app/controllers/admin/application_controller.rb index 9083bfb41cf..cf795d977ce 100644 --- a/app/controllers/admin/application_controller.rb +++ b/app/controllers/admin/application_controller.rb @@ -6,12 +6,6 @@ class Admin::ApplicationController < ApplicationController layout 'admin' 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 + render_404 unless current_user.is_admin? end end diff --git a/app/controllers/admin/impersonation_controller.rb b/app/controllers/admin/impersonation_controller.rb deleted file mode 100644 index bf98af78615..00000000000 --- a/app/controllers/admin/impersonation_controller.rb +++ /dev/null @@ -1,38 +0,0 @@ -class Admin::ImpersonationController < Admin::ApplicationController - skip_before_action :authenticate_admin!, only: :destroy - - before_action :user - before_action :authorize_impersonator! - - def create - if @user.blocked? - flash[:alert] = "You cannot impersonate a blocked user" - - redirect_to admin_user_path(@user) - else - session[:impersonator_id] = current_user.username - session[:impersonator_return_to] = admin_user_path(@user) - - warden.set_user(user, scope: 'user') - - flash[:alert] = "You are impersonating #{user.username}." - - redirect_to root_path - end - 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 diff --git a/app/controllers/admin/impersonations_controller.rb b/app/controllers/admin/impersonations_controller.rb new file mode 100644 index 00000000000..2db824c87ef --- /dev/null +++ b/app/controllers/admin/impersonations_controller.rb @@ -0,0 +1,24 @@ +class Admin::ImpersonationsController < Admin::ApplicationController + skip_before_action :authenticate_admin! + before_action :authenticate_impersonator! + + def destroy + original_user = current_user + + warden.set_user(impersonator, scope: :user) + + session[:impersonator_id] = nil + + redirect_to admin_user_path(original_user) + end + + private + + def impersonator + @impersonator ||= User.find(session[:impersonator_id]) if session[:impersonator_id] + end + + def authenticate_impersonator! + render_404 unless impersonator && impersonator.is_admin? && !impersonator.blocked? + end +end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index d7c927d444c..740d4dd5a8f 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -31,6 +31,22 @@ class Admin::UsersController < Admin::ApplicationController user end + def impersonate + if user.blocked? + flash[:alert] = "You cannot impersonate a blocked user" + + redirect_to admin_user_path(user) + else + session[:impersonator_id] = current_user.id + + warden.set_user(user, scope: :user) + + flash[:alert] = "You are now impersonating #{user.username}" + + redirect_to root_path + end + end + def block if user.block redirect_back_or_admin_user(notice: "Successfully blocked") diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 3892ef8eefa..5e9329ae333 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -15,7 +15,7 @@ = 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', container: 'body' } do + = link_to admin_impersonation_path, method: :delete, title: 'Stop Impersonation', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do = icon('user-secret fw') - if current_user.is_admin? %li diff --git a/config/routes.rb b/config/routes.rb index b9242327de1..226b3af85b1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -190,8 +190,6 @@ Rails.application.routes.draw do resources :keys, only: [:show, :destroy] resources :identities, except: [:show] - delete 'stop_impersonation' => 'impersonation#destroy', on: :collection - member do get :projects get :keys @@ -201,12 +199,14 @@ Rails.application.routes.draw do put :unblock put :unlock put :confirm - post 'impersonate' => 'impersonation#create' + post :impersonate patch :disable_two_factor delete 'remove/:email_id', action: 'remove_email', as: 'remove_email' end end + resource :impersonation, only: :destroy + resources :abuse_reports, only: [:index, :destroy] resources :applications diff --git a/spec/controllers/admin/impersonation_controller_spec.rb b/spec/controllers/admin/impersonation_controller_spec.rb deleted file mode 100644 index d7a7ba1c5b6..00000000000 --- a/spec/controllers/admin/impersonation_controller_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'spec_helper' - -describe Admin::ImpersonationController do - let(:admin) { create(:admin) } - - before do - sign_in(admin) - end - - describe 'CREATE #impersonation when blocked' do - let(:blocked_user) { create(:user, state: :blocked) } - - it 'does not allow impersonation' do - post :create, id: blocked_user.username - - expect(flash[:alert]).to eq 'You cannot impersonate a blocked user' - end - end -end diff --git a/spec/controllers/admin/impersonations_controller_spec.rb b/spec/controllers/admin/impersonations_controller_spec.rb new file mode 100644 index 00000000000..eb82476b179 --- /dev/null +++ b/spec/controllers/admin/impersonations_controller_spec.rb @@ -0,0 +1,95 @@ +require 'spec_helper' + +describe Admin::ImpersonationsController do + let(:impersonator) { create(:admin) } + let(:user) { create(:user) } + + describe "DELETE destroy" do + context "when not signed in" do + it "redirects to the sign in page" do + delete :destroy + + expect(response).to redirect_to(new_user_session_path) + end + end + + context "when signed in" do + before do + sign_in(user) + end + + context "when not impersonating" do + it "responds with status 404" do + delete :destroy + + expect(response.status).to eq(404) + end + + it "doesn't sign us in" do + delete :destroy + + expect(warden.user).to eq(user) + end + end + + context "when impersonating" do + before do + session[:impersonator_id] = impersonator.id + end + + context "when the impersonator is not admin (anymore)" do + before do + impersonator.admin = false + impersonator.save + end + + it "responds with status 404" do + delete :destroy + + expect(response.status).to eq(404) + end + + it "doesn't sign us in as the impersonator" do + delete :destroy + + expect(warden.user).to eq(user) + end + end + + context "when the impersonator is admin" do + context "when the impersonator is blocked" do + before do + impersonator.block! + end + + it "responds with status 404" do + delete :destroy + + expect(response.status).to eq(404) + end + + it "doesn't sign us in as the impersonator" do + delete :destroy + + expect(warden.user).to eq(user) + end + end + + context "when the impersonator is not blocked" do + it "redirects to the impersonated user's page" do + delete :destroy + + expect(response).to redirect_to(admin_user_path(user)) + end + + it "signs us in as the impersonator" do + delete :destroy + + expect(warden.user).to eq(impersonator) + end + end + end + end + end + end +end diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 8b7af4d3a0a..26e35929a6c 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -1,7 +1,8 @@ require 'spec_helper' describe Admin::UsersController do - let(:admin) { create(:admin) } + let(:user) { create(:user) } + let(:admin) { create(:admin) } before do sign_in(admin) @@ -104,4 +105,50 @@ describe Admin::UsersController do patch :disable_two_factor, id: user.to_param end end + + describe "POST impersonate" do + context "when the user is blocked" do + before do + user.block! + end + + it "shows a notice" do + post :impersonate, id: user.username + + expect(flash[:alert]).to eq("You cannot impersonate a blocked user") + end + + it "doesn't sign us in as the user" do + post :impersonate, id: user.username + + expect(warden.user).to eq(admin) + end + end + + context "when the user is not blocked" do + it "stores the impersonator in the session" do + post :impersonate, id: user.username + + expect(session[:impersonator_id]).to eq(admin.id) + end + + it "signs us in as the user" do + post :impersonate, id: user.username + + expect(warden.user).to eq(user) + end + + it "redirects to root" do + post :impersonate, id: user.username + + expect(response).to redirect_to(root_path) + end + + it "shows a notice" do + post :impersonate, id: user.username + + expect(flash[:alert]).to eq("You are now impersonating #{user.username}") + end + end + end end |