mirror of
https://dev.azure.com/schwarzit/schwarzit.stackit-public/_git/audit-go
synced 2026-02-19 06:01:48 +00:00
Fix event type mapping in legacy converter and system event handling
This commit is contained in:
parent
66773aad3d
commit
03d4ae5d1b
9 changed files with 471 additions and 65 deletions
|
|
@ -18,18 +18,25 @@ import (
|
||||||
const ContentTypeCloudEventsProtobuf = "application/cloudevents+protobuf"
|
const ContentTypeCloudEventsProtobuf = "application/cloudevents+protobuf"
|
||||||
const ContentTypeCloudEventsJson = "application/cloudevents+json; charset=UTF-8"
|
const ContentTypeCloudEventsJson = "application/cloudevents+json; charset=UTF-8"
|
||||||
|
|
||||||
// ErrUnknownPluralType indicates that the given input is an unknown plural type
|
// ErrAttributeIdentifierInvalid indicates that the object identifier
|
||||||
var ErrUnknownPluralType = errors.New("unknown plural type")
|
// and the identifier in the checked attribute do not match
|
||||||
|
var ErrAttributeIdentifierInvalid = errors.New("attribute identifier invalid")
|
||||||
|
|
||||||
// ErrUnknownSingularType indicates that the given input is an unknown singular type
|
// ErrAttributeTypeInvalid indicates that an invalid type has been provided.
|
||||||
var ErrUnknownSingularType = errors.New("unknown singular type")
|
var ErrAttributeTypeInvalid = errors.New("attribute type invalid")
|
||||||
|
|
||||||
// ErrUnsupportedRoutableType indicates that the given input is an unsupported routable type
|
// ErrCloudEventNil states that the given cloud event is nil
|
||||||
var ErrUnsupportedRoutableType = errors.New("unsupported routable type")
|
var ErrCloudEventNil = errors.New("cloud event nil")
|
||||||
|
|
||||||
// ErrEventNil indicates that the event was nil
|
// ErrEventNil indicates that the event was nil
|
||||||
var ErrEventNil = errors.New("event is nil")
|
var ErrEventNil = errors.New("event is nil")
|
||||||
|
|
||||||
|
// ErrInvalidRoutableIdentifierForSystemEvent states that the routable identifier is not valid for a system event
|
||||||
|
var ErrInvalidRoutableIdentifierForSystemEvent = errors.New("invalid identifier for system event")
|
||||||
|
|
||||||
|
// ErrMessagingApiNil states that the messaging api is nil
|
||||||
|
var ErrMessagingApiNil = errors.New("messaging api nil")
|
||||||
|
|
||||||
// ErrObjectIdentifierNil indicates that the object identifier was nil
|
// ErrObjectIdentifierNil indicates that the object identifier was nil
|
||||||
var ErrObjectIdentifierNil = errors.New("object identifier is nil")
|
var ErrObjectIdentifierNil = errors.New("object identifier is nil")
|
||||||
|
|
||||||
|
|
@ -40,28 +47,24 @@ var ErrObjectIdentifierNil = errors.New("object identifier is nil")
|
||||||
// * Visibility: Private, ObjectIdentifier: <type | system>
|
// * Visibility: Private, ObjectIdentifier: <type | system>
|
||||||
var ErrObjectIdentifierVisibilityMismatch = errors.New("object reference visibility mismatch")
|
var ErrObjectIdentifierVisibilityMismatch = errors.New("object reference visibility mismatch")
|
||||||
|
|
||||||
// ErrUnsupportedObjectIdentifierType indicates that an unsupported object identifier type has been provided.
|
|
||||||
var ErrUnsupportedObjectIdentifierType = errors.New("unsupported object identifier type")
|
|
||||||
|
|
||||||
// ErrAttributeTypeInvalid indicates that an invalid type has been provided.
|
|
||||||
var ErrAttributeTypeInvalid = errors.New("attribute type invalid")
|
|
||||||
|
|
||||||
// ErrAttributeIdentifierInvalid indicates that the object identifier
|
|
||||||
// and the identifier in the checked attribute do not match
|
|
||||||
var ErrAttributeIdentifierInvalid = errors.New("attribute identifier invalid")
|
|
||||||
|
|
||||||
// ErrTopicNameResolverNil states that the topic name resolve is nil
|
// ErrTopicNameResolverNil states that the topic name resolve is nil
|
||||||
var ErrTopicNameResolverNil = errors.New("topic name resolver nil")
|
var ErrTopicNameResolverNil = errors.New("topic name resolver nil")
|
||||||
|
|
||||||
// ErrMessagingApiNil states that the messaging api is nil
|
// ErrUnknownPluralType indicates that the given input is an unknown plural type
|
||||||
var ErrMessagingApiNil = errors.New("messaging api nil")
|
var ErrUnknownPluralType = errors.New("unknown plural type")
|
||||||
|
|
||||||
// ErrCloudEventNil states that the given cloud event is nil
|
// ErrUnknownSingularType indicates that the given input is an unknown singular type
|
||||||
var ErrCloudEventNil = errors.New("cloud event nil")
|
var ErrUnknownSingularType = errors.New("unknown singular type")
|
||||||
|
|
||||||
// ErrUnsupportedEventTypeDataAccess states that the event type "data-access" is currently not supported
|
// ErrUnsupportedEventTypeDataAccess states that the event type "data-access" is currently not supported
|
||||||
var ErrUnsupportedEventTypeDataAccess = errors.New("unsupported event type data access")
|
var ErrUnsupportedEventTypeDataAccess = errors.New("unsupported event type data access")
|
||||||
|
|
||||||
|
// ErrUnsupportedObjectIdentifierType indicates that an unsupported object identifier type has been provided.
|
||||||
|
var ErrUnsupportedObjectIdentifierType = errors.New("unsupported object identifier type")
|
||||||
|
|
||||||
|
// ErrUnsupportedRoutableType indicates that the given input is an unsupported routable type
|
||||||
|
var ErrUnsupportedRoutableType = errors.New("unsupported routable type")
|
||||||
|
|
||||||
func validateAndSerializePartially(
|
func validateAndSerializePartially(
|
||||||
validator *ProtobufValidator,
|
validator *ProtobufValidator,
|
||||||
event *auditV1.AuditLogEntry,
|
event *auditV1.AuditLogEntry,
|
||||||
|
|
@ -97,11 +100,18 @@ func validateAndSerializePartially(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check identifier consistency across event attributes
|
// Check identifier consistency across event attributes
|
||||||
if err := areIdentifiersIdentical(routableIdentifier, event.LogName); err != nil {
|
if strings.HasSuffix(event.LogName, string(EventTypeSystemEvent)) {
|
||||||
return nil, err
|
if !(routableIdentifier.Identifier == SystemIdentifier.Identifier && routableIdentifier.Type == SingularTypeSystem) {
|
||||||
}
|
return nil, ErrInvalidRoutableIdentifierForSystemEvent
|
||||||
if err := areIdentifiersIdentical(routableIdentifier, event.ProtoPayload.ResourceName); err != nil {
|
}
|
||||||
return nil, err
|
// The resource name can either contain the system identifier or another resource identifier
|
||||||
|
} else {
|
||||||
|
if err := areIdentifiersIdentical(routableIdentifier, event.LogName); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := areIdentifiersIdentical(routableIdentifier, event.ProtoPayload.ResourceName); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test serialization even if the data is dropped later when logging to the legacy solution
|
// Test serialization even if the data is dropped later when logging to the legacy solution
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ func Test_ValidateAndSerializePartially_CheckVisibility_SystemEvent(t *testing.T
|
||||||
&validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, NewRoutableIdentifier(
|
&validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, NewRoutableIdentifier(
|
||||||
&auditV1.ObjectIdentifier{Identifier: uuid.NewString(), Type: string(SingularTypeOrganization)}))
|
&auditV1.ObjectIdentifier{Identifier: uuid.NewString(), Type: string(SingularTypeOrganization)}))
|
||||||
|
|
||||||
assert.ErrorIs(t, err, ErrAttributeIdentifierInvalid)
|
assert.ErrorIs(t, err, ErrInvalidRoutableIdentifierForSystemEvent)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Visibility private - object identifier system", func(t *testing.T) {
|
t.Run("Visibility private - object identifier system", func(t *testing.T) {
|
||||||
|
|
@ -188,7 +188,7 @@ func Test_ValidateAndSerializePartially_CheckVisibility_SystemEvent(t *testing.T
|
||||||
&validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, NewRoutableIdentifier(
|
&validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, NewRoutableIdentifier(
|
||||||
&auditV1.ObjectIdentifier{Identifier: uuid.NewString(), Type: string(SingularTypeOrganization)}))
|
&auditV1.ObjectIdentifier{Identifier: uuid.NewString(), Type: string(SingularTypeOrganization)}))
|
||||||
|
|
||||||
assert.ErrorIs(t, err, ErrAttributeIdentifierInvalid)
|
assert.ErrorIs(t, err, ErrInvalidRoutableIdentifierForSystemEvent)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/audit-go.git/gen/go/audit/v1"
|
auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/audit-go.git/gen/go/audit/v1"
|
||||||
|
|
@ -19,6 +20,20 @@ func convertAndSerializeIntoLegacyFormat(
|
||||||
routableEvent *auditV1.RoutableAuditEvent,
|
routableEvent *auditV1.RoutableAuditEvent,
|
||||||
) ([]byte, error) {
|
) ([]byte, error) {
|
||||||
|
|
||||||
|
// Event type
|
||||||
|
var eventType string
|
||||||
|
if strings.HasSuffix(event.LogName, string(EventTypeAdminActivity)) {
|
||||||
|
eventType = "ADMIN_ACTIVITY"
|
||||||
|
} else if strings.HasSuffix(event.LogName, string(EventTypeSystemEvent)) {
|
||||||
|
eventType = "SYSTEM_EVENT"
|
||||||
|
} else if strings.HasSuffix(event.LogName, string(EventTypePolicyDenied)) {
|
||||||
|
eventType = "POLICY_DENIED"
|
||||||
|
} else if strings.HasSuffix(event.LogName, string(EventTypeDataAccess)) {
|
||||||
|
return nil, ErrUnsupportedEventTypeDataAccess
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("unsupported event type")
|
||||||
|
}
|
||||||
|
|
||||||
// Source IP & User agent
|
// Source IP & User agent
|
||||||
var sourceIpAddress string
|
var sourceIpAddress string
|
||||||
var userAgent string
|
var userAgent string
|
||||||
|
|
@ -106,31 +121,26 @@ func convertAndSerializeIntoLegacyFormat(
|
||||||
|
|
||||||
// Context and event type
|
// Context and event type
|
||||||
var messageContext *LegacyAuditEventContext
|
var messageContext *LegacyAuditEventContext
|
||||||
var eventType string
|
|
||||||
switch routableEvent.ObjectIdentifier.Type {
|
switch routableEvent.ObjectIdentifier.Type {
|
||||||
case string(SingularTypeProject):
|
case string(SingularTypeProject):
|
||||||
eventType = "ADMIN_ACTIVITY"
|
|
||||||
messageContext = &LegacyAuditEventContext{
|
messageContext = &LegacyAuditEventContext{
|
||||||
OrganizationId: nil,
|
OrganizationId: nil,
|
||||||
FolderId: nil,
|
FolderId: nil,
|
||||||
ProjectId: &routableEvent.ObjectIdentifier.Identifier,
|
ProjectId: &routableEvent.ObjectIdentifier.Identifier,
|
||||||
}
|
}
|
||||||
case string(SingularTypeFolder):
|
case string(SingularTypeFolder):
|
||||||
eventType = "ADMIN_ACTIVITY"
|
|
||||||
messageContext = &LegacyAuditEventContext{
|
messageContext = &LegacyAuditEventContext{
|
||||||
OrganizationId: nil,
|
OrganizationId: nil,
|
||||||
FolderId: &routableEvent.ObjectIdentifier.Identifier,
|
FolderId: &routableEvent.ObjectIdentifier.Identifier,
|
||||||
ProjectId: nil,
|
ProjectId: nil,
|
||||||
}
|
}
|
||||||
case string(SingularTypeOrganization):
|
case string(SingularTypeOrganization):
|
||||||
eventType = "ADMIN_ACTIVITY"
|
|
||||||
messageContext = &LegacyAuditEventContext{
|
messageContext = &LegacyAuditEventContext{
|
||||||
OrganizationId: &routableEvent.ObjectIdentifier.Identifier,
|
OrganizationId: &routableEvent.ObjectIdentifier.Identifier,
|
||||||
FolderId: nil,
|
FolderId: nil,
|
||||||
ProjectId: nil,
|
ProjectId: nil,
|
||||||
}
|
}
|
||||||
case string(SingularTypeSystem):
|
case string(SingularTypeSystem):
|
||||||
eventType = "SYSTEM_EVENT"
|
|
||||||
messageContext = nil
|
messageContext = nil
|
||||||
default:
|
default:
|
||||||
return nil, ErrUnsupportedObjectIdentifierType
|
return nil, ErrUnsupportedObjectIdentifierType
|
||||||
|
|
|
||||||
|
|
@ -314,6 +314,64 @@ func TestDynamicLegacyAuditApi(t *testing.T) {
|
||||||
validateSentMessage(t, topicName, message, event, &traceParent, &traceState)
|
validateSentMessage(t, topicName, message, event, &traceParent, &traceState)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 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 := NewDynamicLegacyAuditApi(
|
||||||
|
messagingApi,
|
||||||
|
validator,
|
||||||
|
)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Instantiate test data
|
||||||
|
event := newProjectSystemAuditEvent(nil)
|
||||||
|
|
||||||
|
// Log the event to solace
|
||||||
|
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
|
||||||
|
ctx := context.WithValue(ctx, ContextKeyTopic, topicName)
|
||||||
|
assert.NoError(t,
|
||||||
|
(*auditApi).LogWithTrace(
|
||||||
|
ctx,
|
||||||
|
event,
|
||||||
|
visibility,
|
||||||
|
RoutableSystemIdentifier,
|
||||||
|
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)
|
||||||
|
assert.Nil(t, message.ApplicationProperties["cloudEvents:traceparent"])
|
||||||
|
assert.Nil(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
|
// Check logging of system events
|
||||||
t.Run("Log private system event", func(t *testing.T) {
|
t.Run("Log private system event", func(t *testing.T) {
|
||||||
defer solaceContainer.StopOnError()
|
defer solaceContainer.StopOnError()
|
||||||
|
|
|
||||||
|
|
@ -315,6 +315,64 @@ func TestLegacyAuditApi(t *testing.T) {
|
||||||
validateSentMessage(t, topicName, message, event, &traceParent, &traceState)
|
validateSentMessage(t, topicName, message, event, &traceParent, &traceState)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 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).LogWithTrace(
|
||||||
|
ctx,
|
||||||
|
event,
|
||||||
|
visibility,
|
||||||
|
RoutableSystemIdentifier,
|
||||||
|
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)
|
||||||
|
assert.Nil(t, message.ApplicationProperties["cloudEvents:traceparent"])
|
||||||
|
assert.Nil(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
|
// Check logging of system events
|
||||||
t.Run("Log private system event", func(t *testing.T) {
|
t.Run("Log private system event", func(t *testing.T) {
|
||||||
defer solaceContainer.StopOnError()
|
defer solaceContainer.StopOnError()
|
||||||
|
|
|
||||||
|
|
@ -320,6 +320,58 @@ func TestRoutableAuditApi(t *testing.T) {
|
||||||
&traceState)
|
&traceState)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 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, "system/*"))
|
||||||
|
|
||||||
|
// Instantiate test data
|
||||||
|
event := newProjectSystemAuditEvent(nil)
|
||||||
|
|
||||||
|
// Log the event to solace
|
||||||
|
visibility := auditV1.Visibility_VISIBILITY_PRIVATE
|
||||||
|
assert.NoError(t,
|
||||||
|
(*auditApi).LogWithTrace(
|
||||||
|
ctx,
|
||||||
|
event,
|
||||||
|
visibility,
|
||||||
|
RoutableSystemIdentifier,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
))
|
||||||
|
|
||||||
|
// Receive the event from solace
|
||||||
|
message, err := solaceContainer.NextMessageFromQueue(ctx, queueName, true)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Check topic name
|
||||||
|
assert.Equal(t, systemTopicName, *message.Properties.To)
|
||||||
|
|
||||||
|
// Check cloud event properties
|
||||||
|
applicationProperties := message.ApplicationProperties
|
||||||
|
assert.Equal(t, "1.0", applicationProperties["cloudEvents:specversion"])
|
||||||
|
assert.Equal(t, "resource-manager", applicationProperties["cloudEvents:source"])
|
||||||
|
_, isUuid := uuid.Parse(fmt.Sprintf("%s", applicationProperties["cloudEvents:id"]))
|
||||||
|
assert.True(t, true, isUuid)
|
||||||
|
assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().UnixMilli(), applicationProperties["cloudEvents:time"])
|
||||||
|
assert.Equal(t, "application/cloudevents+protobuf", applicationProperties["cloudEvents:datacontenttype"])
|
||||||
|
assert.Equal(t, "audit.v1.RoutableAuditEvent", applicationProperties["cloudEvents:type"])
|
||||||
|
assert.Nil(t, applicationProperties["cloudEvents:traceparent"])
|
||||||
|
assert.Nil(t, applicationProperties["cloudEvents:tracestate"])
|
||||||
|
|
||||||
|
// Check deserialized message
|
||||||
|
validateRoutableEventPayload(
|
||||||
|
t,
|
||||||
|
message.Data[0],
|
||||||
|
RoutableSystemIdentifier.ToObjectIdentifier(),
|
||||||
|
event,
|
||||||
|
"stackit.resourcemanager.v2.system.changed",
|
||||||
|
visibility)
|
||||||
|
})
|
||||||
|
|
||||||
// Check logging of system events
|
// Check logging of system events
|
||||||
t.Run("Log private system event", func(t *testing.T) {
|
t.Run("Log private system event", func(t *testing.T) {
|
||||||
defer solaceContainer.StopOnError()
|
defer solaceContainer.StopOnError()
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"dev.azure.com/schwarzit/schwarzit.stackit-core-platform/audit-go.git/log"
|
"dev.azure.com/schwarzit/schwarzit.stackit-core-platform/audit-go.git/log"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/google/uuid"
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
@ -38,13 +39,12 @@ type AuditParameters struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getObjectIdAndTypeFromAuditParams(
|
func getObjectIdAndTypeFromAuditParams(
|
||||||
ctx context.Context,
|
|
||||||
auditParams *AuditParameters,
|
auditParams *AuditParameters,
|
||||||
) (string, *SingularType, *PluralType, error) {
|
) (string, *PluralType, error) {
|
||||||
|
|
||||||
objectId := auditParams.ObjectId
|
objectId := auditParams.ObjectId
|
||||||
if objectId == "" {
|
if objectId == "" {
|
||||||
return "", nil, nil, errors.New("object id missing")
|
return "", nil, errors.New("object id missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
var objectType *SingularType
|
var objectType *SingularType
|
||||||
|
|
@ -53,16 +53,16 @@ func getObjectIdAndTypeFromAuditParams(
|
||||||
}
|
}
|
||||||
|
|
||||||
if objectType == nil {
|
if objectType == nil {
|
||||||
return "", nil, nil, errors.New("singular type missing")
|
return "", nil, errors.New("singular type missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to plural type
|
// Convert to plural type
|
||||||
plural, err := objectType.AsPluralType()
|
plural, err := objectType.AsPluralType()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.AuditLogger.Error("failed to convert singular type to plural type", err)
|
log.AuditLogger.Error("failed to convert singular type to plural type", err)
|
||||||
return "", nil, nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
return objectId, objectType, &plural, nil
|
return objectId, &plural, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuditLogEntryBuilder collects audit params to construct auditV1.AuditLogEntry
|
// AuditLogEntryBuilder collects audit params to construct auditV1.AuditLogEntry
|
||||||
|
|
@ -324,7 +324,7 @@ func (builder *AuditLogEntryBuilder) Build(ctx context.Context, sequenceNumber S
|
||||||
auditTime := time.Now()
|
auditTime := time.Now()
|
||||||
builder.auditMetadata.AuditTime = &auditTime
|
builder.auditMetadata.AuditTime = &auditTime
|
||||||
|
|
||||||
objectId, _, pluralType, err := getObjectIdAndTypeFromAuditParams(ctx, &builder.auditParams)
|
objectId, pluralType, err := getObjectIdAndTypeFromAuditParams(&builder.auditParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -340,8 +340,18 @@ func (builder *AuditLogEntryBuilder) Build(ctx context.Context, sequenceNumber S
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceName := fmt.Sprintf("%s/%s", *pluralType, objectId)
|
resourceName := fmt.Sprintf("%s/%s", *pluralType, objectId)
|
||||||
|
var logIdentifier string
|
||||||
|
var logType PluralType
|
||||||
|
if builder.auditParams.EventType == EventTypeSystemEvent {
|
||||||
|
logIdentifier = SystemIdentifier.Identifier
|
||||||
|
logType = PluralTypeSystem
|
||||||
|
} else {
|
||||||
|
logIdentifier = objectId
|
||||||
|
logType = *pluralType
|
||||||
|
}
|
||||||
|
|
||||||
builder.auditMetadata.AuditInsertId = NewInsertId(time.Now().UTC(), builder.location, builder.workerId, uint64(sequenceNumber))
|
builder.auditMetadata.AuditInsertId = NewInsertId(time.Now().UTC(), builder.location, builder.workerId, uint64(sequenceNumber))
|
||||||
builder.auditMetadata.AuditLogName = fmt.Sprintf("%s/%s/logs/%s", *pluralType, objectId, builder.auditParams.EventType)
|
builder.auditMetadata.AuditLogName = fmt.Sprintf("%s/%s/logs/%s", logType, logIdentifier, builder.auditParams.EventType)
|
||||||
builder.auditMetadata.AuditResourceName = resourceName
|
builder.auditMetadata.AuditResourceName = resourceName
|
||||||
|
|
||||||
var details *map[string]interface{} = nil
|
var details *map[string]interface{} = nil
|
||||||
|
|
@ -608,14 +618,29 @@ func (builder *AuditEventBuilder) Build(ctx context.Context, sequenceNumber Sequ
|
||||||
if builder.auditLogEntryBuilder == nil {
|
if builder.auditLogEntryBuilder == nil {
|
||||||
return nil, nil, "", fmt.Errorf("audit log entry builder not set")
|
return nil, nil, "", fmt.Errorf("audit log entry builder not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
objectId := builder.auditLogEntryBuilder.auditParams.ObjectId
|
||||||
|
objectType := builder.auditLogEntryBuilder.auditParams.ObjectType
|
||||||
|
var routingIdentifier *RoutableIdentifier
|
||||||
|
if builder.auditLogEntryBuilder.auditParams.EventType == EventTypeSystemEvent {
|
||||||
|
routingIdentifier = NewAuditRoutingIdentifier(uuid.Nil.String(), SingularTypeSystem)
|
||||||
|
if objectId == "" {
|
||||||
|
objectId = uuid.Nil.String()
|
||||||
|
builder.WithRequiredObjectId(objectId)
|
||||||
|
}
|
||||||
|
if objectType == "" {
|
||||||
|
objectType = SingularTypeSystem
|
||||||
|
builder.WithRequiredObjectType(objectType)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
routingIdentifier = NewAuditRoutingIdentifier(objectId, objectType)
|
||||||
|
}
|
||||||
|
|
||||||
auditLogEntry, err := builder.auditLogEntryBuilder.Build(ctx, sequenceNumber)
|
auditLogEntry, err := builder.auditLogEntryBuilder.Build(ctx, sequenceNumber)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, "", err
|
return nil, nil, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
objectId := builder.auditLogEntryBuilder.auditParams.ObjectId
|
|
||||||
objectType := builder.auditLogEntryBuilder.auditParams.ObjectType
|
|
||||||
|
|
||||||
ctx, span := builder.tracer.Start(ctx, "create-audit-event")
|
ctx, span := builder.tracer.Start(ctx, "create-audit-event")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
|
|
@ -624,7 +649,6 @@ func (builder *AuditEventBuilder) Build(ctx context.Context, sequenceNumber Sequ
|
||||||
var traceState *string = nil
|
var traceState *string = nil
|
||||||
visibility := builder.visibility
|
visibility := builder.visibility
|
||||||
operation := auditLogEntry.ProtoPayload.OperationName
|
operation := auditLogEntry.ProtoPayload.OperationName
|
||||||
routingIdentifier := NewAuditRoutingIdentifier(objectId, objectType)
|
|
||||||
|
|
||||||
// Validate and serialize the protobuf event into a cloud event
|
// Validate and serialize the protobuf event into a cloud event
|
||||||
_, validateSerializeSpan := builder.tracer.Start(ctx, "validate-and-serialize-audit-event")
|
_, validateSerializeSpan := builder.tracer.Start(ctx, "validate-and-serialize-audit-event")
|
||||||
|
|
|
||||||
|
|
@ -20,50 +20,46 @@ func Test_getObjectIdAndTypeFromAuditParams(t *testing.T) {
|
||||||
|
|
||||||
t.Run(
|
t.Run(
|
||||||
"object id empty", func(t *testing.T) {
|
"object id empty", func(t *testing.T) {
|
||||||
objectId, objectType, objectTypePlural, err := getObjectIdAndTypeFromAuditParams(context.Background(), &AuditParameters{})
|
objectId, objectTypePlural, err := getObjectIdAndTypeFromAuditParams(&AuditParameters{})
|
||||||
assert.EqualError(t, err, "object id missing")
|
assert.EqualError(t, err, "object id missing")
|
||||||
assert.Equal(t, "", objectId)
|
assert.Equal(t, "", objectId)
|
||||||
assert.Nil(t, objectType)
|
|
||||||
assert.Nil(t, objectTypePlural)
|
assert.Nil(t, objectTypePlural)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
t.Run(
|
t.Run(
|
||||||
"object type empty", func(t *testing.T) {
|
"object type empty", func(t *testing.T) {
|
||||||
objectId, objectType, objectTypePlural, err := getObjectIdAndTypeFromAuditParams(context.Background(), &AuditParameters{ObjectId: "value"})
|
objectId, objectTypePlural, err := getObjectIdAndTypeFromAuditParams(&AuditParameters{ObjectId: "value"})
|
||||||
assert.EqualError(t, err, "singular type missing")
|
assert.EqualError(t, err, "singular type missing")
|
||||||
assert.Equal(t, "", objectId)
|
assert.Equal(t, "", objectId)
|
||||||
assert.Nil(t, objectType)
|
|
||||||
assert.Nil(t, objectTypePlural)
|
assert.Nil(t, objectTypePlural)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
t.Run(
|
t.Run(
|
||||||
"object id and invalid type set", func(t *testing.T) {
|
"object id and invalid type set", func(t *testing.T) {
|
||||||
objectId, objectType, objectTypePlural, err := getObjectIdAndTypeFromAuditParams(
|
objectId, objectTypePlural, err := getObjectIdAndTypeFromAuditParams(
|
||||||
context.Background(), &AuditParameters{
|
&AuditParameters{
|
||||||
ObjectId: "value",
|
ObjectId: "value",
|
||||||
ObjectType: AsSingularType("invalid"),
|
ObjectType: AsSingularType("invalid"),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
assert.EqualError(t, err, "unknown singular type")
|
assert.EqualError(t, err, "unknown singular type")
|
||||||
assert.Equal(t, "", objectId)
|
assert.Equal(t, "", objectId)
|
||||||
assert.Nil(t, objectType)
|
|
||||||
assert.Nil(t, objectTypePlural)
|
assert.Nil(t, objectTypePlural)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
t.Run(
|
t.Run(
|
||||||
"object id and type set", func(t *testing.T) {
|
"object id and type set", func(t *testing.T) {
|
||||||
objectId, objectType, objectTypePlural, err := getObjectIdAndTypeFromAuditParams(
|
objectId, objectTypePlural, err := getObjectIdAndTypeFromAuditParams(
|
||||||
context.Background(), &AuditParameters{
|
&AuditParameters{
|
||||||
ObjectId: "value",
|
ObjectId: "value",
|
||||||
ObjectType: SingularTypeProject,
|
ObjectType: SingularTypeProject,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "value", objectId)
|
assert.Equal(t, "value", objectId)
|
||||||
assert.Equal(t, SingularTypeProject, *objectType)
|
|
||||||
assert.Equal(t, PluralTypeProject, *objectTypePlural)
|
assert.Equal(t, PluralTypeProject, *objectTypePlural)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -329,7 +325,7 @@ func Test_AuditLogEntryBuilder(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, logEntry)
|
assert.NotNil(t, logEntry)
|
||||||
|
|
||||||
assert.Equal(t, "projects/1/logs/system-event", logEntry.LogName)
|
assert.Equal(t, fmt.Sprintf("system/%s/logs/system-event", uuid.Nil.String()), logEntry.LogName)
|
||||||
assert.Nil(t, logEntry.Labels)
|
assert.Nil(t, logEntry.Labels)
|
||||||
assert.Nil(t, logEntry.TraceState)
|
assert.Nil(t, logEntry.TraceState)
|
||||||
assert.Nil(t, logEntry.TraceParent)
|
assert.Nil(t, logEntry.TraceParent)
|
||||||
|
|
@ -601,7 +597,8 @@ func Test_AuditEventBuilder(t *testing.T) {
|
||||||
assert.NotNil(t, cloudEvent.Data)
|
assert.NotNil(t, cloudEvent.Data)
|
||||||
assert.NoError(t, proto.Unmarshal(cloudEvent.Data, &routableAuditEvent))
|
assert.NoError(t, proto.Unmarshal(cloudEvent.Data, &routableAuditEvent))
|
||||||
|
|
||||||
assert.Equal(t, routableIdentifier.ToObjectIdentifier(), routableAuditEvent.ObjectIdentifier)
|
assert.Equal(t, routableIdentifier.ToObjectIdentifier().Identifier, routableAuditEvent.ObjectIdentifier.Identifier)
|
||||||
|
assert.Equal(t, routableIdentifier.ToObjectIdentifier().Type, routableAuditEvent.ObjectIdentifier.Type)
|
||||||
assert.Equal(t, auditV1.Visibility_VISIBILITY_PUBLIC, routableAuditEvent.Visibility)
|
assert.Equal(t, auditV1.Visibility_VISIBILITY_PUBLIC, routableAuditEvent.Visibility)
|
||||||
assert.Equal(t, operation, routableAuditEvent.OperationName)
|
assert.Equal(t, operation, routableAuditEvent.OperationName)
|
||||||
|
|
||||||
|
|
@ -714,7 +711,7 @@ func Test_AuditEventBuilder(t *testing.T) {
|
||||||
WithAuditPermission(permission).
|
WithAuditPermission(permission).
|
||||||
WithAuditPermissionCheckResult(permissionCheckResult).
|
WithAuditPermissionCheckResult(permissionCheckResult).
|
||||||
WithDetails(details).
|
WithDetails(details).
|
||||||
WithEventType(EventTypeSystemEvent).
|
WithEventType(EventTypeAdminActivity).
|
||||||
WithLabels(map[string]string{"key": "label"}).
|
WithLabels(map[string]string{"key": "label"}).
|
||||||
WithNumResponseItems(int64(10)).
|
WithNumResponseItems(int64(10)).
|
||||||
WithRequestCorrelationId("correlationId").
|
WithRequestCorrelationId("correlationId").
|
||||||
|
|
@ -751,7 +748,8 @@ func Test_AuditEventBuilder(t *testing.T) {
|
||||||
assert.NotNil(t, cloudEvent.Data)
|
assert.NotNil(t, cloudEvent.Data)
|
||||||
assert.NoError(t, proto.Unmarshal(cloudEvent.Data, &routableAuditEvent))
|
assert.NoError(t, proto.Unmarshal(cloudEvent.Data, &routableAuditEvent))
|
||||||
|
|
||||||
assert.Equal(t, routableIdentifier.ToObjectIdentifier(), routableAuditEvent.ObjectIdentifier)
|
assert.Equal(t, routableIdentifier.ToObjectIdentifier().Identifier, routableAuditEvent.ObjectIdentifier.Identifier)
|
||||||
|
assert.Equal(t, routableIdentifier.ToObjectIdentifier().Type, routableAuditEvent.ObjectIdentifier.Type)
|
||||||
assert.Equal(t, auditV1.Visibility_VISIBILITY_PRIVATE, routableAuditEvent.Visibility)
|
assert.Equal(t, auditV1.Visibility_VISIBILITY_PRIVATE, routableAuditEvent.Visibility)
|
||||||
assert.Equal(t, operation, routableAuditEvent.OperationName)
|
assert.Equal(t, operation, routableAuditEvent.OperationName)
|
||||||
|
|
||||||
|
|
@ -759,7 +757,7 @@ func Test_AuditEventBuilder(t *testing.T) {
|
||||||
assert.NotNil(t, routableAuditEvent.GetUnencryptedData().Data)
|
assert.NotNil(t, routableAuditEvent.GetUnencryptedData().Data)
|
||||||
assert.NoError(t, proto.Unmarshal(routableAuditEvent.GetUnencryptedData().Data, &logEntry))
|
assert.NoError(t, proto.Unmarshal(routableAuditEvent.GetUnencryptedData().Data, &logEntry))
|
||||||
|
|
||||||
assert.Equal(t, fmt.Sprintf("projects/%s/logs/system-event", objectId), logEntry.LogName)
|
assert.Equal(t, fmt.Sprintf("projects/%s/logs/admin-activity", objectId), logEntry.LogName)
|
||||||
assert.Equal(t, map[string]string{"key": "label"}, logEntry.Labels)
|
assert.Equal(t, map[string]string{"key": "label"}, logEntry.Labels)
|
||||||
assert.Nil(t, logEntry.TraceState)
|
assert.Nil(t, logEntry.TraceState)
|
||||||
assert.Nil(t, logEntry.TraceParent)
|
assert.Nil(t, logEntry.TraceParent)
|
||||||
|
|
@ -836,14 +834,13 @@ func Test_AuditEventBuilder(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("system event", func(t *testing.T) {
|
t.Run("system event with object reference", func(t *testing.T) {
|
||||||
api, _ := NewMockAuditApi()
|
api, _ := NewMockAuditApi()
|
||||||
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
|
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
|
||||||
tracer := otel.Tracer("test")
|
tracer := otel.Tracer("test")
|
||||||
|
|
||||||
objectId := uuid.NewString()
|
objectId := uuid.NewString()
|
||||||
operation := "stackit.demo-service.v1.operation"
|
operation := "stackit.demo-service.v1.operation"
|
||||||
routableIdentifier := RoutableIdentifier{Identifier: objectId, Type: SingularTypeProject}
|
|
||||||
builder := NewAuditEventBuilder(api, sequenceNumberGenerator, tracer, "demo-service", "worker-id", "eu01").
|
builder := NewAuditEventBuilder(api, sequenceNumberGenerator, tracer, "demo-service", "worker-id", "eu01").
|
||||||
WithRequiredObjectId(objectId).
|
WithRequiredObjectId(objectId).
|
||||||
WithRequiredObjectType(SingularTypeProject).
|
WithRequiredObjectType(SingularTypeProject).
|
||||||
|
|
@ -854,7 +851,8 @@ func Test_AuditEventBuilder(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.True(t, builder.IsBuilt())
|
assert.True(t, builder.IsBuilt())
|
||||||
|
|
||||||
assert.Equal(t, &routableIdentifier, routingIdentifier)
|
assert.Equal(t, SystemIdentifier.Identifier, routingIdentifier.ToObjectIdentifier().Identifier)
|
||||||
|
assert.Equal(t, SystemIdentifier.Type, routingIdentifier.ToObjectIdentifier().Type)
|
||||||
assert.Equal(t, operation, op)
|
assert.Equal(t, operation, op)
|
||||||
|
|
||||||
assert.NotNil(t, cloudEvent)
|
assert.NotNil(t, cloudEvent)
|
||||||
|
|
@ -872,7 +870,8 @@ func Test_AuditEventBuilder(t *testing.T) {
|
||||||
assert.NotNil(t, cloudEvent.Data)
|
assert.NotNil(t, cloudEvent.Data)
|
||||||
assert.NoError(t, proto.Unmarshal(cloudEvent.Data, &routableAuditEvent))
|
assert.NoError(t, proto.Unmarshal(cloudEvent.Data, &routableAuditEvent))
|
||||||
|
|
||||||
assert.Equal(t, routableIdentifier.ToObjectIdentifier(), routableAuditEvent.ObjectIdentifier)
|
assert.Equal(t, SystemIdentifier.Identifier, routableAuditEvent.ObjectIdentifier.Identifier)
|
||||||
|
assert.Equal(t, SystemIdentifier.Type, routableAuditEvent.ObjectIdentifier.Type)
|
||||||
assert.Equal(t, auditV1.Visibility_VISIBILITY_PRIVATE, routableAuditEvent.Visibility)
|
assert.Equal(t, auditV1.Visibility_VISIBILITY_PRIVATE, routableAuditEvent.Visibility)
|
||||||
assert.Equal(t, operation, routableAuditEvent.OperationName)
|
assert.Equal(t, operation, routableAuditEvent.OperationName)
|
||||||
|
|
||||||
|
|
@ -880,7 +879,7 @@ func Test_AuditEventBuilder(t *testing.T) {
|
||||||
assert.NotNil(t, routableAuditEvent.GetUnencryptedData().Data)
|
assert.NotNil(t, routableAuditEvent.GetUnencryptedData().Data)
|
||||||
assert.NoError(t, proto.Unmarshal(routableAuditEvent.GetUnencryptedData().Data, &logEntry))
|
assert.NoError(t, proto.Unmarshal(routableAuditEvent.GetUnencryptedData().Data, &logEntry))
|
||||||
|
|
||||||
assert.Equal(t, fmt.Sprintf("projects/%s/logs/system-event", objectId), logEntry.LogName)
|
assert.Equal(t, fmt.Sprintf("system/%s/logs/system-event", uuid.Nil.String()), logEntry.LogName)
|
||||||
assert.Nil(t, logEntry.Labels)
|
assert.Nil(t, logEntry.Labels)
|
||||||
assert.Nil(t, logEntry.TraceState)
|
assert.Nil(t, logEntry.TraceState)
|
||||||
assert.Nil(t, logEntry.TraceParent)
|
assert.Nil(t, logEntry.TraceParent)
|
||||||
|
|
@ -951,6 +950,119 @@ func Test_AuditEventBuilder(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("system event", func(t *testing.T) {
|
||||||
|
api, _ := NewMockAuditApi()
|
||||||
|
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
|
||||||
|
tracer := otel.Tracer("test")
|
||||||
|
|
||||||
|
operation := "stackit.demo-service.v1.operation"
|
||||||
|
builder := NewAuditEventBuilder(api, sequenceNumberGenerator, tracer, "demo-service", "worker-id", "eu01").
|
||||||
|
WithRequiredOperation(operation).
|
||||||
|
AsSystemEvent()
|
||||||
|
|
||||||
|
cloudEvent, routingIdentifier, op, err := builder.Build(context.Background(), SequenceNumber(1))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, builder.IsBuilt())
|
||||||
|
|
||||||
|
assert.Equal(t, SystemIdentifier.Identifier, routingIdentifier.ToObjectIdentifier().Identifier)
|
||||||
|
assert.Equal(t, SystemIdentifier.Type, routingIdentifier.ToObjectIdentifier().Type)
|
||||||
|
assert.Equal(t, operation, op)
|
||||||
|
|
||||||
|
assert.NotNil(t, cloudEvent)
|
||||||
|
assert.Equal(t, "application/cloudevents+protobuf", cloudEvent.DataContentType)
|
||||||
|
assert.Equal(t, "audit.v1.RoutableAuditEvent", cloudEvent.DataType)
|
||||||
|
assert.Regexp(t, "[0-9]+/eu01/worker-id/1", cloudEvent.Id)
|
||||||
|
assert.Equal(t, "demo-service", cloudEvent.Source)
|
||||||
|
assert.Equal(t, "1.0", cloudEvent.SpecVersion)
|
||||||
|
assert.Equal(t, fmt.Sprintf("system/%s", uuid.Nil.String()), cloudEvent.Subject)
|
||||||
|
assert.NotNil(t, cloudEvent.Time)
|
||||||
|
assert.Equal(t, "00-00000000000000000000000000000000-0000000000000000-00", *cloudEvent.TraceParent)
|
||||||
|
assert.Nil(t, cloudEvent.TraceState)
|
||||||
|
|
||||||
|
var routableAuditEvent auditV1.RoutableAuditEvent
|
||||||
|
assert.NotNil(t, cloudEvent.Data)
|
||||||
|
assert.NoError(t, proto.Unmarshal(cloudEvent.Data, &routableAuditEvent))
|
||||||
|
|
||||||
|
assert.Equal(t, SystemIdentifier.Identifier, routableAuditEvent.ObjectIdentifier.Identifier)
|
||||||
|
assert.Equal(t, SystemIdentifier.Type, routableAuditEvent.ObjectIdentifier.Type)
|
||||||
|
assert.Equal(t, auditV1.Visibility_VISIBILITY_PRIVATE, routableAuditEvent.Visibility)
|
||||||
|
assert.Equal(t, operation, routableAuditEvent.OperationName)
|
||||||
|
|
||||||
|
var logEntry auditV1.AuditLogEntry
|
||||||
|
assert.NotNil(t, routableAuditEvent.GetUnencryptedData().Data)
|
||||||
|
assert.NoError(t, proto.Unmarshal(routableAuditEvent.GetUnencryptedData().Data, &logEntry))
|
||||||
|
|
||||||
|
assert.Equal(t, fmt.Sprintf("system/%s/logs/system-event", uuid.Nil.String()), logEntry.LogName)
|
||||||
|
assert.Nil(t, logEntry.Labels)
|
||||||
|
assert.Nil(t, logEntry.TraceState)
|
||||||
|
assert.Nil(t, logEntry.TraceParent)
|
||||||
|
assert.Equal(t, auditV1.LogSeverity_LOG_SEVERITY_DEFAULT, logEntry.Severity)
|
||||||
|
assert.NotNil(t, logEntry.Timestamp)
|
||||||
|
assert.Nil(t, logEntry.CorrelationId)
|
||||||
|
assert.Regexp(t, "[0-9]+/eu01/worker-id/1", logEntry.InsertId)
|
||||||
|
|
||||||
|
assert.NotNil(t, logEntry.ProtoPayload)
|
||||||
|
|
||||||
|
authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo
|
||||||
|
assert.NotNil(t, authenticationInfo)
|
||||||
|
assert.Equal(t, "do-not-reply@stackit.cloud", authenticationInfo.PrincipalEmail)
|
||||||
|
assert.Equal(t, "none", authenticationInfo.PrincipalId)
|
||||||
|
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
|
||||||
|
assert.Nil(t, authenticationInfo.ServiceAccountName)
|
||||||
|
|
||||||
|
assert.Nil(t, logEntry.ProtoPayload.AuthorizationInfo)
|
||||||
|
assert.Nil(t, logEntry.ProtoPayload.Metadata)
|
||||||
|
assert.Equal(t, operation, logEntry.ProtoPayload.OperationName)
|
||||||
|
assert.Nil(t, logEntry.ProtoPayload.Request)
|
||||||
|
|
||||||
|
requestMetadata := logEntry.ProtoPayload.RequestMetadata
|
||||||
|
assert.NotNil(t, requestMetadata)
|
||||||
|
assert.Equal(t, "0.0.0.0", requestMetadata.CallerIp)
|
||||||
|
assert.Equal(t, "none", requestMetadata.CallerSuppliedUserAgent)
|
||||||
|
|
||||||
|
requestAttributes := requestMetadata.RequestAttributes
|
||||||
|
assert.NotNil(t, requestAttributes)
|
||||||
|
assert.Equal(t, "none", requestAttributes.Path)
|
||||||
|
assert.NotNil(t, requestAttributes.Time)
|
||||||
|
assert.Equal(t, "0.0.0.0", requestAttributes.Host)
|
||||||
|
assert.Equal(t, auditV1.AttributeContext_HTTP_METHOD_OTHER, requestAttributes.Method)
|
||||||
|
assert.Nil(t, requestAttributes.Id)
|
||||||
|
assert.Equal(t, "none", requestAttributes.Scheme)
|
||||||
|
assert.Equal(t, map[string]string{"user-agent": "none"}, requestAttributes.Headers)
|
||||||
|
assert.Nil(t, requestAttributes.Query)
|
||||||
|
assert.Equal(t, "none", requestAttributes.Protocol)
|
||||||
|
|
||||||
|
requestAttributesAuth := requestAttributes.Auth
|
||||||
|
assert.NotNil(t, requestAttributesAuth)
|
||||||
|
assert.Equal(t, "none/none", requestAttributesAuth.Principal)
|
||||||
|
assert.Nil(t, requestAttributesAuth.Audiences)
|
||||||
|
assert.NotNil(t, requestAttributesAuth.Claims)
|
||||||
|
assert.Equal(t, map[string]any{}, requestAttributesAuth.Claims.AsMap())
|
||||||
|
|
||||||
|
assert.Equal(t, fmt.Sprintf("system/%s", uuid.Nil.String()), logEntry.ProtoPayload.ResourceName)
|
||||||
|
assert.Nil(t, logEntry.ProtoPayload.Response)
|
||||||
|
|
||||||
|
responseMetadata := logEntry.ProtoPayload.ResponseMetadata
|
||||||
|
assert.NotNil(t, responseMetadata)
|
||||||
|
assert.Nil(t, responseMetadata.ErrorDetails)
|
||||||
|
assert.Nil(t, responseMetadata.ErrorMessage)
|
||||||
|
assert.Equal(t, wrapperspb.Int32(200), responseMetadata.StatusCode)
|
||||||
|
|
||||||
|
responseAttributes := responseMetadata.ResponseAttributes
|
||||||
|
assert.NotNil(t, responseAttributes)
|
||||||
|
assert.Nil(t, responseAttributes.Headers)
|
||||||
|
assert.Nil(t, responseAttributes.NumResponseItems)
|
||||||
|
assert.Nil(t, responseAttributes.Size)
|
||||||
|
assert.NotNil(t, responseAttributes.Time)
|
||||||
|
|
||||||
|
assert.Equal(t, "demo-service", logEntry.ProtoPayload.ServiceName)
|
||||||
|
|
||||||
|
validator, err := protovalidate.New()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = validator.Validate(&logEntry)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("with responsebody unserialized", func(t *testing.T) {
|
t.Run("with responsebody unserialized", func(t *testing.T) {
|
||||||
api, _ := NewMockAuditApi()
|
api, _ := NewMockAuditApi()
|
||||||
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
|
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
|
||||||
|
|
@ -984,7 +1096,7 @@ func Test_AuditEventBuilder(t *testing.T) {
|
||||||
WithAuditPermission(permission).
|
WithAuditPermission(permission).
|
||||||
WithAuditPermissionCheckResult(permissionCheckResult).
|
WithAuditPermissionCheckResult(permissionCheckResult).
|
||||||
WithDetails(details).
|
WithDetails(details).
|
||||||
WithEventType(EventTypeSystemEvent).
|
WithEventType(EventTypeAdminActivity).
|
||||||
WithLabels(map[string]string{"key": "label"}).
|
WithLabels(map[string]string{"key": "label"}).
|
||||||
WithNumResponseItems(int64(10)).
|
WithNumResponseItems(int64(10)).
|
||||||
WithRequestCorrelationId("correlationId").
|
WithRequestCorrelationId("correlationId").
|
||||||
|
|
|
||||||
|
|
@ -289,6 +289,88 @@ func newProjectAuditEvent(
|
||||||
return auditEvent, objectIdentifier
|
return auditEvent, objectIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newProjectSystemAuditEvent(
|
||||||
|
customization *func(*auditV1.AuditLogEntry)) *auditV1.AuditLogEntry {
|
||||||
|
|
||||||
|
identifier := uuid.New()
|
||||||
|
requestId := fmt.Sprintf("%s/1", identifier)
|
||||||
|
claims, _ := structpb.NewStruct(map[string]interface{}{})
|
||||||
|
correlationId := "9b5a8e9b-32a0-435f-b97b-a9a42b9e016b"
|
||||||
|
headers := make(map[string]string)
|
||||||
|
headers["Content-Type"] = "application/json"
|
||||||
|
labels := make(map[string]string)
|
||||||
|
labels["label1"] = "value1"
|
||||||
|
serviceAccountId := uuid.NewString()
|
||||||
|
serviceAccountName := fmt.Sprintf("projects/%s/service-accounts/%s", identifier, serviceAccountId)
|
||||||
|
delegationPrincipal := auditV1.ServiceAccountDelegationInfo{Authority: &auditV1.ServiceAccountDelegationInfo_SystemPrincipal_{}}
|
||||||
|
auditEvent := &auditV1.AuditLogEntry{
|
||||||
|
LogName: fmt.Sprintf("%s/%s/logs/%s", SystemIdentifier.Type, SystemIdentifier.Identifier, EventTypeSystemEvent),
|
||||||
|
ProtoPayload: &auditV1.AuditLog{
|
||||||
|
ServiceName: "resource-manager",
|
||||||
|
OperationName: "stackit.resourcemanager.v2.system.changed",
|
||||||
|
ResourceName: fmt.Sprintf("%s/%s", PluralTypeProject, identifier),
|
||||||
|
AuthenticationInfo: &auditV1.AuthenticationInfo{
|
||||||
|
PrincipalId: serviceAccountId,
|
||||||
|
PrincipalEmail: "service-account@sa.stackit.cloud",
|
||||||
|
ServiceAccountName: &serviceAccountName,
|
||||||
|
ServiceAccountDelegationInfo: []*auditV1.ServiceAccountDelegationInfo{&delegationPrincipal},
|
||||||
|
},
|
||||||
|
AuthorizationInfo: []*auditV1.AuthorizationInfo{{
|
||||||
|
Resource: fmt.Sprintf("%s/%s", PluralTypeProject, identifier),
|
||||||
|
Permission: nil,
|
||||||
|
Granted: nil,
|
||||||
|
}},
|
||||||
|
RequestMetadata: &auditV1.RequestMetadata{
|
||||||
|
CallerIp: "127.0.0.1",
|
||||||
|
CallerSuppliedUserAgent: "OpenAPI-Generator/ 1.0.0/ go",
|
||||||
|
RequestAttributes: &auditV1.AttributeContext_Request{
|
||||||
|
Id: &requestId,
|
||||||
|
Method: auditV1.AttributeContext_HTTP_METHOD_POST,
|
||||||
|
Headers: headers,
|
||||||
|
Path: "/v2/projects",
|
||||||
|
Host: "stackit-resource-manager-dev.apps.01.cf.eu01.stackit.cloud",
|
||||||
|
Scheme: "https",
|
||||||
|
Query: nil,
|
||||||
|
Time: timestamppb.New(time.Now().UTC()),
|
||||||
|
Protocol: "http/1.1",
|
||||||
|
Auth: &auditV1.AttributeContext_Auth{
|
||||||
|
Principal: "https%3A%2F%2Faccounts.dev.stackit.cloud/stackit-resource-manager-dev",
|
||||||
|
Audiences: []string{"https:// stackit-resource-manager-dev.apps.01.cf.eu01.stackit.cloud", "stackit", "api"},
|
||||||
|
Claims: claims,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Request: nil,
|
||||||
|
ResponseMetadata: &auditV1.ResponseMetadata{
|
||||||
|
StatusCode: wrapperspb.Int32(200),
|
||||||
|
ErrorMessage: nil,
|
||||||
|
ErrorDetails: nil,
|
||||||
|
ResponseAttributes: &auditV1.AttributeContext_Response{
|
||||||
|
NumResponseItems: nil,
|
||||||
|
Size: nil,
|
||||||
|
Headers: nil,
|
||||||
|
Time: timestamppb.New(time.Now().UTC()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Response: nil,
|
||||||
|
Metadata: nil,
|
||||||
|
},
|
||||||
|
InsertId: fmt.Sprintf("%d/eu01/e72182e8-0bb9-4be2-a19f-87fc0dd6e738/00000000001", time.Now().UnixNano()),
|
||||||
|
Labels: labels,
|
||||||
|
CorrelationId: &correlationId,
|
||||||
|
Timestamp: timestamppb.New(time.Now()),
|
||||||
|
Severity: auditV1.LogSeverity_LOG_SEVERITY_DEFAULT,
|
||||||
|
TraceParent: nil,
|
||||||
|
TraceState: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
if customization != nil {
|
||||||
|
(*customization)(auditEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
return auditEvent
|
||||||
|
}
|
||||||
|
|
||||||
func newSystemAuditEvent(
|
func newSystemAuditEvent(
|
||||||
customization *func(*auditV1.AuditLogEntry)) *auditV1.AuditLogEntry {
|
customization *func(*auditV1.AuditLogEntry)) *auditV1.AuditLogEntry {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue