audit-go/audit/api/api_legacy.go
2024-11-14 13:30:25 +00:00

155 lines
4.6 KiB
Go

package api
import (
"context"
"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"
"errors"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
"strings"
"google.golang.org/protobuf/proto"
)
const DataTypeLegacyAuditEventV1 = "audit.v1.LegacyAuditEvent"
// LegacyTopicNameResolver implements TopicNameResolver.
// A hard-coded topic name is used, routing identifiers are ignored.
type LegacyTopicNameResolver struct {
topicName string
}
// Resolve implements TopicNameResolver.Resolve
func (r *LegacyTopicNameResolver) Resolve(*RoutableIdentifier) (string, error) {
return r.topicName, nil
}
// LegacyTopicNameConfig provides topic name information required for the topic name resolution.
type LegacyTopicNameConfig struct {
TopicName string
}
// LegacyAuditApi is an implementation of AuditApi to send events to the legacy audit log system.
//
// Note: The implementation will be deprecated and replaced with the "routableAuditApi" once the new audit log routing is implemented
type LegacyAuditApi struct {
messagingApi messaging.Api
topicNameResolver TopicNameResolver
tracer trace.Tracer
validator ProtobufValidator
}
// NewLegacyAuditApi can be used to initialize the audit log api.
//
// Note: The NewLegacyAuditApi method will be deprecated and replaced with "newRoutableAuditApi" once the new audit log routing is implemented
func NewLegacyAuditApi(
messagingApi messaging.Api,
topicNameConfig LegacyTopicNameConfig,
validator ProtobufValidator,
) (AuditApi, error) {
if messagingApi == nil {
return nil, ErrMessagingApiNil
}
// Topic resolver
if topicNameConfig.TopicName == "" {
return nil, errors.New("topic name is required")
}
var topicNameResolver TopicNameResolver = &LegacyTopicNameResolver{topicName: topicNameConfig.TopicName}
// Audit api
var auditApi AuditApi = &LegacyAuditApi{
messagingApi: messagingApi,
topicNameResolver: topicNameResolver,
tracer: otel.Tracer("legacy-audit-api"),
validator: validator,
}
return auditApi, nil
}
// Log implements AuditApi.Log
func (a *LegacyAuditApi) Log(
ctx context.Context,
event *auditV1.AuditLogEntry,
visibility auditV1.Visibility,
routableIdentifier *RoutableIdentifier,
) error {
cloudEvent, err := a.ValidateAndSerialize(ctx, event, visibility, routableIdentifier)
if err != nil {
return err
}
return a.Send(ctx, routableIdentifier, cloudEvent)
}
// 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 *RoutableIdentifier,
) (*CloudEvent, error) {
ctx, span := a.tracer.Start(ctx, "validate-and-serialize")
defer span.End()
routableEvent, err := 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(EventTypeDataAccess)) {
return nil, ErrUnsupportedEventTypeDataAccess
}
// Do nothing with the serialized data in the legacy solution
_, err = proto.Marshal(routableEvent)
if err != nil {
return nil, err
}
// Convert attributes
legacyBytes, err := convertAndSerializeIntoLegacyFormat(event, routableEvent)
if err != nil {
return nil, err
}
traceParent, traceState := TraceParentAndStateFromContext(ctx)
message := CloudEvent{
SpecVersion: "1.0",
Source: event.ProtoPayload.ServiceName,
Id: event.InsertId,
Time: event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(),
DataContentType: ContentTypeCloudEventsJson,
DataType: DataTypeLegacyAuditEventV1,
Subject: event.ProtoPayload.ResourceName,
Data: legacyBytes,
TraceParent: &traceParent,
TraceState: &traceState,
}
return &message, nil
}
// Send implements AuditApi.Send
func (a *LegacyAuditApi) Send(
ctx context.Context,
routableIdentifier *RoutableIdentifier,
cloudEvent *CloudEvent,
) error {
if cloudEvent != nil && cloudEvent.TraceParent != nil && cloudEvent.TraceState != nil {
ctx = AddTraceParentAndStateToContext(ctx, *cloudEvent.TraceParent, *cloudEvent.TraceState)
}
ctx, span := a.tracer.Start(ctx, "send")
defer span.End()
return send(a.topicNameResolver, a.messagingApi, ctx, routableIdentifier, cloudEvent)
}