mirror of
https://dev.azure.com/schwarzit/schwarzit.stackit-public/_git/audit-go
synced 2026-02-08 00:57:24 +00:00
So far the SDK provided a messaging API that was not thread-safe (i.e. goroutine-safe). Additionally the SDK provided a MutexAPI which made it thread-safe at the cost of removed concurrency possibilities. The changes implemented in this commit replace both implementations with a thread-safe connection pool based solution. The api gateway is a SDK user that requires reliable high performance send capabilities with a limit amount of amqp connections. These changes in the PR try address their requirements by moving the responsibility of connection management into the SDK. From this change other SDK users will benefit as well. Security-concept-update-needed: false. JIRA Work Item: STACKITALO-62
661 lines
23 KiB
Go
661 lines
23 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"go.opentelemetry.io/otel"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/audit/messaging"
|
|
auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.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) {
|
|
|
|
// 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.NewAmqpApi(messaging.AmqpConnectionPoolConfig{
|
|
Parameters: messaging.AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString},
|
|
PoolSize: 1,
|
|
})
|
|
assert.NoError(t, err)
|
|
|
|
// Validator
|
|
validator, err := protovalidate.New()
|
|
assert.NoError(t, err)
|
|
|
|
topicSubscriptionTopicPattern := "audit-log/>"
|
|
|
|
// Check that event-type data-access is rejected as it is currently
|
|
// not supported by downstream services
|
|
t.Run("reject data access event", func(t *testing.T) {
|
|
defer solaceContainer.StopOnError()
|
|
|
|
// Create the queue and topic subscription in solace
|
|
queueName := "org-reject-data-access-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-rejected"
|
|
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, objectIdentifier := newOrganizationAuditEvent(nil)
|
|
event.LogName = strings.Replace(event.LogName, string(EventTypeAdminActivity), string(EventTypeDataAccess), 1)
|
|
|
|
// Log the event to solace
|
|
assert.ErrorIs(t, auditApi.Log(
|
|
ctx,
|
|
event,
|
|
auditV1.Visibility_VISIBILITY_PUBLIC,
|
|
NewRoutableIdentifier(objectIdentifier),
|
|
), ErrUnsupportedEventTypeDataAccess)
|
|
})
|
|
|
|
// Check logging of organization events
|
|
t.Run("Log public organization event", func(t *testing.T) {
|
|
defer solaceContainer.StopOnError()
|
|
|
|
// 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, objectIdentifier := newOrganizationAuditEvent(nil)
|
|
|
|
// Log the event to solace
|
|
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
|
|
assert.NoError(t, auditApi.Log(
|
|
ctx,
|
|
event,
|
|
visibility,
|
|
NewRoutableIdentifier(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, objectIdentifier := newOrganizationAuditEvent(nil)
|
|
|
|
// Log the event to solace
|
|
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
|
|
assert.NoError(t, auditApi.Log(
|
|
ctx,
|
|
event,
|
|
visibility,
|
|
NewRoutableIdentifier(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, objectIdentifier := newFolderAuditEvent(nil)
|
|
|
|
// Log the event to solace
|
|
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
|
|
assert.NoError(t, auditApi.Log(
|
|
ctx,
|
|
event,
|
|
visibility,
|
|
NewRoutableIdentifier(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, objectIdentifier := newFolderAuditEvent(nil)
|
|
|
|
// Log the event to solace
|
|
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
|
|
assert.NoError(t, auditApi.Log(
|
|
ctx,
|
|
event,
|
|
visibility,
|
|
NewRoutableIdentifier(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, objectIdentifier := newProjectAuditEvent(nil)
|
|
|
|
// Log the event to solace
|
|
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
|
|
assert.NoError(t, auditApi.Log(
|
|
ctx,
|
|
event,
|
|
visibility,
|
|
NewRoutableIdentifier(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, objectIdentifier := newProjectAuditEvent(nil)
|
|
|
|
// Log the event to solace
|
|
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
|
|
assert.NoError(t, auditApi.Log(
|
|
ctx,
|
|
event,
|
|
visibility,
|
|
NewRoutableIdentifier(objectIdentifier),
|
|
))
|
|
|
|
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
|
|
assert.NoError(t, err)
|
|
|
|
validateSentMessage(t, topicName, message, event)
|
|
})
|
|
|
|
// Check logging of system events with identifier
|
|
t.Run("Log private project system event", func(t *testing.T) {
|
|
defer solaceContainer.StopOnError()
|
|
|
|
queueName := "project-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/project-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 := newProjectSystemAuditEvent(nil)
|
|
|
|
// Log the event to solace
|
|
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
|
|
assert.NoError(t,
|
|
auditApi.Log(
|
|
ctx,
|
|
event,
|
|
visibility,
|
|
RoutableSystemIdentifier,
|
|
))
|
|
|
|
// 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)
|
|
assert.Equal(t, "", message.ApplicationProperties["cloudEvents:traceparent"])
|
|
assert.Equal(t, "", message.ApplicationProperties["cloudEvents:tracestate"])
|
|
|
|
// Check deserialized message
|
|
var auditEvent LegacyAuditEvent
|
|
assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent))
|
|
|
|
assert.Equal(t, event.ProtoPayload.ResourceName, *auditEvent.ResourceName)
|
|
assert.Equal(t, event.ProtoPayload.OperationName, auditEvent.EventName)
|
|
assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(), auditEvent.EventTimeStamp)
|
|
assert.Equal(t, event.ProtoPayload.AuthenticationInfo.PrincipalId, auditEvent.Initiator.Id)
|
|
assert.Equal(t, "SYSTEM_EVENT", auditEvent.EventType)
|
|
assert.Equal(t, "INFO", auditEvent.Severity)
|
|
assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Path, auditEvent.Request.Endpoint)
|
|
assert.Equal(t, event.ProtoPayload.RequestMetadata.CallerIp, auditEvent.SourceIpAddress)
|
|
assert.Equal(t, event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent, auditEvent.UserAgent)
|
|
})
|
|
|
|
// 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,
|
|
RoutableSystemIdentifier,
|
|
))
|
|
|
|
// 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)
|
|
assert.Equal(t, "", message.ApplicationProperties["cloudEvents:traceparent"])
|
|
assert.Equal(t, "", message.ApplicationProperties["cloudEvents:tracestate"])
|
|
|
|
// Check deserialized message
|
|
var auditEvent LegacyAuditEvent
|
|
assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent))
|
|
|
|
assert.Equal(t, event.ProtoPayload.OperationName, auditEvent.EventName)
|
|
assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(), auditEvent.EventTimeStamp)
|
|
assert.Equal(t, event.ProtoPayload.AuthenticationInfo.PrincipalId, auditEvent.Initiator.Id)
|
|
assert.Equal(t, "SYSTEM_EVENT", auditEvent.EventType)
|
|
assert.Equal(t, "INFO", auditEvent.Severity)
|
|
assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Path, auditEvent.Request.Endpoint)
|
|
assert.Equal(t, event.ProtoPayload.RequestMetadata.CallerIp, auditEvent.SourceIpAddress)
|
|
assert.Equal(t, event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent, 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, objectIdentifier := newOrganizationAuditEvent(nil)
|
|
escapedQuery := url.QueryEscape("param=value")
|
|
event.ProtoPayload.RequestMetadata.RequestAttributes.Query = &escapedQuery
|
|
|
|
// Log the event to solace
|
|
visibility := auditV1.Visibility_VISIBILITY_PUBLIC
|
|
assert.NoError(t, auditApi.Log(
|
|
ctx,
|
|
event,
|
|
visibility,
|
|
NewRoutableIdentifier(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.AuditLogEntry,
|
|
) {
|
|
|
|
// Check message properties
|
|
assert.Equal(t, topicName, *message.Properties.To)
|
|
assert.Equal(t, "", message.ApplicationProperties["cloudEvents:traceparent"])
|
|
assert.Equal(t, "", message.ApplicationProperties["cloudEvents:tracestate"])
|
|
|
|
// Check deserialized message
|
|
var auditEvent LegacyAuditEvent
|
|
assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent))
|
|
|
|
var severity string
|
|
switch event.Severity {
|
|
case auditV1.LogSeverity_LOG_SEVERITY_DEFAULT:
|
|
fallthrough
|
|
case auditV1.LogSeverity_LOG_SEVERITY_DEBUG:
|
|
fallthrough
|
|
case auditV1.LogSeverity_LOG_SEVERITY_INFO:
|
|
fallthrough
|
|
case auditV1.LogSeverity_LOG_SEVERITY_NOTICE:
|
|
fallthrough
|
|
case auditV1.LogSeverity_LOG_SEVERITY_WARNING:
|
|
severity = "INFO"
|
|
case auditV1.LogSeverity_LOG_SEVERITY_ERROR:
|
|
fallthrough
|
|
case auditV1.LogSeverity_LOG_SEVERITY_CRITICAL:
|
|
fallthrough
|
|
case auditV1.LogSeverity_LOG_SEVERITY_ALERT:
|
|
fallthrough
|
|
case auditV1.LogSeverity_LOG_SEVERITY_EMERGENCY:
|
|
severity = "ERROR"
|
|
default:
|
|
assert.Fail(t, "unknown log severity")
|
|
}
|
|
|
|
assert.Equal(t, event.ProtoPayload.OperationName, auditEvent.EventName)
|
|
assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(), auditEvent.EventTimeStamp)
|
|
assert.Equal(t, event.ProtoPayload.AuthenticationInfo.PrincipalId, auditEvent.Initiator.Id)
|
|
assert.Equal(t, "ADMIN_ACTIVITY", auditEvent.EventType)
|
|
assert.Equal(t, severity, auditEvent.Severity)
|
|
assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Path, auditEvent.Request.Endpoint)
|
|
assert.Equal(t, event.ProtoPayload.RequestMetadata.CallerIp, auditEvent.SourceIpAddress)
|
|
assert.Equal(t, event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent, auditEvent.UserAgent)
|
|
}
|
|
|
|
func validateSentMessageWithDetails(
|
|
t *testing.T,
|
|
topicName string,
|
|
message *amqp.Message,
|
|
event *auditV1.AuditLogEntry,
|
|
) {
|
|
|
|
// Check topic name
|
|
assert.Equal(t, topicName, *message.Properties.To)
|
|
assert.Equal(t, ContentTypeCloudEventsJson, message.ApplicationProperties["cloudEvents:datacontenttype"])
|
|
assert.Equal(t, DataTypeLegacyAuditEventV1, message.ApplicationProperties["cloudEvents:type"])
|
|
assert.Equal(t, "", message.ApplicationProperties["cloudEvents:traceparent"])
|
|
assert.Equal(t, "", message.ApplicationProperties["cloudEvents:tracestate"])
|
|
|
|
// Check deserialized message
|
|
var auditEvent LegacyAuditEvent
|
|
assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent))
|
|
|
|
assert.Equal(t, event.ProtoPayload.OperationName, auditEvent.EventName)
|
|
assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(), auditEvent.EventTimeStamp)
|
|
assert.Equal(t, event.ProtoPayload.AuthenticationInfo.PrincipalId, auditEvent.Initiator.Id)
|
|
assert.Equal(t, "ADMIN_ACTIVITY", auditEvent.EventType)
|
|
assert.Equal(t, "INFO", auditEvent.Severity)
|
|
assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Path, auditEvent.Request.Endpoint)
|
|
var parameters map[string]interface{} = nil
|
|
if event.ProtoPayload.RequestMetadata.RequestAttributes.Path != "" &&
|
|
event.ProtoPayload.RequestMetadata.RequestAttributes.Query != nil &&
|
|
*event.ProtoPayload.RequestMetadata.RequestAttributes.Query != "" {
|
|
parameters = map[string]interface{}{}
|
|
|
|
unescapedQuery, err := url.QueryUnescape(*event.ProtoPayload.RequestMetadata.RequestAttributes.Query)
|
|
assert.NoError(t, err)
|
|
parsedUrl, err := url.Parse(fmt.Sprintf("%s?%s",
|
|
event.ProtoPayload.RequestMetadata.RequestAttributes.Path,
|
|
unescapedQuery))
|
|
|
|
assert.NoError(t, err)
|
|
for k, v := range parsedUrl.Query() {
|
|
parameters[k] = v
|
|
}
|
|
}
|
|
assert.Equal(t, event.ProtoPayload.Request.AsMap(), *auditEvent.Request.Body)
|
|
assert.Equal(t, parameters, *auditEvent.Request.Parameters)
|
|
for key, value := range event.ProtoPayload.RequestMetadata.RequestAttributes.Headers {
|
|
assert.Equal(t, value, (*auditEvent.Request.Headers)[key])
|
|
}
|
|
assert.Equal(t, event.ProtoPayload.RequestMetadata.CallerIp, auditEvent.SourceIpAddress)
|
|
assert.Equal(t, event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent, auditEvent.UserAgent)
|
|
assert.Equal(t, event.ProtoPayload.Request.AsMap(), *auditEvent.Details)
|
|
assert.Equal(t, event.ProtoPayload.Response.AsMap(), *auditEvent.Result)
|
|
assert.True(t, auditEvent.Context.OrganizationId != nil || auditEvent.Context.FolderId != nil || auditEvent.Context.ProjectId != nil)
|
|
|
|
for idx, principal := range event.ProtoPayload.AuthenticationInfo.ServiceAccountDelegationInfo {
|
|
switch principalValue := principal.Authority.(type) {
|
|
case *auditV1.ServiceAccountDelegationInfo_IdpPrincipal_:
|
|
assert.Equal(t, principalValue.IdpPrincipal.PrincipalId, auditEvent.ServiceAccountDelegationInfo.Principals[idx].Id)
|
|
assert.Equal(t, principalValue.IdpPrincipal.PrincipalEmail, auditEvent.ServiceAccountDelegationInfo.Principals[idx].Email)
|
|
case *auditV1.ServiceAccountDelegationInfo_SystemPrincipal_:
|
|
assert.Equal(t, "system", auditEvent.ServiceAccountDelegationInfo.Principals[idx].Id)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLegacyAuditApi_NewLegacyAuditApi(t *testing.T) {
|
|
|
|
t.Run("messaging api nil", func(t *testing.T) {
|
|
auditApi, err := NewLegacyAuditApi(nil, LegacyTopicNameConfig{}, nil)
|
|
assert.Nil(t, auditApi)
|
|
assert.EqualError(t, err, "messaging api nil")
|
|
})
|
|
|
|
t.Run("topic name is blank", func(t *testing.T) {
|
|
// Start solace docker container
|
|
solaceContainer, err := messaging.NewSolaceContainer(context.Background())
|
|
assert.NoError(t, err)
|
|
defer solaceContainer.Stop()
|
|
|
|
// Instantiate the messaging api
|
|
messagingApi, err := messaging.NewAmqpApi(messaging.AmqpConnectionPoolConfig{
|
|
Parameters: messaging.AmqpConnectionConfig{BrokerUrl: solaceContainer.AmqpConnectionString},
|
|
PoolSize: 1,
|
|
})
|
|
assert.NoError(t, err)
|
|
|
|
// Validator
|
|
validator, err := protovalidate.New()
|
|
assert.NoError(t, err)
|
|
|
|
auditApi, err := NewLegacyAuditApi(messagingApi, LegacyTopicNameConfig{
|
|
TopicName: "",
|
|
}, validator)
|
|
|
|
assert.Nil(t, auditApi)
|
|
assert.EqualError(t, err, "topic name is required")
|
|
})
|
|
}
|
|
|
|
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{
|
|
tracer: otel.Tracer("test"),
|
|
validator: protobufValidator,
|
|
}
|
|
|
|
event := newSystemAuditEvent(nil)
|
|
_, err := auditApi.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier)
|
|
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{
|
|
tracer: otel.Tracer("test"),
|
|
validator: protobufValidator,
|
|
}
|
|
|
|
event := newSystemAuditEvent(nil)
|
|
err := auditApi.Log(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier)
|
|
assert.ErrorIs(t, err, expectedError)
|
|
}
|
|
|
|
func TestLegacyAuditApi_Log_NilEvent(t *testing.T) {
|
|
auditApi := LegacyAuditApi{tracer: otel.Tracer("test")}
|
|
err := auditApi.Log(context.Background(), nil, auditV1.Visibility_VISIBILITY_PUBLIC, RoutableSystemIdentifier)
|
|
assert.ErrorIs(t, err, ErrEventNil)
|
|
}
|
|
|
|
func TestLegacyAuditApi_ConvertAndSerializeIntoLegacyFormatInvalidObjectIdentifierType(t *testing.T) {
|
|
customization := func(event *auditV1.AuditLogEntry,
|
|
objectIdentifier *auditV1.ObjectIdentifier) {
|
|
objectIdentifier.Type = "invalid"
|
|
}
|
|
event, objectIdentifier := newProjectAuditEvent(&customization)
|
|
|
|
validator := &ProtobufValidatorMock{}
|
|
validator.On("Validate", mock.Anything).Return(nil)
|
|
var protobufValidator ProtobufValidator = validator
|
|
|
|
auditApi := LegacyAuditApi{
|
|
tracer: otel.Tracer("test"),
|
|
validator: protobufValidator,
|
|
}
|
|
_, err := auditApi.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, NewRoutableIdentifier(objectIdentifier))
|
|
assert.ErrorIs(t, err, ErrUnsupportedRoutableType)
|
|
}
|