mirror of
https://dev.azure.com/schwarzit/schwarzit.stackit-public/_git/audit-go
synced 2026-02-08 00:57:24 +00:00
198 lines
5.8 KiB
Go
198 lines
5.8 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"google.golang.org/protobuf/proto"
|
|
|
|
"dev.azure.com/schwarzit/schwarzit.stackit-core-platform/audit-go.git/audit/messaging"
|
|
auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/audit-go.git/gen/go/audit/v1"
|
|
)
|
|
|
|
// routableTopicNameResolver implements TopicNameResolver.
|
|
// Resolves topic names by concatenating topic type prefixes with routing identifiers.
|
|
type routableTopicNameResolver struct {
|
|
folderTopicPrefix string
|
|
organizationTopicPrefix string
|
|
projectTopicPrefix string
|
|
// If no identifier is provided for routing, it will be routed to a system topic
|
|
systemTopicName string
|
|
}
|
|
|
|
// Resolve implements TopicNameResolver.Resolve.
|
|
func (r *routableTopicNameResolver) Resolve(routableIdentifier *RoutableIdentifier) (string, error) {
|
|
|
|
if routableIdentifier == nil {
|
|
return "", ErrObjectIdentifierNil
|
|
}
|
|
|
|
switch routableIdentifier.Type {
|
|
case SingularTypeOrganization:
|
|
return fmt.Sprintf("topic://%s/%s", r.organizationTopicPrefix, routableIdentifier.Identifier), nil
|
|
case SingularTypeProject:
|
|
return fmt.Sprintf("topic://%s/%s", r.projectTopicPrefix, routableIdentifier.Identifier), nil
|
|
case SingularTypeFolder:
|
|
return fmt.Sprintf("topic://%s/%s", r.folderTopicPrefix, routableIdentifier.Identifier), nil
|
|
case SingularTypeSystem:
|
|
return r.systemTopicName, nil
|
|
default:
|
|
return "", ErrUnsupportedObjectIdentifierType
|
|
}
|
|
}
|
|
|
|
// topicNameConfig provides topic name information required for the topic name resolution.
|
|
type topicNameConfig struct {
|
|
FolderTopicPrefix string
|
|
OrganizationTopicPrefix string
|
|
ProjectTopicPrefix string
|
|
SystemTopicName string
|
|
}
|
|
|
|
// routableAuditApi is an implementation of AuditApi.
|
|
// Warning: It is only there for local (compatibility) testing.
|
|
// DO NOT USE IT!
|
|
type routableAuditApi struct {
|
|
messagingApi *messaging.Api
|
|
topicNameResolver *TopicNameResolver
|
|
validator *ProtobufValidator
|
|
}
|
|
|
|
// NewRoutableAuditApi can be used to initialize the audit log api.
|
|
func newRoutableAuditApi(
|
|
messagingApi *messaging.Api,
|
|
topicNameConfig topicNameConfig,
|
|
validator ProtobufValidator,
|
|
) (*AuditApi, error) {
|
|
|
|
if messagingApi == nil {
|
|
return nil, errors.New("messaging api nil")
|
|
}
|
|
|
|
// Topic resolver
|
|
if topicNameConfig.FolderTopicPrefix == "" {
|
|
return nil, errors.New("folder topic prefix is required")
|
|
}
|
|
if topicNameConfig.OrganizationTopicPrefix == "" {
|
|
return nil, errors.New("organization topic prefix is required")
|
|
}
|
|
if topicNameConfig.ProjectTopicPrefix == "" {
|
|
return nil, errors.New("project topic prefix is required")
|
|
}
|
|
if topicNameConfig.SystemTopicName == "" {
|
|
return nil, errors.New("system topic name is required")
|
|
}
|
|
|
|
var topicNameResolver TopicNameResolver = &routableTopicNameResolver{
|
|
folderTopicPrefix: topicNameConfig.FolderTopicPrefix,
|
|
organizationTopicPrefix: topicNameConfig.OrganizationTopicPrefix,
|
|
projectTopicPrefix: topicNameConfig.ProjectTopicPrefix,
|
|
systemTopicName: topicNameConfig.SystemTopicName,
|
|
}
|
|
|
|
// Audit api
|
|
var auditApi AuditApi = &routableAuditApi{
|
|
messagingApi: messagingApi,
|
|
topicNameResolver: &topicNameResolver,
|
|
validator: &validator,
|
|
}
|
|
|
|
return &auditApi, nil
|
|
}
|
|
|
|
// Log implements AuditApi.Log
|
|
func (a *routableAuditApi) 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 *routableAuditApi) 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
|
|
func (a *routableAuditApi) ValidateAndSerialize(
|
|
event *auditV1.AuditLogEntry,
|
|
visibility auditV1.Visibility,
|
|
routableIdentifier *RoutableIdentifier,
|
|
) (*CloudEvent, error) {
|
|
|
|
return a.ValidateAndSerializeWithTrace(event, visibility, routableIdentifier, nil, nil)
|
|
}
|
|
|
|
// ValidateAndSerializeWithTrace implements AuditApi.ValidateAndSerializeWithTrace
|
|
func (a *routableAuditApi) 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
|
|
}
|
|
|
|
routableEventBytes, err := proto.Marshal(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: ContentTypeCloudEventsProtobuf,
|
|
DataType: fmt.Sprintf("%v", routableEvent.ProtoReflect().Descriptor().FullName()),
|
|
Subject: event.ProtoPayload.ResourceName,
|
|
Data: routableEventBytes,
|
|
TraceParent: traceParent,
|
|
TraceState: traceState,
|
|
}
|
|
|
|
return &message, nil
|
|
}
|
|
|
|
// Send implements AuditApi.Send
|
|
func (a *routableAuditApi) Send(
|
|
ctx context.Context,
|
|
routableIdentifier *RoutableIdentifier,
|
|
cloudEvent *CloudEvent,
|
|
) error {
|
|
|
|
return send(a.topicNameResolver, a.messagingApi, ctx, routableIdentifier, cloudEvent)
|
|
}
|