package api import ( "context" "errors" "fmt" "strings" "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" "google.golang.org/protobuf/proto" ) type ContextKey string const ContextKeyTopic ContextKey = "topic" var ErrNoTopicNameProvided = errors.New("no topic name provided") var ErrTopicNameEmpty = errors.New("empty topic name provided") // DynamicLegacyAuditApi is an implementation of AuditApi to send events to the legacy audit log system // by setting the topic name explicitly in the context with the key "topic". // // Note: The implementation will be deprecated and replaced with the "routableAuditApi" once the new audit log routing is implemented type DynamicLegacyAuditApi struct { messagingApi *messaging.Api validator *ProtobufValidator } // NewDynamicLegacyAuditApi 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 NewDynamicLegacyAuditApi( messagingApi *messaging.Api, validator ProtobufValidator, ) (*AuditApi, error) { if messagingApi == nil { return nil, ErrMessagingApiNil } // Audit api var auditApi AuditApi = &DynamicLegacyAuditApi{ messagingApi: messagingApi, validator: &validator, } return &auditApi, nil } // Log implements AuditApi.Log func (a *DynamicLegacyAuditApi) Log( ctx context.Context, event *auditV1.AuditLogEntry, visibility auditV1.Visibility, routableIdentifier *RoutableIdentifier, ) error { return a.LogWithTrace(ctx, event, visibility, routableIdentifier, nil, nil) } // LogWithTrace implements AuditApi.LogWithTrace func (a *DynamicLegacyAuditApi) LogWithTrace( ctx context.Context, event *auditV1.AuditLogEntry, visibility auditV1.Visibility, routableIdentifier *RoutableIdentifier, traceParent *string, traceState *string, ) error { cloudEvent, err := a.ValidateAndSerializeWithTrace(event, visibility, routableIdentifier, traceParent, traceState) 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 *DynamicLegacyAuditApi) ValidateAndSerialize( event *auditV1.AuditLogEntry, visibility auditV1.Visibility, routableIdentifier *RoutableIdentifier, ) (*CloudEvent, error) { return a.ValidateAndSerializeWithTrace(event, visibility, routableIdentifier, nil, nil) } // ValidateAndSerializeWithTrace implements AuditApi.ValidateAndSerializeWithTrace. // It serializes the event into the byte representation of the legacy audit log system. func (a *DynamicLegacyAuditApi) ValidateAndSerializeWithTrace( event *auditV1.AuditLogEntry, visibility auditV1.Visibility, routableIdentifier *RoutableIdentifier, traceParent *string, traceState *string, ) (*CloudEvent, error) { 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 } 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 // // Requires to have the topic name set as key "topic" in the context. func (a *DynamicLegacyAuditApi) Send( ctx context.Context, routableIdentifier *RoutableIdentifier, cloudEvent *CloudEvent, ) error { rawTopicName := ctx.Value(ContextKeyTopic) if rawTopicName == nil { return ErrNoTopicNameProvided } topicName := fmt.Sprintf("%s", rawTopicName) if len(topicName) == 0 { return ErrTopicNameEmpty } var topicNameResolver TopicNameResolver = &LegacyTopicNameResolver{topicName: topicName} return send(&topicNameResolver, a.messagingApi, ctx, routableIdentifier, cloudEvent) }