audit-go/pkg/audit/common/api.go
Christian Schaible (EXT) 85aae1c2e7 Merged PR 779949: feat: Refactor module structure to reflect best practices
Security-concept-update-needed: false.

JIRA Work Item: STACKITALO-259
2025-05-19 11:54:00 +00:00

259 lines
8.3 KiB
Go

package common
import (
"context"
"regexp"
"time"
"buf.build/go/protovalidate"
"github.com/google/uuid"
"google.golang.org/protobuf/proto"
auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1"
)
// ContentTypeCloudEventsProtobuf the cloudevents protobuf content-type sent in metadata of messages
const ContentTypeCloudEventsProtobuf = "application/cloudevents+protobuf"
const ContentTypeCloudEventsJson = "application/cloudevents+json; charset=UTF-8"
var TopicNamePattern = regexp.MustCompile(`^topic://stackit-platform/t/swz/audit-log/(?:conway|eu01|eu02|sx-stoi01)/[Vv][1-9](?:\.\d)?/[A-Za-z0-9-]+/[A-Za-z0-9-/]+`)
type EventType string
const (
EventTypeAdminActivity EventType = "admin-activity"
EventTypeSystemEvent EventType = "system-event"
EventTypePolicyDenied EventType = "policy-denied"
EventTypeDataAccess EventType = "data-access"
)
type ObjectType string
const (
ObjectTypeSystem ObjectType = "system"
ObjectTypeOrganization ObjectType = "organization"
ObjectTypeFolder ObjectType = "folder"
ObjectTypeProject ObjectType = "project"
)
func ObjectTypeFromPluralString(value string) ObjectType {
pluralSuffix := "s"
switch value {
case string(ObjectTypeOrganization) + pluralSuffix:
return ObjectTypeOrganization
case string(ObjectTypeFolder) + pluralSuffix:
return ObjectTypeFolder
case string(ObjectTypeProject) + pluralSuffix:
return ObjectTypeProject
case string(ObjectTypeSystem):
return ObjectTypeSystem
default:
return ObjectType(value)
}
}
func (t ObjectType) IsSupportedType() error {
switch t {
case ObjectTypeOrganization, ObjectTypeFolder, ObjectTypeProject, ObjectTypeSystem:
return nil
default:
return ErrUnknownObjectType
}
}
func (t ObjectType) Plural() string {
pluralSuffix := "s"
switch t {
case ObjectTypeOrganization:
return string(ObjectTypeOrganization) + pluralSuffix
case ObjectTypeFolder:
return string(ObjectTypeFolder) + pluralSuffix
case ObjectTypeProject:
return string(ObjectTypeProject) + pluralSuffix
case ObjectTypeSystem:
return string(ObjectTypeSystem)
default:
return ""
}
}
var SystemIdentifier = &auditV1.ObjectIdentifier{Identifier: uuid.Nil.String(), Type: string(ObjectTypeSystem)}
var RoutableSystemIdentifier = NewRoutableIdentifier(SystemIdentifier)
// AuditApi is the interface to log audit events.
//
// It provides a Log method that can be used to validate and directly send events.
// If the transactional outbox pattern should be used, the ValidateAndSerialize and Send methods
// can be called manually to decouple operations.
type AuditApi interface {
// Log is a convenience method that validates, serializes and sends data over the wire.
// If the transactional outbox pattern should be used, the ValidateAndSerialize method
// and Send method can be called separately.
// If an error is returned it is the responsibility of the caller to retry. The api does
// not store, buffer events or retry failed invocation automatically.
//
// Parameters:
// * ctx - the context object
// * event - the auditV1.AuditEvent
// * visibility - route the event only internally or to the customer (no routing in the legacy solution)
// * routableIdentifier - the identifier of the object
//
// Returns:
// * an error if the validation, serialization or send failed
Log(
ctx context.Context,
event *auditV1.AuditLogEntry,
visibility auditV1.Visibility,
routableIdentifier *RoutableIdentifier,
) error
// ValidateAndSerialize validates and serializes the event into a byte representation.
// The result has to be sent explicitly by calling the Send method.
//
// Parameters:
// * ctx - the context object
// * event - the auditV1.AuditEvent
// * visibility - route the event only internally or to the customer (no routing in the legacy solution)
// * routableIdentifier - the identifier of the object
//
// Returns:
// * the CloudEvent (i.e. the serialized AuditLogEntry with metadata)
// * an error if validation or serialization failed
ValidateAndSerialize(
ctx context.Context,
event *auditV1.AuditLogEntry,
visibility auditV1.Visibility,
routableIdentifier *RoutableIdentifier,
) (*CloudEvent, error)
// Send the serialized content as byte array to the audit log system.
// If an error is returned it is the responsibility of the caller to
// retry. The api does not store, buffer events or retry failed
// invocation automatically.
//
// Parameters:
// * ctx - the context object
// * routableIdentifier - the identifier of the object
// * cloudEvent - the serialized AuditLogEntry with metadata
//
// Returns:
// * an error if the event could not be sent
Send(
ctx context.Context,
routableIdentifier *RoutableIdentifier,
cloudEvent *CloudEvent,
) error
}
// ProtobufValidator is an abstraction for validators.
// Concrete implementations are e.g. protovalidate.Validator
type ProtobufValidator interface {
Validate(msg proto.Message, options ...protovalidate.ValidationOption) error
}
// CloudEvent is a representation of a cloudevents.io object.
//
// More information about the schema and attribute semantics can be found here:
// https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#required-attributes
type CloudEvent struct {
// The version of the CloudEvents specification which the event uses.
// This enables the interpretation of the context. Compliant event producers MUST use a value of 1.0
// when referring to this version of the specification.
//
// Currently, this attribute will only have the 'major' and 'minor' version numbers included in it.
// This allows for 'patch' changes to the specification to be made without changing this property's
// value in the serialization.
SpecVersion string
// The source system uri-reference. Producers MUST ensure that source + id is unique for each distinct event.
Source string
// Identifier of the event. Producers MUST ensure that source + id is unique for each distinct event.
// If a duplicate event is re-sent (e.g. due to a network error) it MAY have the same id.
// Consumers MAY assume that Events with identical source and id are duplicates.
Id string
// The time when the event happened
Time time.Time
// The content type of the payload
// Examples could be:
// - application/cloudevents+json
// - application/cloudevents+json; charset=UTF-8
// - application/cloudevents-batch+json
// - application/cloudevents+protobuf
// - application/cloudevents+avro
// Source: https://github.com/cloudevents/spec/blob/main/cloudevents/formats/protobuf-format.md
DataContentType string
// The object type (i.e. the fully qualified protobuf type name)
DataType string
// The identifier of the referring object.
Subject string
// The serialized payload
Data []byte
// Optional W3C conform trace parent:
// https://www.w3.org/TR/trace-context/#traceparent-header
//
// Format: <version>-<trace-id>-<parent-id>-<trace-flags>
//
// Examples:
// "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
TraceParent *string
// Optional W3C conform trace state header:
// https://www.w3.org/TR/trace-context/#tracestate-header
//
// Format: <key1>=<value1>[,<keyN>=<valueN>]
//
// Examples:
// "rojo=00f067aa0ba902b7,congo=t61rcWkgMzE"
TraceState *string
}
// TopicNameResolver is an abstraction for dynamic topic name resolution
// based on event data or api parameters.
type TopicNameResolver interface {
// Resolve returns a topic name for the given object identifier
Resolve(routableIdentifier *RoutableIdentifier) (string, error)
}
// StaticTopicNameTestResolver implements TopicNameResolver.
// A hard-coded topic name is used, routable identifiers are ignored.
type StaticTopicNameTestResolver struct {
TopicName string
}
// Resolve implements TopicNameResolver.Resolve
func (r *StaticTopicNameTestResolver) Resolve(*RoutableIdentifier) (string, error) {
return r.TopicName, nil
}
type RoutableIdentifier struct {
Identifier string
Type ObjectType
}
func NewRoutableIdentifier(objectIdentifier *auditV1.ObjectIdentifier) *RoutableIdentifier {
if objectIdentifier == nil {
return nil
}
return &RoutableIdentifier{
Identifier: objectIdentifier.Identifier,
Type: ObjectType(objectIdentifier.Type),
}
}
func (r RoutableIdentifier) ToObjectIdentifier() *auditV1.ObjectIdentifier {
return &auditV1.ObjectIdentifier{
Identifier: r.Identifier,
Type: string(r.Type),
}
}