Skip to content
Snippets Groups Projects
Commit 0ec45317 authored by Harsimar Sandhu's avatar Harsimar Sandhu :three: Committed by Dylan Griffith
Browse files

Google cloud logging configuration create api

This commit implements GraphQL api to create
google cloud logging configurations

EE: true
Changelog: added
parent 198e65f6
No related branches found
No related tags found
1 merge request!120723Google cloud logging configuration create api
......@@ -3521,6 +3521,29 @@ Input type: `GitlabSubscriptionActivateInput`
| <a id="mutationgitlabsubscriptionactivatefuturesubscriptions"></a>`futureSubscriptions` | [`[SubscriptionFutureEntry!]`](#subscriptionfutureentry) | Array of future subscriptions. |
| <a id="mutationgitlabsubscriptionactivatelicense"></a>`license` | [`CurrentLicense`](#currentlicense) | Current license. |
 
### `Mutation.googleCloudLoggingConfigurationCreate`
Input type: `GoogleCloudLoggingConfigurationCreateInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationgooglecloudloggingconfigurationcreateclientemail"></a>`clientEmail` | [`String!`](#string) | Client email. |
| <a id="mutationgooglecloudloggingconfigurationcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationgooglecloudloggingconfigurationcreategoogleprojectidname"></a>`googleProjectIdName` | [`String!`](#string) | Google project ID. |
| <a id="mutationgooglecloudloggingconfigurationcreategrouppath"></a>`groupPath` | [`ID!`](#id) | Group path. |
| <a id="mutationgooglecloudloggingconfigurationcreatelogidname"></a>`logIdName` | [`String`](#string) | Log ID. (defaults to `audit_events`). |
| <a id="mutationgooglecloudloggingconfigurationcreateprivatekey"></a>`privateKey` | [`String!`](#string) | Private key. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationgooglecloudloggingconfigurationcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationgooglecloudloggingconfigurationcreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationgooglecloudloggingconfigurationcreategooglecloudloggingconfiguration"></a>`googleCloudLoggingConfiguration` | [`GoogleCloudLoggingConfigurationType`](#googlecloudloggingconfigurationtype) | configuration created. |
### `Mutation.groupMemberBulkUpdate`
 
Input type: `GroupMemberBulkUpdateInput`
......@@ -15154,6 +15177,21 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="geonodeuploadregistriesreplicationstate"></a>`replicationState` | [`ReplicationStateEnum`](#replicationstateenum) | Filters registries by their replication state. |
| <a id="geonodeuploadregistriesverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Filters registries by their verification state. |
 
### `GoogleCloudLoggingConfigurationType`
Stores Google Cloud Logging configurations associated with IAM service accounts,used for generating access tokens.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="googlecloudloggingconfigurationtypeclientemail"></a>`clientEmail` | [`String!`](#string) | Client email. |
| <a id="googlecloudloggingconfigurationtypegoogleprojectidname"></a>`googleProjectIdName` | [`String!`](#string) | Google project ID. |
| <a id="googlecloudloggingconfigurationtypegroup"></a>`group` | [`Group!`](#group) | Group the configuration belongs to. |
| <a id="googlecloudloggingconfigurationtypeid"></a>`id` | [`ID!`](#id) | ID of the configuration. |
| <a id="googlecloudloggingconfigurationtypelogidname"></a>`logIdName` | [`String!`](#string) | Log ID. |
| <a id="googlecloudloggingconfigurationtypeprivatekey"></a>`privateKey` | [`String!`](#string) | Private key. |
### `GpgSignature`
 
GPG signature for a signed commit.
......@@ -115,6 +115,7 @@ module MutationType
mount_mutation ::Mutations::AuditEvents::InstanceExternalAuditEventDestinations::Create
mount_mutation ::Mutations::AuditEvents::InstanceExternalAuditEventDestinations::Destroy
mount_mutation ::Mutations::AuditEvents::InstanceExternalAuditEventDestinations::Update
mount_mutation ::Mutations::AuditEvents::GoogleCloudLoggingConfigurations::Create
mount_mutation ::Mutations::Forecasting::BuildForecast, alpha: { milestone: '16.0' }
prepend(Types::DeprecatedMutations)
......
# frozen_string_literal: true
module Mutations
module AuditEvents
module GoogleCloudLoggingConfigurations
class Create < BaseMutation
graphql_name 'GoogleCloudLoggingConfigurationCreate'
authorize :admin_external_audit_events
argument :group_path, GraphQL::Types::ID,
required: true,
description: 'Group path.'
argument :google_project_id_name, GraphQL::Types::String,
required: true,
description: 'Google project ID.'
argument :client_email, GraphQL::Types::String,
required: true,
description: 'Client email.'
argument :log_id_name, GraphQL::Types::String,
required: false,
description: 'Log ID. (defaults to `audit_events`).',
default_value: 'audit_events'
argument :private_key, GraphQL::Types::String,
required: true,
description: 'Private key.'
field :google_cloud_logging_configuration, ::Types::AuditEvents::GoogleCloudLoggingConfigurationType,
null: true,
description: 'configuration created.'
def resolve(group_path:, google_project_id_name:, client_email:, private_key:, log_id_name: nil)
group = authorized_find!(group_path)
config_attributes = {
group: group,
google_project_id_name: google_project_id_name,
client_email: client_email,
private_key: private_key
}
config_attributes[:log_id_name] = log_id_name if log_id_name.present?
config = ::AuditEvents::GoogleCloudLoggingConfiguration.new(config_attributes)
if config.save
{ google_cloud_logging_configuration: config, errors: [] }
else
{ google_cloud_logging_configuration: nil, errors: Array(config.errors) }
end
end
private
def find_object(group_path)
::Group.find_by_full_path(group_path)
end
end
end
end
end
# frozen_string_literal: true
module Types
module AuditEvents
class GoogleCloudLoggingConfigurationType < ::Types::BaseObject
graphql_name 'GoogleCloudLoggingConfigurationType'
description 'Stores Google Cloud Logging configurations associated with IAM service accounts,' \
'used for generating access tokens.'
authorize :admin_external_audit_events
field :group, ::Types::GroupType,
null: false,
description: 'Group the configuration belongs to.'
field :id, GraphQL::Types::ID,
null: false,
description: 'ID of the configuration.'
field :google_project_id_name, GraphQL::Types::String,
null: false,
description: 'Google project ID.'
field :client_email, GraphQL::Types::String,
null: false,
description: 'Client email.'
field :log_id_name, GraphQL::Types::String,
null: false,
description: 'Log ID.'
field :private_key, GraphQL::Types::String,
null: false,
description: 'Private key.'
end
end
end
......@@ -11,6 +11,10 @@ class GoogleCloudLoggingConfiguration < ApplicationRecord
GOOGLE_PROJECT_ID_NAME_REGEX = %r{\A[a-z][a-z0-9-]*[a-z0-9]\z}
LOG_ID_NAME_REGEX = %r{\A[\w/.-]+\z}
DEFAULT_LOG_ID_NAME = "audit_events"
attribute :log_id_name, :string, default: DEFAULT_LOG_ID_NAME
belongs_to :group, class_name: '::Group', foreign_key: 'namespace_id',
inverse_of: :google_cloud_logging_configurations
......@@ -18,7 +22,8 @@ class GoogleCloudLoggingConfiguration < ApplicationRecord
format: { with: GOOGLE_PROJECT_ID_NAME_REGEX,
message: 'must only contain lowercase letters, digits, or hyphens, ' \
'and must start and end with a letter or digit' },
length: { in: 6..30 }
length: { in: 6..30 },
uniqueness: { scope: [:namespace_id, :log_id_name] }
validates :log_id_name, presence: true,
format: { with: LOG_ID_NAME_REGEX,
......
# frozen_string_literal: true
module AuditEvents
class GoogleCloudLoggingConfigurationPolicy < ::BasePolicy
delegate { @subject.group }
end
end
......@@ -45,6 +45,24 @@
it { is_expected.not_to allow_value('#AUDIT_EVENT').for(:log_id_name) }
it { is_expected.not_to allow_value('%audit_events/123').for(:log_id_name) }
context 'when the same google_project_id_name for the same namespace and log_id_name exists' do
let(:group) { create(:group) }
let(:google_project_id_name) { 'valid-project-id' }
let(:log_id_name) { 'audit_events' }
before do
create(:google_cloud_logging_configuration, group: group, google_project_id_name: google_project_id_name,
log_id_name: log_id_name)
end
it 'is not valid and adds an error message' do
config = build(:google_cloud_logging_configuration, group: group,
google_project_id_name: google_project_id_name, log_id_name: log_id_name)
expect(config).not_to be_valid
expect(config.errors[:google_project_id_name]).to include('has already been taken')
end
end
context 'when the group is a subgroup' do
let_it_be(:group) { create(:group) }
let_it_be(:subgroup) { create(:group, parent: group) }
......@@ -60,6 +78,12 @@
end
end
describe 'default values' do
it "uses 'audit_events' as default value for log_id_name" do
expect(described_class.new.log_id_name).to eq('audit_events')
end
end
it_behaves_like 'includes Limitable concern' do
subject { build(:google_cloud_logging_configuration, group: create(:group)) }
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Create google cloud logging configuration', feature_category: :audit_events do
include GraphqlHelpers
let_it_be(:group) { create(:group) }
let_it_be(:owner) { create(:user) }
let_it_be(:google_project_id_name) { 'test-project' }
let_it_be(:client_email) { 'test-email@example.com' }
let_it_be(:private_key) { OpenSSL::PKey::RSA.new(4096).to_pem }
let_it_be(:default_log_id_name) { 'audit_events' }
let(:current_user) { owner }
let(:mutation) { graphql_mutation(:google_cloud_logging_configuration_create, input) }
let(:mutation_response) { graphql_mutation_response(:google_cloud_logging_configuration_create) }
let(:input) do
{
groupPath: group.full_path,
googleProjectIdName: google_project_id_name,
clientEmail: client_email,
privateKey: private_key
}
end
subject(:mutate) { post_graphql_mutation(mutation, current_user: owner) }
shared_examples 'a mutation that does not create a configuration' do
it 'does not destroy the configuration' do
expect { mutate }
.not_to change { AuditEvents::GoogleCloudLoggingConfiguration.count }
end
end
shared_examples 'an unauthorized mutation that does not create a configuration' do
it_behaves_like 'a mutation on an unauthorized resource'
it_behaves_like 'a mutation that does not create a configuration'
end
context 'when feature is licensed' do
before do
stub_licensed_features(external_audit_events: true)
end
context 'when current user is a group owner' do
before do
group.add_owner(owner)
end
it 'resolves group by full path' do
expect(::Group).to receive(:find_by_full_path).with(group.full_path)
mutate
end
it 'creates the configuration' do
expect { mutate }
.to change { AuditEvents::GoogleCloudLoggingConfiguration.count }.by(1)
config = AuditEvents::GoogleCloudLoggingConfiguration.last
expect(config.group).to eq(group)
expect(config.google_project_id_name).to eq(google_project_id_name)
expect(config.client_email).to eq(client_email)
expect(config.log_id_name).to eq(default_log_id_name)
expect(config.private_key).to eq(private_key)
end
context 'when overriding log id name' do
let_it_be(:log_id_name) { 'test-log-id' }
let(:input) do
{
groupPath: group.full_path,
googleProjectIdName: google_project_id_name,
clientEmail: client_email,
privateKey: private_key,
logIdName: log_id_name
}
end
it 'creates the configuration' do
expect { mutate }
.to change { AuditEvents::GoogleCloudLoggingConfiguration.count }.by(1)
config = AuditEvents::GoogleCloudLoggingConfiguration.last
expect(config.group).to eq(group)
expect(config.google_project_id_name).to eq(google_project_id_name)
expect(config.client_email).to eq(client_email)
expect(config.log_id_name).to eq(log_id_name)
expect(config.private_key).to eq(private_key)
end
end
context 'when there is error while saving' do
before do
allow_next_instance_of(AuditEvents::GoogleCloudLoggingConfiguration) do |instance|
allow(instance).to receive(:save).and_return(false)
errors = ActiveModel::Errors.new(instance).tap { |e| e.add(:log_id_name, 'error message') }
allow(instance).to receive(:errors).and_return(errors)
end
end
it 'does not create the configuration and returns the error' do
expect { mutate }
.not_to change { AuditEvents::GoogleCloudLoggingConfiguration.count }
expect(mutation_response).to include(
'googleCloudLoggingConfiguration' => nil,
'errors' => ["Log id name error message"]
)
end
end
end
context 'when current user is a group maintainer' do
before do
group.add_maintainer(owner)
end
it_behaves_like 'an unauthorized mutation that does not create a configuration'
end
context 'when current user is a group developer' do
before do
group.add_developer(owner)
end
it_behaves_like 'an unauthorized mutation that does not create a configuration'
end
context 'when current user is a group guest' do
before do
group.add_guest(owner)
end
it_behaves_like 'an unauthorized mutation that does not create a configuration'
end
end
context 'when feature is unlicensed' do
before do
stub_licensed_features(external_audit_events: false)
end
it_behaves_like 'an unauthorized mutation that does not create a configuration'
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment