From 4437c7b5107bd3f7787d648a7a4b505b105361b7 Mon Sep 17 00:00:00 2001 From: Christian Schaible Date: Mon, 15 Jul 2024 07:51:41 +0200 Subject: [PATCH] Rename module to dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git --- api.go => audit/api/api.go | 35 ++++- api_common.go => audit/api/api_common.go | 29 +++- .../api/api_common_test.go | 81 ++++++++--- api_legacy.go => audit/api/api_legacy.go | 40 ++++-- .../api/api_legacy_test.go | 21 +-- api_mock.go => audit/api/api_mock.go | 14 +- .../api/api_mock_test.go | 20 ++- api_routable.go => audit/api/api_routable.go | 47 +++++-- .../api/api_routable_test.go | 131 +++++++++++++++--- test_data.go => audit/api/test_data.go | 8 +- client.go => audit/messaging/client.go | 7 +- messaging.go => audit/messaging/messaging.go | 11 +- .../messaging/messaging_test.go | 11 +- solace.go => audit/messaging/solace.go | 6 +- go.mod | 2 +- main.go | 4 +- 16 files changed, 352 insertions(+), 115 deletions(-) rename api.go => audit/api/api.go (77%) rename api_common.go => audit/api/api_common.go (87%) rename api_common_test.go => audit/api/api_common_test.go (73%) rename api_legacy.go => audit/api/api_legacy.go (91%) rename api_legacy_test.go => audit/api/api_legacy_test.go (97%) rename api_mock.go => audit/api/api_mock.go (79%) rename api_mock_test.go => audit/api/api_mock_test.go (63%) rename api_routable.go => audit/api/api_routable.go (72%) rename api_routable_test.go => audit/api/api_routable_test.go (81%) rename test_data.go => audit/api/test_data.go (97%) rename client.go => audit/messaging/client.go (98%) rename messaging.go => audit/messaging/messaging.go (94%) rename messaging_test.go => audit/messaging/messaging_test.go (95%) rename solace.go => audit/messaging/solace.go (97%) diff --git a/api.go b/audit/api/api.go similarity index 77% rename from api.go rename to audit/api/api.go index 66ab0f7..1f43266 100644 --- a/api.go +++ b/audit/api/api.go @@ -1,8 +1,10 @@ -package main +package api import ( - auditV1 "audit-schema/gen/go/audit/v1" "context" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/gen/go/audit/v1" + "github.com/google/uuid" "google.golang.org/protobuf/proto" ) @@ -41,14 +43,29 @@ type AuditApi interface { - protovalidate.ValidationError - if schema validation errors have been detected - protobuf serialization errors - if the event couldn't be serialized */ - Log(ctx context.Context, event *auditV1.AuditEvent, visibility auditV1.Visibility, routingIdentifier *RoutingIdentifier, objectIdentifier *auditV1.ObjectIdentifier) error + Log( + ctx context.Context, + event *auditV1.AuditEvent, + visibility auditV1.Visibility, + routingIdentifier *RoutingIdentifier, + objectIdentifier *auditV1.ObjectIdentifier, + ) error // ValidateAndSerialize validates and serializes the event into a byte representation. // The result has to be sent explicitly by calling the Send method. - ValidateAndSerialize(event *auditV1.AuditEvent, visibility auditV1.Visibility, routingIdentifier *RoutingIdentifier, objectIdentifier *auditV1.ObjectIdentifier) (SerializedPayload, error) + ValidateAndSerialize( + event *auditV1.AuditEvent, + visibility auditV1.Visibility, + routingIdentifier *RoutingIdentifier, + objectIdentifier *auditV1.ObjectIdentifier, + ) (SerializedPayload, error) // Send the serialized content as byte array to the audit log system. - Send(ctx context.Context, routingIdentifier *RoutingIdentifier, serializedPayload *SerializedPayload) error + Send( + ctx context.Context, + routingIdentifier *RoutingIdentifier, + serializedPayload *SerializedPayload, + ) error } // ProtobufValidator is an abstraction for validators. @@ -81,3 +98,11 @@ type RoutingIdentifier struct { Identifier uuid.UUID Type RoutingIdentifierType } + +// TopicNameResolver is an abstraction for dynamic topic name resolution +// based on event data or api parameters. +type TopicNameResolver interface { + + // Resolve returns a topic name for the given routing identifier + Resolve(routingIdentifier *RoutingIdentifier) (string, error) +} diff --git a/api_common.go b/audit/api/api_common.go similarity index 87% rename from api_common.go rename to audit/api/api_common.go index f9b8c15..ed054b7 100644 --- a/api_common.go +++ b/audit/api/api_common.go @@ -1,10 +1,13 @@ -package main +package api import ( - auditV1 "audit-schema/gen/go/audit/v1" "context" "errors" "fmt" + + "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/audit/messaging" + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/gen/go/audit/v1" + "google.golang.org/protobuf/proto" ) @@ -45,7 +48,13 @@ var ErrMessagingApiNil = errors.New("messaging api nil") // ErrSerializedPayloadNil states that the give serialized payload is nil var ErrSerializedPayloadNil = errors.New("serialized payload nil") -func validateAndSerializePartially(validator *ProtobufValidator, event *auditV1.AuditEvent, visibility auditV1.Visibility, routingIdentifier *RoutingIdentifier, objectIdentifier *auditV1.ObjectIdentifier) (*auditV1.RoutableAuditEvent, error) { +func validateAndSerializePartially( + validator *ProtobufValidator, + event *auditV1.AuditEvent, + visibility auditV1.Visibility, + routingIdentifier *RoutingIdentifier, + objectIdentifier *auditV1.ObjectIdentifier, +) (*auditV1.RoutableAuditEvent, error) { // Return error if the given event is nil if event == nil { @@ -107,9 +116,11 @@ func validateAndSerializePartially(validator *ProtobufValidator, event *auditV1. // Set oneof protobuf fields after creation of the object if objectIdentifier == nil { - routableEvent.ResourceReference = &auditV1.RoutableAuditEvent_ObjectName{ObjectName: auditV1.ObjectName_OBJECT_NAME_SYSTEM} + routableEvent.ResourceReference = &auditV1.RoutableAuditEvent_ObjectName{ + ObjectName: auditV1.ObjectName_OBJECT_NAME_SYSTEM} } else { - routableEvent.ResourceReference = &auditV1.RoutableAuditEvent_ObjectIdentifier{ObjectIdentifier: objectIdentifier} + routableEvent.ResourceReference = &auditV1.RoutableAuditEvent_ObjectIdentifier{ + ObjectIdentifier: objectIdentifier} } err = (*validator).Validate(&routableEvent) @@ -146,7 +157,13 @@ func serializeToProtobufMessage(routableEvent *auditV1.RoutableAuditEvent) (Seri } // Send implements AuditApi.Send -func send(topicNameResolver *TopicNameResolver, messagingApi *MessagingApi, ctx context.Context, routingIdentifier *RoutingIdentifier, serializedPayload *SerializedPayload) error { +func send( + topicNameResolver *TopicNameResolver, + messagingApi *messaging.MessagingApi, + ctx context.Context, + routingIdentifier *RoutingIdentifier, + serializedPayload *SerializedPayload, +) error { if topicNameResolver == nil { return ErrTopicNameResolverNil diff --git a/api_common_test.go b/audit/api/api_common_test.go similarity index 73% rename from api_common_test.go rename to audit/api/api_common_test.go index 1f88f24..83bc648 100644 --- a/api_common_test.go +++ b/audit/api/api_common_test.go @@ -1,18 +1,30 @@ -package main +package api import ( - auditV1 "audit-schema/gen/go/audit/v1" "context" "errors" "fmt" + "testing" + + "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/audit/messaging" + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/gen/go/audit/v1" + "github.com/bufbuild/protovalidate-go" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "google.golang.org/protobuf/proto" - "testing" ) +type MessagingApiMock struct { + mock.Mock +} + +func (m *MessagingApiMock) Send(ctx context.Context, topic string, data []byte, contentType string) error { + args := m.Called(ctx, topic, data, contentType) + return args.Error(0) +} + type ProtobufValidatorMock struct { mock.Mock } @@ -42,7 +54,9 @@ func NewValidator(t *testing.T) ProtobufValidator { func Test_ValidateAndSerializePartially_EventNil(t *testing.T) { validator := NewValidator(t) - _, err := validateAndSerializePartially(&validator, nil, auditV1.Visibility_VISIBILITY_PUBLIC, nil, nil) + _, err := validateAndSerializePartially( + &validator, nil, auditV1.Visibility_VISIBILITY_PUBLIC, nil, nil) + assert.ErrorIs(t, err, ErrEventNil) } @@ -52,7 +66,9 @@ func Test_ValidateAndSerializePartially_AuditEventValidationFailed(t *testing.T) event, routingIdentifier, objectIdentifier := NewOrganizationAuditEvent(nil) event.EventName = "" - _, err := validateAndSerializePartially(&validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, objectIdentifier) + _, err := validateAndSerializePartially( + &validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, objectIdentifier) + assert.EqualError(t, err, "validation error:\n - event_name: value is required [required]") } @@ -60,8 +76,8 @@ func Test_ValidateAndSerializePartially_RoutableEventValidationFailed(t *testing validator := NewValidator(t) event, routingIdentifier, objectIdentifier := NewOrganizationAuditEvent(nil) - _, err := validateAndSerializePartially(&validator, event, 3, routingIdentifier, objectIdentifier) + assert.EqualError(t, err, "validation error:\n - visibility: value must be one of the defined enum values [enum.defined_only]") } @@ -71,46 +87,62 @@ func Test_ValidateAndSerializePartially_CheckVisibility(t *testing.T) { event, routingIdentifier, objectIdentifier := NewOrganizationAuditEvent(nil) t.Run("Visibility public - object identifier nil - routing identifier nil", func(t *testing.T) { - _, err := validateAndSerializePartially(&validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, nil, nil) + _, err := validateAndSerializePartially( + &validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, nil, nil) + assert.ErrorIs(t, err, ErrObjectIdentifierVisibilityMismatch) }) t.Run("Visibility public - object identifier nil - routing identifier set", func(t *testing.T) { - _, err := validateAndSerializePartially(&validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, nil) + _, err := validateAndSerializePartially( + &validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, nil) + assert.ErrorIs(t, err, ErrObjectIdentifierVisibilityMismatch) }) t.Run("Visibility public - object identifier set - routing identifier nil", func(t *testing.T) { - _, err := validateAndSerializePartially(&validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, nil, objectIdentifier) + _, err := validateAndSerializePartially( + &validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, nil, objectIdentifier) + assert.ErrorIs(t, err, ErrRoutableIdentifierMissing) }) t.Run("Visibility public - object identifier set - routing identifier set", func(t *testing.T) { - routableEvent, err := validateAndSerializePartially(&validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, objectIdentifier) + routableEvent, err := validateAndSerializePartially( + &validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, objectIdentifier) + assert.NoError(t, err) assert.NotNil(t, routableEvent) }) t.Run("Visibility private - object identifier nil - routing identifier nil", func(t *testing.T) { - routableEvent, err := validateAndSerializePartially(&validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, nil, nil) + routableEvent, err := validateAndSerializePartially( + &validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, nil, nil) + assert.NoError(t, err) assert.NotNil(t, routableEvent) }) t.Run("Visibility private - object identifier nil - routing identifier set", func(t *testing.T) { - routableEvent, err := validateAndSerializePartially(&validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, routingIdentifier, nil) + routableEvent, err := validateAndSerializePartially( + &validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, routingIdentifier, nil) + assert.NoError(t, err) assert.NotNil(t, routableEvent) }) t.Run("Visibility private - object identifier set - routing identifier nil", func(t *testing.T) { - routableEvent, err := validateAndSerializePartially(&validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, nil, objectIdentifier) + routableEvent, err := validateAndSerializePartially( + &validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, nil, objectIdentifier) + assert.NoError(t, err) assert.NotNil(t, routableEvent) }) t.Run("Visibility private - object identifier set - routing identifier set", func(t *testing.T) { - routableEvent, err := validateAndSerializePartially(&validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, routingIdentifier, objectIdentifier) + routableEvent, err := validateAndSerializePartially( + &validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, routingIdentifier, objectIdentifier) + assert.NoError(t, err) assert.NotNil(t, routableEvent) }) @@ -122,7 +154,9 @@ func Test_ValidateAndSerializePartially_IdentifierTypeMismatch(t *testing.T) { event, routingIdentifier, objectIdentifier := NewFolderAuditEvent(nil) routingIdentifier.Type = RoutingIdentifierTypeProject - _, err := validateAndSerializePartially(&validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, objectIdentifier) + _, err := validateAndSerializePartially( + &validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, objectIdentifier) + assert.ErrorIs(t, err, ErrRoutableIdentifierTypeMismatch) } @@ -132,7 +166,9 @@ func Test_ValidateAndSerializePartially_IdentifierMismatch(t *testing.T) { event, routingIdentifier, objectIdentifier := NewProjectAuditEvent(nil) routingIdentifier.Identifier = uuid.New() - _, err := validateAndSerializePartially(&validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, objectIdentifier) + _, err := validateAndSerializePartially( + &validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, objectIdentifier) + assert.ErrorIs(t, err, ErrRoutableIdentifierMismatch) } @@ -141,7 +177,9 @@ func Test_ValidateAndSerializePartially_SystemEvent(t *testing.T) { event := NewSystemAuditEvent(nil) - routableEvent, err := validateAndSerializePartially(&validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, nil, nil) + routableEvent, err := validateAndSerializePartially( + &validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, nil, nil) + assert.NoError(t, err) switch reference := routableEvent.ResourceReference.(type) { @@ -159,7 +197,8 @@ func Test_SerializeToProtobufMessage(t *testing.T) { event, identifier, objectIdentifier := NewOrganizationAuditEventWithDetails() // Serialize to routable event - routableEvent, err := validateAndSerializePartially(&validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, identifier, objectIdentifier) + routableEvent, err := validateAndSerializePartially( + &validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, identifier, objectIdentifier) assert.NoError(t, err) // Serialize to protobuf message @@ -193,7 +232,7 @@ func Test_Send_TopicNameResolutionError(t *testing.T) { var serializedPayload SerializedPayload = &routablePayload{} - var messagingApi MessagingApi = &AmqpMessagingApi{} + var messagingApi messaging.MessagingApi = &messaging.AmqpMessagingApi{} err := send(&topicNameResolver, &messagingApi, context.Background(), nil, &serializedPayload) assert.ErrorIs(t, err, expectedError) } @@ -206,7 +245,7 @@ func Test_Send_MessagingApiNil(t *testing.T) { func Test_Send_SerializedPayloadNil(t *testing.T) { var topicNameResolver TopicNameResolver = &LegacyTopicNameResolver{topicName: "test"} - var messagingApi MessagingApi = &AmqpMessagingApi{} + var messagingApi messaging.MessagingApi = &messaging.AmqpMessagingApi{} err := send(&topicNameResolver, &messagingApi, context.Background(), nil, nil) assert.ErrorIs(t, err, ErrSerializedPayloadNil) } @@ -220,7 +259,7 @@ func Test_Send(t *testing.T) { messagingApiMock := MessagingApiMock{} messagingApiMock.On("Send", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) - var messagingApi MessagingApi = &messagingApiMock + var messagingApi messaging.MessagingApi = &messagingApiMock assert.NoError(t, send(&topicNameResolver, &messagingApi, context.Background(), nil, &serializedPayload)) assert.True(t, messagingApiMock.AssertNumberOfCalls(t, "Send", 1)) diff --git a/api_legacy.go b/audit/api/api_legacy.go similarity index 91% rename from api_legacy.go rename to audit/api/api_legacy.go index b0a1d26..8816b4e 100644 --- a/api_legacy.go +++ b/audit/api/api_legacy.go @@ -1,12 +1,15 @@ -package main +package api import ( - auditV1 "audit-schema/gen/go/audit/v1" "context" "encoding/json" "errors" - "google.golang.org/protobuf/proto" "time" + + "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/audit/messaging" + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/gen/go/audit/v1" + + "google.golang.org/protobuf/proto" ) // legacyPayload implements SerializedPayload. @@ -44,7 +47,7 @@ type LegacyTopicNameConfig struct { // // Note: The implementation will be deprecated and replaced with the "routableAuditApi" once the new audit log routing is implemented type LegacyAuditApi struct { - messagingApi *MessagingApi + messagingApi *messaging.MessagingApi topicNameResolver *TopicNameResolver validator *ProtobufValidator } @@ -52,7 +55,11 @@ type LegacyAuditApi struct { // NewLegacyAuditApi can be used to initialize the audit log api with LegacyAuditLogConnectionDetails. // // Note: The NewLegacyAuditApi method will be deprecated and replaced with "newRoutableAuditApi" once the new audit log routing is implemented -func NewLegacyAuditApi(messagingApi *MessagingApi, topicNameConfig LegacyTopicNameConfig, validator ProtobufValidator) (*AuditApi, error) { +func NewLegacyAuditApi( + messagingApi *messaging.MessagingApi, + topicNameConfig LegacyTopicNameConfig, + validator ProtobufValidator, +) (*AuditApi, error) { if messagingApi == nil { return nil, errors.New("messaging api nil") @@ -72,7 +79,13 @@ func NewLegacyAuditApi(messagingApi *MessagingApi, topicNameConfig LegacyTopicNa } // Log implements AuditApi.Log -func (a *LegacyAuditApi) Log(ctx context.Context, event *auditV1.AuditEvent, visibility auditV1.Visibility, routingIdentifier *RoutingIdentifier, objectIdentifier *auditV1.ObjectIdentifier) error { +func (a *LegacyAuditApi) Log( + ctx context.Context, + event *auditV1.AuditEvent, + visibility auditV1.Visibility, + routingIdentifier *RoutingIdentifier, + objectIdentifier *auditV1.ObjectIdentifier, +) error { serializedPayload, err := a.ValidateAndSerialize(event, visibility, routingIdentifier, objectIdentifier) if err != nil { @@ -84,7 +97,13 @@ func (a *LegacyAuditApi) Log(ctx context.Context, event *auditV1.AuditEvent, vis // ValidateAndSerialize implements AuditApi.ValidateAndSerialize. // It serializes the event into the byte representation of the legacy audit log system. -func (a *LegacyAuditApi) ValidateAndSerialize(event *auditV1.AuditEvent, visibility auditV1.Visibility, routingIdentifier *RoutingIdentifier, objectIdentifier *auditV1.ObjectIdentifier) (SerializedPayload, error) { +func (a *LegacyAuditApi) ValidateAndSerialize( + event *auditV1.AuditEvent, + visibility auditV1.Visibility, + routingIdentifier *RoutingIdentifier, + objectIdentifier *auditV1.ObjectIdentifier, +) (SerializedPayload, error) { + routableEvent, err := validateAndSerializePartially(a.validator, event, visibility, routingIdentifier, objectIdentifier) if err != nil { return nil, err @@ -106,7 +125,12 @@ func (a *LegacyAuditApi) ValidateAndSerialize(event *auditV1.AuditEvent, visibil } // Send implements AuditApi.Send -func (a *LegacyAuditApi) Send(ctx context.Context, routingIdentifier *RoutingIdentifier, serializedPayload *SerializedPayload) error { +func (a *LegacyAuditApi) Send( + ctx context.Context, + routingIdentifier *RoutingIdentifier, + serializedPayload *SerializedPayload, +) error { + return send(a.topicNameResolver, a.messagingApi, ctx, routingIdentifier, serializedPayload) } diff --git a/api_legacy_test.go b/audit/api/api_legacy_test.go similarity index 97% rename from api_legacy_test.go rename to audit/api/api_legacy_test.go index 0465370..0f1fc17 100644 --- a/api_legacy_test.go +++ b/audit/api/api_legacy_test.go @@ -1,18 +1,21 @@ -package main +package api import ( - auditV1 "audit-schema/gen/go/audit/v1" "context" "encoding/json" "errors" - "github.com/Azure/go-amqp" - "github.com/bufbuild/protovalidate-go" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "log/slog" "os" "testing" "time" + + "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/audit/messaging" + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/gen/go/audit/v1" + + "github.com/Azure/go-amqp" + "github.com/bufbuild/protovalidate-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func TestLegacyAuditApi(t *testing.T) { @@ -23,12 +26,12 @@ func TestLegacyAuditApi(t *testing.T) { defer cancelFn() // Start solace docker container - solaceContainer, err := NewSolaceContainer(context.Background()) + solaceContainer, err := messaging.NewSolaceContainer(context.Background()) assert.NoError(t, err) defer solaceContainer.Stop() // Instantiate the messaging api - messagingApi, err := NewAmqpMessagingApi(AmqpConfig{URL: solaceContainer.AmqpConnectionString}) + messagingApi, err := messaging.NewAmqpMessagingApi(messaging.AmqpConfig{URL: solaceContainer.AmqpConnectionString}) assert.NoError(t, err) // Validator @@ -408,7 +411,7 @@ func validateSentMessageWithDetails(t *testing.T, topicName string, message *amq assert.Equal(t, header.Value, (*auditEvent.Request.Headers)[header.Key]) } - for idx, _ := range event.Principals { + for idx := range event.Principals { assert.Equal(t, event.Principals[idx].Id, auditEvent.ServiceAccountDelegationInfo.Principals[idx].Id) assert.Equal(t, event.Principals[idx].Email, auditEvent.ServiceAccountDelegationInfo.Principals[idx].Email) } diff --git a/api_mock.go b/audit/api/api_mock.go similarity index 79% rename from api_mock.go rename to audit/api/api_mock.go index 96ecf49..279ae5f 100644 --- a/api_mock.go +++ b/audit/api/api_mock.go @@ -1,8 +1,10 @@ -package main +package api import ( - auditV1 "audit-schema/gen/go/audit/v1" "context" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/gen/go/audit/v1" + "github.com/bufbuild/protovalidate-go" ) @@ -36,7 +38,13 @@ func (a *MockAuditApi) Log( } // ValidateAndSerialize implements AuditApi.ValidateAndSerialize -func (a *MockAuditApi) ValidateAndSerialize(event *auditV1.AuditEvent, visibility auditV1.Visibility, routingIdentifier *RoutingIdentifier, objectIdentifier *auditV1.ObjectIdentifier) (SerializedPayload, error) { +func (a *MockAuditApi) ValidateAndSerialize( + event *auditV1.AuditEvent, + visibility auditV1.Visibility, + routingIdentifier *RoutingIdentifier, + objectIdentifier *auditV1.ObjectIdentifier, +) (SerializedPayload, error) { + routableEvent, err := validateAndSerializePartially(a.validator, event, visibility, routingIdentifier, objectIdentifier) if err != nil { return nil, err diff --git a/api_mock_test.go b/audit/api/api_mock_test.go similarity index 63% rename from api_mock_test.go rename to audit/api/api_mock_test.go index 726b0bd..bb8a1e0 100644 --- a/api_mock_test.go +++ b/audit/api/api_mock_test.go @@ -1,10 +1,12 @@ -package main +package api import ( - auditV1 "audit-schema/gen/go/audit/v1" "context" - "github.com/stretchr/testify/assert" "testing" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/gen/go/audit/v1" + + "github.com/stretchr/testify/assert" ) func TestMockAuditApi_Log(t *testing.T) { @@ -17,25 +19,31 @@ func TestMockAuditApi_Log(t *testing.T) { // Test t.Run("Log", func(t *testing.T) { - assert.Nil(t, (*auditApi).Log(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, objectIdentifier)) + assert.Nil(t, (*auditApi).Log( + context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, objectIdentifier)) }) t.Run("ValidateAndSerialize", func(t *testing.T) { visibility := auditV1.Visibility_VISIBILITY_PUBLIC - serializedPayload, err := (*auditApi).ValidateAndSerialize(event, visibility, routingIdentifier, objectIdentifier) + serializedPayload, err := (*auditApi).ValidateAndSerialize( + event, visibility, routingIdentifier, objectIdentifier) + assert.NoError(t, err) - validateProtobufMessagePayload(t, serializedPayload.GetPayload(), routingIdentifier, objectIdentifier, event, event.EventName, visibility) + validateProtobufMessagePayload( + t, serializedPayload.GetPayload(), routingIdentifier, objectIdentifier, event, event.EventName, visibility) }) t.Run("ValidateAndSerialize event nil", func(t *testing.T) { visibility := auditV1.Visibility_VISIBILITY_PUBLIC _, err := (*auditApi).ValidateAndSerialize(nil, visibility, routingIdentifier, objectIdentifier) + assert.ErrorIs(t, err, ErrEventNil) }) t.Run("Send", func(t *testing.T) { var payload SerializedPayload = &routablePayload{} + assert.Nil(t, (*auditApi).Send(context.Background(), routingIdentifier, &payload)) }) } diff --git a/api_routable.go b/audit/api/api_routable.go similarity index 72% rename from api_routable.go rename to audit/api/api_routable.go index 48385e1..112933c 100644 --- a/api_routable.go +++ b/audit/api/api_routable.go @@ -1,10 +1,12 @@ -package main +package api import ( - auditV1 "audit-schema/gen/go/audit/v1" "context" "errors" "fmt" + + "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/audit/messaging" + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/gen/go/audit/v1" ) // routablePayload implements SerializedPayload. @@ -34,9 +36,11 @@ type routableTopicNameResolver struct { // Resolve implements TopicNameResolver.Resolve. func (r *routableTopicNameResolver) Resolve(routingIdentifier *RoutingIdentifier) (string, error) { + if routingIdentifier == nil { return r.systemTopicName, nil } + switch routingIdentifier.Type { case RoutingIdentifierTypeOrganization: return fmt.Sprintf("topic://%s/%s", r.organizationTopicPrefix, routingIdentifier.Identifier), nil @@ -58,13 +62,17 @@ type topicNameConfig struct { // Warning: It is only there for local (compatibility) testing. // DO NOT USE IT! type routableAuditApi struct { - messagingApi *MessagingApi + messagingApi *messaging.MessagingApi topicNameResolver *TopicNameResolver validator *ProtobufValidator } // NewRoutableAuditApi can be used to initialize the audit log api. -func newRoutableAuditApi(messagingApi *MessagingApi, topicNameConfig topicNameConfig, validator ProtobufValidator) (*AuditApi, error) { +func newRoutableAuditApi( + messagingApi *messaging.MessagingApi, + topicNameConfig topicNameConfig, + validator ProtobufValidator, +) (*AuditApi, error) { if messagingApi == nil { return nil, errors.New("messaging api nil") @@ -88,7 +96,13 @@ func newRoutableAuditApi(messagingApi *MessagingApi, topicNameConfig topicNameCo } // Log implements AuditApi.Log -func (a *routableAuditApi) Log(ctx context.Context, event *auditV1.AuditEvent, visibility auditV1.Visibility, routingIdentifier *RoutingIdentifier, objectIdentifier *auditV1.ObjectIdentifier) error { +func (a *routableAuditApi) Log( + ctx context.Context, + event *auditV1.AuditEvent, + visibility auditV1.Visibility, + routingIdentifier *RoutingIdentifier, + objectIdentifier *auditV1.ObjectIdentifier, +) error { serializedPayload, err := a.ValidateAndSerialize(event, visibility, routingIdentifier, objectIdentifier) if err != nil { @@ -99,8 +113,20 @@ func (a *routableAuditApi) Log(ctx context.Context, event *auditV1.AuditEvent, v } // ValidateAndSerialize implements AuditApi.ValidateAndSerialize -func (a *routableAuditApi) ValidateAndSerialize(event *auditV1.AuditEvent, visibility auditV1.Visibility, routingIdentifier *RoutingIdentifier, objectIdentifier *auditV1.ObjectIdentifier) (SerializedPayload, error) { - routableEvent, err := validateAndSerializePartially(a.validator, event, visibility, routingIdentifier, objectIdentifier) +func (a *routableAuditApi) ValidateAndSerialize( + event *auditV1.AuditEvent, + visibility auditV1.Visibility, + routingIdentifier *RoutingIdentifier, + objectIdentifier *auditV1.ObjectIdentifier, +) (SerializedPayload, error) { + + routableEvent, err := validateAndSerializePartially( + a.validator, + event, + visibility, + routingIdentifier, + objectIdentifier) + if err != nil { return nil, err } @@ -109,6 +135,11 @@ func (a *routableAuditApi) ValidateAndSerialize(event *auditV1.AuditEvent, visib } // Send implements AuditApi.Send -func (a *routableAuditApi) Send(ctx context.Context, routingIdentifier *RoutingIdentifier, serializedPayload *SerializedPayload) error { +func (a *routableAuditApi) Send( + ctx context.Context, + routingIdentifier *RoutingIdentifier, + serializedPayload *SerializedPayload, +) error { + return send(a.topicNameResolver, a.messagingApi, ctx, routingIdentifier, serializedPayload) } diff --git a/api_routable_test.go b/audit/api/api_routable_test.go similarity index 81% rename from api_routable_test.go rename to audit/api/api_routable_test.go index 45ff26f..e0a8c31 100644 --- a/api_routable_test.go +++ b/audit/api/api_routable_test.go @@ -1,17 +1,20 @@ -package main +package api import ( - auditV1 "audit-schema/gen/go/audit/v1" "context" "errors" "fmt" + "testing" + "time" + + "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/audit/messaging" + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/gen/go/audit/v1" + "github.com/Azure/go-amqp" "github.com/bufbuild/protovalidate-go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "google.golang.org/protobuf/proto" - "testing" - "time" ) func TestRoutableAuditApi(t *testing.T) { @@ -21,12 +24,12 @@ func TestRoutableAuditApi(t *testing.T) { defer cancelFn() // Start solace docker container - solaceContainer, err := NewSolaceContainer(context.Background()) + solaceContainer, err := messaging.NewSolaceContainer(context.Background()) assert.NoError(t, err) defer solaceContainer.Stop() // Instantiate the messaging api - messagingApi, err := NewAmqpMessagingApi(AmqpConfig{URL: solaceContainer.AmqpConnectionString}) + messagingApi, err := messaging.NewAmqpMessagingApi(messaging.AmqpConfig{URL: solaceContainer.AmqpConnectionString}) assert.NoError(t, err) // Validator @@ -39,7 +42,10 @@ func TestRoutableAuditApi(t *testing.T) { systemTopicName := "topic://system/admin-events" auditApi, err := newRoutableAuditApi( messagingApi, - topicNameConfig{OrganizationTopicPrefix: organizationTopicPrefix, ProjectTopicPrefix: projectTopicPrefix, SystemTopicName: systemTopicName}, + topicNameConfig{ + OrganizationTopicPrefix: organizationTopicPrefix, + ProjectTopicPrefix: projectTopicPrefix, + SystemTopicName: systemTopicName}, validator, ) assert.NoError(t, err) @@ -69,7 +75,15 @@ func TestRoutableAuditApi(t *testing.T) { message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) assert.NoError(t, err) - validateSentEvent(t, organizationTopicPrefix, message, routingIdentifier, objectIdentifier, event, "ORGANIZATION_CREATED", visibility) + validateSentEvent( + t, + organizationTopicPrefix, + message, + routingIdentifier, + objectIdentifier, + event, + "ORGANIZATION_CREATED", + visibility) }) t.Run("Log private organization event", func(t *testing.T) { @@ -81,7 +95,10 @@ func TestRoutableAuditApi(t *testing.T) { // Instantiate test data event, routingIdentifier, objectIdentifier := NewOrganizationAuditEvent(nil) - assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, fmt.Sprintf("org/%s", routingIdentifier.Identifier))) + topicName := fmt.Sprintf("org/%s", routingIdentifier.Identifier) + assert.NoError( + t, + solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicName)) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PRIVATE @@ -98,7 +115,15 @@ func TestRoutableAuditApi(t *testing.T) { message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) assert.NoError(t, err) - validateSentEvent(t, organizationTopicPrefix, message, routingIdentifier, objectIdentifier, event, "ORGANIZATION_CREATED", visibility) + validateSentEvent( + t, + organizationTopicPrefix, + message, + routingIdentifier, + objectIdentifier, + event, + "ORGANIZATION_CREATED", + visibility) }) // Check logging of folder events @@ -126,7 +151,15 @@ func TestRoutableAuditApi(t *testing.T) { message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) assert.NoError(t, err) - validateSentEvent(t, organizationTopicPrefix, message, routingIdentifier, objectIdentifier, event, "FOLDER_CREATED", visibility) + validateSentEvent( + t, + organizationTopicPrefix, + message, + routingIdentifier, + objectIdentifier, + event, + "FOLDER_CREATED", + visibility) }) t.Run("Log private folder event", func(t *testing.T) { @@ -138,7 +171,8 @@ func TestRoutableAuditApi(t *testing.T) { // Instantiate test data event, routingIdentifier, objectIdentifier := NewFolderAuditEvent(nil) - assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, fmt.Sprintf("org/%s", routingIdentifier.Identifier))) + topicName := fmt.Sprintf("org/%s", routingIdentifier.Identifier) + assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicName)) // Log the event to solace visibility := auditV1.Visibility_VISIBILITY_PRIVATE @@ -155,7 +189,15 @@ func TestRoutableAuditApi(t *testing.T) { message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) assert.NoError(t, err) - validateSentEvent(t, organizationTopicPrefix, message, routingIdentifier, objectIdentifier, event, "FOLDER_CREATED", visibility) + validateSentEvent( + t, + organizationTopicPrefix, + message, + routingIdentifier, + objectIdentifier, + event, + "FOLDER_CREATED", + visibility) }) // Check logging of project events @@ -184,7 +226,15 @@ func TestRoutableAuditApi(t *testing.T) { message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) assert.NoError(t, err) - validateSentEvent(t, projectTopicPrefix, message, routingIdentifier, objectIdentifier, event, "PROJECT_CREATED", visibility) + validateSentEvent( + t, + projectTopicPrefix, + message, + routingIdentifier, + objectIdentifier, + event, + "PROJECT_CREATED", + visibility) }) t.Run("Log private project event", func(t *testing.T) { @@ -212,7 +262,15 @@ func TestRoutableAuditApi(t *testing.T) { message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) assert.NoError(t, err) - validateSentEvent(t, projectTopicPrefix, message, routingIdentifier, objectIdentifier, event, "PROJECT_CREATED", visibility) + validateSentEvent( + t, + projectTopicPrefix, + message, + routingIdentifier, + objectIdentifier, + event, + "PROJECT_CREATED", + visibility) }) // Check logging of system events @@ -249,7 +307,14 @@ func TestRoutableAuditApi(t *testing.T) { assert.NoError(t, proto.Unmarshal(message.Data[0], &protobufMessage)) assert.Equal(t, "audit.v1.RoutableAuditEvent", protobufMessage.ProtobufType) - validateProtobufMessagePayload(t, message.Data[0], nil, nil, event, "SYSTEM_CHANGED", visibility) + validateProtobufMessagePayload( + t, + message.Data[0], + nil, + nil, + event, + "SYSTEM_CHANGED", + visibility) }) // Check logging of organization events @@ -277,11 +342,28 @@ func TestRoutableAuditApi(t *testing.T) { message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true) assert.NoError(t, err) - validateSentEvent(t, organizationTopicPrefix, message, routingIdentifier, objectIdentifier, event, "ORGANIZATION_CREATED", visibility) + validateSentEvent( + t, + organizationTopicPrefix, + message, + routingIdentifier, + objectIdentifier, + event, + "ORGANIZATION_CREATED", + visibility) }) } -func validateSentEvent(t *testing.T, topicPrefix string, message *amqp.Message, routingIdentifier *RoutingIdentifier, objectIdentifier *auditV1.ObjectIdentifier, event *auditV1.AuditEvent, eventName string, visibility auditV1.Visibility) { +func validateSentEvent( + t *testing.T, + topicPrefix string, + message *amqp.Message, + routingIdentifier *RoutingIdentifier, + objectIdentifier *auditV1.ObjectIdentifier, + event *auditV1.AuditEvent, + eventName string, + visibility auditV1.Visibility, +) { // Check topic name assert.Equal(t, @@ -293,10 +375,19 @@ func validateSentEvent(t *testing.T, topicPrefix string, message *amqp.Message, assert.NoError(t, proto.Unmarshal(message.Data[0], &protobufMessage)) assert.Equal(t, "audit.v1.RoutableAuditEvent", protobufMessage.ProtobufType) - validateProtobufMessagePayload(t, message.Data[0], routingIdentifier, objectIdentifier, event, eventName, visibility) + validateProtobufMessagePayload( + t, message.Data[0], routingIdentifier, objectIdentifier, event, eventName, visibility) } -func validateProtobufMessagePayload(t *testing.T, payload []byte, routingIdentifier *RoutingIdentifier, objectIdentifier *auditV1.ObjectIdentifier, event *auditV1.AuditEvent, eventName string, visibility auditV1.Visibility) { +func validateProtobufMessagePayload( + t *testing.T, + payload []byte, + routingIdentifier *RoutingIdentifier, + objectIdentifier *auditV1.ObjectIdentifier, + event *auditV1.AuditEvent, + eventName string, + visibility auditV1.Visibility, +) { // Check deserialized message var protobufMessage auditV1.ProtobufMessage diff --git a/test_data.go b/audit/api/test_data.go similarity index 97% rename from test_data.go rename to audit/api/test_data.go index ae9570d..0000e31 100644 --- a/test_data.go +++ b/audit/api/test_data.go @@ -1,11 +1,13 @@ -package main +package api import ( - auditV1 "audit-schema/gen/go/audit/v1" + "time" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/gen/go/audit/v1" + "github.com/google/uuid" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" - "time" ) func NewOrganizationAuditEvent( diff --git a/client.go b/audit/messaging/client.go similarity index 98% rename from client.go rename to audit/messaging/client.go index 3bef6ad..c2d0563 100644 --- a/client.go +++ b/audit/messaging/client.go @@ -1,4 +1,4 @@ -package main +package messaging // terraform-provider-solacebroker // @@ -23,12 +23,13 @@ import ( "encoding/json" "errors" "fmt" - "github.com/hashicorp/go-retryablehttp" "io" "log/slog" "net/http" "net/http/cookiejar" "time" + + "github.com/hashicorp/go-retryablehttp" ) var ( @@ -172,7 +173,7 @@ func (c *Client) doRequest(request *http.Request) ([]byte, error) { return rawBody, nil } -func parseResponseAsObject(ctx context.Context, request *http.Request, dataResponse []byte) (map[string]any, error) { +func parseResponseAsObject(_ context.Context, request *http.Request, dataResponse []byte) (map[string]any, error) { data := map[string]any{} err := json.Unmarshal(dataResponse, &data) if err != nil { diff --git a/messaging.go b/audit/messaging/messaging.go similarity index 94% rename from messaging.go rename to audit/messaging/messaging.go index 9994ed3..e64e9cd 100644 --- a/messaging.go +++ b/audit/messaging/messaging.go @@ -1,4 +1,4 @@ -package main +package messaging import ( "context" @@ -7,19 +7,12 @@ import ( "log/slog" "strings" "time" + //"dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/audit/api" ) // Default connection timeout for the AMQP connection const connectionTimeoutSeconds = 10 -// TopicNameResolver is an abstraction for dynamic topic name resolution -// based on event data or api parameters. -type TopicNameResolver interface { - - // Resolve returns a topic name for the given routing identifier - Resolve(routingIdentifier *RoutingIdentifier) (string, error) -} - // MessagingApi is an abstraction for a messaging system that can be used to send // audit logs to the audit log system. type MessagingApi interface { diff --git a/messaging_test.go b/audit/messaging/messaging_test.go similarity index 95% rename from messaging_test.go rename to audit/messaging/messaging_test.go index 8216a53..025787d 100644 --- a/messaging_test.go +++ b/audit/messaging/messaging_test.go @@ -1,4 +1,4 @@ -package main +package messaging import ( "context" @@ -11,15 +11,6 @@ import ( "time" ) -type MessagingApiMock struct { - mock.Mock -} - -func (m *MessagingApiMock) Send(ctx context.Context, topic string, data []byte, contentType string) error { - args := m.Called(ctx, topic, data, contentType) - return args.Error(0) -} - type AmqpSessionMock struct { mock.Mock } diff --git a/solace.go b/audit/messaging/solace.go similarity index 97% rename from solace.go rename to audit/messaging/solace.go index e9abe72..b704db0 100644 --- a/solace.go +++ b/audit/messaging/solace.go @@ -1,4 +1,4 @@ -package main +package messaging import ( "context" @@ -229,10 +229,12 @@ func (c SolaceContainer) ValidateTopicName(topicSubscriptionTopicPattern string, } // Check topic subscription topic pattern - allowedTopicSubscriptionCharacters, err := regexp.Compile("(?:(?:[0-9A-Za-z-.]+|[0-9A-Za-z-.]*\\*)(?:/(?:[0-9A-Za-z-.]+|[0-9A-Za-z-.]*\\*))+|(?:[0-9A-Za-z-.]+|[0-9A-Za-z-.]*\\*)|/>)|>") + allowedTopicSubscriptionCharacters, err := regexp.Compile( + "(?:(?:[0-9A-Za-z-.]+|[0-9A-Za-z-.]*\\*)(?:/(?:[0-9A-Za-z-.]+|[0-9A-Za-z-.]*\\*))+|(?:[0-9A-Za-z-.]+|[0-9A-Za-z-.]*\\*)|/>)|>") if err != nil { return err } + if !allowedTopicSubscriptionCharacters.MatchString(topicSubscriptionTopicPattern) { return errors.New("invalid topic subscription name") } diff --git a/go.mod b/go.mod index 33f769c..f30af02 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module audit-schema +module dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git go 1.22 diff --git a/main.go b/main.go index 486cdcd..f318f0a 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,10 @@ package main import ( - auditV1 "audit-schema/gen/go/audit/v1" "fmt" + + auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/gen/go/audit/v1" + "github.com/bufbuild/protovalidate-go" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect"