diff --git a/db/docs/audit_events_instance_external_streaming_destinations.yml b/db/docs/audit_events_instance_external_streaming_destinations.yml new file mode 100644 index 0000000000000000000000000000000000000000..966e89284462b317880d60f0c940e12acfcb1b83 --- /dev/null +++ b/db/docs/audit_events_instance_external_streaming_destinations.yml @@ -0,0 +1,10 @@ +--- +table_name: audit_events_instance_external_streaming_destinations +classes: +- AuditEvents::Instance::ExternalStreamingDestination +feature_categories: +- audit_events +description: Stores external audit event destinations configurations for instance. +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141739 +milestone: '16.9' +gitlab_schema: gitlab_main_clusterwide diff --git a/db/migrate/20240130162148_create_audit_events_instance_external_streaming_destinations.rb b/db/migrate/20240130162148_create_audit_events_instance_external_streaming_destinations.rb new file mode 100644 index 0000000000000000000000000000000000000000..2f1d906767726afc68c3b82782a8c9bb7f7c172f --- /dev/null +++ b/db/migrate/20240130162148_create_audit_events_instance_external_streaming_destinations.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class CreateAuditEventsInstanceExternalStreamingDestinations < Gitlab::Database::Migration[2.2] + milestone '16.9' + + def change + create_table :audit_events_instance_external_streaming_destinations do |t| + t.timestamps_with_timezone null: false + t.integer :type, null: false, limit: 2 + t.text :name, null: false, limit: 72 + t.jsonb :config, null: false + t.binary :encrypted_secret_token, null: false + t.binary :encrypted_secret_token_iv, null: false + end + end +end diff --git a/db/schema_migrations/20240130162148 b/db/schema_migrations/20240130162148 new file mode 100644 index 0000000000000000000000000000000000000000..593bb247644f0a81c4f05d93e8493da44fa47417 --- /dev/null +++ b/db/schema_migrations/20240130162148 @@ -0,0 +1 @@ +37166b2aa9addf6d1effd34da3242866a13c9a24636f0be677d72c89bc09376d \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index bf51d637ab1697dd4c4d51ca6af923b39203f62f..456d47abb8d3e903dcbdd600f2ad88210305a418 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -13222,6 +13222,27 @@ CREATE SEQUENCE audit_events_instance_external_audit_event_destinations_id_seq ALTER SEQUENCE audit_events_instance_external_audit_event_destinations_id_seq OWNED BY audit_events_instance_external_audit_event_destinations.id; +CREATE TABLE audit_events_instance_external_streaming_destinations ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + type smallint NOT NULL, + name text NOT NULL, + config jsonb NOT NULL, + encrypted_secret_token bytea NOT NULL, + encrypted_secret_token_iv bytea NOT NULL, + CONSTRAINT check_219decfb51 CHECK ((char_length(name) <= 72)) +); + +CREATE SEQUENCE audit_events_instance_external_streaming_destinations_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE audit_events_instance_external_streaming_destinations_id_seq OWNED BY audit_events_instance_external_streaming_destinations.id; + CREATE TABLE audit_events_instance_google_cloud_logging_configurations ( id bigint NOT NULL, created_at timestamp with time zone NOT NULL, @@ -27021,6 +27042,8 @@ ALTER TABLE ONLY audit_events_instance_amazon_s3_configurations ALTER COLUMN id ALTER TABLE ONLY audit_events_instance_external_audit_event_destinations ALTER COLUMN id SET DEFAULT nextval('audit_events_instance_external_audit_event_destinations_id_seq'::regclass); +ALTER TABLE ONLY audit_events_instance_external_streaming_destinations ALTER COLUMN id SET DEFAULT nextval('audit_events_instance_external_streaming_destinations_id_seq'::regclass); + ALTER TABLE ONLY audit_events_instance_google_cloud_logging_configurations ALTER COLUMN id SET DEFAULT nextval('audit_events_instance_google_cloud_logging_configuration_id_seq'::regclass); ALTER TABLE ONLY audit_events_streaming_event_type_filters ALTER COLUMN id SET DEFAULT nextval('audit_events_streaming_event_type_filters_id_seq'::regclass); @@ -28994,6 +29017,9 @@ ALTER TABLE ONLY audit_events_instance_amazon_s3_configurations ALTER TABLE ONLY audit_events_instance_external_audit_event_destinations ADD CONSTRAINT audit_events_instance_external_audit_event_destinations_pkey PRIMARY KEY (id); +ALTER TABLE ONLY audit_events_instance_external_streaming_destinations + ADD CONSTRAINT audit_events_instance_external_streaming_destinations_pkey PRIMARY KEY (id); + ALTER TABLE ONLY audit_events_instance_google_cloud_logging_configurations ADD CONSTRAINT audit_events_instance_google_cloud_logging_configurations_pkey PRIMARY KEY (id); diff --git a/ee/app/models/audit_events/instance/external_streaming_destination.rb b/ee/app/models/audit_events/instance/external_streaming_destination.rb new file mode 100644 index 0000000000000000000000000000000000000000..070dda144eb61be58d0ac38ff7366c25cd1d492a --- /dev/null +++ b/ee/app/models/audit_events/instance/external_streaming_destination.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module AuditEvents + module Instance + class ExternalStreamingDestination < ApplicationRecord + include Limitable + include ExternallyStreamable + + self.limit_name = 'external_audit_event_destinations' + self.limit_scope = Limitable::GLOBAL_SCOPE + self.table_name = 'audit_events_instance_external_streaming_destinations' + + validates :name, uniqueness: true + end + end +end diff --git a/ee/spec/factories/audit_events/audit_events_instance_external_streaming_destination.rb b/ee/spec/factories/audit_events/audit_events_instance_external_streaming_destination.rb new file mode 100644 index 0000000000000000000000000000000000000000..67d38cf0270560291a167015991c800bfbdf655d --- /dev/null +++ b/ee/spec/factories/audit_events/audit_events_instance_external_streaming_destination.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :audit_events_instance_external_streaming_destination, + class: 'AuditEvents::Instance::ExternalStreamingDestination' do + type { 'http' } + config { { url: 'https://www.example.com' } } + secret_token { 'hello' } + end +end diff --git a/ee/spec/models/audit_events/instance/external_streaming_destination_spec.rb b/ee/spec/models/audit_events/instance/external_streaming_destination_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..1dd38cfac869843b0c68ea1d449e64487081b230 --- /dev/null +++ b/ee/spec/models/audit_events/instance/external_streaming_destination_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe AuditEvents::Instance::ExternalStreamingDestination, feature_category: :audit_events do + subject(:destination) { build(:audit_events_instance_external_streaming_destination) } + + describe 'Validations' do + it 'validates uniqueness of name scoped to namespace' do + create(:audit_events_instance_external_streaming_destination, name: 'Test Destination') + destination = build(:audit_events_instance_external_streaming_destination, name: 'Test Destination') + + expect(destination).not_to be_valid + expect(destination.errors.full_messages).to include('Name has already been taken') + end + end + + it_behaves_like 'includes Limitable concern' do + subject { build(:audit_events_instance_external_streaming_destination) } + end + + it_behaves_like 'includes ExternallyStreamable concern' do + subject { build(:audit_events_instance_external_streaming_destination) } + + let(:model_factory_name) { :audit_events_instance_external_streaming_destination } + end +end