audit-go/pkg/audit/api/api_legacy.go
Christian Schaible (EXT) f24cb8dc80 Merged PR 942474: feat: Separate validation+serialization and send functionality into two independent APIs
Decouple APIs to remove hard dependency to solace.

Security-concept-update-needed: false.

JIRA Work Item: [STACKITRMA-936](https://jira.schwarz/browse/STACKITRMA-936)
2026-03-04 13:47:42 +00:00

152 lines
5.2 KiB
Go

package api
import (
"context"
"errors"
"fmt"
"strings"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
"google.golang.org/protobuf/proto"
auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1"
internalAuditApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/internal/audit/api"
pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common"
pkgMessagingApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/messaging/api"
)
// StaticTopicNameConfig provides topic name information required for the topic name resolution.
type StaticTopicNameConfig struct {
TopicName string
}
// LegacyAuditApi is an implementation of pkgAuditCommon.AuditApi to validate and serialize events
// for the legacy audit log system.
type LegacyAuditApi struct {
tracer trace.Tracer
validator pkgAuditCommon.ProtobufValidator
}
var _ pkgAuditCommon.AuditApi = (*LegacyAuditApi)(nil)
// NewLegacyAuditApi can be used to initialize the audit log api.
func NewLegacyAuditApi(
validator pkgAuditCommon.ProtobufValidator,
) (pkgAuditCommon.AuditApi, error) {
var auditApi pkgAuditCommon.AuditApi = &LegacyAuditApi{
tracer: otel.Tracer("legacy-audit-api"),
validator: validator,
}
return auditApi, nil
}
// ValidateAndSerialize implements AuditApi.ValidateAndSerialize.
// It serializes the event into the byte representation of the legacy audit log system.
func (a *LegacyAuditApi) ValidateAndSerialize(
ctx context.Context,
event *auditV1.AuditLogEntry,
visibility auditV1.Visibility,
routableIdentifier *pkgAuditCommon.RoutableIdentifier,
) (*pkgAuditCommon.CloudEvent, error) {
ctx, span := a.tracer.Start(ctx, "validate-and-serialize")
defer span.End()
routableEvent, err := internalAuditApi.ValidateAndSerializePartially(a.validator, event, visibility, routableIdentifier)
if err != nil {
return nil, err
}
// Reject event type data-access as the downstream services
// cannot handle it at the moment
if strings.HasSuffix(event.LogName, string(pkgAuditCommon.EventTypeDataAccess)) {
return nil, pkgAuditCommon.ErrUnsupportedEventTypeDataAccess
}
// Serialized event
routableEventBytes, err := proto.Marshal(routableEvent)
if err != nil {
return nil, err
}
// Convert attributes into legacy format testwise
_, err = internalAuditApi.ConvertAndSerializeIntoLegacyFormat(event, routableEvent)
if err != nil {
return nil, err
}
traceParent, traceState := internalAuditApi.TraceParentAndStateFromContext(ctx)
message := pkgAuditCommon.CloudEvent{
SpecVersion: "1.0",
Source: event.ProtoPayload.ServiceName,
Id: event.InsertId,
Time: event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(),
DataContentType: pkgAuditCommon.ContentTypeCloudEventsJson,
DataType: pkgAuditCommon.DataTypeAuditCloudEventV1,
Subject: event.ProtoPayload.ResourceName,
Data: routableEventBytes,
TraceParent: &traceParent,
TraceState: &traceState,
}
return &message, nil
}
// LegacyAuditSendApi is an implementation of pkgAuditCommon.AuditSendApi to send events to the legacy audit log system.
type LegacyAuditSendApi struct {
messagingApi pkgMessagingApi.Api
topicNameResolver pkgAuditCommon.TopicNameResolver
tracer trace.Tracer
}
var _ pkgAuditCommon.AuditSendApi = (*LegacyAuditSendApi)(nil)
// NewLegacyAuditSendApi can be used to initialize the audit log send api.
func NewLegacyAuditSendApi(
messagingApi pkgMessagingApi.Api,
topicNameConfig StaticTopicNameConfig,
) (pkgAuditCommon.AuditSendApi, error) {
if messagingApi == nil {
return nil, pkgAuditCommon.ErrMessagingApiNil
}
// Topic resolver
if topicNameConfig.TopicName == "" {
return nil, errors.New("topic name is required")
}
if !pkgAuditCommon.TopicNamePattern.MatchString(topicNameConfig.TopicName) {
return nil, fmt.Errorf("invalid topic name: %s - "+
"expected stackit-platform/t/swz/audit-log/{region}/{version}/{eventSource}/{additionalParts} "+
"where region is one of [conway, eu01, eu02, sx-stoi01], version is vX.Y, eventSource is the service name "+
"and additionalParts is a describing string the audit log relates to or 'events'", topicNameConfig.TopicName)
}
var topicNameResolver pkgAuditCommon.TopicNameResolver = &pkgAuditCommon.StaticTopicNameTestResolver{TopicName: topicNameConfig.TopicName}
// Audit send api
var auditSendApi pkgAuditCommon.AuditSendApi = &LegacyAuditSendApi{
messagingApi: messagingApi,
topicNameResolver: topicNameResolver,
tracer: otel.Tracer("legacy-audit-send-api"),
}
return auditSendApi, nil
}
// Send implements AuditApi.Send
func (a *LegacyAuditSendApi) Send(
ctx context.Context,
routableIdentifier *pkgAuditCommon.RoutableIdentifier,
cloudEvent *pkgAuditCommon.CloudEvent,
) error {
if cloudEvent != nil && cloudEvent.TraceParent != nil && cloudEvent.TraceState != nil {
ctx = internalAuditApi.AddTraceParentAndStateToContext(ctx, *cloudEvent.TraceParent, *cloudEvent.TraceState)
}
ctx, span := a.tracer.Start(ctx, "send")
defer span.End()
return internalAuditApi.Send(a.topicNameResolver, a.messagingApi, ctx, routableIdentifier, cloudEvent)
}