audit-go/audit/api/api_legacy_test.go

489 lines
16 KiB
Go

package api
import (
"context"
"encoding/json"
"errors"
"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) {
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, nil)))
// Specify test timeout
ctx, cancelFn := context.WithTimeout(context.Background(), 120*time.Second)
defer cancelFn()
// Start solace docker container
solaceContainer, err := messaging.NewSolaceContainer(context.Background())
assert.NoError(t, err)
defer solaceContainer.Stop()
// Instantiate the messaging api
messagingApi, err := messaging.NewAmqpMessagingApi(messaging.AmqpConfig{URL: solaceContainer.AmqpConnectionString})
assert.NoError(t, err)
// Validator
validator, err := protovalidate.New()
assert.NoError(t, err)
topicSubscriptionTopicPattern := "audit-log/>"
// Check logging of organization events
t.Run("Log public organization event", func(t *testing.T) {
defer solaceContainer.StopOnError()
slog.Info("test abc")
// Create the queue and topic subscription in solace
queueName := "org-event-public-legacy"
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
topicName := "topic://audit-log/eu01/v1/resource-manager/organization-created"
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
// Instantiate audit api
auditApi, err := NewLegacyAuditApi(
messagingApi,
LegacyTopicNameConfig{TopicName: topicName},
validator,
)
assert.NoError(t, err)
// Instantiate test data
event, routingIdentifier, objectIdentifier := NewOrganizationAuditEvent(nil)
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
assert.NoError(t, (*auditApi).Log(
ctx,
event,
visibility,
routingIdentifier,
objectIdentifier,
))
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
assert.NoError(t, err)
validateSentMessage(t, topicName, message, event)
})
t.Run("Log private organization event", func(t *testing.T) {
defer solaceContainer.StopOnError()
// Create the queue and topic subscription in solace
queueName := "org-event-private-legacy"
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
topicName := "topic://audit-log/eu01/v1/resource-manager/organization-created"
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
// Instantiate audit api
auditApi, err := NewLegacyAuditApi(
messagingApi,
LegacyTopicNameConfig{TopicName: topicName},
validator,
)
assert.NoError(t, err)
// Instantiate test data
event, routingIdentifier, objectIdentifier := NewOrganizationAuditEvent(nil)
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
assert.NoError(t, (*auditApi).Log(
ctx,
event,
visibility,
routingIdentifier,
objectIdentifier,
))
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
assert.NoError(t, err)
validateSentMessage(t, topicName, message, event)
})
// Check logging of folder events
t.Run("Log public folder event", func(t *testing.T) {
defer solaceContainer.StopOnError()
// Create the queue and topic subscription in solace
queueName := "folder-event-public-legacy"
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
topicName := "topic://audit-log/eu01/v1/resource-manager/folder-created"
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
// Instantiate audit api
auditApi, err := NewLegacyAuditApi(
messagingApi,
LegacyTopicNameConfig{TopicName: topicName},
validator,
)
assert.NoError(t, err)
// Instantiate test data
event, routingIdentifier, objectIdentifier := NewFolderAuditEvent(nil)
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
assert.NoError(t, (*auditApi).Log(
ctx,
event,
visibility,
routingIdentifier,
objectIdentifier,
))
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
assert.NoError(t, err)
validateSentMessage(t, topicName, message, event)
})
t.Run("Log private folder event", func(t *testing.T) {
defer solaceContainer.StopOnError()
// Create the queue and topic subscription in solace
queueName := "folder-event-private-legacy"
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
topicName := "topic://audit-log/eu01/v1/resource-manager/folder-created"
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
// Instantiate audit api
auditApi, err := NewLegacyAuditApi(
messagingApi,
LegacyTopicNameConfig{TopicName: topicName},
validator,
)
assert.NoError(t, err)
// Instantiate test data
event, routingIdentifier, objectIdentifier := NewFolderAuditEvent(nil)
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
assert.NoError(t, (*auditApi).Log(
ctx,
event,
visibility,
routingIdentifier,
objectIdentifier,
))
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
assert.NoError(t, err)
validateSentMessage(t, topicName, message, event)
})
// Check logging of project events
t.Run("Log public project event", func(t *testing.T) {
defer solaceContainer.StopOnError()
// Create the queue and topic subscription in solace
queueName := "project-event-public-legacy"
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
topicName := "topic://audit-log/eu01/v1/resource-manager/project-created"
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
// Instantiate audit api
auditApi, err := NewLegacyAuditApi(
messagingApi,
LegacyTopicNameConfig{TopicName: topicName},
validator,
)
assert.NoError(t, err)
// Instantiate test data
event, routingIdentifier, objectIdentifier := NewProjectAuditEvent(nil)
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
assert.NoError(t, (*auditApi).Log(
ctx,
event,
visibility,
routingIdentifier,
objectIdentifier,
))
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
assert.NoError(t, err)
validateSentMessage(t, topicName, message, event)
})
t.Run("Log private project event", func(t *testing.T) {
defer solaceContainer.StopOnError()
// Create the queue and topic subscription in solace
queueName := "project-event-private-legacy"
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
topicName := "topic://audit-log/eu01/v1/resource-manager/project-created"
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
// Instantiate audit api
auditApi, err := NewLegacyAuditApi(
messagingApi,
LegacyTopicNameConfig{TopicName: topicName},
validator,
)
assert.NoError(t, err)
// Instantiate test data
event, routingIdentifier, objectIdentifier := NewProjectAuditEvent(nil)
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
assert.NoError(t, (*auditApi).Log(
ctx,
event,
visibility,
routingIdentifier,
objectIdentifier,
))
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
assert.NoError(t, err)
validateSentMessage(t, topicName, message, event)
})
// Check logging of system events
t.Run("Log private system event", func(t *testing.T) {
defer solaceContainer.StopOnError()
queueName := "system-event-private"
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
topicName := "topic://audit-log/eu01/v1/resource-manager/system-changed"
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
// Instantiate audit api
auditApi, err := NewLegacyAuditApi(
messagingApi,
LegacyTopicNameConfig{TopicName: topicName},
validator,
)
assert.NoError(t, err)
// Instantiate test data
event := NewSystemAuditEvent(nil)
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
assert.NoError(t,
(*auditApi).Log(
ctx,
event,
visibility,
nil,
nil,
))
// Receive the event from solace
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
assert.NoError(t, err)
// Check topic name
assert.Equal(t, topicName, *message.Properties.To)
// Check topic name
assert.Equal(t, topicName, *message.Properties.To)
// Check deserialized message
var auditEvent LegacyAuditEvent
assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent))
assert.Equal(t, event.EventName, auditEvent.EventName)
assert.Equal(t, event.EventTimeStamp.AsTime(), auditEvent.EventTimeStamp)
assert.Equal(t, event.Initiator.Id, auditEvent.Initiator.Id)
assert.Equal(t, "SYSTEM_EVENT", auditEvent.EventType)
assert.Equal(t, "INFO", auditEvent.Severity)
assert.Equal(t, "none", auditEvent.Request.Endpoint)
assert.Equal(t, "0.0.0.0", auditEvent.SourceIpAddress)
assert.Equal(t, "none", auditEvent.UserAgent)
})
t.Run("Log event with details", func(t *testing.T) {
defer solaceContainer.StopOnError()
// Create the queue and topic subscription in solace
queueName := "org-event-with-details-legacy"
assert.NoError(t, solaceContainer.QueueCreate(ctx, queueName))
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, topicSubscriptionTopicPattern))
topicName := "topic://audit-log/eu01/v1/resource-manager/organization-created"
assert.NoError(t, solaceContainer.ValidateTopicName(topicSubscriptionTopicPattern, topicName))
// Instantiate audit api
auditApi, err := NewLegacyAuditApi(
messagingApi,
LegacyTopicNameConfig{TopicName: topicName},
validator,
)
assert.NoError(t, err)
// Instantiate test data
event, routingIdentifier, objectIdentifier := NewOrganizationAuditEventWithDetails()
// Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
assert.NoError(t, (*auditApi).Log(
ctx,
event,
visibility,
routingIdentifier,
objectIdentifier,
))
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
assert.NoError(t, err)
validateSentMessageWithDetails(t, topicName, message, event)
})
}
func validateSentMessage(t *testing.T, topicName string, message *amqp.Message, event *auditV1.AuditEvent) {
// Check topic name
assert.Equal(t, topicName, *message.Properties.To)
// Check deserialized message
var auditEvent LegacyAuditEvent
assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent))
assert.Equal(t, event.EventName, auditEvent.EventName)
assert.Equal(t, event.EventTimeStamp.AsTime(), auditEvent.EventTimeStamp)
assert.Equal(t, event.Initiator.Id, auditEvent.Initiator.Id)
assert.Equal(t, "ADMIN_ACTIVITY", auditEvent.EventType)
assert.Equal(t, "INFO", auditEvent.Severity)
assert.Equal(t, "none", auditEvent.Request.Endpoint)
assert.Equal(t, "0.0.0.0", auditEvent.SourceIpAddress)
assert.Equal(t, "none", auditEvent.UserAgent)
}
func validateSentMessageWithDetails(t *testing.T, topicName string, message *amqp.Message, event *auditV1.AuditEvent) {
// Check topic name
assert.Equal(t, topicName, *message.Properties.To)
// Check deserialized message
var auditEvent LegacyAuditEvent
assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent))
assert.Equal(t, event.EventName, auditEvent.EventName)
assert.Equal(t, event.EventTimeStamp.AsTime(), auditEvent.EventTimeStamp)
assert.Equal(t, event.Initiator.Id, auditEvent.Initiator.Id)
assert.Equal(t, "ADMIN_ACTIVITY", auditEvent.EventType)
assert.Equal(t, "INFO", auditEvent.Severity)
assert.Equal(t, event.Request.Endpoint, auditEvent.Request.Endpoint)
assert.Equal(t, event.Request.SourceIpAddress, auditEvent.SourceIpAddress)
assert.Equal(t, *event.Request.UserAgent, auditEvent.UserAgent)
assert.Equal(t, event.Request.Body.AsMap(), *auditEvent.Request.Body)
assert.Equal(t, event.Request.Parameters.AsMap(), *auditEvent.Request.Parameters)
assert.Equal(t, event.Details.AsMap(), *auditEvent.Details)
assert.Equal(t, event.Result.AsMap(), *auditEvent.Result)
for _, header := range event.Request.Headers {
assert.Equal(t, header.Value, (*auditEvent.Request.Headers)[header.Key])
}
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)
}
}
func TestLegacyAuditApi_NewLegacyAuditApi_MessagingApiNil(t *testing.T) {
auditApi, err := NewLegacyAuditApi(nil, LegacyTopicNameConfig{}, nil)
assert.Nil(t, auditApi)
assert.EqualError(t, err, "messaging api nil")
}
func TestLegacyAuditApi_ValidateAndSerialize_ValidationFailed(t *testing.T) {
expectedError := errors.New("expected error")
validator := &ProtobufValidatorMock{}
validator.On("Validate", mock.Anything).Return(expectedError)
var protobufValidator ProtobufValidator = validator
auditApi := LegacyAuditApi{validator: &protobufValidator}
event := NewSystemAuditEvent(nil)
_, err := auditApi.ValidateAndSerialize(event, auditV1.Visibility_VISIBILITY_PUBLIC, nil, nil)
assert.ErrorIs(t, err, expectedError)
}
func TestLegacyAuditApi_Log_ValidationFailed(t *testing.T) {
expectedError := errors.New("expected error")
validator := &ProtobufValidatorMock{}
validator.On("Validate", mock.Anything).Return(expectedError)
var protobufValidator ProtobufValidator = validator
auditApi := LegacyAuditApi{validator: &protobufValidator}
event := NewSystemAuditEvent(nil)
err := auditApi.Log(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, nil, nil)
assert.ErrorIs(t, err, expectedError)
}
func TestLegacyAuditApi_Log_NilEvent(t *testing.T) {
auditApi := LegacyAuditApi{}
err := auditApi.Log(context.Background(), nil, auditV1.Visibility_VISIBILITY_PUBLIC, nil, nil)
assert.ErrorIs(t, err, ErrEventNil)
}
func TestLegacyAuditApi_ConvertAndSerializeIntoLegacyFormatInvalidObjectIdentifierType(t *testing.T) {
customization := func(event *auditV1.AuditEvent,
routingIdentifier *RoutingIdentifier,
objectIdentifier *auditV1.ObjectIdentifier) {
objectIdentifier.Type = 4
}
event, identifier, objectIdentifier := NewProjectAuditEvent(&customization)
validator := &ProtobufValidatorMock{}
validator.On("Validate", mock.Anything).Return(nil)
var protobufValidator ProtobufValidator = validator
auditApi := LegacyAuditApi{validator: &protobufValidator}
_, err := auditApi.ValidateAndSerialize(event, auditV1.Visibility_VISIBILITY_PUBLIC, identifier, objectIdentifier)
assert.ErrorIs(t, err, ErrUnsupportedObjectIdentifierType)
}
func TestLegacyAuditApi_ConvertAndSerializeIntoLegacyFormat_NoResourceReference(t *testing.T) {
event, _, _ := NewProjectAuditEvent(nil)
routableEvent := auditV1.RoutableAuditEvent{
EventName: event.EventName,
Visibility: auditV1.Visibility_VISIBILITY_PUBLIC,
ResourceReference: nil,
Data: nil,
}
auditApi := LegacyAuditApi{}
_, err := auditApi.convertAndSerializeIntoLegacyFormat(event, &routableEvent)
assert.ErrorIs(t, err, ErrUnsupportedResourceReferenceType)
}