diff --git a/ee/app/services/ee/members/approve_access_request_service.rb b/ee/app/services/ee/members/approve_access_request_service.rb index f9335b946d2c9ff4a2846c0fa6d609bb603f5ef0..c38d3d6410bd02827373b08e923519cc576c7d4e 100644 --- a/ee/app/services/ee/members/approve_access_request_service.rb +++ b/ee/app/services/ee/members/approve_access_request_service.rb @@ -12,11 +12,21 @@ def after_execute(member:, skip_log_audit_event: false) private def log_audit_event(member:) - ::AuditEventService.new( - current_user, - member.source, - action: :create - ).for_member(member).security_event + audit_context = { + name: 'member_created', + author: current_user || ::Gitlab::Audit::UnauthenticatedAuthor.new(name: '(System)'), + scope: member.source, + target: member.user || ::Gitlab::Audit::NullTarget.new, + target_details: member.user ? member.user.name : 'Deleted User', + message: 'Membership created', + additional_details: { + add: 'user_access', + as: ::Gitlab::Access.options_with_owner.key(member.access_level.to_i), + member_id: member.id + } + } + + ::Gitlab::Audit::Auditor.audit(audit_context) end end end diff --git a/ee/app/services/ee/members/create_service.rb b/ee/app/services/ee/members/create_service.rb index fb209e6c3526a6954c8d7a228c80cf1c6b8f4e2c..c2188329f39c4c0929d740ad89fcd3c9a973b707 100644 --- a/ee/app/services/ee/members/create_service.rb +++ b/ee/app/services/ee/members/create_service.rb @@ -44,11 +44,21 @@ def after_execute(member:) end def log_audit_event(member:) - ::AuditEventService.new( - current_user, - member.source, - action: :create - ).for_member(member).security_event + audit_context = { + name: 'member_created', + author: current_user || ::Gitlab::Audit::UnauthenticatedAuthor.new(name: '(System)'), + scope: member.source, + target: member.user || ::Gitlab::Audit::NullTarget.new, + target_details: member.user ? member.user.name : 'Deleted User', + message: 'Membership created', + additional_details: { + add: 'user_access', + as: ::Gitlab::Access.options_with_owner.key(member.access_level.to_i), + member_id: member.id + } + } + + ::Gitlab::Audit::Auditor.audit(audit_context) end end end diff --git a/ee/app/services/ee/members/destroy_service.rb b/ee/app/services/ee/members/destroy_service.rb index b58629f31546ee8b116cd8f7498d419bc1ab6e5c..ff3c41f2791ed7ad99dd1fb331be829236c7449a 100644 --- a/ee/app/services/ee/members/destroy_service.rb +++ b/ee/app/services/ee/members/destroy_service.rb @@ -29,11 +29,35 @@ def system_event? end def log_audit_event(member:, author:, action:) - ::AuditEventService.new( - author, - member.source, - action: action - ).for_member(member).security_event + audit_context = { + name: 'member_destroyed', + scope: member.source, + target: member.user || ::Gitlab::Audit::NullTarget.new, + target_details: member.user ? member.user.name : 'Deleted User', + additional_details: { + remove: "user_access", + member_id: member.id + } + } + + case action + when :destroy + audit_context.update( + author: author, + message: 'Membership destroyed' + ) + when :expired + audit_context.update( + author: ::Gitlab::Audit::UnauthenticatedAuthor.new(name: '(System)'), + message: "Membership expired on #{member.expires_at}" + ) + audit_context[:additional_details].update( + system_event: true, + reason: "access expired on #{member.expires_at}" + ) + end + + ::Gitlab::Audit::Auditor.audit(audit_context) end def cleanup_group_identity(member) diff --git a/ee/app/services/ee/members/update_service.rb b/ee/app/services/ee/members/update_service.rb index 96096f5466f2ebc71b94e44c369da00793b37ea1..ea9d5dcbafa613616f1bce8df1db4745f17feb5d 100644 --- a/ee/app/services/ee/members/update_service.rb +++ b/ee/app/services/ee/members/update_service.rb @@ -8,7 +8,7 @@ module UpdateService def after_execute(action:, old_access_level:, old_expiry:, member:) super - log_audit_event(action: action, old_access_level: old_access_level, old_expiry: old_expiry, member: member) + log_audit_event(old_access_level: old_access_level, old_expiry: old_expiry, member: member) end private @@ -27,14 +27,26 @@ def update_member(member, permission) super end - def log_audit_event(action:, old_access_level:, old_expiry:, member:) - ::AuditEventService.new( - current_user, - member.source, - action: action, - old_access_level: old_access_level, - old_expiry: old_expiry - ).for_member(member).security_event + def log_audit_event(old_access_level:, old_expiry:, member:) + audit_context = { + name: 'member_updated', + author: current_user || ::Gitlab::Audit::UnauthenticatedAuthor.new(name: '(System)'), + scope: member.source, + target: member.user || ::Gitlab::Audit::NullTarget.new, + target_details: member.user ? member.user.name : 'Deleted User', + message: 'Membership updated', + additional_details: { + change: 'access_level', + from: old_access_level, + to: member.human_access, + expiry_from: old_expiry, + expiry_to: member.expires_at, + as: ::Gitlab::Access.options_with_owner.key(member.access_level.to_i), + member_id: member.id + } + } + + ::Gitlab::Audit::Auditor.audit(audit_context) end end end diff --git a/ee/config/audit_events/types/member_created.yml b/ee/config/audit_events/types/member_created.yml new file mode 100644 index 0000000000000000000000000000000000000000..06132d317f0a8a3a06cdc9695f40ff8f708c9c1f --- /dev/null +++ b/ee/config/audit_events/types/member_created.yml @@ -0,0 +1,9 @@ +--- +name: member_created +description: Event triggered when a membership is created +introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/374112 +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/109711 +feature_category: compliance_management +milestone: '15.9' +saved_to_database: true +streamed: true diff --git a/ee/config/audit_events/types/member_destroyed.yml b/ee/config/audit_events/types/member_destroyed.yml new file mode 100644 index 0000000000000000000000000000000000000000..241ddf79af765d51edb8b44eafdaf3c3e9fa6b2e --- /dev/null +++ b/ee/config/audit_events/types/member_destroyed.yml @@ -0,0 +1,9 @@ +--- +name: member_destroyed +description: Event triggered when a membership is destroyed +introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/374112 +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/109711 +feature_category: compliance_management +milestone: '15.9' +saved_to_database: true +streamed: true diff --git a/ee/config/audit_events/types/member_updated.yml b/ee/config/audit_events/types/member_updated.yml new file mode 100644 index 0000000000000000000000000000000000000000..504475c538f3e86cb6cd34bb8bd72abc0915a599 --- /dev/null +++ b/ee/config/audit_events/types/member_updated.yml @@ -0,0 +1,9 @@ +--- +name: member_updated +description: Event triggered when a membership is updated +introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/374112 +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/109711 +feature_category: compliance_management +milestone: '15.9' +saved_to_database: true +streamed: true diff --git a/ee/spec/services/ee/members/approve_access_request_service_spec.rb b/ee/spec/services/ee/members/approve_access_request_service_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..d12fbb12454a141d7b6b6c2cc31ffd39ebfd2400 --- /dev/null +++ b/ee/spec/services/ee/members/approve_access_request_service_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Members::ApproveAccessRequestService, feature_category: :subgroups do + let_it_be(:project) { create(:project, :public) } + let_it_be(:group) { create(:group, :public) } + let_it_be(:current_user) { create(:user) } + let_it_be(:access_requester_user) { create(:user) } + let(:access_requester) { source.requesters.find_by!(user_id: access_requester_user.id) } + let(:opts) { {} } + let(:params) { {} } + let(:custom_access_level) { Gitlab::Access::MAINTAINER } + + shared_examples "auditor with context" do + it "creates audit event with name" do + expect(::Gitlab::Audit::Auditor).to receive(:audit).with( + hash_including(name: "member_created") + ).and_call_original + + described_class.new(current_user, params).execute(access_requester, **opts) + end + end + + context "with auditing" do + context "for project access" do + let(:source) { project } + + before do + project.add_maintainer(current_user) + project.request_access(access_requester_user) + end + + it_behaves_like "auditor with context" + end + + context "for group access" do + let(:source) { group } + + before do + group.add_owner(current_user) + group.request_access(access_requester_user) + end + + it_behaves_like "auditor with context" + end + end +end diff --git a/ee/spec/services/ee/members/create_service_spec.rb b/ee/spec/services/ee/members/create_service_spec.rb index d8067ac0523e2fe26bfd1d4112d5c5282c740e6f..42105976c66b528f881bcc519018a70bbaf1df5a 100644 --- a/ee/spec/services/ee/members/create_service_spec.rb +++ b/ee/spec/services/ee/members/create_service_spec.rb @@ -135,6 +135,14 @@ } end + it 'audits event with name' do + expect(::Gitlab::Audit::Auditor).to receive(:audit).with( + hash_including(name: "member_created") + ).and_call_original + + subject + end + include_examples 'sends streaming audit event' end diff --git a/ee/spec/services/ee/members/destroy_service_spec.rb b/ee/spec/services/ee/members/destroy_service_spec.rb index 0374d883fb8059d2bf2c99d3e003c1ff5c72535a..c080d56ff5f50d733a4869d6ba226f2f57b966bf 100644 --- a/ee/spec/services/ee/members/destroy_service_spec.rb +++ b/ee/spec/services/ee/members/destroy_service_spec.rb @@ -15,6 +15,10 @@ shared_examples_for 'logs an audit event' do specify do + expect(::Gitlab::Audit::Auditor).to receive(:audit).with( + hash_including(name: "member_destroyed") + ).and_call_original + expect { event }.to change { AuditEvent.count }.by(1) end end @@ -61,6 +65,14 @@ context 'streaming audit event' do subject { described_class.new(current_user).execute(member, skip_authorization: true) } + it 'audits event with name' do + expect(::Gitlab::Audit::Auditor).to receive(:audit).with( + hash_including(name: "member_destroyed") + ).and_call_original + + subject + end + include_examples 'sends streaming audit event' end diff --git a/ee/spec/services/ee/members/update_service_spec.rb b/ee/spec/services/ee/members/update_service_spec.rb index e3347f9b219e514eca3ebd57e37ddc1a03d8158b..fac5aea7ba5366ef1383ff3430bc5f559dd1a44b 100644 --- a/ee/spec/services/ee/members/update_service_spec.rb +++ b/ee/spec/services/ee/members/update_service_spec.rb @@ -20,6 +20,10 @@ shared_examples_for 'logs an audit event' do specify do + expect(::Gitlab::Audit::Auditor).to receive(:audit).with( + hash_including(name: "member_updated") + ).and_call_original + expect do described_class.new(current_user, params).execute(member, permission: permission) end.to change { AuditEvent.count }.by(1)