audit-go/audit/api/api_common.go

184 lines
5.9 KiB
Go

package api
import (
"context"
"errors"
"fmt"
"dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/audit/messaging"
auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/gen/go/audit/v1"
"google.golang.org/protobuf/proto"
)
const ContentTypeProtobuf = "application/x-protobuf"
// ErrEventNil indicates that the event was nil
var ErrEventNil = errors.New("event is nil")
// ErrObjectIdentifierVisibilityMismatch indicates that a reference mismatch was detected.
//
// Valid combinations are:
// * Visibility: Public, ObjectIdentifier: <value>
// * Visibility: Private, ObjectIdentifier: <value | nil> -> If ObjectIdentifier is nil,
// the ObjectReference in the message will be ObjectName_OBJECT_NAME_SYSTEM
var ErrObjectIdentifierVisibilityMismatch = errors.New("object reference visibility mismatch")
// ErrRoutableIdentifierMissing indicates that a routable identifier is expected for the constellation of data.
var ErrRoutableIdentifierMissing = errors.New("routable identifier expected")
// ErrRoutableIdentifierMismatch indicates that a routable identifier (uuid) does not match.
var ErrRoutableIdentifierMismatch = errors.New("routable identifier type does not match")
// ErrRoutableIdentifierTypeMismatch indicates that a routable identifier type does not match the expected type.
var ErrRoutableIdentifierTypeMismatch = errors.New("routable identifier type does not match")
// ErrUnsupportedObjectIdentifierType indicates that an unsupported object identifier type has been provided.
var ErrUnsupportedObjectIdentifierType = errors.New("unsupported object identifier type")
// ErrUnsupportedResourceReferenceType indicates that an unsupported reference type has been provided.
var ErrUnsupportedResourceReferenceType = errors.New("unsupported resource reference type")
// ErrTopicNameResolverNil states that the topic name resolve is nil
var ErrTopicNameResolverNil = errors.New("topic name resolver nil")
// ErrMessagingApiNil states that the messaging api is nil
var ErrMessagingApiNil = errors.New("messaging api nil")
// ErrSerializedPayloadNil states that the give serialized payload is nil
var ErrSerializedPayloadNil = errors.New("serialized payload nil")
func validateAndSerializePartially(
validator *ProtobufValidator,
event *auditV1.AuditEvent,
visibility auditV1.Visibility,
routingIdentifier *RoutingIdentifier,
objectIdentifier *auditV1.ObjectIdentifier,
) (*auditV1.RoutableAuditEvent, error) {
// Return error if the given event is nil
if event == nil {
return nil, ErrEventNil
}
// Validate the actual event
err := (*validator).Validate(event)
if err != nil {
return nil, err
}
// Ensure that an object identifier is set if the event is public
if objectIdentifier == nil && visibility == auditV1.Visibility_VISIBILITY_PUBLIC {
return nil, ErrObjectIdentifierVisibilityMismatch
}
// Routing identifier not set but object identifier with routable identifier
if routingIdentifier == nil && objectIdentifier != nil &&
visibility != auditV1.Visibility_VISIBILITY_PRIVATE {
return nil, ErrRoutableIdentifierMissing
}
// Routing identifier type and object identifier types are not compatible
if routingIdentifier != nil &&
objectIdentifier != nil &&
objectIdentifier.Type == auditV1.ObjectType_OBJECT_TYPE_FOLDER &&
routingIdentifier.Type != RoutingIdentifierTypeOrganization {
return nil, ErrRoutableIdentifierTypeMismatch
}
// Routing identifier type and object identifier do not match
if routingIdentifier != nil &&
objectIdentifier != nil &&
objectIdentifier.Type != auditV1.ObjectType_OBJECT_TYPE_FOLDER &&
routingIdentifier.Identifier.String() != objectIdentifier.Identifier {
return nil, ErrRoutableIdentifierMismatch
}
// Test serialization even if the data is dropped later while logging to the legacy solution
auditEventBytes, err := proto.Marshal(event)
if err != nil {
return nil, err
}
payload := auditV1.UnencryptedData{
Data: auditEventBytes,
ProtobufType: fmt.Sprintf("%v", event.ProtoReflect().Descriptor().FullName()),
}
routableEvent := auditV1.RoutableAuditEvent{
EventName: event.EventName,
Visibility: visibility,
Data: &auditV1.RoutableAuditEvent_UnencryptedData{UnencryptedData: &payload},
}
// Set oneof protobuf fields after creation of the object
if objectIdentifier == nil {
routableEvent.ResourceReference = &auditV1.RoutableAuditEvent_ObjectName{
ObjectName: auditV1.ObjectName_OBJECT_NAME_SYSTEM}
} else {
routableEvent.ResourceReference = &auditV1.RoutableAuditEvent_ObjectIdentifier{
ObjectIdentifier: objectIdentifier}
}
err = (*validator).Validate(&routableEvent)
if err != nil {
return nil, err
}
return &routableEvent, nil
}
func serializeToProtobufMessage(routableEvent *auditV1.RoutableAuditEvent) (SerializedPayload, error) {
// Serialize routable event
routableEventBytes, err := proto.Marshal(routableEvent)
if err != nil {
return nil, err
}
// Package the routable event with information about the type in a message
message := auditV1.ProtobufMessage{
Value: routableEventBytes,
ProtobufType: fmt.Sprintf("%v", routableEvent.ProtoReflect().Descriptor().FullName()),
}
// Serialize message
messageBytes, err := proto.Marshal(&message)
if err != nil {
return nil, err
}
return &routablePayload{
payload: messageBytes,
contentType: ContentTypeProtobuf,
}, nil
}
// Send implements AuditApi.Send
func send(
topicNameResolver *TopicNameResolver,
messagingApi *messaging.MessagingApi,
ctx context.Context,
routingIdentifier *RoutingIdentifier,
serializedPayload *SerializedPayload,
) error {
if topicNameResolver == nil {
return ErrTopicNameResolverNil
}
if messagingApi == nil {
return ErrMessagingApiNil
}
if serializedPayload == nil {
return ErrSerializedPayloadNil
}
topic, err := (*topicNameResolver).Resolve(routingIdentifier)
if err != nil {
return err
}
return (*messagingApi).Send(ctx, topic, (*serializedPayload).GetPayload(), (*serializedPayload).GetContentType())
}