diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md index 9f00a46dbb635463f4178ec7ac3e5ba874402eb6..e8d19363242bba09b7263e7bc03806ad452f17dc 100644 --- a/doc/administration/audit_events.md +++ b/doc/administration/audit_events.md @@ -227,6 +227,7 @@ The following user actions on a GitLab instance generate instance audit events: - Removed SSH key ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220127) in GitLab 14.1) - Added or removed GPG key ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220127) in GitLab 14.1) - A user's two-factor authentication was disabled ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/238177) in GitLab 15.1) +- Enabled Admin Mode ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/362101) in GitLab 15.7) Instance events can also be accessed via the [Instance Audit Events API](../api/audit_events.md#instance-audit-events). diff --git a/ee/config/audit_events/types/user_enable_admin_mode.yml b/ee/config/audit_events/types/user_enable_admin_mode.yml new file mode 100644 index 0000000000000000000000000000000000000000..73a595f2d18b25864906779833389055db8eb5b9 --- /dev/null +++ b/ee/config/audit_events/types/user_enable_admin_mode.yml @@ -0,0 +1,9 @@ +--- +name: user_enable_admin_mode +description: Event triggered on enabling admin mode +introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/362101 +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104754 +group: "group::authentication and authorization" +milestone: "15.7" +saved_to_database: true +streamed: true diff --git a/ee/lib/ee/gitlab/auth/current_user_mode.rb b/ee/lib/ee/gitlab/auth/current_user_mode.rb new file mode 100644 index 0000000000000000000000000000000000000000..a04bdfb9e0a5f849bb7c319cf79286ef11805b9d --- /dev/null +++ b/ee/lib/ee/gitlab/auth/current_user_mode.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module EE + module Gitlab + module Auth + module CurrentUserMode + extend ::Gitlab::Utils::Override + + private + + override :audit_user_enable_admin_mode + def audit_user_enable_admin_mode + audit_context = { + name: 'user_enable_admin_mode', + author: user, + scope: user, + target: user, + message: 'Enabled admin mode', + created_at: DateTime.current + } + + ::Gitlab::Audit::Auditor.audit(audit_context) + end + end + end + end +end diff --git a/ee/spec/lib/ee/gitlab/auth/current_user_mode_spec.rb b/ee/spec/lib/ee/gitlab/auth/current_user_mode_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..cdd49e4a2ab9a395f38c7e9192f63c81f1f65eec --- /dev/null +++ b/ee/spec/lib/ee/gitlab/auth/current_user_mode_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Auth::CurrentUserMode, :request_store do + let_it_be(:user) { create(:user, :admin) } + + 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 + + describe '#enable_admin_mode!' do + before do + stub_licensed_features(extended_audit_events: true) + end + + context 'when enabling admin mode succeeds' do + it 'creates an audit event', :aggregate_failures do + subject.request_admin_mode! + + expect do + subject.enable_admin_mode!(password: user.password) + end.to change { AuditEvent.count }.by(1) + + expect(AuditEvent.last).to have_attributes( + author: user, + entity: user, + target_id: user.id, + target_type: user.class.name, + target_details: user.name, + details: include(custom_message: 'Enabled admin mode') + ) + end + end + + context 'when enabling admin mode fails' do + it 'does not create an audit event' do + subject.request_admin_mode! + + expect do + subject.enable_admin_mode!(password: 'wrong password') + end.not_to change { AuditEvent.count } + end + end + end + end +end diff --git a/lib/gitlab/auth/current_user_mode.rb b/lib/gitlab/auth/current_user_mode.rb index fc391543f4d44e91f1be2faf030e8995f5ef67e5..9bd4711c4bbf16bf3c5993d76e90d07160b01c42 100644 --- a/lib/gitlab/auth/current_user_mode.rb +++ b/lib/gitlab/auth/current_user_mode.rb @@ -106,8 +106,8 @@ def admin_mode_requested? end def enable_admin_mode!(password: nil, skip_password_validation: false) - return unless user&.admin? - return unless skip_password_validation || user&.valid_password?(password) + return false unless user&.admin? + return false unless skip_password_validation || user&.valid_password?(password) raise NotRequestedError unless admin_mode_requested? @@ -115,6 +115,10 @@ def enable_admin_mode!(password: nil, skip_password_validation: false) current_session_data[ADMIN_MODE_REQUESTED_TIME_KEY] = nil current_session_data[ADMIN_MODE_START_TIME_KEY] = Time.now + + audit_user_enable_admin_mode + + true end def disable_admin_mode! @@ -175,6 +179,10 @@ def reset_request_store_cache_entries def privileged_runtime? Gitlab::Runtime.rake? || Gitlab::Runtime.rails_runner? || Gitlab::Runtime.console? end + + def audit_user_enable_admin_mode; end end end end + +Gitlab::Auth::CurrentUserMode.prepend_mod_with('Gitlab::Auth::CurrentUserMode') diff --git a/spec/lib/gitlab/auth/current_user_mode_spec.rb b/spec/lib/gitlab/auth/current_user_mode_spec.rb index a21f0931b7880802a03832234f0139b5497a1d95..0a68a4a0ae23ab08ea1ef3db3b9d0a5a8dac7abb 100644 --- a/spec/lib/gitlab/auth/current_user_mode_spec.rb +++ b/spec/lib/gitlab/auth/current_user_mode_spec.rb @@ -194,10 +194,41 @@ 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