From 814b883466e47d6dbfa74639941ac771c5714c16 Mon Sep 17 00:00:00 2001 From: Hitesh Raghuvanshi Date: Thu, 14 Sep 2023 15:32:07 +0530 Subject: [PATCH 1/5] Added delete api for instance gcp config Added deletion api for instance level gcp config for streaming audit events Changelog: added EE: true --- .../audit_event_types.md | 1 + doc/api/graphql/reference/index.md | 24 +++++ ee/app/graphql/ee/types/mutation_type.rb | 1 + .../base.rb | 44 +++++++++ .../destroy.rb | 25 +++++ ...le_cloud_logging_configuration_deleted.yml | 9 ++ .../destroy_spec.rb | 96 +++++++++++++++++++ 7 files changed, 200 insertions(+) create mode 100644 ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/base.rb create mode 100644 ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy.rb create mode 100644 ee/config/audit_events/types/instance_google_cloud_logging_configuration_deleted.yml create mode 100644 ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy_spec.rb diff --git a/doc/administration/audit_event_streaming/audit_event_types.md b/doc/administration/audit_event_streaming/audit_event_types.md index 01523e173546ba..d448fcfb60e1d3 100644 --- a/doc/administration/audit_event_streaming/audit_event_types.md +++ b/doc/administration/audit_event_streaming/audit_event_types.md @@ -149,6 +149,7 @@ Every audit event is associated with an event type. The association with the eve | [`incident_closed_by_project_bot`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121485) | Triggered when an incident is closed using a project access token | **{check-circle}** Yes | **{check-circle}** Yes | `incident_management` | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/323299) | | [`incident_created_by_project_bot`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121485) | Triggered when an incident is created using a project access token | **{check-circle}** Yes | **{check-circle}** Yes | `incident_management` | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/323299) | | [`incident_reopened_by_project_bot`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121485) | Triggered when an incident is reopened using a project access token | **{check-circle}** Yes | **{check-circle}** Yes | `incident_management` | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/323299) | +| [`instance_google_cloud_logging_configuration_deleted`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/130663) | Triggered when instance level Google Cloud Logging configuration is deleted. | **{check-circle}** Yes | **{check-circle}** Yes | `audit_events` | GitLab [16.5](https://gitlab.com/gitlab-org/gitlab/-/issues/423040) | | [`ip_restrictions_changed`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86037) | Event triggered on any changes in the IP AllowList | **{check-circle}** Yes | **{check-circle}** Yes | `system_access` | GitLab [15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/358986) | | [`issue_closed_by_project_bot`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121485) | Triggered when an issue is closed using a project access token | **{check-circle}** Yes | **{check-circle}** Yes | `team_planning` | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/323299) | | [`issue_created_by_project_bot`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121485) | Triggered when an issue is created using a project access token | **{check-circle}** Yes | **{check-circle}** Yes | `team_planning` | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/323299) | diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index efaf2216a712cf..fa82748a4723a7 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -4085,6 +4085,24 @@ Input type: `InstanceExternalAuditEventDestinationUpdateInput` | `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | `instanceExternalAuditEventDestination` | [`InstanceExternalAuditEventDestination`](#instanceexternalauditeventdestination) | Updated destination. | +### `Mutation.instanceGoogleCloudLoggingConfigurationDestroy` + +Input type: `InstanceGoogleCloudLoggingConfigurationDestroyInput` + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| `id` | [`AuditEventsInstanceGoogleCloudLoggingConfigurationID!`](#auditeventsinstancegooglecloudloggingconfigurationid) | ID of the Google Cloud logging configuration to destroy. | + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | + ### `Mutation.issuableResourceLinkCreate` Input type: `IssuableResourceLinkCreateInput` @@ -29176,6 +29194,12 @@ A `AuditEventsInstanceExternalAuditEventDestinationID` is a global ID. It is enc An example `AuditEventsInstanceExternalAuditEventDestinationID` is: `"gid://gitlab/AuditEvents::InstanceExternalAuditEventDestination/1"`. +### `AuditEventsInstanceGoogleCloudLoggingConfigurationID` + +A `AuditEventsInstanceGoogleCloudLoggingConfigurationID` is a global ID. It is encoded as a string. + +An example `AuditEventsInstanceGoogleCloudLoggingConfigurationID` is: `"gid://gitlab/AuditEvents::Instance::GoogleCloudLoggingConfiguration/1"`. + ### `AuditEventsStreamingHeaderID` A `AuditEventsStreamingHeaderID` is a global ID. It is encoded as a string. diff --git a/ee/app/graphql/ee/types/mutation_type.rb b/ee/app/graphql/ee/types/mutation_type.rb index b80d0ea8eb6747..3790f1cea190d6 100644 --- a/ee/app/graphql/ee/types/mutation_type.rb +++ b/ee/app/graphql/ee/types/mutation_type.rb @@ -128,6 +128,7 @@ module MutationType mount_mutation ::Mutations::AuditEvents::Streaming::InstanceHeaders::Destroy mount_mutation ::Mutations::AuditEvents::Streaming::InstanceEventTypeFilters::Create mount_mutation ::Mutations::AuditEvents::Streaming::InstanceEventTypeFilters::Destroy + mount_mutation ::Mutations::AuditEvents::Instance::GoogleCloudLoggingConfigurations::Destroy prepend(Types::DeprecatedMutations) end diff --git a/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/base.rb b/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/base.rb new file mode 100644 index 00000000000000..1b10b196f5202d --- /dev/null +++ b/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/base.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Mutations + module AuditEvents + module Instance + module GoogleCloudLoggingConfigurations + class Base < BaseMutation + authorize :admin_instance_external_audit_events + + def ready?(**args) + raise_resource_not_available_error! unless current_user&.can?(:admin_instance_external_audit_events) + + super + end + + private + + def find_object(config_gid) + destination = GitlabSchema.object_from_id( + config_gid, + expected_type: ::AuditEvents::Instance::GoogleCloudLoggingConfiguration).sync + + raise_resource_not_available_error! if destination.blank? + + destination + end + + def audit(config, action:) + audit_context = { + name: "instance_google_cloud_logging_configuration_#{action}", + author: current_user, + scope: Gitlab::Audit::InstanceScope.new, + target: config, + message: "#{action.capitalize} Instance Google Cloud logging configuration with name: #{config.name} " \ + "project id: #{config.google_project_id_name} and log id: #{config.log_id_name}" + } + + ::Gitlab::Audit::Auditor.audit(audit_context) + end + end + end + end + end +end diff --git a/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy.rb b/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy.rb new file mode 100644 index 00000000000000..b35687366e7934 --- /dev/null +++ b/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Mutations + module AuditEvents + module Instance + module GoogleCloudLoggingConfigurations + class Destroy < Base + graphql_name 'InstanceGoogleCloudLoggingConfigurationDestroy' + + argument :id, ::Types::GlobalIDType[::AuditEvents::Instance::GoogleCloudLoggingConfiguration], + required: true, + description: 'ID of the Google Cloud logging configuration to destroy.' + + def resolve(id:) + config = authorized_find!(id) + + audit(config, action: :deleted) if config.destroy + + { errors: Array(config.errors) } + end + end + end + end + end +end diff --git a/ee/config/audit_events/types/instance_google_cloud_logging_configuration_deleted.yml b/ee/config/audit_events/types/instance_google_cloud_logging_configuration_deleted.yml new file mode 100644 index 00000000000000..cf619458ab0bc2 --- /dev/null +++ b/ee/config/audit_events/types/instance_google_cloud_logging_configuration_deleted.yml @@ -0,0 +1,9 @@ +--- +name: instance_google_cloud_logging_configuration_deleted +description: Triggered when instance level Google Cloud Logging configuration is deleted. +introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/423040 +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/130663 +milestone: '16.5' +feature_category: audit_events +saved_to_database: true +streamed: true diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy_spec.rb new file mode 100644 index 00000000000000..4b2c0be2e39d93 --- /dev/null +++ b/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy_spec.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Destroy Instance Google Cloud logging configuration', feature_category: :audit_events do + include GraphqlHelpers + + let_it_be(:config) { create(:instance_google_cloud_logging_configuration) } + let_it_be(:admin) { create(:admin) } + + let(:current_user) { admin } + let(:mutation) { graphql_mutation(:instance_google_cloud_logging_configuration_destroy, id: global_id_of(config)) } + let(:mutation_response) { graphql_mutation_response(:instance_google_cloud_logging_configuration_destroy) } + + subject(:mutate) { post_graphql_mutation(mutation, current_user: current_user) } + + shared_examples 'a mutation that does not destroy a configuration' do + it_behaves_like 'a mutation that returns top-level errors', + errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR] + + it 'does not destroy the configuration' do + expect { mutate } + .not_to change { AuditEvents::Instance::GoogleCloudLoggingConfiguration.count } + end + + it 'does not create audit event' do + expect { mutate }.not_to change { AuditEvent.count } + end + end + + context 'when feature is licensed' do + before do + stub_licensed_features(external_audit_events: true) + end + + context 'when current user is instance admin' do + before do + allow(Gitlab::Audit::Auditor).to receive(:audit) + end + + it 'destroys the configuration' do + expect { mutate } + .to change { AuditEvents::Instance::GoogleCloudLoggingConfiguration.count }.by(-1) + end + + it 'audits the deletion' do + subject + + expect(Gitlab::Audit::Auditor).to have_received(:audit) do |args| + expect(args[:name]).to eq('instance_google_cloud_logging_configuration_destroyed') + expect(args[:author]).to eq(current_user) + expect(args[:scope]).to be_an_instance_of(Gitlab::Audit::InstanceScope) + expect(args[:target]).to eq(config) + expect(args[:message]).to eq("Deleted Instance Google Cloud logging configuration with name: #{config.name} " \ + "project id: #{config.google_project_id_name} and log id: #{config.log_id_name}") + end + end + + context 'when there is an error during destroy' do + before do + allow_next_instance_of(Mutations::AuditEvents::Instance::GoogleCloudLoggingConfigurations::Destroy) do |mutation| + allow(mutation).to receive(:authorized_find!).and_return(config) + end + + allow(config).to receive(:destroy).and_return(false) + + errors = ActiveModel::Errors.new(config).tap { |e| e.add(:base, 'error message') } + allow(config).to receive(:errors).and_return(errors) + end + + it 'does not destroy the configuration and returns the error' do + expect { mutate } + .not_to change { AuditEvents::Instance::GoogleCloudLoggingConfiguration.count } + + expect(mutation_response).to include( + 'errors' => ['error message'] + ) + end + end + end + + context 'when current user is not instance admin' do + let_it_be(:current_user) { user } + + it_behaves_like 'a mutation that does not destroy a configuration' + end + end + + context 'when feature is unlicensed' do + before do + stub_licensed_features(external_audit_events: false) + end + + it_behaves_like 'a mutation that does not destroy a configuration' + end +end -- GitLab From 387e01ed54e862ee07ce2c50de68e395c5ba7d40 Mon Sep 17 00:00:00 2001 From: Hitesh Raghuvanshi Date: Thu, 14 Sep 2023 15:37:01 +0530 Subject: [PATCH 2/5] Fixed rubocop offences --- .../google_cloud_logging_configurations/destroy.rb | 4 ++-- .../destroy_spec.rb | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy.rb b/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy.rb index b35687366e7934..ebf7b98794146c 100644 --- a/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy.rb +++ b/ee/app/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy.rb @@ -8,8 +8,8 @@ class Destroy < Base graphql_name 'InstanceGoogleCloudLoggingConfigurationDestroy' argument :id, ::Types::GlobalIDType[::AuditEvents::Instance::GoogleCloudLoggingConfiguration], - required: true, - description: 'ID of the Google Cloud logging configuration to destroy.' + required: true, + description: 'ID of the Google Cloud logging configuration to destroy.' def resolve(id:) config = authorized_find!(id) diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy_spec.rb index 4b2c0be2e39d93..a5727a8a40b8d2 100644 --- a/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy_spec.rb +++ b/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy_spec.rb @@ -16,7 +16,7 @@ shared_examples 'a mutation that does not destroy a configuration' do it_behaves_like 'a mutation that returns top-level errors', - errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR] + errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR] it 'does not destroy the configuration' do expect { mutate } @@ -51,14 +51,16 @@ expect(args[:author]).to eq(current_user) expect(args[:scope]).to be_an_instance_of(Gitlab::Audit::InstanceScope) expect(args[:target]).to eq(config) - expect(args[:message]).to eq("Deleted Instance Google Cloud logging configuration with name: #{config.name} " \ - "project id: #{config.google_project_id_name} and log id: #{config.log_id_name}") + expect(args[:message]) + .to eq("Deleted Instance Google Cloud logging configuration with name: #{config.name} " \ + "project id: #{config.google_project_id_name} and log id: #{config.log_id_name}") end end context 'when there is an error during destroy' do before do - allow_next_instance_of(Mutations::AuditEvents::Instance::GoogleCloudLoggingConfigurations::Destroy) do |mutation| + allow_next_instance_of( + Mutations::AuditEvents::Instance::GoogleCloudLoggingConfigurations::Destroy) do |mutation| allow(mutation).to receive(:authorized_find!).and_return(config) end @@ -72,9 +74,7 @@ expect { mutate } .not_to change { AuditEvents::Instance::GoogleCloudLoggingConfiguration.count } - expect(mutation_response).to include( - 'errors' => ['error message'] - ) + expect(mutation_response).to include('errors' => ['error message']) end end end -- GitLab From 09d02b64cce11a1303f30589bc58c23dac1529bc Mon Sep 17 00:00:00 2001 From: Hitesh Raghuvanshi Date: Thu, 14 Sep 2023 16:04:13 +0530 Subject: [PATCH 3/5] Updated mr in audit event yml --- .../instance_google_cloud_logging_configuration_deleted.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/config/audit_events/types/instance_google_cloud_logging_configuration_deleted.yml b/ee/config/audit_events/types/instance_google_cloud_logging_configuration_deleted.yml index cf619458ab0bc2..5ef3f8908ffede 100644 --- a/ee/config/audit_events/types/instance_google_cloud_logging_configuration_deleted.yml +++ b/ee/config/audit_events/types/instance_google_cloud_logging_configuration_deleted.yml @@ -2,7 +2,7 @@ name: instance_google_cloud_logging_configuration_deleted description: Triggered when instance level Google Cloud Logging configuration is deleted. introduced_by_issue: https://gitlab.com/gitlab-org/gitlab/-/issues/423040 -introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/130663 +introduced_by_mr: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131752 milestone: '16.5' feature_category: audit_events saved_to_database: true -- GitLab From 3791b471d8c9ed8c83c3baad1c874b022656e391 Mon Sep 17 00:00:00 2001 From: Hitesh Raghuvanshi Date: Thu, 14 Sep 2023 17:09:03 +0530 Subject: [PATCH 4/5] Auto generated md file --- doc/administration/audit_event_streaming/audit_event_types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/administration/audit_event_streaming/audit_event_types.md b/doc/administration/audit_event_streaming/audit_event_types.md index d448fcfb60e1d3..79f92cb74aedbd 100644 --- a/doc/administration/audit_event_streaming/audit_event_types.md +++ b/doc/administration/audit_event_streaming/audit_event_types.md @@ -149,7 +149,7 @@ Every audit event is associated with an event type. The association with the eve | [`incident_closed_by_project_bot`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121485) | Triggered when an incident is closed using a project access token | **{check-circle}** Yes | **{check-circle}** Yes | `incident_management` | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/323299) | | [`incident_created_by_project_bot`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121485) | Triggered when an incident is created using a project access token | **{check-circle}** Yes | **{check-circle}** Yes | `incident_management` | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/323299) | | [`incident_reopened_by_project_bot`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121485) | Triggered when an incident is reopened using a project access token | **{check-circle}** Yes | **{check-circle}** Yes | `incident_management` | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/323299) | -| [`instance_google_cloud_logging_configuration_deleted`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/130663) | Triggered when instance level Google Cloud Logging configuration is deleted. | **{check-circle}** Yes | **{check-circle}** Yes | `audit_events` | GitLab [16.5](https://gitlab.com/gitlab-org/gitlab/-/issues/423040) | +| [`instance_google_cloud_logging_configuration_deleted`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131752) | Triggered when instance level Google Cloud Logging configuration is deleted. | **{check-circle}** Yes | **{check-circle}** Yes | `audit_events` | GitLab [16.5](https://gitlab.com/gitlab-org/gitlab/-/issues/423040) | | [`ip_restrictions_changed`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86037) | Event triggered on any changes in the IP AllowList | **{check-circle}** Yes | **{check-circle}** Yes | `system_access` | GitLab [15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/358986) | | [`issue_closed_by_project_bot`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121485) | Triggered when an issue is closed using a project access token | **{check-circle}** Yes | **{check-circle}** Yes | `team_planning` | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/323299) | | [`issue_created_by_project_bot`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121485) | Triggered when an issue is created using a project access token | **{check-circle}** Yes | **{check-circle}** Yes | `team_planning` | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/323299) | -- GitLab From c647e4bda55c9c1c88796a499e251d68aca28573 Mon Sep 17 00:00:00 2001 From: Hitesh Raghuvanshi Date: Thu, 14 Sep 2023 17:12:12 +0530 Subject: [PATCH 5/5] Fixed failing rspec --- .../google_cloud_logging_configurations/destroy_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy_spec.rb b/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy_spec.rb index a5727a8a40b8d2..d3853463dfedd6 100644 --- a/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy_spec.rb +++ b/ee/spec/requests/api/graphql/mutations/audit_events/instance/google_cloud_logging_configurations/destroy_spec.rb @@ -47,7 +47,7 @@ subject expect(Gitlab::Audit::Auditor).to have_received(:audit) do |args| - expect(args[:name]).to eq('instance_google_cloud_logging_configuration_destroyed') + expect(args[:name]).to eq('instance_google_cloud_logging_configuration_deleted') expect(args[:author]).to eq(current_user) expect(args[:scope]).to be_an_instance_of(Gitlab::Audit::InstanceScope) expect(args[:target]).to eq(config) @@ -80,7 +80,7 @@ end context 'when current user is not instance admin' do - let_it_be(:current_user) { user } + let_it_be(:current_user) { create(:user) } it_behaves_like 'a mutation that does not destroy a configuration' end -- GitLab