summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Speicher <robert@gitlab.com>2016-04-25 20:50:38 +0000
committerRobert Speicher <robert@gitlab.com>2016-04-25 20:50:38 +0000
commit4f5eeb2a65895853e14afc90315751d06d9853b7 (patch)
tree88ca93b28287e90d3a31ab2395b0592ebb6861d7
parent7e47f8762e917d64501d074e23923b5584ed3b70 (diff)
parentc6c985bc690c88f2819787824a50b22cc86cacf4 (diff)
downloadgitlab-ce-4f5eeb2a65895853e14afc90315751d06d9853b7.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.rb8
-rw-r--r--app/controllers/admin/impersonation_controller.rb38
-rw-r--r--app/controllers/admin/impersonations_controller.rb24
-rw-r--r--app/controllers/admin/users_controller.rb16
-rw-r--r--app/views/layouts/header/_default.html.haml2
-rw-r--r--config/routes.rb6
-rw-r--r--spec/controllers/admin/impersonation_controller_spec.rb19
-rw-r--r--spec/controllers/admin/impersonations_controller_spec.rb95
-rw-r--r--spec/controllers/admin/users_controller_spec.rb49
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 9abf08d0e19..b8976fa09a9 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 3beb8ff7c0d..cde9e1b918b 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -15,7 +15,7 @@
- if current_user
- 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 2f820aafed1..9c6fb682fc9 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -212,8 +212,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
@@ -223,12 +221,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 :spam_logs, only: [:index, :destroy]
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 9ef8ba1b097..ce2a62ae1fd 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -2,9 +2,10 @@ require 'spec_helper'
describe Admin::UsersController do
let(:user) { create(:user) }
+ let(:admin) { create(:admin) }
before do
- sign_in(create(:admin))
+ sign_in(admin)
end
describe 'DELETE #user with projects' do
@@ -112,4 +113,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