364 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			364 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| # frozen_string_literal: true
 | |
| 
 | |
| require 'spec_helper'
 | |
| 
 | |
| RSpec.describe Gitlab::Auth::CurrentUserMode, :request_store, feature_category: :system_access do
 | |
|   let(:user) { build_stubbed(:user) }
 | |
| 
 | |
|   subject { described_class.new(user) }
 | |
| 
 | |
|   context 'when session is available' do
 | |
|     include_context 'custom session'
 | |
| 
 | |
|     before do
 | |
|       allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session])
 | |
|     end
 | |
| 
 | |
|     shared_examples 'admin mode cannot be enabled' do
 | |
|       it 'is false by default' do
 | |
|         expect(subject.admin_mode?).to be(false)
 | |
|       end
 | |
| 
 | |
|       it 'cannot be enabled with a valid password' do
 | |
|         subject.enable_admin_mode!(password: user.password)
 | |
| 
 | |
|         expect(subject.admin_mode?).to be(false)
 | |
|       end
 | |
| 
 | |
|       it 'cannot be enabled with an invalid password' do
 | |
|         subject.enable_admin_mode!(password: nil)
 | |
| 
 | |
|         expect(subject.admin_mode?).to be(false)
 | |
|       end
 | |
| 
 | |
|       it 'cannot be enabled with empty params' do
 | |
|         subject.enable_admin_mode!
 | |
| 
 | |
|         expect(subject.admin_mode?).to be(false)
 | |
|       end
 | |
| 
 | |
|       it 'disable has no effect' do
 | |
|         subject.enable_admin_mode!
 | |
|         subject.disable_admin_mode!
 | |
| 
 | |
|         expect(subject.admin_mode?).to be(false)
 | |
|       end
 | |
| 
 | |
|       context 'skipping password validation' do
 | |
|         it 'cannot be enabled with a valid password' do
 | |
|           subject.enable_admin_mode!(password: user.password, skip_password_validation: true)
 | |
| 
 | |
|           expect(subject.admin_mode?).to be(false)
 | |
|         end
 | |
| 
 | |
|         it 'cannot be enabled with an invalid password' do
 | |
|           subject.enable_admin_mode!(skip_password_validation: true)
 | |
| 
 | |
|           expect(subject.admin_mode?).to be(false)
 | |
|         end
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     describe '#admin_mode?' do
 | |
|       context 'when the user is a regular user' do
 | |
|         it_behaves_like 'admin mode cannot be enabled'
 | |
| 
 | |
|         context 'bypassing session' do
 | |
|           it_behaves_like 'admin mode cannot be enabled' do
 | |
|             around do |example|
 | |
|               described_class.bypass_session!(user.id) { example.run }
 | |
|             end
 | |
|           end
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       context 'when the user is an admin' do
 | |
|         let(:user) { build_stubbed(:user, :admin) }
 | |
| 
 | |
|         context 'when admin mode not requested' do
 | |
|           it 'is false by default' do
 | |
|             expect(subject.admin_mode?).to be(false)
 | |
|           end
 | |
| 
 | |
|           it 'raises exception if we try to enable it' do
 | |
|             expect do
 | |
|               subject.enable_admin_mode!(password: user.password)
 | |
|             end.to raise_error(::Gitlab::Auth::CurrentUserMode::NotRequestedError)
 | |
| 
 | |
|             expect(subject.admin_mode?).to be(false)
 | |
|           end
 | |
|         end
 | |
| 
 | |
|         context 'when admin mode requested first' do
 | |
|           before do
 | |
|             subject.request_admin_mode!
 | |
|           end
 | |
| 
 | |
|           it 'is false by default' do
 | |
|             expect(subject.admin_mode?).to be(false)
 | |
|           end
 | |
| 
 | |
|           it 'cannot be enabled with an invalid password' do
 | |
|             subject.enable_admin_mode!(password: nil)
 | |
| 
 | |
|             expect(subject.admin_mode?).to be(false)
 | |
|           end
 | |
| 
 | |
|           it 'can be enabled with a valid password' do
 | |
|             subject.enable_admin_mode!(password: user.password)
 | |
| 
 | |
|             expect(subject.admin_mode?).to be(true)
 | |
|           end
 | |
| 
 | |
|           it 'can be disabled' do
 | |
|             subject.enable_admin_mode!(password: user.password)
 | |
|             subject.disable_admin_mode!
 | |
| 
 | |
|             expect(subject.admin_mode?).to be(false)
 | |
|           end
 | |
| 
 | |
|           it 'will expire in the future' do
 | |
|             subject.enable_admin_mode!(password: user.password)
 | |
|             expect(subject.admin_mode?).to be(true), 'admin mode is not active in the present'
 | |
| 
 | |
|             travel_to(Gitlab::Auth::CurrentUserMode::MAX_ADMIN_MODE_TIME.from_now) do
 | |
|               # in the future this will be a new request, simulate by clearing the RequestStore
 | |
|               Gitlab::SafeRequestStore.clear!
 | |
| 
 | |
|               expect(subject.admin_mode?).to be(false), 'admin mode did not expire in the future'
 | |
|             end
 | |
|           end
 | |
| 
 | |
|           context 'skipping password validation' do
 | |
|             it 'can be enabled with a valid password' do
 | |
|               subject.enable_admin_mode!(password: user.password, skip_password_validation: true)
 | |
| 
 | |
|               expect(subject.admin_mode?).to be(true)
 | |
|             end
 | |
| 
 | |
|             it 'can be enabled with an invalid password' do
 | |
|               subject.enable_admin_mode!(skip_password_validation: true)
 | |
| 
 | |
|               expect(subject.admin_mode?).to be(true)
 | |
|             end
 | |
|           end
 | |
| 
 | |
|           context 'with two independent sessions' do
 | |
|             let(:another_session) { {} }
 | |
|             let(:another_subject) { described_class.new(user) }
 | |
| 
 | |
|             before do
 | |
|               allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session, another_session])
 | |
|             end
 | |
| 
 | |
|             it 'cannot be enabled in one and seen in the other' do
 | |
|               Gitlab::Session.with_session(another_session) do
 | |
|                 another_subject.request_admin_mode!
 | |
|                 another_subject.enable_admin_mode!(password: user.password)
 | |
|               end
 | |
| 
 | |
|               expect(subject.admin_mode?).to be(false)
 | |
|             end
 | |
|           end
 | |
|         end
 | |
| 
 | |
|         context 'bypassing session' do
 | |
|           it 'is active by default' do
 | |
|             described_class.bypass_session!(user.id) do
 | |
|               expect(subject.admin_mode?).to be(true)
 | |
|             end
 | |
|           end
 | |
| 
 | |
|           it 'enable has no effect' do
 | |
|             described_class.bypass_session!(user.id) do
 | |
|               subject.request_admin_mode!
 | |
|               subject.enable_admin_mode!(password: user.password)
 | |
| 
 | |
|               expect(subject.admin_mode?).to be(true)
 | |
|             end
 | |
|           end
 | |
| 
 | |
|           it 'disable has no effect' do
 | |
|             described_class.bypass_session!(user.id) do
 | |
|               subject.disable_admin_mode!
 | |
| 
 | |
|               expect(subject.admin_mode?).to be(true)
 | |
|             end
 | |
|           end
 | |
|         end
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     describe '#enable_admin_mode!' do
 | |
|       let(:user) { build_stubbed(:user, :admin) }
 | |
| 
 | |
|       it 'creates a timestamp in the session' do
 | |
|         subject.request_admin_mode!
 | |
| 
 | |
|         subject.enable_admin_mode!(password: user.password)
 | |
| 
 | |
|         expect(session).to include(expected_session_entry(be_within(1.second).of(Time.now)))
 | |
|       end
 | |
| 
 | |
|       it 'returns true after successful enable' do
 | |
|         subject.request_admin_mode!
 | |
| 
 | |
|         expect(subject.enable_admin_mode!(password: user.password)).to eq(true)
 | |
|       end
 | |
| 
 | |
|       it 'returns false after unsuccessful enable' do
 | |
|         subject.request_admin_mode!
 | |
| 
 | |
|         expect(subject.enable_admin_mode!(password: 'wrong password')).to eq(false)
 | |
|       end
 | |
| 
 | |
|       context 'when user is not an admin' do
 | |
|         let(:user) { build_stubbed(:user) }
 | |
| 
 | |
|         it 'returns false' do
 | |
|           subject.request_admin_mode!
 | |
| 
 | |
|           expect(subject.enable_admin_mode!(password: user.password)).to eq(false)
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       context 'when admin mode is not requested' do
 | |
|         it 'raises error' do
 | |
|           expect do
 | |
|             subject.enable_admin_mode!(password: user.password)
 | |
|           end.to raise_error(Gitlab::Auth::CurrentUserMode::NotRequestedError)
 | |
|         end
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     describe '#disable_admin_mode!' do
 | |
|       let(:user) { build_stubbed(:user, :admin) }
 | |
| 
 | |
|       it 'sets the session timestamp to nil' do
 | |
|         subject.request_admin_mode!
 | |
|         subject.disable_admin_mode!
 | |
| 
 | |
|         expect(session).to include(expected_session_entry(be_nil))
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     describe '.with_current_request_admin_mode' do
 | |
|       context 'with a regular user' do
 | |
|         it 'user is not available inside nor outside the yielded block' do
 | |
|           described_class.with_current_admin(user) do
 | |
|             expect(described_class.current_admin).to be_nil
 | |
|           end
 | |
| 
 | |
|           expect(described_class.bypass_session_admin_id).to be_nil
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       context 'with an admin user' do
 | |
|         let(:user) { build_stubbed(:user, :admin) }
 | |
| 
 | |
|         context 'admin mode is disabled' do
 | |
|           it 'user is not available inside nor outside the yielded block' do
 | |
|             described_class.with_current_admin(user) do
 | |
|               expect(described_class.current_admin).to be_nil
 | |
|             end
 | |
| 
 | |
|             expect(described_class.bypass_session_admin_id).to be_nil
 | |
|           end
 | |
|         end
 | |
| 
 | |
|         context 'admin mode is enabled' do
 | |
|           before do
 | |
|             subject.request_admin_mode!
 | |
|             subject.enable_admin_mode!(password: user.password)
 | |
|           end
 | |
| 
 | |
|           it 'user is available only inside the yielded block' do
 | |
|             described_class.with_current_admin(user) do
 | |
|               expect(described_class.current_admin).to be(user)
 | |
|             end
 | |
| 
 | |
|             expect(described_class.current_admin).to be_nil
 | |
|           end
 | |
|         end
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     def expected_session_entry(value_matcher)
 | |
|       {
 | |
|         Gitlab::Auth::CurrentUserMode::SESSION_STORE_KEY => a_hash_including(
 | |
|           Gitlab::Auth::CurrentUserMode::ADMIN_MODE_START_TIME_KEY => value_matcher)
 | |
|       }
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   context 'when no session available' do
 | |
|     around do |example|
 | |
|       Gitlab::Session.with_session(nil) do
 | |
|         example.run
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     describe '.bypass_session!' do
 | |
|       context 'when providing a block' do
 | |
|         context 'with a regular user' do
 | |
|           it 'admin mode is false' do
 | |
|             described_class.bypass_session!(user.id) do
 | |
|               expect(Gitlab::Session.current).to be_nil
 | |
|               expect(subject.admin_mode?).to be(false)
 | |
|               expect(described_class.bypass_session_admin_id).to be(user.id)
 | |
|             end
 | |
| 
 | |
|             expect(described_class.bypass_session_admin_id).to be_nil
 | |
|           end
 | |
|         end
 | |
| 
 | |
|         context 'with an admin user' do
 | |
|           let(:user) { build_stubbed(:user, :admin) }
 | |
| 
 | |
|           it 'admin mode is true' do
 | |
|             described_class.bypass_session!(user.id) do
 | |
|               expect(Gitlab::Session.current).to be_nil
 | |
|               expect(subject.admin_mode?).to be(true)
 | |
|               expect(described_class.bypass_session_admin_id).to be(user.id)
 | |
|             end
 | |
| 
 | |
|             expect(described_class.bypass_session_admin_id).to be_nil
 | |
|           end
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       context 'when not providing a block' do
 | |
|         context 'with a regular user' do
 | |
|           it 'admin mode is false' do
 | |
|             described_class.bypass_session!(user.id)
 | |
| 
 | |
|             expect(Gitlab::Session.current).to be_nil
 | |
|             expect(subject.admin_mode?).to be(false)
 | |
|             expect(described_class.bypass_session_admin_id).to be(user.id)
 | |
| 
 | |
|             described_class.reset_bypass_session!
 | |
| 
 | |
|             expect(described_class.bypass_session_admin_id).to be_nil
 | |
|           end
 | |
|         end
 | |
| 
 | |
|         context 'with an admin user' do
 | |
|           let(:user) { build_stubbed(:user, :admin) }
 | |
| 
 | |
|           it 'admin mode is true' do
 | |
|             described_class.bypass_session!(user.id)
 | |
| 
 | |
|             expect(Gitlab::Session.current).to be_nil
 | |
|             expect(subject.admin_mode?).to be(true)
 | |
|             expect(described_class.bypass_session_admin_id).to be(user.id)
 | |
| 
 | |
|             described_class.reset_bypass_session!
 | |
| 
 | |
|             expect(described_class.bypass_session_admin_id).to be_nil
 | |
|           end
 | |
|         end
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 |