Update schema and fix tests

This commit is contained in:
Christian Schaible 2024-07-25 14:54:03 +02:00
parent da9ef5f707
commit ed68f3c6d9
17 changed files with 1796 additions and 1453 deletions

View file

@ -185,9 +185,11 @@ In the future related code and configurations for both additional languages (Jav
Python) will be extracted into separate repositories. Python) will be extracted into separate repositories.
### Open Issues ### Open Issues
- Finalizing messaging schema - Check TODOs in the code
- Extraction of python / java configurations and code - Finalizing messaging schema
- Check if fields and constraints are correct and compatible with expected use cases
- Check that routing api specific parameters are independent of AuditEventLog
- Clarify if a mutex is needed? Should we provide a MutexMessagingApi?
- Clarify if `client.go` file can be used for licence / legal reasons - Clarify if `client.go` file can be used for licence / legal reasons
- Clean up repo (delete main.go, etc. files) - Extraction of python / java configurations and code
- Finalizing API Design - Clean up repo (delete main.go, etc. files)
- Decision whether serialized payload should be replaced with []byte or base64 encoded string

View file

@ -119,6 +119,24 @@ type CloudEvent struct {
// The serialized payload // The serialized payload
data []byte 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
} }
// RoutingIdentifierType is an enumeration of allowed identifier types. // RoutingIdentifierType is an enumeration of allowed identifier types.

View file

@ -11,7 +11,8 @@ import (
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
const ContentTypeProtobuf = "application/x-protobuf" // ContentTypeCloudEventsProtobuf the cloudevents protobuf content-type sent in metadata of messages
const ContentTypeCloudEventsProtobuf = "application/cloudevents+protobuf"
// ErrEventNil indicates that the event was nil // ErrEventNil indicates that the event was nil
var ErrEventNil = errors.New("event is nil") var ErrEventNil = errors.New("event is nil")
@ -109,7 +110,7 @@ func validateAndSerializePartially(
} }
routableEvent := auditV1.RoutableAuditEvent{ routableEvent := auditV1.RoutableAuditEvent{
EventName: event.LogName, EventName: event.ProtoPayload.MethodName,
Visibility: visibility, Visibility: visibility,
Data: &auditV1.RoutableAuditEvent_UnencryptedData{UnencryptedData: &payload}, Data: &auditV1.RoutableAuditEvent_UnencryptedData{UnencryptedData: &payload},
} }
@ -155,6 +156,8 @@ func send(
return err return err
} }
// Naming according to AMQP protocol binding spec
// https://github.com/cloudevents/spec/blob/main/cloudevents/bindings/amqp-protocol-binding.md
applicationAttributes := make(map[string]any) applicationAttributes := make(map[string]any)
applicationAttributes["cloudEvents:specversion"] = cloudEvent.specVersion applicationAttributes["cloudEvents:specversion"] = cloudEvent.specVersion
applicationAttributes["cloudEvents:source"] = cloudEvent.source applicationAttributes["cloudEvents:source"] = cloudEvent.source
@ -163,6 +166,12 @@ func send(
applicationAttributes["cloudEvents:datacontenttype"] = cloudEvent.dataContentType applicationAttributes["cloudEvents:datacontenttype"] = cloudEvent.dataContentType
applicationAttributes["cloudEvents:type"] = cloudEvent.dataType applicationAttributes["cloudEvents:type"] = cloudEvent.dataType
applicationAttributes["cloudEvents:subject"] = cloudEvent.subject applicationAttributes["cloudEvents:subject"] = cloudEvent.subject
if cloudEvent.traceParent != nil {
applicationAttributes["cloudEvents:traceparent"] = cloudEvent.traceParent
}
if cloudEvent.traceState != nil {
applicationAttributes["cloudEvents:tracestate"] = cloudEvent.traceState
}
return (*messagingApi).Send( return (*messagingApi).Send(
ctx, ctx,

View file

@ -2,14 +2,10 @@ package api
import ( import (
"context" "context"
"errors"
"google.golang.org/protobuf/types/known/timestamppb"
"google.golang.org/protobuf/types/known/wrapperspb"
"testing"
"time"
"dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/audit/messaging" "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" auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/gen/go/audit/v1"
"errors"
"testing"
"github.com/bufbuild/protovalidate-go" "github.com/bufbuild/protovalidate-go"
"github.com/google/uuid" "github.com/google/uuid"
@ -69,104 +65,16 @@ func Test_ValidateAndSerializePartially_EventNil(t *testing.T) {
assert.ErrorIs(t, err, ErrEventNil) assert.ErrorIs(t, err, ErrEventNil)
} }
func Test_ValidateAndSerializePartially_AuditEventSequenceNumber(t *testing.T) {
validator := NewValidator(t)
t.Run("Sequence number too low", func(t *testing.T) {
event, routingIdentifier, objectIdentifier := NewOrganizationAuditEvent(nil)
event.SequenceNumber = wrapperspb.Int64(-2)
_, err := validateAndSerializePartially(
&validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, objectIdentifier)
assert.EqualError(t, err, "validation error:\n - sequence_number: value must be greater than or equal to -1 [int64.gte]")
})
t.Run("Sequence number is minimum", func(t *testing.T) {
event, routingIdentifier, objectIdentifier := NewOrganizationAuditEvent(nil)
event.SequenceNumber = wrapperspb.Int64(-1)
e, err := validateAndSerializePartially(
&validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, objectIdentifier)
assert.NoError(t, err)
validateSequenceNumber(t, e, -1)
})
t.Run("Sequence number is default", func(t *testing.T) {
event, routingIdentifier, objectIdentifier := NewOrganizationAuditEvent(nil)
event.SequenceNumber = wrapperspb.Int64(0)
e, err := validateAndSerializePartially(
&validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, objectIdentifier)
assert.NoError(t, err)
validateSequenceNumber(t, e, 0)
})
t.Run("Sequence number is greater than default", func(t *testing.T) {
event, routingIdentifier, objectIdentifier := NewOrganizationAuditEvent(nil)
event.SequenceNumber = wrapperspb.Int64(1)
e, err := validateAndSerializePartially(
&validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, objectIdentifier)
assert.NoError(t, err)
validateSequenceNumber(t, e, 1)
})
t.Run("Sequence number not set", func(t *testing.T) {
event := &auditV1.AuditEvent{
EventSource: "resource-manager",
Region: auditV1.Region_REGION_EU01,
EventName: "ORGANIZATION_CREATED",
EventTimeStamp: timestamppb.New(time.Now()),
EventTrigger: auditV1.EventTrigger_EVENT_TRIGGER_EVENT,
Initiator: &auditV1.Principal{
Id: uuid.NewString(),
},
}
identifier := uuid.New()
routingIdentifier := &RoutingIdentifier{
Identifier: identifier,
Type: RoutingIdentifierTypeOrganization,
}
objectIdentifier := &auditV1.ObjectIdentifier{
Identifier: identifier.String(),
Type: auditV1.ObjectType_OBJECT_TYPE_ORGANIZATION,
}
event.ResourceContainerReference = &auditV1.AuditEvent_ObjectIdentifier{ObjectIdentifier: objectIdentifier}
_, err := validateAndSerializePartially(
&validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, objectIdentifier)
assert.EqualError(t, err, "validation error:\n - sequence_number: value is required [required]")
})
}
func validateSequenceNumber(t *testing.T, event *auditV1.RoutableAuditEvent, expectedNumber int64) {
switch data := event.GetData().(type) {
case *auditV1.RoutableAuditEvent_UnencryptedData:
var auditEvent auditV1.AuditEvent
assert.NoError(t, proto.Unmarshal(data.UnencryptedData.Data, &auditEvent))
assert.Equal(t, expectedNumber, auditEvent.SequenceNumber.Value)
default:
assert.Fail(t, "expected unencrypted data")
}
}
func Test_ValidateAndSerializePartially_AuditEventValidationFailed(t *testing.T) { func Test_ValidateAndSerializePartially_AuditEventValidationFailed(t *testing.T) {
validator := NewValidator(t) validator := NewValidator(t)
event, routingIdentifier, objectIdentifier := NewOrganizationAuditEvent(nil) event, routingIdentifier, objectIdentifier := NewOrganizationAuditEvent(nil)
event.EventName = "" event.LogName = ""
_, err := validateAndSerializePartially( _, err := validateAndSerializePartially(
&validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, objectIdentifier) &validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routingIdentifier, objectIdentifier)
assert.EqualError(t, err, "validation error:\n - event_name: value is required [required]") assert.EqualError(t, err, "validation error:\n - log_name: value is required [required]")
} }
func Test_ValidateAndSerializePartially_RoutableEventValidationFailed(t *testing.T) { func Test_ValidateAndSerializePartially_RoutableEventValidationFailed(t *testing.T) {

View file

@ -15,6 +15,8 @@ import (
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
var ErrUnsupportedSeverity = errors.New("unsupported severity level")
// LegacyTopicNameResolver implements TopicNameResolver. // LegacyTopicNameResolver implements TopicNameResolver.
// A hard-coded topic name is used, routing identifiers are ignored. // A hard-coded topic name is used, routing identifiers are ignored.
type LegacyTopicNameResolver struct { type LegacyTopicNameResolver struct {
@ -50,7 +52,7 @@ func NewLegacyAuditApi(
) (*AuditApi, error) { ) (*AuditApi, error) {
if messagingApi == nil { if messagingApi == nil {
return nil, errors.New("messaging api nil") return nil, ErrMessagingApiNil
} }
// Topic resolver // Topic resolver
@ -115,11 +117,14 @@ func (a *LegacyAuditApi) ValidateAndSerialize(
// TODO what is the correct id? // TODO what is the correct id?
id: uuid.NewString(), id: uuid.NewString(),
time: event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(), time: event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(),
dataContentType: "application/cloudevents+protobuf", dataContentType: ContentTypeCloudEventsProtobuf,
dataType: fmt.Sprintf("%v", routableEvent.ProtoReflect().Descriptor().FullName()), dataType: fmt.Sprintf("%v", routableEvent.ProtoReflect().Descriptor().FullName()),
// TODO check if this is correct // TODO check if this is correct
subject: event.ProtoPayload.ResourceName, subject: event.ProtoPayload.ResourceName,
data: legacyBytes, data: legacyBytes,
// TODO set trace fields
traceParent: nil,
traceState: nil,
} }
return &message, nil return &message, nil
} }
@ -160,13 +165,14 @@ func (a *LegacyAuditApi) convertAndSerializeIntoLegacyFormat(
case *auditV1.ServiceAccountDelegationInfo_IdpPrincipal_: case *auditV1.ServiceAccountDelegationInfo_IdpPrincipal_:
principals = append(principals, LegacyAuditEventPrincipal{ principals = append(principals, LegacyAuditEventPrincipal{
Id: principalValue.IdpPrincipal.PrincipalId, Id: principalValue.IdpPrincipal.PrincipalId,
Email: principalValue.IdpPrincipal.PrincipalEmail, Email: &principalValue.IdpPrincipal.PrincipalEmail,
}) })
case *auditV1.ServiceAccountDelegationInfo_SystemPrincipal_: case *auditV1.ServiceAccountDelegationInfo_SystemPrincipal_:
principals = append(principals, LegacyAuditEventPrincipal{ principals = append(principals, LegacyAuditEventPrincipal{
Id: "system", Id: "system",
}) })
default:
return nil, errors.New("unsupported principal type")
} }
} }
serviceAccountDelegationInfo = &LegacyAuditEventServiceAccountDelegationInfo{Principals: principals} serviceAccountDelegationInfo = &LegacyAuditEventServiceAccountDelegationInfo{Principals: principals}
@ -179,11 +185,18 @@ func (a *LegacyAuditApi) convertAndSerializeIntoLegacyFormat(
} }
} else { } else {
var parameters map[string]interface{} = nil var parameters map[string]interface{} = nil
if event.ProtoPayload.RequestMetadata.RequestAttributes.Path != "" && event.ProtoPayload.RequestMetadata.RequestAttributes.Query != "" { if event.ProtoPayload.RequestMetadata.RequestAttributes.Path != "" &&
parsedUrl, err := url.Parse(fmt.Sprintf("%s?%s", event.ProtoPayload.RequestMetadata.RequestAttributes.Path, event.ProtoPayload.RequestMetadata.RequestAttributes.Query)) event.ProtoPayload.RequestMetadata.RequestAttributes.Query != nil &&
*event.ProtoPayload.RequestMetadata.RequestAttributes.Query != "" {
parameters = map[string]interface{}{}
parsedUrl, err := url.Parse(fmt.Sprintf("%s?%s",
event.ProtoPayload.RequestMetadata.RequestAttributes.Path,
*event.ProtoPayload.RequestMetadata.RequestAttributes.Query))
if err != nil { if err != nil {
return nil, err return nil, err
} }
for k, v := range parsedUrl.Query() { for k, v := range parsedUrl.Query() {
parameters[k] = v parameters[k] = v
} }
@ -258,13 +271,38 @@ func (a *LegacyAuditApi) convertAndSerializeIntoLegacyFormat(
// Result // Result
var result = event.ProtoPayload.Response.AsMap() var result = event.ProtoPayload.Response.AsMap()
// Severity
var severity string
switch event.Severity {
case auditV1.LogSeverity_DEFAULT:
fallthrough
case auditV1.LogSeverity_DEBUG:
fallthrough
case auditV1.LogSeverity_INFO:
fallthrough
case auditV1.LogSeverity_NOTICE:
fallthrough
case auditV1.LogSeverity_WARNING:
severity = "INFO"
case auditV1.LogSeverity_ERROR:
fallthrough
case auditV1.LogSeverity_CRITICAL:
fallthrough
case auditV1.LogSeverity_ALERT:
fallthrough
case auditV1.LogSeverity_EMERGENCY:
severity = "ERROR"
default:
return nil, ErrUnsupportedSeverity
}
// Instantiate the legacy event - missing values are filled with defaults // Instantiate the legacy event - missing values are filled with defaults
legacyAuditEvent := LegacyAuditEvent{ legacyAuditEvent := LegacyAuditEvent{
Severity: "INFO", Severity: severity,
Visibility: visibility, Visibility: visibility,
EventType: eventType, EventType: eventType,
EventTimeStamp: event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(), EventTimeStamp: event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(),
EventName: event.LogName, EventName: event.ProtoPayload.MethodName,
SourceIpAddress: sourceIpAddress, SourceIpAddress: sourceIpAddress,
UserAgent: userAgent, UserAgent: userAgent,
Initiator: LegacyAuditEventPrincipal{ Initiator: LegacyAuditEventPrincipal{
@ -274,11 +312,12 @@ func (a *LegacyAuditApi) convertAndSerializeIntoLegacyFormat(
ServiceAccountDelegationInfo: serviceAccountDelegationInfo, ServiceAccountDelegationInfo: serviceAccountDelegationInfo,
Request: request, Request: request,
Context: messageContext, Context: messageContext,
ResourceId: &event.LogName, // TODO clarify
ResourceName: &event.ProtoPayload.ResourceName, ResourceId: &event.LogName,
CorrelationId: &event.CorrelationId, ResourceName: &event.ProtoPayload.ResourceName,
Result: &result, CorrelationId: event.CorrelationId,
Details: &details, Result: &result,
Details: &details,
} }
bytes, err := json.Marshal(legacyAuditEvent) bytes, err := json.Marshal(legacyAuditEvent)

View file

@ -4,7 +4,9 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"log/slog" "log/slog"
"net/url"
"os" "os"
"testing" "testing"
"time" "time"
@ -320,14 +322,14 @@ func TestLegacyAuditApi(t *testing.T) {
var auditEvent LegacyAuditEvent var auditEvent LegacyAuditEvent
assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent)) assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent))
assert.Equal(t, event.EventName, auditEvent.EventName) assert.Equal(t, event.ProtoPayload.MethodName, auditEvent.EventName)
assert.Equal(t, event.EventTimeStamp.AsTime(), auditEvent.EventTimeStamp) assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(), auditEvent.EventTimeStamp)
assert.Equal(t, event.Initiator.Id, auditEvent.Initiator.Id) assert.Equal(t, event.ProtoPayload.AuthenticationInfo.PrincipalId, auditEvent.Initiator.Id)
assert.Equal(t, "SYSTEM_EVENT", auditEvent.EventType) assert.Equal(t, "SYSTEM_EVENT", auditEvent.EventType)
assert.Equal(t, "INFO", auditEvent.Severity) assert.Equal(t, "INFO", auditEvent.Severity)
assert.Equal(t, "none", auditEvent.Request.Endpoint) assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Path, auditEvent.Request.Endpoint)
assert.Equal(t, "0.0.0.0", auditEvent.SourceIpAddress) assert.Equal(t, event.ProtoPayload.RequestMetadata.CallerIp, auditEvent.SourceIpAddress)
assert.Equal(t, "none", auditEvent.UserAgent) assert.Equal(t, event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent, auditEvent.UserAgent)
}) })
t.Run("Log event with details", func(t *testing.T) { t.Run("Log event with details", func(t *testing.T) {
@ -350,7 +352,7 @@ func TestLegacyAuditApi(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
// Instantiate test data // Instantiate test data
event, routingIdentifier, objectIdentifier := NewOrganizationAuditEventWithDetails() event, routingIdentifier, objectIdentifier := NewOrganizationAuditEvent(nil)
// Log the event to solace // Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PUBLIC visibility := auditV1.Visibility_VISIBILITY_PUBLIC
@ -369,7 +371,7 @@ func TestLegacyAuditApi(t *testing.T) {
}) })
} }
func validateSentMessage(t *testing.T, topicName string, message *amqp.Message, event *auditV1.AuditEvent) { func validateSentMessage(t *testing.T, topicName string, message *amqp.Message, event *auditV1.AuditLogEntry) {
// Check topic name // Check topic name
assert.Equal(t, topicName, *message.Properties.To) assert.Equal(t, topicName, *message.Properties.To)
@ -377,43 +379,88 @@ func validateSentMessage(t *testing.T, topicName string, message *amqp.Message,
var auditEvent LegacyAuditEvent var auditEvent LegacyAuditEvent
assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent)) assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent))
assert.Equal(t, event.EventName, auditEvent.EventName) var severity string
assert.Equal(t, event.EventTimeStamp.AsTime(), auditEvent.EventTimeStamp) switch event.Severity {
assert.Equal(t, event.Initiator.Id, auditEvent.Initiator.Id) case auditV1.LogSeverity_DEFAULT:
assert.Equal(t, "ADMIN_ACTIVITY", auditEvent.EventType) fallthrough
assert.Equal(t, "INFO", auditEvent.Severity) case auditV1.LogSeverity_DEBUG:
assert.Equal(t, "none", auditEvent.Request.Endpoint) fallthrough
assert.Equal(t, "0.0.0.0", auditEvent.SourceIpAddress) case auditV1.LogSeverity_INFO:
assert.Equal(t, "none", auditEvent.UserAgent) fallthrough
} case auditV1.LogSeverity_NOTICE:
fallthrough
func validateSentMessageWithDetails(t *testing.T, topicName string, message *amqp.Message, event *auditV1.AuditEvent) { case auditV1.LogSeverity_WARNING:
// Check topic name severity = "INFO"
assert.Equal(t, topicName, *message.Properties.To) case auditV1.LogSeverity_ERROR:
fallthrough
// Check deserialized message case auditV1.LogSeverity_CRITICAL:
var auditEvent LegacyAuditEvent fallthrough
assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent)) case auditV1.LogSeverity_ALERT:
fallthrough
assert.Equal(t, event.EventName, auditEvent.EventName) case auditV1.LogSeverity_EMERGENCY:
assert.Equal(t, event.EventTimeStamp.AsTime(), auditEvent.EventTimeStamp) severity = "ERROR"
assert.Equal(t, event.Initiator.Id, auditEvent.Initiator.Id) default:
assert.Equal(t, "ADMIN_ACTIVITY", auditEvent.EventType) assert.Fail(t, "unknown log severity")
assert.Equal(t, "INFO", auditEvent.Severity)
assert.Equal(t, event.Request.Endpoint, auditEvent.Request.Endpoint)
assert.Equal(t, event.Request.SourceIpAddress, auditEvent.SourceIpAddress)
assert.Equal(t, *event.Request.UserAgent, auditEvent.UserAgent)
assert.Equal(t, event.Request.Body.AsMap(), *auditEvent.Request.Body)
assert.Equal(t, event.Request.Parameters.AsMap(), *auditEvent.Request.Parameters)
assert.Equal(t, event.Details.AsMap(), *auditEvent.Details)
assert.Equal(t, event.Result.AsMap(), *auditEvent.Result)
for _, header := range event.Request.Headers {
assert.Equal(t, header.Value, (*auditEvent.Request.Headers)[header.Key])
} }
for idx := range event.Principals { assert.Equal(t, event.ProtoPayload.MethodName, auditEvent.EventName)
assert.Equal(t, event.Principals[idx].Id, auditEvent.ServiceAccountDelegationInfo.Principals[idx].Id) assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(), auditEvent.EventTimeStamp)
assert.Equal(t, event.Principals[idx].Email, auditEvent.ServiceAccountDelegationInfo.Principals[idx].Email) assert.Equal(t, event.ProtoPayload.AuthenticationInfo.PrincipalId, auditEvent.Initiator.Id)
assert.Equal(t, "ADMIN_ACTIVITY", auditEvent.EventType)
assert.Equal(t, severity, auditEvent.Severity)
assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Path, auditEvent.Request.Endpoint)
assert.Equal(t, event.ProtoPayload.RequestMetadata.CallerIp, auditEvent.SourceIpAddress)
assert.Equal(t, event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent, auditEvent.UserAgent)
}
func validateSentMessageWithDetails(t *testing.T, topicName string, message *amqp.Message, event *auditV1.AuditLogEntry) {
// Check topic name
assert.Equal(t, topicName, *message.Properties.To)
// Check deserialized message
var auditEvent LegacyAuditEvent
assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent))
assert.Equal(t, event.ProtoPayload.MethodName, auditEvent.EventName)
assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(), auditEvent.EventTimeStamp)
assert.Equal(t, event.ProtoPayload.AuthenticationInfo.PrincipalId, auditEvent.Initiator.Id)
assert.Equal(t, "ADMIN_ACTIVITY", auditEvent.EventType)
assert.Equal(t, "INFO", auditEvent.Severity)
assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Path, auditEvent.Request.Endpoint)
var parameters map[string]interface{} = nil
if event.ProtoPayload.RequestMetadata.RequestAttributes.Path != "" &&
event.ProtoPayload.RequestMetadata.RequestAttributes.Query != nil &&
*event.ProtoPayload.RequestMetadata.RequestAttributes.Query != "" {
parameters = map[string]interface{}{}
parsedUrl, err := url.Parse(fmt.Sprintf("%s?%s",
event.ProtoPayload.RequestMetadata.RequestAttributes.Path,
*event.ProtoPayload.RequestMetadata.RequestAttributes.Query))
assert.NoError(t, err)
for k, v := range parsedUrl.Query() {
parameters[k] = v
}
}
assert.Equal(t, event.ProtoPayload.Request.AsMap(), *auditEvent.Request.Body)
assert.Equal(t, parameters, *auditEvent.Request.Parameters)
for key, value := range event.ProtoPayload.RequestMetadata.RequestAttributes.Headers {
assert.Equal(t, value, (*auditEvent.Request.Headers)[key])
}
assert.Equal(t, event.ProtoPayload.RequestMetadata.CallerIp, auditEvent.SourceIpAddress)
assert.Equal(t, event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent, auditEvent.UserAgent)
assert.Equal(t, event.ProtoPayload.Request.AsMap(), *auditEvent.Details)
assert.Equal(t, event.ProtoPayload.Response.AsMap(), *auditEvent.Result)
assert.True(t, auditEvent.Context.OrganizationId != nil || auditEvent.Context.FolderId != nil || auditEvent.Context.ProjectId != nil)
for idx, principal := range event.ProtoPayload.AuthenticationInfo.ServiceAccountDelegationInfo {
switch principalValue := principal.Authority.(type) {
case *auditV1.ServiceAccountDelegationInfo_IdpPrincipal_:
assert.Equal(t, principalValue.IdpPrincipal.PrincipalId, auditEvent.ServiceAccountDelegationInfo.Principals[idx].Id)
assert.Equal(t, principalValue.IdpPrincipal.PrincipalEmail, auditEvent.ServiceAccountDelegationInfo.Principals[idx].Email)
case *auditV1.ServiceAccountDelegationInfo_SystemPrincipal_:
assert.Equal(t, "system", auditEvent.ServiceAccountDelegationInfo.Principals[idx].Id)
}
} }
} }
@ -458,7 +505,7 @@ func TestLegacyAuditApi_Log_NilEvent(t *testing.T) {
} }
func TestLegacyAuditApi_ConvertAndSerializeIntoLegacyFormatInvalidObjectIdentifierType(t *testing.T) { func TestLegacyAuditApi_ConvertAndSerializeIntoLegacyFormatInvalidObjectIdentifierType(t *testing.T) {
customization := func(event *auditV1.AuditEvent, customization := func(event *auditV1.AuditLogEntry,
routingIdentifier *RoutingIdentifier, routingIdentifier *RoutingIdentifier,
objectIdentifier *auditV1.ObjectIdentifier) { objectIdentifier *auditV1.ObjectIdentifier) {
objectIdentifier.Type = 4 objectIdentifier.Type = 4
@ -477,7 +524,7 @@ func TestLegacyAuditApi_ConvertAndSerializeIntoLegacyFormatInvalidObjectIdentifi
func TestLegacyAuditApi_ConvertAndSerializeIntoLegacyFormat_NoResourceReference(t *testing.T) { func TestLegacyAuditApi_ConvertAndSerializeIntoLegacyFormat_NoResourceReference(t *testing.T) {
event, _, _ := NewProjectAuditEvent(nil) event, _, _ := NewProjectAuditEvent(nil)
routableEvent := auditV1.RoutableAuditEvent{ routableEvent := auditV1.RoutableAuditEvent{
EventName: event.EventName, EventName: event.ProtoPayload.MethodName,
Visibility: auditV1.Visibility_VISIBILITY_PUBLIC, Visibility: auditV1.Visibility_VISIBILITY_PUBLIC,
ResourceReference: nil, ResourceReference: nil,
Data: nil, Data: nil,

View file

@ -69,6 +69,9 @@ func (a *MockAuditApi) ValidateAndSerialize(
// TODO check if this is correct // TODO check if this is correct
subject: event.ProtoPayload.ResourceName, subject: event.ProtoPayload.ResourceName,
data: routableEventBytes, data: routableEventBytes,
// TODO set trace fields
traceParent: nil,
traceState: nil,
} }
return &message, nil return &message, nil

View file

@ -31,7 +31,7 @@ func TestMockAuditApi_Log(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
validateRoutableEventPayload( validateRoutableEventPayload(
t, cloudEvent.data, routingIdentifier, objectIdentifier, event, event.EventName, visibility) t, cloudEvent.data, routingIdentifier, objectIdentifier, event, event.ProtoPayload.MethodName, visibility)
}) })
t.Run("ValidateAndSerialize event nil", func(t *testing.T) { t.Run("ValidateAndSerialize event nil", func(t *testing.T) {

View file

@ -128,11 +128,14 @@ func (a *routableAuditApi) ValidateAndSerialize(
// TODO what is the correct id? // TODO what is the correct id?
id: uuid.NewString(), id: uuid.NewString(),
time: event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(), time: event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime(),
dataContentType: "application/cloudevents+protobuf", dataContentType: ContentTypeCloudEventsProtobuf,
dataType: fmt.Sprintf("%v", routableEvent.ProtoReflect().Descriptor().FullName()), dataType: fmt.Sprintf("%v", routableEvent.ProtoReflect().Descriptor().FullName()),
// TODO check if this is correct // TODO check if this is correct
subject: event.ProtoPayload.ResourceName, subject: event.ProtoPayload.ResourceName,
data: routableEventBytes, data: routableEventBytes,
// TODO set trace fields
traceParent: nil,
traceState: nil,
} }
return &message, nil return &message, nil

View file

@ -83,7 +83,7 @@ func TestRoutableAuditApi(t *testing.T) {
routingIdentifier, routingIdentifier,
objectIdentifier, objectIdentifier,
event, event,
"ORGANIZATION_CREATED", "stackit.resourcemanager.v2.organization.created",
visibility) visibility)
}) })
@ -123,7 +123,7 @@ func TestRoutableAuditApi(t *testing.T) {
routingIdentifier, routingIdentifier,
objectIdentifier, objectIdentifier,
event, event,
"ORGANIZATION_CREATED", "stackit.resourcemanager.v2.organization.created",
visibility) visibility)
}) })
@ -159,7 +159,7 @@ func TestRoutableAuditApi(t *testing.T) {
routingIdentifier, routingIdentifier,
objectIdentifier, objectIdentifier,
event, event,
"FOLDER_CREATED", "stackit.resourcemanager.v2.folder.created",
visibility) visibility)
}) })
@ -197,7 +197,7 @@ func TestRoutableAuditApi(t *testing.T) {
routingIdentifier, routingIdentifier,
objectIdentifier, objectIdentifier,
event, event,
"FOLDER_CREATED", "stackit.resourcemanager.v2.folder.created",
visibility) visibility)
}) })
@ -234,7 +234,7 @@ func TestRoutableAuditApi(t *testing.T) {
routingIdentifier, routingIdentifier,
objectIdentifier, objectIdentifier,
event, event,
"PROJECT_CREATED", "stackit.resourcemanager.v2.project.created",
visibility) visibility)
}) })
@ -270,7 +270,7 @@ func TestRoutableAuditApi(t *testing.T) {
routingIdentifier, routingIdentifier,
objectIdentifier, objectIdentifier,
event, event,
"PROJECT_CREATED", "stackit.resourcemanager.v2.project.created",
visibility) visibility)
}) })
@ -309,7 +309,7 @@ func TestRoutableAuditApi(t *testing.T) {
assert.Equal(t, "resource-manager", applicationProperties["cloudEvents:source"]) assert.Equal(t, "resource-manager", applicationProperties["cloudEvents:source"])
_, isUuid := uuid.Parse(fmt.Sprintf("%s", applicationProperties["cloudEvents:id"])) _, isUuid := uuid.Parse(fmt.Sprintf("%s", applicationProperties["cloudEvents:id"]))
assert.True(t, true, isUuid) assert.True(t, true, isUuid)
assert.Equal(t, event.EventTimeStamp.AsTime().UnixMilli(), applicationProperties["cloudEvents:time"]) assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().UnixMilli(), applicationProperties["cloudEvents:time"])
assert.Equal(t, "application/cloudevents+protobuf", applicationProperties["cloudEvents:datacontenttype"]) assert.Equal(t, "application/cloudevents+protobuf", applicationProperties["cloudEvents:datacontenttype"])
assert.Equal(t, "audit.v1.RoutableAuditEvent", applicationProperties["cloudEvents:type"]) assert.Equal(t, "audit.v1.RoutableAuditEvent", applicationProperties["cloudEvents:type"])
@ -320,7 +320,7 @@ func TestRoutableAuditApi(t *testing.T) {
nil, nil,
nil, nil,
event, event,
"SYSTEM_CHANGED", "stackit.resourcemanager.v2.system.changed",
visibility) visibility)
}) })
@ -334,7 +334,7 @@ func TestRoutableAuditApi(t *testing.T) {
assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, "org/*")) assert.NoError(t, solaceContainer.TopicSubscriptionCreate(ctx, queueName, "org/*"))
// Instantiate test data // Instantiate test data
event, routingIdentifier, objectIdentifier := NewOrganizationAuditEventWithDetails() event, routingIdentifier, objectIdentifier := NewOrganizationAuditEvent(nil)
// Log the event to solace // Log the event to solace
visibility := auditV1.Visibility_VISIBILITY_PUBLIC visibility := auditV1.Visibility_VISIBILITY_PUBLIC
@ -356,7 +356,7 @@ func TestRoutableAuditApi(t *testing.T) {
routingIdentifier, routingIdentifier,
objectIdentifier, objectIdentifier,
event, event,
"ORGANIZATION_CREATED", "stackit.resourcemanager.v2.organization.created",
visibility) visibility)
}) })
} }
@ -367,7 +367,7 @@ func validateSentEvent(
message *amqp.Message, message *amqp.Message,
routingIdentifier *RoutingIdentifier, routingIdentifier *RoutingIdentifier,
objectIdentifier *auditV1.ObjectIdentifier, objectIdentifier *auditV1.ObjectIdentifier,
event *auditV1.AuditEvent, event *auditV1.AuditLogEntry,
eventName string, eventName string,
visibility auditV1.Visibility, visibility auditV1.Visibility,
) { ) {
@ -383,7 +383,7 @@ func validateSentEvent(
assert.Equal(t, "resource-manager", applicationProperties["cloudEvents:source"]) assert.Equal(t, "resource-manager", applicationProperties["cloudEvents:source"])
_, isUuid := uuid.Parse(fmt.Sprintf("%s", applicationProperties["cloudEvents:id"])) _, isUuid := uuid.Parse(fmt.Sprintf("%s", applicationProperties["cloudEvents:id"]))
assert.True(t, true, isUuid) assert.True(t, true, isUuid)
assert.Equal(t, event.EventTimeStamp.AsTime().UnixMilli(), applicationProperties["cloudEvents:time"]) assert.Equal(t, event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().UnixMilli(), applicationProperties["cloudEvents:time"])
assert.Equal(t, "application/cloudevents+protobuf", applicationProperties["cloudEvents:datacontenttype"]) assert.Equal(t, "application/cloudevents+protobuf", applicationProperties["cloudEvents:datacontenttype"])
assert.Equal(t, "audit.v1.RoutableAuditEvent", applicationProperties["cloudEvents:type"]) assert.Equal(t, "audit.v1.RoutableAuditEvent", applicationProperties["cloudEvents:type"])
@ -397,7 +397,7 @@ func validateRoutableEventPayload(
payload []byte, payload []byte,
routingIdentifier *RoutingIdentifier, routingIdentifier *RoutingIdentifier,
objectIdentifier *auditV1.ObjectIdentifier, objectIdentifier *auditV1.ObjectIdentifier,
event *auditV1.AuditEvent, event *auditV1.AuditLogEntry,
eventName string, eventName string,
visibility auditV1.Visibility, visibility auditV1.Visibility,
) { ) {
@ -432,7 +432,7 @@ func validateRoutableEventPayload(
assert.Fail(t, "Object name not expected") assert.Fail(t, "Object name not expected")
} }
var auditEvent auditV1.AuditEvent var auditEvent auditV1.AuditLogEntry
switch data := routableAuditEvent.Data.(type) { switch data := routableAuditEvent.Data.(type) {
case *auditV1.RoutableAuditEvent_UnencryptedData: case *auditV1.RoutableAuditEvent_UnencryptedData:
assert.NoError(t, proto.Unmarshal(data.UnencryptedData.Data, &auditEvent)) assert.NoError(t, proto.Unmarshal(data.UnencryptedData.Data, &auditEvent))

View file

@ -1,6 +1,7 @@
package api package api
import ( import (
"fmt"
"google.golang.org/protobuf/types/known/wrapperspb" "google.golang.org/protobuf/types/known/wrapperspb"
"time" "time"
@ -22,19 +23,74 @@ func NewOrganizationAuditEvent(
*auditV1.ObjectIdentifier, *auditV1.ObjectIdentifier,
) { ) {
identifier := uuid.New()
permission := "resourcemanager.organization.edit"
permissionGranted := true
requestId := fmt.Sprintf("%s/1", identifier)
claims, _ := structpb.NewStruct(map[string]interface{}{})
request, _ := structpb.NewStruct(map[string]interface{}{})
response, _ := structpb.NewStruct(map[string]interface{}{})
correlationId := "cad100e2-e139-43b9-8c3b-335731e032bc"
headers := make(map[string]string)
headers["Content-Type"] = "application/json"
labels := make(map[string]string)
labels["label1"] = "value1"
auditEvent := &auditV1.AuditLogEntry{ auditEvent := &auditV1.AuditLogEntry{
EventSource: "resource-manager", LogName: fmt.Sprintf("organizations/%s/logs/admin-activity", identifier),
Region: auditV1.Region_REGION_EU01, ProtoPayload: &auditV1.AuditLog{
SequenceNumber: wrapperspb.Int64(0), ServiceName: "resource-manager",
EventName: "ORGANIZATION_CREATED", MethodName: "stackit.resourcemanager.v2.organization.created",
EventTimeStamp: timestamppb.New(time.Now()), ResourceName: fmt.Sprintf("organizations/%s", identifier),
EventTrigger: auditV1.EventTrigger_EVENT_TRIGGER_EVENT, AuthenticationInfo: &auditV1.AuthenticationInfo{
Initiator: &auditV1.Principal{ PrincipalId: uuid.NewString(),
Id: uuid.NewString(), PrincipalEmail: "user@example.com",
ServiceAccountName: nil,
ServiceAccountDelegationInfo: nil,
},
AuthorizationInfo: []*auditV1.AuthorizationInfo{{
Resource: fmt.Sprintf("organizations/%s", identifier),
Permission: &permission,
Granted: &permissionGranted,
}},
RequestMetadata: &auditV1.RequestMetadata{
CallerIp: "127.0.0.1",
CallerSuppliedUserAgent: "OpenAPI-Generator/ 1.0.0/ go",
RequestAttributes: &auditV1.AttributeContext_Request{
Id: &requestId,
Method: "POST",
Headers: headers,
Path: "/v2/organizations",
Host: "stackit-resource-manager-dev.apps.01.cf.eu01.stackit.cloud",
Scheme: "https",
Query: nil,
Time: timestamppb.New(time.Now().UTC()),
Protocol: "http/1.1",
Auth: &auditV1.AttributeContext_Auth{
Principal: "https%3A%2F%2Faccounts.dev.stackit.cloud/stackit-resource-manager-dev",
Audiences: []string{"https:// stackit-resource-manager-dev.apps.01.cf.eu01.stackit.cloud", "stackit", "api"},
Claims: claims,
},
},
},
Request: request,
Status: &auditV1.ResponseStatus{
Code: wrapperspb.Int32(200),
Message: nil,
Details: nil,
},
NumResponseItems: nil,
Response: response,
Metadata: nil,
}, },
InsertId: fmt.Sprintf("%d/eu01/e72182e8-0bb9-4be2-a19f-87fc0dd6e738/00000000001", time.Now().UnixNano()),
Labels: labels,
CorrelationId: &correlationId,
Timestamp: timestamppb.New(time.Now()),
Severity: auditV1.LogSeverity_DEFAULT,
TraceParent: nil,
TraceState: nil,
} }
identifier := uuid.New()
routingIdentifier := &RoutingIdentifier{ routingIdentifier := &RoutingIdentifier{
Identifier: identifier, Identifier: identifier,
Type: RoutingIdentifierTypeOrganization, Type: RoutingIdentifierTypeOrganization,
@ -44,7 +100,6 @@ func NewOrganizationAuditEvent(
Identifier: identifier.String(), Identifier: identifier.String(),
Type: auditV1.ObjectType_OBJECT_TYPE_ORGANIZATION, Type: auditV1.ObjectType_OBJECT_TYPE_ORGANIZATION,
} }
auditEvent.ResourceContainerReference = &auditV1.AuditEvent_ObjectIdentifier{ObjectIdentifier: objectIdentifier}
if customization != nil { if customization != nil {
(*customization)(auditEvent, routingIdentifier, objectIdentifier) (*customization)(auditEvent, routingIdentifier, objectIdentifier)
@ -53,51 +108,6 @@ func NewOrganizationAuditEvent(
return auditEvent, routingIdentifier, objectIdentifier return auditEvent, routingIdentifier, objectIdentifier
} }
func NewOrganizationAuditEventWithDetails() (*auditV1.AuditLogEntry,
*RoutingIdentifier,
*auditV1.ObjectIdentifier) {
customization := func(event *auditV1.AuditLogEntry,
routingIdentifier *RoutingIdentifier,
objectIdentifier *auditV1.ObjectIdentifier) {
userAgent := "firefox"
parameters, _ := structpb.NewStruct(map[string]any{"parameter1": "value"})
body, _ := structpb.NewStruct(map[string]any{"body": "value"})
event.Request = &auditV1.RequestDetails{
Endpoint: "/test",
SourceIpAddress: "127.0.0.1",
UserAgent: &userAgent,
Parameters: parameters,
Body: body,
Headers: []*auditV1.RequestHeader{
{
Key: "header1",
Value: "value",
},
},
}
email := "test@example.com"
event.Principals = []*auditV1.Principal{
{
Id: "id",
Email: &email,
},
}
details, _ := structpb.NewStruct(map[string]interface{}{
"detail": "value",
})
event.Details = details
result, _ := structpb.NewStruct(map[string]interface{}{
"result": "value",
})
event.Result = result
}
return NewOrganizationAuditEvent(&customization)
}
func NewFolderAuditEvent( func NewFolderAuditEvent(
customization *func( customization *func(
*auditV1.AuditLogEntry, *auditV1.AuditLogEntry,
@ -109,28 +119,83 @@ func NewFolderAuditEvent(
*auditV1.ObjectIdentifier, *auditV1.ObjectIdentifier,
) { ) {
identifier := uuid.New()
permission := "resourcemanager.folder.edit"
permissionGranted := true
requestId := fmt.Sprintf("%s/1", identifier)
claims, _ := structpb.NewStruct(map[string]interface{}{})
request, _ := structpb.NewStruct(map[string]interface{}{})
response, _ := structpb.NewStruct(map[string]interface{}{})
correlationId := "9c71cedf-ca52-4f9c-a519-ed006e810cdd"
headers := make(map[string]string)
headers["Content-Type"] = "application/json"
labels := make(map[string]string)
labels["label1"] = "value1"
auditEvent := &auditV1.AuditLogEntry{ auditEvent := &auditV1.AuditLogEntry{
EventSource: "resource-manager", LogName: fmt.Sprintf("folders/%s/logs/admin-activity", identifier),
Region: auditV1.Region_REGION_EU01, ProtoPayload: &auditV1.AuditLog{
SequenceNumber: wrapperspb.Int64(0), ServiceName: "resource-manager",
EventName: "FOLDER_CREATED", MethodName: "stackit.resourcemanager.v2.folder.created",
EventTimeStamp: timestamppb.New(time.Now()), ResourceName: fmt.Sprintf("folders/%s", identifier),
EventTrigger: auditV1.EventTrigger_EVENT_TRIGGER_EVENT, AuthenticationInfo: &auditV1.AuthenticationInfo{
Initiator: &auditV1.Principal{ PrincipalId: uuid.NewString(),
Id: uuid.NewString(), PrincipalEmail: "user@example.com",
ServiceAccountName: nil,
ServiceAccountDelegationInfo: nil,
},
AuthorizationInfo: []*auditV1.AuthorizationInfo{{
Resource: fmt.Sprintf("folders/%s", identifier),
Permission: &permission,
Granted: &permissionGranted,
}},
RequestMetadata: &auditV1.RequestMetadata{
CallerIp: "127.0.0.1",
CallerSuppliedUserAgent: "OpenAPI-Generator/ 1.0.0/ go",
RequestAttributes: &auditV1.AttributeContext_Request{
Id: &requestId,
Method: "POST",
Headers: headers,
Path: "/v2/folders",
Host: "stackit-resource-manager-dev.apps.01.cf.eu01.stackit.cloud",
Scheme: "https",
Query: nil,
Time: timestamppb.New(time.Now().UTC()),
Protocol: "http/1.1",
Auth: &auditV1.AttributeContext_Auth{
Principal: "https%3A%2F%2Faccounts.dev.stackit.cloud/stackit-resource-manager-dev",
Audiences: []string{"https:// stackit-resource-manager-dev.apps.01.cf.eu01.stackit.cloud", "stackit", "api"},
Claims: claims,
},
},
},
Request: request,
Status: &auditV1.ResponseStatus{
Code: wrapperspb.Int32(200),
Message: nil,
Details: nil,
},
NumResponseItems: nil,
Response: response,
Metadata: nil,
}, },
InsertId: fmt.Sprintf("%d/eu01/e72182e8-0bb9-4be2-a19f-87fc0dd6e738/00000000001", time.Now().UnixNano()),
Labels: labels,
CorrelationId: &correlationId,
Timestamp: timestamppb.New(time.Now()),
Severity: auditV1.LogSeverity_DEFAULT,
TraceParent: nil,
TraceState: nil,
} }
routingIdentifier := &RoutingIdentifier{ routingIdentifier := &RoutingIdentifier{
Identifier: uuid.New(), Identifier: identifier,
Type: RoutingIdentifierTypeOrganization, Type: RoutingIdentifierTypeOrganization,
} }
objectIdentifier := &auditV1.ObjectIdentifier{ objectIdentifier := &auditV1.ObjectIdentifier{
Identifier: uuid.New().String(), Identifier: identifier.String(),
Type: auditV1.ObjectType_OBJECT_TYPE_FOLDER, Type: auditV1.ObjectType_OBJECT_TYPE_FOLDER,
} }
auditEvent.ResourceContainerReference = &auditV1.AuditEvent_ObjectIdentifier{ObjectIdentifier: objectIdentifier}
if customization != nil { if customization != nil {
(*customization)(auditEvent, routingIdentifier, objectIdentifier) (*customization)(auditEvent, routingIdentifier, objectIdentifier)
@ -150,19 +215,74 @@ func NewProjectAuditEvent(
*auditV1.ObjectIdentifier, *auditV1.ObjectIdentifier,
) { ) {
identifier := uuid.New()
permission := "resourcemanager.project.edit"
permissionGranted := true
requestId := fmt.Sprintf("%s/1", identifier)
claims, _ := structpb.NewStruct(map[string]interface{}{})
request, _ := structpb.NewStruct(map[string]interface{}{})
response, _ := structpb.NewStruct(map[string]interface{}{})
correlationId := "14d5b611-ccce-4cfa-9085-9ccbfccce3cb"
headers := make(map[string]string)
headers["Content-Type"] = "application/json"
labels := make(map[string]string)
labels["label1"] = "value1"
auditEvent := &auditV1.AuditLogEntry{ auditEvent := &auditV1.AuditLogEntry{
EventSource: "resource-manager", LogName: fmt.Sprintf("projects/%s/logs/admin-activity", identifier),
Region: auditV1.Region_REGION_EU01, ProtoPayload: &auditV1.AuditLog{
SequenceNumber: wrapperspb.Int64(0), ServiceName: "resource-manager",
EventName: "PROJECT_CREATED", MethodName: "stackit.resourcemanager.v2.project.created",
EventTimeStamp: timestamppb.New(time.Now()), ResourceName: fmt.Sprintf("projects/%s", identifier),
EventTrigger: auditV1.EventTrigger_EVENT_TRIGGER_EVENT, AuthenticationInfo: &auditV1.AuthenticationInfo{
Initiator: &auditV1.Principal{ PrincipalId: uuid.NewString(),
Id: uuid.NewString(), PrincipalEmail: "user@example.com",
ServiceAccountName: nil,
ServiceAccountDelegationInfo: nil,
},
AuthorizationInfo: []*auditV1.AuthorizationInfo{{
Resource: fmt.Sprintf("projects/%s", identifier),
Permission: &permission,
Granted: &permissionGranted,
}},
RequestMetadata: &auditV1.RequestMetadata{
CallerIp: "127.0.0.1",
CallerSuppliedUserAgent: "OpenAPI-Generator/ 1.0.0/ go",
RequestAttributes: &auditV1.AttributeContext_Request{
Id: &requestId,
Method: "POST",
Headers: headers,
Path: "/v2/projects",
Host: "stackit-resource-manager-dev.apps.01.cf.eu01.stackit.cloud",
Scheme: "https",
Query: nil,
Time: timestamppb.New(time.Now().UTC()),
Protocol: "http/1.1",
Auth: &auditV1.AttributeContext_Auth{
Principal: "https%3A%2F%2Faccounts.dev.stackit.cloud/stackit-resource-manager-dev",
Audiences: []string{"https:// stackit-resource-manager-dev.apps.01.cf.eu01.stackit.cloud", "stackit", "api"},
Claims: claims,
},
},
},
Request: request,
Status: &auditV1.ResponseStatus{
Code: wrapperspb.Int32(200),
Message: nil,
Details: nil,
},
NumResponseItems: nil,
Response: response,
Metadata: nil,
}, },
InsertId: fmt.Sprintf("%d/eu01/e72182e8-0bb9-4be2-a19f-87fc0dd6e738/00000000001", time.Now().UnixNano()),
Labels: labels,
CorrelationId: &correlationId,
Timestamp: timestamppb.New(time.Now()),
Severity: auditV1.LogSeverity_DEFAULT,
TraceParent: nil,
TraceState: nil,
} }
identifier := uuid.New()
routingIdentifier := &RoutingIdentifier{ routingIdentifier := &RoutingIdentifier{
Identifier: identifier, Identifier: identifier,
Type: RoutingIdentifierTypeProject, Type: RoutingIdentifierTypeProject,
@ -172,7 +292,6 @@ func NewProjectAuditEvent(
Identifier: identifier.String(), Identifier: identifier.String(),
Type: auditV1.ObjectType_OBJECT_TYPE_PROJECT, Type: auditV1.ObjectType_OBJECT_TYPE_PROJECT,
} }
auditEvent.ResourceContainerReference = &auditV1.AuditEvent_ObjectIdentifier{ObjectIdentifier: objectIdentifier}
if customization != nil { if customization != nil {
(*customization)(auditEvent, routingIdentifier, objectIdentifier) (*customization)(auditEvent, routingIdentifier, objectIdentifier)
@ -184,19 +303,74 @@ func NewProjectAuditEvent(
func NewSystemAuditEvent( func NewSystemAuditEvent(
customization *func(*auditV1.AuditLogEntry)) *auditV1.AuditLogEntry { customization *func(*auditV1.AuditLogEntry)) *auditV1.AuditLogEntry {
identifier := uuid.New()
requestId := fmt.Sprintf("%s/1", identifier)
claims, _ := structpb.NewStruct(map[string]interface{}{})
request, _ := structpb.NewStruct(map[string]interface{}{})
response, _ := structpb.NewStruct(map[string]interface{}{})
correlationId := "14d5b611-ccce-4cfa-9085-9ccbfccce3cb"
headers := make(map[string]string)
headers["Content-Type"] = "application/json"
labels := make(map[string]string)
labels["label1"] = "value1"
serviceAccountId := uuid.NewString()
serviceAccountName := fmt.Sprintf("projects/%s/serviceAccounts/%s", identifier, serviceAccountId)
delegationPrincipal := auditV1.ServiceAccountDelegationInfo{Authority: &auditV1.ServiceAccountDelegationInfo_SystemPrincipal_{}}
auditEvent := &auditV1.AuditLogEntry{ auditEvent := &auditV1.AuditLogEntry{
EventSource: "resource-manager", LogName: fmt.Sprintf("projects/%s/logs/system-event", identifier),
Region: auditV1.Region_REGION_EU01, ProtoPayload: &auditV1.AuditLog{
SequenceNumber: wrapperspb.Int64(0), ServiceName: "resource-manager",
EventName: "SYSTEM_CHANGED", MethodName: "stackit.resourcemanager.v2.system.changed",
EventTimeStamp: timestamppb.New(time.Now()), ResourceName: fmt.Sprintf("projects/%s", identifier),
EventTrigger: auditV1.EventTrigger_EVENT_TRIGGER_EVENT, AuthenticationInfo: &auditV1.AuthenticationInfo{
Initiator: &auditV1.Principal{ PrincipalId: serviceAccountId,
Id: uuid.NewString(), PrincipalEmail: "service-account@sa.stackit.cloud",
ServiceAccountName: &serviceAccountName,
ServiceAccountDelegationInfo: []*auditV1.ServiceAccountDelegationInfo{&delegationPrincipal},
},
AuthorizationInfo: []*auditV1.AuthorizationInfo{{
Resource: fmt.Sprintf("projects/%s", identifier),
Permission: nil,
Granted: nil,
}},
RequestMetadata: &auditV1.RequestMetadata{
CallerIp: "127.0.0.1",
CallerSuppliedUserAgent: "OpenAPI-Generator/ 1.0.0/ go",
RequestAttributes: &auditV1.AttributeContext_Request{
Id: &requestId,
Method: "POST",
Headers: headers,
Path: "/v2/projects",
Host: "stackit-resource-manager-dev.apps.01.cf.eu01.stackit.cloud",
Scheme: "https",
Query: nil,
Time: timestamppb.New(time.Now().UTC()),
Protocol: "http/1.1",
Auth: &auditV1.AttributeContext_Auth{
Principal: "https%3A%2F%2Faccounts.dev.stackit.cloud/stackit-resource-manager-dev",
Audiences: []string{"https:// stackit-resource-manager-dev.apps.01.cf.eu01.stackit.cloud", "stackit", "api"},
Claims: claims,
},
},
},
Request: request,
Status: &auditV1.ResponseStatus{
Code: wrapperspb.Int32(200),
Message: nil,
Details: nil,
},
NumResponseItems: nil,
Response: response,
Metadata: nil,
}, },
InsertId: fmt.Sprintf("%d/eu01/e72182e8-0bb9-4be2-a19f-87fc0dd6e738/00000000001", time.Now().UnixNano()),
Labels: labels,
CorrelationId: &correlationId,
Timestamp: timestamppb.New(time.Now()),
Severity: auditV1.LogSeverity_DEFAULT,
TraceParent: nil,
TraceState: nil,
} }
auditEvent.ResourceContainerReference = &auditV1.AuditEvent_ObjectName{
ObjectName: auditV1.ObjectName_OBJECT_NAME_SYSTEM}
if customization != nil { if customization != nil {
(*customization)(auditEvent) (*customization)(auditEvent)

File diff suppressed because it is too large Load diff

View file

@ -92,8 +92,6 @@ func (m *AuditLogEntry) validate(all bool) error {
// no validation rules for Labels // no validation rules for Labels
// no validation rules for CorrelationId
if all { if all {
switch v := interface{}(m.GetTimestamp()).(type) { switch v := interface{}(m.GetTimestamp()).(type) {
case interface{ ValidateAll() error }: case interface{ ValidateAll() error }:
@ -125,9 +123,17 @@ func (m *AuditLogEntry) validate(all bool) error {
// no validation rules for Severity // no validation rules for Severity
// no validation rules for TraceParent if m.CorrelationId != nil {
// no validation rules for CorrelationId
}
// no validation rules for TraceState if m.TraceParent != nil {
// no validation rules for TraceParent
}
if m.TraceState != nil {
// no validation rules for TraceState
}
if len(errors) > 0 { if len(errors) > 0 {
return AuditLogEntryMultiError(errors) return AuditLogEntryMultiError(errors)
@ -231,6 +237,8 @@ func (m *AuditLog) validate(all bool) error {
// no validation rules for ServiceName // no validation rules for ServiceName
// no validation rules for MethodName
// no validation rules for ResourceName // no validation rules for ResourceName
if all { if all {
@ -412,41 +420,70 @@ func (m *AuditLog) validate(all bool) error {
} }
} }
if all {
switch v := interface{}(m.GetMetadata()).(type) {
case interface{ ValidateAll() error }:
if err := v.ValidateAll(); err != nil {
errors = append(errors, AuditLogValidationError{
field: "Metadata",
reason: "embedded message failed validation",
cause: err,
})
}
case interface{ Validate() error }:
if err := v.Validate(); err != nil {
errors = append(errors, AuditLogValidationError{
field: "Metadata",
reason: "embedded message failed validation",
cause: err,
})
}
}
} else if v, ok := interface{}(m.GetMetadata()).(interface{ Validate() error }); ok {
if err := v.Validate(); err != nil {
return AuditLogValidationError{
field: "Metadata",
reason: "embedded message failed validation",
cause: err,
}
}
}
if m.MethodName != nil {
// no validation rules for MethodName
}
if m.NumResponseItems != nil { if m.NumResponseItems != nil {
// no validation rules for NumResponseItems
if all {
switch v := interface{}(m.GetNumResponseItems()).(type) {
case interface{ ValidateAll() error }:
if err := v.ValidateAll(); err != nil {
errors = append(errors, AuditLogValidationError{
field: "NumResponseItems",
reason: "embedded message failed validation",
cause: err,
})
}
case interface{ Validate() error }:
if err := v.Validate(); err != nil {
errors = append(errors, AuditLogValidationError{
field: "NumResponseItems",
reason: "embedded message failed validation",
cause: err,
})
}
}
} else if v, ok := interface{}(m.GetNumResponseItems()).(interface{ Validate() error }); ok {
if err := v.Validate(); err != nil {
return AuditLogValidationError{
field: "NumResponseItems",
reason: "embedded message failed validation",
cause: err,
}
}
}
}
if m.Metadata != nil {
if all {
switch v := interface{}(m.GetMetadata()).(type) {
case interface{ ValidateAll() error }:
if err := v.ValidateAll(); err != nil {
errors = append(errors, AuditLogValidationError{
field: "Metadata",
reason: "embedded message failed validation",
cause: err,
})
}
case interface{ Validate() error }:
if err := v.Validate(); err != nil {
errors = append(errors, AuditLogValidationError{
field: "Metadata",
reason: "embedded message failed validation",
cause: err,
})
}
}
} else if v, ok := interface{}(m.GetMetadata()).(interface{ Validate() error }); ok {
if err := v.Validate(); err != nil {
return AuditLogValidationError{
field: "Metadata",
reason: "embedded message failed validation",
cause: err,
}
}
}
} }
if len(errors) > 0 { if len(errors) > 0 {
@ -694,37 +731,12 @@ func (m *AuthorizationInfo) validate(all bool) error {
// no validation rules for Resource // no validation rules for Resource
// no validation rules for Permission if m.Permission != nil {
// no validation rules for Permission
}
// no validation rules for Granted if m.Granted != nil {
// no validation rules for Granted
if all {
switch v := interface{}(m.GetResourceAttributes()).(type) {
case interface{ ValidateAll() error }:
if err := v.ValidateAll(); err != nil {
errors = append(errors, AuthorizationInfoValidationError{
field: "ResourceAttributes",
reason: "embedded message failed validation",
cause: err,
})
}
case interface{ Validate() error }:
if err := v.Validate(); err != nil {
errors = append(errors, AuthorizationInfoValidationError{
field: "ResourceAttributes",
reason: "embedded message failed validation",
cause: err,
})
}
}
} else if v, ok := interface{}(m.GetResourceAttributes()).(interface{ Validate() error }); ok {
if err := v.Validate(); err != nil {
return AuthorizationInfoValidationError{
field: "ResourceAttributes",
reason: "embedded message failed validation",
cause: err,
}
}
} }
if len(errors) > 0 { if len(errors) > 0 {
@ -1062,9 +1074,34 @@ func (m *ResponseStatus) validate(all bool) error {
var errors []error var errors []error
// no validation rules for Code if all {
switch v := interface{}(m.GetCode()).(type) {
// no validation rules for Message case interface{ ValidateAll() error }:
if err := v.ValidateAll(); err != nil {
errors = append(errors, ResponseStatusValidationError{
field: "Code",
reason: "embedded message failed validation",
cause: err,
})
}
case interface{ Validate() error }:
if err := v.Validate(); err != nil {
errors = append(errors, ResponseStatusValidationError{
field: "Code",
reason: "embedded message failed validation",
cause: err,
})
}
}
} else if v, ok := interface{}(m.GetCode()).(interface{ Validate() error }); ok {
if err := v.Validate(); err != nil {
return ResponseStatusValidationError{
field: "Code",
reason: "embedded message failed validation",
cause: err,
}
}
}
for idx, item := range m.GetDetails() { for idx, item := range m.GetDetails() {
_, _ = idx, item _, _ = idx, item
@ -1100,6 +1137,10 @@ func (m *ResponseStatus) validate(all bool) error {
} }
if m.Message != nil {
// no validation rules for Message
}
if len(errors) > 0 { if len(errors) > 0 {
return ResponseStatusMultiError(errors) return ResponseStatusMultiError(errors)
} }
@ -1392,8 +1433,6 @@ func (m *AttributeContext_Auth) validate(all bool) error {
// no validation rules for Principal // no validation rules for Principal
// no validation rules for Presenter
if all { if all {
switch v := interface{}(m.GetClaims()).(type) { switch v := interface{}(m.GetClaims()).(type) {
case interface{ ValidateAll() error }: case interface{ ValidateAll() error }:
@ -1525,8 +1564,6 @@ func (m *AttributeContext_Request) validate(all bool) error {
var errors []error var errors []error
// no validation rules for Id
// no validation rules for Method // no validation rules for Method
// no validation rules for Headers // no validation rules for Headers
@ -1537,8 +1574,6 @@ func (m *AttributeContext_Request) validate(all bool) error {
// no validation rules for Scheme // no validation rules for Scheme
// no validation rules for Query
if all { if all {
switch v := interface{}(m.GetTime()).(type) { switch v := interface{}(m.GetTime()).(type) {
case interface{ ValidateAll() error }: case interface{ ValidateAll() error }:
@ -1599,6 +1634,14 @@ func (m *AttributeContext_Request) validate(all bool) error {
} }
} }
if m.Id != nil {
// no validation rules for Id
}
if m.Query != nil {
// no validation rules for Query
}
if len(errors) > 0 { if len(errors) > 0 {
return AttributeContext_RequestMultiError(errors) return AttributeContext_RequestMultiError(errors)
} }
@ -1701,10 +1744,6 @@ func (m *AttributeContext_Response) validate(all bool) error {
var errors []error var errors []error
// no validation rules for Code
// no validation rules for Size
// no validation rules for Headers // no validation rules for Headers
if all { if all {
@ -1736,6 +1775,39 @@ func (m *AttributeContext_Response) validate(all bool) error {
} }
} }
if m.Size != nil {
if all {
switch v := interface{}(m.GetSize()).(type) {
case interface{ ValidateAll() error }:
if err := v.ValidateAll(); err != nil {
errors = append(errors, AttributeContext_ResponseValidationError{
field: "Size",
reason: "embedded message failed validation",
cause: err,
})
}
case interface{ Validate() error }:
if err := v.Validate(); err != nil {
errors = append(errors, AttributeContext_ResponseValidationError{
field: "Size",
reason: "embedded message failed validation",
cause: err,
})
}
}
} else if v, ok := interface{}(m.GetSize()).(interface{ Validate() error }); ok {
if err := v.Validate(); err != nil {
return AttributeContext_ResponseValidationError{
field: "Size",
reason: "embedded message failed validation",
cause: err,
}
}
}
}
if len(errors) > 0 { if len(errors) > 0 {
return AttributeContext_ResponseMultiError(errors) return AttributeContext_ResponseMultiError(errors)
} }
@ -1816,116 +1888,6 @@ var _ interface {
ErrorName() string ErrorName() string
} = AttributeContext_ResponseValidationError{} } = AttributeContext_ResponseValidationError{}
// Validate checks the field values on AttributeContext_Resource with the rules
// defined in the proto definition for this message. If any rules are
// violated, the first error encountered is returned, or nil if there are no violations.
func (m *AttributeContext_Resource) Validate() error {
return m.validate(false)
}
// ValidateAll checks the field values on AttributeContext_Resource with the
// rules defined in the proto definition for this message. If any rules are
// violated, the result is a list of violation errors wrapped in
// AttributeContext_ResourceMultiError, or nil if none found.
func (m *AttributeContext_Resource) ValidateAll() error {
return m.validate(true)
}
func (m *AttributeContext_Resource) validate(all bool) error {
if m == nil {
return nil
}
var errors []error
// no validation rules for Service
// no validation rules for Name
// no validation rules for Type
// no validation rules for Labels
if len(errors) > 0 {
return AttributeContext_ResourceMultiError(errors)
}
return nil
}
// AttributeContext_ResourceMultiError is an error wrapping multiple validation
// errors returned by AttributeContext_Resource.ValidateAll() if the
// designated constraints aren't met.
type AttributeContext_ResourceMultiError []error
// Error returns a concatenation of all the error messages it wraps.
func (m AttributeContext_ResourceMultiError) Error() string {
var msgs []string
for _, err := range m {
msgs = append(msgs, err.Error())
}
return strings.Join(msgs, "; ")
}
// AllErrors returns a list of validation violation errors.
func (m AttributeContext_ResourceMultiError) AllErrors() []error { return m }
// AttributeContext_ResourceValidationError is the validation error returned by
// AttributeContext_Resource.Validate if the designated constraints aren't met.
type AttributeContext_ResourceValidationError struct {
field string
reason string
cause error
key bool
}
// Field function returns field value.
func (e AttributeContext_ResourceValidationError) Field() string { return e.field }
// Reason function returns reason value.
func (e AttributeContext_ResourceValidationError) Reason() string { return e.reason }
// Cause function returns cause value.
func (e AttributeContext_ResourceValidationError) Cause() error { return e.cause }
// Key function returns key value.
func (e AttributeContext_ResourceValidationError) Key() bool { return e.key }
// ErrorName returns error name.
func (e AttributeContext_ResourceValidationError) ErrorName() string {
return "AttributeContext_ResourceValidationError"
}
// Error satisfies the builtin error interface
func (e AttributeContext_ResourceValidationError) Error() string {
cause := ""
if e.cause != nil {
cause = fmt.Sprintf(" | caused by: %v", e.cause)
}
key := ""
if e.key {
key = "key for "
}
return fmt.Sprintf(
"invalid %sAttributeContext_Resource.%s: %s%s",
key,
e.field,
e.reason,
cause)
}
var _ error = AttributeContext_ResourceValidationError{}
var _ interface {
Field() string
Reason() string
Key() bool
Cause() error
ErrorName() string
} = AttributeContext_ResourceValidationError{}
// Validate checks the field values on // Validate checks the field values on
// ServiceAccountDelegationInfo_SystemPrincipal with the rules defined in the // ServiceAccountDelegationInfo_SystemPrincipal with the rules defined in the
// proto definition for this message. If any rules are violated, the first // proto definition for this message. If any rules are violated, the first
@ -1950,33 +1912,37 @@ func (m *ServiceAccountDelegationInfo_SystemPrincipal) validate(all bool) error
var errors []error var errors []error
if all { if m.ServiceMetadata != nil {
switch v := interface{}(m.GetServiceMetadata()).(type) {
case interface{ ValidateAll() error }: if all {
if err := v.ValidateAll(); err != nil { switch v := interface{}(m.GetServiceMetadata()).(type) {
errors = append(errors, ServiceAccountDelegationInfo_SystemPrincipalValidationError{ case interface{ ValidateAll() error }:
field: "ServiceMetadata", if err := v.ValidateAll(); err != nil {
reason: "embedded message failed validation", errors = append(errors, ServiceAccountDelegationInfo_SystemPrincipalValidationError{
cause: err, field: "ServiceMetadata",
}) reason: "embedded message failed validation",
cause: err,
})
}
case interface{ Validate() error }:
if err := v.Validate(); err != nil {
errors = append(errors, ServiceAccountDelegationInfo_SystemPrincipalValidationError{
field: "ServiceMetadata",
reason: "embedded message failed validation",
cause: err,
})
}
} }
case interface{ Validate() error }: } else if v, ok := interface{}(m.GetServiceMetadata()).(interface{ Validate() error }); ok {
if err := v.Validate(); err != nil { if err := v.Validate(); err != nil {
errors = append(errors, ServiceAccountDelegationInfo_SystemPrincipalValidationError{ return ServiceAccountDelegationInfo_SystemPrincipalValidationError{
field: "ServiceMetadata", field: "ServiceMetadata",
reason: "embedded message failed validation", reason: "embedded message failed validation",
cause: err, cause: err,
}) }
}
}
} else if v, ok := interface{}(m.GetServiceMetadata()).(interface{ Validate() error }); ok {
if err := v.Validate(); err != nil {
return ServiceAccountDelegationInfo_SystemPrincipalValidationError{
field: "ServiceMetadata",
reason: "embedded message failed validation",
cause: err,
} }
} }
} }
if len(errors) > 0 { if len(errors) > 0 {
@ -2088,37 +2054,39 @@ func (m *ServiceAccountDelegationInfo_IdpPrincipal) validate(all bool) error {
// no validation rules for PrincipalId // no validation rules for PrincipalId
if all { // no validation rules for PrincipalEmail
switch v := interface{}(m.GetServiceMetadata()).(type) {
case interface{ ValidateAll() error }: if m.ServiceMetadata != nil {
if err := v.ValidateAll(); err != nil {
errors = append(errors, ServiceAccountDelegationInfo_IdpPrincipalValidationError{ if all {
field: "ServiceMetadata", switch v := interface{}(m.GetServiceMetadata()).(type) {
reason: "embedded message failed validation", case interface{ ValidateAll() error }:
cause: err, if err := v.ValidateAll(); err != nil {
}) errors = append(errors, ServiceAccountDelegationInfo_IdpPrincipalValidationError{
} field: "ServiceMetadata",
case interface{ Validate() error }: reason: "embedded message failed validation",
if err := v.Validate(); err != nil { cause: err,
errors = append(errors, ServiceAccountDelegationInfo_IdpPrincipalValidationError{ })
field: "ServiceMetadata", }
reason: "embedded message failed validation", case interface{ Validate() error }:
cause: err, if err := v.Validate(); err != nil {
}) errors = append(errors, ServiceAccountDelegationInfo_IdpPrincipalValidationError{
} field: "ServiceMetadata",
} reason: "embedded message failed validation",
} else if v, ok := interface{}(m.GetServiceMetadata()).(interface{ Validate() error }); ok { cause: err,
if err := v.Validate(); err != nil { })
return ServiceAccountDelegationInfo_IdpPrincipalValidationError{ }
field: "ServiceMetadata", }
reason: "embedded message failed validation", } else if v, ok := interface{}(m.GetServiceMetadata()).(interface{ Validate() error }); ok {
cause: err, if err := v.Validate(); err != nil {
} return ServiceAccountDelegationInfo_IdpPrincipalValidationError{
} field: "ServiceMetadata",
} reason: "embedded message failed validation",
cause: err,
}
}
}
if m.PrincipalEmail != nil {
// no validation rules for PrincipalEmail
} }
if len(errors) > 0 { if len(errors) > 0 {

View file

@ -380,50 +380,53 @@ var file_audit_v1_routable_event_proto_rawDesc = []byte{
0x10, 0x01, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2f, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x74, 0x10, 0x01, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2f, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x6f, 0x62, 0x75, 0x66, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42,
0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x0a, 0xba, 0x48, 0x07, 0xc8, 0x01, 0x01, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0c, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x54, 0x79, 0x70, 0x65, 0x22, 0xcb, 0x03, 0x0a, 0x12, 0x52, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x54, 0x79, 0x70, 0x65, 0x22, 0xf4, 0x03, 0x0a, 0x12, 0x52, 0x6f,
0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74,
0x12, 0x38, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x12, 0x61, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x42, 0x19, 0xba, 0x48, 0x16, 0xc8, 0x01, 0x01, 0x72, 0x11, 0x32, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x42, 0x42, 0xba, 0x48, 0x3f, 0xc8, 0x01, 0x01, 0x72, 0x3a, 0x32, 0x38,
0x5e, 0x5b, 0x41, 0x2d, 0x5a, 0x5d, 0x2b, 0x5f, 0x5b, 0x41, 0x2d, 0x5a, 0x5d, 0x2b, 0x24, 0x52, 0x5e, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x69, 0x74, 0x5c, 0x2e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d,
0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x76, 0x69, 0x39, 0x5d, 0x2b, 0x5c, 0x2e, 0x76, 0x5b, 0x31, 0x2d, 0x39, 0x5d, 0x5b, 0x30, 0x2d, 0x39, 0x5d,
0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2a, 0x5c, 0x2e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2b, 0x5c, 0x2e, 0x5b, 0x61,
0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2b, 0x24, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4e,
0x6c, 0x69, 0x74, 0x79, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x82, 0x01, 0x02, 0x10, 0x61, 0x6d, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74,
0x01, 0x52, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x37, 0x0a, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e,
0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x76, 0x31, 0x2e, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x42, 0x0b, 0xba,
0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x76, 0x69, 0x73, 0x69,
0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x37, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x49, 0x0a, 0x11, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, 0x75,
0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d,
0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12,
0x65, 0x63, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x49, 0x0a, 0x11, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x10, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x66, 0x69, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x75, 0x64,
0x72, 0x12, 0x46, 0x0a, 0x10, 0x75, 0x6e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x65, 0x6e,
0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x75, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x10, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x46, 0x0a, 0x10, 0x75, 0x6e,
0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x48, 0x01, 0x52, 0x0f, 0x75, 0x6e, 0x65, 0x6e, 0x63, 0x72, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05,
0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x12, 0x40, 0x0a, 0x0e, 0x65, 0x6e, 0x63, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e,
0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x55, 0x6e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x48,
0x0b, 0x32, 0x17, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x63, 0x01, 0x52, 0x0f, 0x75, 0x6e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61,
0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x48, 0x01, 0x52, 0x0d, 0x65, 0x6e, 0x74, 0x61, 0x12, 0x40, 0x0a, 0x0e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f,
0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x42, 0x1b, 0x0a, 0x12, 0x72, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x75, 0x64,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44,
0x65, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x42, 0x0d, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x61, 0x74, 0x61, 0x48, 0x01, 0x52, 0x0d, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64,
0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x2a, 0x57, 0x0a, 0x0a, 0x56, 0x69, 0x73, 0x69, 0x62, 0x44, 0x61, 0x74, 0x61, 0x42, 0x1b, 0x0a, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x1a, 0x0a, 0x16, 0x56, 0x49, 0x53, 0x49, 0x42, 0x49, 0x4c, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08,
0x49, 0x54, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x01, 0x42, 0x0d, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01,
0x00, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x49, 0x53, 0x49, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x2a, 0x57, 0x0a, 0x0a, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x1a,
0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x49, 0x53, 0x49, 0x0a, 0x16, 0x56, 0x49, 0x53, 0x49, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x55, 0x4e, 0x53,
0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x10, 0x02, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x49,
0x42, 0x84, 0x01, 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x63, 0x68, 0x77, 0x61, 0x72, 0x7a, 0x53, 0x49, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10,
0x2e, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x69, 0x74, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x49, 0x53, 0x49, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f,
0x31, 0x42, 0x12, 0x52, 0x6f, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x10, 0x02, 0x42, 0x84, 0x01, 0x0a, 0x1c, 0x63, 0x6f,
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x0f, 0x2e, 0x2f, 0x61, 0x75, 0x64, 0x69, 0x74, 0x6d, 0x2e, 0x73, 0x63, 0x68, 0x77, 0x61, 0x72, 0x7a, 0x2e, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x69,
0x3b, 0x61, 0x75, 0x64, 0x69, 0x74, 0x56, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x74, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x42, 0x12, 0x52, 0x6f, 0x75, 0x74,
0x08, 0x41, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x08, 0x41, 0x75, 0x64, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01,
0x74, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x14, 0x41, 0x75, 0x64, 0x69, 0x74, 0x5c, 0x56, 0x31, 0x5c, 0x5a, 0x0f, 0x2e, 0x2f, 0x61, 0x75, 0x64, 0x69, 0x74, 0x3b, 0x61, 0x75, 0x64, 0x69, 0x74, 0x56,
0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x09, 0x41, 0x75, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x08, 0x41, 0x75, 0x64, 0x69, 0x74, 0x2e,
0x64, 0x69, 0x74, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x56, 0x31, 0xca, 0x02, 0x08, 0x41, 0x75, 0x64, 0x69, 0x74, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x14,
0x41, 0x75, 0x64, 0x69, 0x74, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61,
0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x09, 0x41, 0x75, 0x64, 0x69, 0x74, 0x3a, 0x3a, 0x56, 0x31,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

117
main.go
View file

@ -1,117 +0,0 @@
package main
import (
"fmt"
auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/common-audit.git/gen/go/audit/v1"
"github.com/bufbuild/protovalidate-go"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/types/known/structpb"
)
func main() {
agent := "aaa"
parameters, _ := structpb.NewValue(map[string]any{
"parameter": "b",
})
body, _ := structpb.NewValue(map[string]any{
"body": "b",
})
auditEvent := auditV1.AuditEvent{
EventName: "XXX",
EventTrigger: auditV1.EventTrigger_EVENT_TRIGGER_REQUEST,
Request: &auditV1.RequestDetails{
Endpoint: "XXX",
SourceIpAddress: "127.0.0.1",
UserAgent: &agent,
Parameters: parameters.GetStructValue(),
Body: body.GetStructValue(),
Headers: []*auditV1.RequestHeader{
{
Key: "abc",
Value: "def",
},
},
},
EventTimeStamp: nil,
Initiator: nil,
Principals: nil,
ResourceId: nil,
ResourceName: nil,
CorrelationId: nil,
Details: nil,
}
auditEventBytes, err := proto.Marshal(&auditEvent)
if err != nil {
fmt.Println(err.Error())
return
}
payload := auditV1.UnencryptedData{
Data: auditEventBytes,
ProtobufType: fmt.Sprintf("%v", auditEvent.ProtoReflect().Descriptor().FullName()),
}
//var version int32 = 0
routableEvent := auditV1.RoutableAuditEvent{
EventName: "A_V1",
Visibility: auditV1.Visibility_VISIBILITY_PRIVATE,
ResourceReference: &auditV1.RoutableAuditEvent_ObjectName{ObjectName: auditV1.ObjectName_OBJECT_NAME_SYSTEM},
Data: &auditV1.RoutableAuditEvent_UnencryptedData{UnencryptedData: &payload},
}
validator, err := protovalidate.New()
if err != nil {
fmt.Println("failed to initialize validator:", err)
}
err = validator.Validate(&auditEvent)
if err != nil {
fmt.Println(err.Error())
}
err = validator.Validate(&routableEvent)
if err != nil {
fmt.Println(err.Error())
}
routableEventBytes, err := proto.Marshal(&routableEvent)
if err != nil {
fmt.Println(err.Error())
return
}
var deserializedRoutableEvent auditV1.RoutableAuditEvent
err = proto.Unmarshal(routableEventBytes, &deserializedRoutableEvent)
if err != nil {
fmt.Println(err.Error())
return
}
if proto.Equal(&routableEvent, &deserializedRoutableEvent) {
fmt.Println("Event Matched")
} else {
fmt.Println("Event Not Matched")
}
//fmt.Println(deserializedRoutableEvent.String())
protoMessage := getProto(deserializedRoutableEvent.GetUnencryptedData().ProtobufType)
err = proto.Unmarshal(deserializedRoutableEvent.GetUnencryptedData().Data, protoMessage)
if err != nil {
fmt.Println(err.Error())
return
}
deserializedAuditEvent, isAuditEvent := protoMessage.(*auditV1.AuditEvent)
if isAuditEvent {
fmt.Println(deserializedAuditEvent.String())
}
fmt.Println(string(auditEventBytes))
}
func getProto(dataType string) protoreflect.ProtoMessage {
t, _ := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(dataType))
m := t.New().Interface()
return m
}

View file

@ -4,6 +4,7 @@ import "buf/validate/validate.proto";
import "google/protobuf/any.proto"; import "google/protobuf/any.proto";
import "google/protobuf/struct.proto"; import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto"; import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";
import "audit/v1/common.proto"; import "audit/v1/common.proto";
@ -13,66 +14,123 @@ option go_package = "./audit;auditV1";
option java_multiple_files = true; option java_multiple_files = true;
option java_package = "com.schwarz.stackit.audit.v1"; option java_package = "com.schwarz.stackit.audit.v1";
// TODO update numbers of elements in messages // TODO max attribute length
// TODO decide which fields should be optional // TODO check regex patterns
// TODO check if gRPC, messaging ans scheduled triggered events work with the schema
// The audit log entry can be used to record an incident in the audit log. // The audit log entry can be used to record an incident in the audit log.
message AuditLogEntry { message AuditLogEntry {
// The resource name of the log to which this log entry belongs. // The resource name of the log to which this log entry belongs.
// Example: projects/<identifier>/logs/<eventType> //
string log_name = 12; // Format: <type>/<identifier>/logs/<eventType>
// Where:
// Event-Types: admin-activity, system-event, policy-denied, data-access
//
// Examples:
// "projects/00b0f972-59ff-48f2-a4f9-29c57b75c2fa/logs/admin-activity"
//
// Required: true
string log_name = 1 [
(buf.validate.field).required = true,
(buf.validate.field).string.pattern = "^[a-z]+/[a-z0-9-]+/logs/(?:admin-activity|system-event|policy-denied|data-access)$"
];
// The log entry payload, which is always an AuditLog for STACKIT Audit Log events. // The log entry payload, which is always an AuditLog for STACKIT Audit Log events.
AuditLog proto_payload = 2; //
// Required: true
AuditLog proto_payload = 2 [
(buf.validate.field).required = true
];
// TODO can we specify how the format should look like?
// TODO Encode sequence number into it?
// https://softwaremind.com/blog/the-unique-features-of-snowflake-id-and-its-comparison-to-uuid/
// A unique identifier for the log entry. // A unique identifier for the log entry.
// Is generated and set by the SDK. // Is used to check completeness of audit events over time.
// Format: //
// <timestamp>/<region>/<worker-id>/<sequence-number> // Format: <unix-timestamp>/<region-zone>/<worker-id>/<sequence-number>
string insert_id = 4; // Where:
// Unix-Timestamp: A UTC unix timestamp in seconds is expected
// Region-Zone: The region and (optional) zone id. If both, separated with a - (dash)
// Worker-Id: The ID of the K8s Pod, Service-Instance, etc (must be unique for a sending service)
// Sequence-Number: Increasing number, representing the message offset per Worker-Id
// If the Worker-Id changes, the sequence-number has to be reset to 0.
//
// Examples:
// "1721899117/eu01/319a7fb9-edd2-46c6-953a-a724bb377c61/8792726390909855142"
//
// Required: true
string insert_id = 3[
(buf.validate.field).required = true,
// TODO how do worker ids look like?
(buf.validate.field).string.pattern = "^[0-9]+/[a-z0-9]+/[a-z0-9-]+/[0-9]+$"
];
// A set of user-defined (key, value) data that provides additional // A set of user-defined (key, value) data that provides additional
// information about the log entry. // information about the log entry.
map<string, string> labels = 11; //
// Required: true
map<string, string> labels = 4 [
(buf.validate.field).required = true
];
// Correlate multiple audit logs by setting the same id // Correlate multiple audit logs by setting the same id
string correlation_id = 15; //
// Required: false
optional string correlation_id = 5 [
(buf.validate.field).string.min_len = 1
];
// The time the event described by the log entry occurred. // The time the event described by the log entry occurred.
google.protobuf.Timestamp timestamp = 9; //
// Required: true
google.protobuf.Timestamp timestamp = 6 [
(buf.validate.field).required = true,
(buf.validate.field).timestamp.lt_now = true
];
// The severity of the log entry. // The severity of the log entry.
LogSeverity severity = 10; //
// Required: true
LogSeverity severity = 7 [
(buf.validate.field).required = true,
(buf.validate.field).enum.defined_only = true
];
// W3C conform trace parent header: // Customer set W3C conform trace parent header:
// https://www.w3.org/TR/trace-context/#traceparent-header // https://www.w3.org/TR/trace-context/#traceparent-header
// //
// Example: // Format: <version>-<trace-id>-<parent-id>-<trace-flags>
// `00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01` //
string trace_parent = 22; // Examples:
// "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
//
// Required: false
optional string trace_parent = 8 [
(buf.validate.field).string.pattern = "^[0-9]+-[a-z0-9]+-[a-z0-9]+-[0-9]+$"
];
// W3C conform trace state header: // Customer set W3C conform trace state header:
// https://www.w3.org/TR/trace-context/#tracestate-header // https://www.w3.org/TR/trace-context/#tracestate-header
// //
// Example: // Format: <key1>=<value1>[,<keyN>=<valueN>]
// `rojo=00f067aa0ba902b7,congo=t61rcWkgMzE`. //
string trace_state = 27; // Examples:
// "rojo=00f067aa0ba902b7,congo=t61rcWkgMzE"
//
// Required: false
optional string trace_state = 9 [
(buf.validate.field).string.pattern = "[a-zA-Z0-9]+=[a-zA-Z0-9]+(?:,[a-zA-Z0-9]+=[a-zA-Z0-9]+)*"
];
} }
// The severity of the event described in a log entry, expressed as one of the // The severity of the event described in a log entry, expressed as one of the
// standard severity levels listed below. For your reference, the levels are // standard severity levels listed below.
// assigned the listed numeric values. The effect of using numeric values other
// than those listed is undefined.
// Copied from
// https://github.com/googleapis/googleapis/blob/master/google/logging/type/log_severity.proto
enum LogSeverity { enum LogSeverity {
// (0) The log entry has no assigned severity level. UNSPECIFIED = 0;
DEFAULT = 0;
// (1) The log entry has no assigned severity level.
// TODO check index
DEFAULT = 1;
// (100) Debug or trace information. // (100) Debug or trace information.
DEBUG = 100; DEBUG = 100;
@ -103,87 +161,156 @@ enum LogSeverity {
// Common audit log format for STACKIT API operations. // Common audit log format for STACKIT API operations.
message AuditLog { message AuditLog {
// The name of the API service performing the operation. For example, // The name of the API service performing the operation.
// `"resource-manager"`. //
string service_name = 7; // Examples:
// "resource-manager"
//
// Required: true
string service_name = 1 [
(buf.validate.field).required = true,
(buf.validate.field).string.min_len = 1
];
// TODO: Add extra field to open api spec for the method_name // TODO rename into operation?
// The name of the service method or operation. // The name of the service method or operation.
// The format should is:
// stackit.<product>.<version>.<object>.<operation>
// //
// For example, // Format: stackit.<product>.<version>.<type>.<operation>
// //
// "stackit.resourcemanager.v1.organization.created" // Examples:
// "stackit.authorization.v2.organization.moved" // "stackit.resourcemanager.v1.organization.created"
// "stackit.authorization.v2.folder.moved" // "stackit.authorization.v2.organization.moved"
optional string method_name = 8; // "stackit.authorization.v2.folder.moved"
//
// Required: true
string method_name = 2 [
(buf.validate.field).required = true,
(buf.validate.field).string.pattern = "^stackit\\.[a-z0-9]+\\.v[1-9][0-9]*\\.[a-z0-9]+\\.[a-z0-9]+$"
];
// The resource or collection that is the target of the operation. // The resource or collection that is the target of the operation.
// The name is a scheme-less URI, not including the API service name. // The name is a scheme-less URI, not including the API service name.
// For example:
// //
// "projects/<id>/zones/<region-zone>/vms/<vm-id>" // Format: <type>/<id>[/locations/<region-zone>][/<details>]
// "projects/<id>/zones/<region-zone>/vms/<vm-id>/ports/<port-id>" // Where:
// "projects/<id>/zones/<region-zone>/instances/instance-20240723-184227 // Region-Zone: Optional region and zone id. If both, separated with a - (dash). Alternatively _ (underscore).
string resource_name = 11; // Details: Optional "<key>/<id>" pairs
//
// Examples:
// "organizations/40ab14ad-b7b0-4b1c-be41-5bc820a968d1"
// "projects/7046e7b6-5ae9-441c-99fe-2cd28a5078ec/locations/_/instances/instance-20240723-174217"
// "projects/7046e7b6-5ae9-441c-99fe-2cd28a5078ec/locations/eu01/instances/instance-20240723-174217"
// "projects/dd7d1807-54e9-4426-8994-721758b5b554/locations/eu01/vms/b6851b4e-7a9d-4973-ab0f-a80a13ee3060/ports/78f8bad4-a291-4fa3-b07f-4a1985d3dbe8"
//
// Required: true
string resource_name = 3[
(buf.validate.field).required = true,
(buf.validate.field).string.pattern = "^[a-z]+/[a-z0-9-]+(?:/locations/(?:(?:[a-z0-9]+(?:-[a-z0-9]+)?)|_))?(?:/[a-z0-9]+/[a-z0-9-]+)*$"
];
// Authentication information. // Authentication information.
AuthenticationInfo authentication_info = 3; //
// Required: true
AuthenticationInfo authentication_info = 4 [
(buf.validate.field).required = true
];
// Authorization information. If there are multiple // Authorization information. If there are multiple resources or permissions involved, then there is
// resources or permissions involved, then there is
// one AuthorizationInfo element for each {resource, permission} tuple. // one AuthorizationInfo element for each {resource, permission} tuple.
repeated AuthorizationInfo authorization_info = 9; //
// Required: false
repeated AuthorizationInfo authorization_info = 5;
// Metadata about the operation. // Metadata about the operation.
RequestMetadata request_metadata = 4; //
// Required: true
RequestMetadata request_metadata = 6 [
(buf.validate.field).required = true
];
// The operation request. This may not include all request parameters, // The operation request. This may not include all request parameters,
// such as those that are too large, privacy-sensitive, or duplicated // such as those that are too large, privacy-sensitive, or duplicated
// elsewhere in the log record. // elsewhere in the log record.
// It should never include user-generated data, such as file contents. // It should never include user-generated data, such as file contents.
google.protobuf.Struct request = 16; //
// Required: true
google.protobuf.Struct request = 7 [
(buf.validate.field).required = true
];
// The status of the overall operation. // The status of the overall operation.
ResponseStatus status = 2; //
// Required: true
ResponseStatus status = 8 [
(buf.validate.field).required = true
];
// The number of items returned from a List or Query API method, // The number of items returned from a List or Query API method,
// if applicable. // if applicable.
optional int64 num_response_items = 12; //
// Required: false
optional google.protobuf.Int64Value num_response_items = 9 [
(buf.validate.field).int64.gte = 0
];
// The operation response. This may not include all response elements, // The operation response. This may not include all response elements,
// such as those that are too large, privacy-sensitive, or duplicated // such as those that are too large, privacy-sensitive, or duplicated
// elsewhere in the log record. // elsewhere in the log record.
google.protobuf.Struct response = 17; //
// Required: true
google.protobuf.Struct response = 10 [
(buf.validate.field).required = true
];
// Other service-specific data about the request, response, and other // Other service-specific data about the request, response, and other
// information associated with the current audited event. // information associated with the current audited event.
google.protobuf.Struct metadata = 18; //
// Required: false
optional google.protobuf.Struct metadata = 11;
} }
// Authentication information for the operation. // Authentication information for the operation.
message AuthenticationInfo { message AuthenticationInfo {
// Principal id // STACKIT principal id
string principal_id = 1; //
// Required: true
string principal_id = 1 [
(buf.validate.field).required = true,
(buf.validate.field).string.min_len = 1
];
// The email address of the authenticated user // The email address of the authenticated user.
string principal_email = 2; // Service accounts have email addresses that can be used.
//
// Required: true
string principal_email = 2 [
(buf.validate.field).required = true,
(buf.validate.field).string.min_len = 1,
(buf.validate.field).string.max_len = 255
];
// The name of the service account used to create or exchange // The name of the service account used to create or exchange
// credentials for authenticating the service account making the request. // credentials for authenticating the service account making the request.
// Example:
// //
// "projects/{PROJECT_ID}/serviceAccounts/{ACCOUNT}" // Format: projects/<id>/serviceAccounts/<account>
optional string service_account_name = 5; //
// Examples:
// "projects/29b2c56f-f712-4a9c-845b-f0907158e53c/serviceAccounts/a606dc68-8b97-421b-89a9-116bcbd004df"
//
// Required: false
optional string service_account_name = 3 [
(buf.validate.field).string.pattern = "^[a-z]+/[a-z0-9-]+/serviceAccounts/[a-zA-Z0-9-]+$"
];
// Identity delegation history of an authenticated service account that makes // Identity delegation history of an authenticated service account that makes
// the request. It contains information on the real authorities that try to // the request. It contains information on the real authorities that try to
// access STACKIT resources by delegating on a service account. When multiple // access STACKIT resources by delegating on a service account. When multiple
// authorities present, they are guaranteed to be sorted based on the original // authorities present, they are guaranteed to be sorted based on the original
// ordering of the identity delegation events. // ordering of the identity delegation events.
repeated ServiceAccountDelegationInfo service_account_delegation_info = 6; //
// Required: false
repeated ServiceAccountDelegationInfo service_account_delegation_info = 4;
} }
// Authorization information for the operation. // Authorization information for the operation.
@ -191,37 +318,44 @@ message AuthorizationInfo {
// The resource being accessed, as a REST-style string. // The resource being accessed, as a REST-style string.
// //
// For example: // Format: <type>/<id>[/locations/<region-zone>][/<details>]
// Project scoped resource: projects/test-project-123/zones/us-central1-b/instances/instance-20240723-174217 // Where:
// Global Resource: projects/_/buckets/adfeaf // Region-Zone: Optional region and zone id. If both, separated with a - (dash). Alternatively _ (underscore).
string resource = 1; // Details: Optional "<key>/<id>" pairs
//
// Examples:
// "organizations/40ab14ad-b7b0-4b1c-be41-5bc820a968d1"
// "projects/7046e7b6-5ae9-441c-99fe-2cd28a5078ec/locations/_/instances/instance-20240723-174217"
// "projects/7046e7b6-5ae9-441c-99fe-2cd28a5078ec/locations/eu01/instances/instance-20240723-174217"
// "projects/7046e7b6-5ae9-441c-99fe-2cd28a5078ec/locations/eu01/vms/b6851b4e-7a9d-4973-ab0f-a80a13ee3060/ports/78f8bad4-a291-4fa3-b07f-4a1985d3dbe8"
//
// Required: true
string resource = 1 [
(buf.validate.field).required = true,
(buf.validate.field).string.pattern = "^[a-z]+/[a-z0-9-]+(?:/locations/(?:(?:[a-z0-9]+(?:-[a-z0-9]+)?)|_))?(?:/[a-z0-9]+/[a-z0-9-]+)*$"
];
// The required IAM permission. // The required IAM permission.
string permission = 2; //
// Examples:
// "resourcemanager.project.edit"
//
// Required: false
optional string permission = 2 [
(buf.validate.field).string.pattern = "^[a-z-]+(?:\\.[a-z-]+)*\\.[a-z-]+$"
];
// Whether or not authorization for `resource` and `permission` // Whether or not authorization for `resource` and `permission`
// was granted. // was granted.
bool granted = 3; //
// Required: false
optional bool granted = 3;
} }
// TODO check description
// This message defines the standard attribute vocabulary for STACKIT APIs. // This message defines the standard attribute vocabulary for STACKIT APIs.
// //
// An attribute is a piece of metadata that describes an activity on a network // An attribute is a piece of metadata that describes an activity on a network
// service. For example, the size of an HTTP request, or the status code of // service.
// an HTTP response.
//
// Each attribute has a type and a name, which is logically defined as
// a proto message field in `AttributeContext`. The field type becomes the
// attribute type, and the field path becomes the attribute name. For example,
// the attribute `source.ip` maps to field `AttributeContext.source.ip`.
//
// This message definition is guaranteed not to have any wire breaking change.
// So you can use it directly for passing attributes across different systems.
//
// NOTE: Different system may generate different subset of attributes. Please
// verify the system specification before relying on an attribute generated
// a system.
message AttributeContext { message AttributeContext {
// This message defines request authentication attributes. Terminology is // This message defines request authentication attributes. Terminology is
@ -229,55 +363,57 @@ message AttributeContext {
// correlate to concepts in other standards. // correlate to concepts in other standards.
message Auth { message Auth {
// TODO check description // The authenticated principal. Reflects the issuer ("iss") and subject
// The authenticated principal. Reflects the issuer (`iss`) and subject // ("sub") claims within a JWT.
// (`sub`) claims within a JWT. The issuer and subject should be `/` //
// delimited, with `/` percent-encoded within the subject fragment. For // Format: <sub-claim>/<iss-claim>
// Google accounts, the principal format is: // Where:
// "https://accounts.google.com/{id}" // Sub-Claim: Sub-Claim from JWT with `/` percent-encoded (url-encoded)
string principal = 1; //
// Examples:
// "https%3A%2F%2Faccounts.dev.stackit.cloud/stackit-resource-manager-dev"
//
// Required: true
string principal = 1 [
(buf.validate.field).required = true,
(buf.validate.field).string.pattern = "^[a-zA-Z0-9-%.]+/[a-zA-Z0-9-%.]+$"
];
// TODO check description
// The intended audience(s) for this authentication information. Reflects // The intended audience(s) for this authentication information. Reflects
// the audience (`aud`) claim within a JWT. The audience // the audience ("aud") claim within a JWT, typically the services intended
// value(s) depends on the `issuer`, but typically include one or more of // to receive the credential.
// the following pieces of information:
// //
// * The services intended to receive the credential such as // Examples:
// ["pubsub.googleapis.com", "storage.googleapis.com"] // ["https://stackit-resource-manager-dev.apps.01.cf.eu01.stackit.cloud", "stackit", "api"]
// * A set of service-based scopes. For example,
// ["https://www.googleapis.com/auth/cloud-platform"]
// * The client id of an app, such as the Firebase project id for JWTs
// from Firebase Auth.
// //
// Consult the documentation for the credential issuer to determine the // Required: false
// information provided.
repeated string audiences = 2; repeated string audiences = 2;
// TODO check description
// The authorized presenter of the credential. Reflects the optional
// Authorized Presenter (`azp`) claim within a JWT or the
// OAuth client id. For example, a Google Cloud Platform client id looks
// as follows: "123456789012.apps.googleusercontent.com".
string presenter = 3;
// TODO check description
// Structured claims presented with the credential. JWTs include // Structured claims presented with the credential. JWTs include
// `{key: value}` pairs for standard and private claims. The following // {"key": <value>} pairs for standard and private claims.
// is a subset of the standard required and optional claims that would
// typically be presented for a Google-based JWT:
// //
// {'iss': 'accounts.google.com', // The following is a subset of the standard required and optional claims that would
// 'sub': '113289723416554971153', // typically be presented for a STACKIT JWT:
// 'aud': ['123456789012', 'pubsub.googleapis.com'],
// 'azp': '123456789012.apps.googleusercontent.com',
// 'email': 'jsmith@example.com',
// 'iat': 1353601026,
// 'exp': 1353604926}
// //
// SAML assertions are similarly specified, but with an identity provider // {
// dependent structure. // "aud": "https://stackit-resource-manager-dev.apps.01.cf.eu01.stackit.cloud",
google.protobuf.Struct claims = 4; // "email": "max@mail.schwarz",
// "exp": 1721905449,
// "iat": 1721901849,
// "iss": "https://api.dev.stackit.cloud",
// "jti": "45a196e0-480f-4c34-a592-dc5db81c8c3a",
// "nbf": 1721900462,
// "roles": null,
// "sub": "cd94f01a-df2e-4456-902f-48f5e57f0b63",
// "user_id": "",
// "x_client_id": "",
// "zid": ""
// }
//
// Required: true
google.protobuf.Struct claims = 3 [
(buf.validate.field).required = true
];
} }
// This message defines attributes for an HTTP request. If the actual // This message defines attributes for an HTTP request. If the actual
@ -288,62 +424,126 @@ message AttributeContext {
// The unique ID for a request, which can be propagated to downstream // The unique ID for a request, which can be propagated to downstream
// systems. The ID should have low probability of collision // systems. The ID should have low probability of collision
// within a single day for a specific service. // within a single day for a specific service.
string id = 1; //
// More information can be found here: https://google.aip.dev/155
//
// Format: <idempotency-key>
// Where:
// Idempotency-key: Typically consists of a id + version
//
// Examples:
// 5e3952a9-b628-4be6-ac61-b1c6eb4a110c/5
//
// Required: false
optional string id = 1;
// The HTTP request method, such as `GET`, `POST`. // The HTTP request method, such as `GET`, `POST`.
string method = 2; //
// Required: true
// TODO does it make sense to define an enum?
string method = 2 [
(buf.validate.field).required = true,
(buf.validate.field).string.min_len = 1
];
// The HTTP request headers. If multiple headers share the same key, they // The HTTP request headers. If multiple headers share the same key, they
// must be merged according to the HTTP spec. All header keys must be // must be merged according to the HTTP spec. All header keys must be
// lowercased, because HTTP header keys are case-insensitive. // lowercased, because HTTP header keys are case-insensitive.
map<string, string> headers = 3; //
// Required: true
map<string, string> headers = 3 [
(buf.validate.field).required = true
];
// The HTTP URL path. // The HTTP URL path.
string path = 4; //
// Required: true
string path = 4 [
(buf.validate.field).required = true,
(buf.validate.field).string.min_len = 1
];
// The HTTP request `Host` header value. // The HTTP request `Host` header value.
string host = 5; //
// Required: true
string host = 5 [
(buf.validate.field).required = true,
(buf.validate.field).string.min_len = 1
];
// The HTTP URL scheme, such as `http` and `https`. // The HTTP URL scheme, such as `http` and `https`.
string scheme = 6; //
// Required: true
string scheme = 6 [
(buf.validate.field).required = true,
(buf.validate.field).string.min_len = 1
];
// The HTTP URL query in the format of `name1=value1&name2=value2`, as it // The HTTP URL query in the format of "name1=value1&name2=value2", as it
// appears in the first line of the HTTP request. No decoding is performed. // appears in the first line of the HTTP request. No decoding is performed.
string query = 7; //
// Required: false
optional string query = 7 [
(buf.validate.field).string.pattern = "^[a-zA-Z0-9]+=[a-zA-Z0-9]+(?:&[a-zA-Z0-9]+=[a-zA-Z0-9]+)*$"
];
// The timestamp when the `destination` service receives the first byte of // The timestamp when the `destination` service receives the first byte of
// the request. // the request.
google.protobuf.Timestamp time = 9; //
// Required: true
google.protobuf.Timestamp time = 8 [
(buf.validate.field).required = true,
(buf.validate.field).timestamp.lt_now = true
];
// The network protocol used with the request, such as "http/1.1", // The network protocol used with the request, such as "http/1.1",
// "spdy/3", "h2", "h2c", "webrtc", "tcp", "udp", "quic". See // "spdy/3", "h2", "h2c", "webrtc", "tcp", "udp", "quic". See
// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids // https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
// for details. // for details.
string protocol = 11; //
// Required: true
string protocol = 9 [
(buf.validate.field).required = true,
(buf.validate.field).string.min_len = 1
];
// The request authentication. May be absent for unauthenticated requests. // The request authentication.
// Derived from the HTTP request `Authorization` header or equivalent. //
Auth auth = 13; // Required: true
Auth auth = 10 [
(buf.validate.field).required = true
];
} }
// This message defines attributes for a typical network response. It // This message defines attributes for a typical network response. It
// generally models semantics of an HTTP response. // generally models semantics of an HTTP response.
// TODO do we need another status code attribute in the Response?
message Response { message Response {
// The HTTP response status code, such as `200` and `404`. // The HTTP response size in bytes.
int64 code = 1; //
// Required: false
// The HTTP response size in bytes. If unknown, it must be -1. optional google.protobuf.Int64Value size = 1 [
int64 size = 2; (buf.validate.field).int64.gte = 0
];
// The HTTP response headers. If multiple headers share the same key, they // The HTTP response headers. If multiple headers share the same key, they
// must be merged according to HTTP spec. All header keys must be // must be merged according to HTTP spec. All header keys must be
// lowercased, because HTTP header keys are case-insensitive. // lowercased, because HTTP header keys are case-insensitive.
map<string, string> headers = 3; //
// Required: true
map<string, string> headers = 2 [
(buf.validate.field).required = true
];
// The timestamp when the `destination` service generates the first byte of // The timestamp when the "destination" service generates the first byte of
// the response. // the response.
google.protobuf.Timestamp time = 4; //
// Required: true
google.protobuf.Timestamp time = 3 [
(buf.validate.field).required = true,
(buf.validate.field).timestamp.lt_now = true
];
} }
} }
@ -352,33 +552,34 @@ message RequestMetadata {
// The IP address of the caller. // The IP address of the caller.
// For caller from internet, this will be public IPv4 or IPv6 address. // For caller from internet, this will be public IPv4 or IPv6 address.
// For caller from a VM / K8s Service / etc, this // For caller from a VM / K8s Service / etc, this will be the SIT proxy's IPv4 address.
// will be the SIT proxy's IPv4 address. //
string caller_ip = 1; // Required: true
string caller_ip = 1 [
(buf.validate.field).required = true,
(buf.validate.field).string.ip = true
];
// TODO check description
// The user agent of the caller. // The user agent of the caller.
// This information is not authenticated and should be treated accordingly.
// For example:
// //
// + `google-api-python-client/1.4.0`: // Examples:
// The request was made by the Google API client for Python. // "OpenAPI-Generator/1.0.0/go"
// + `Cloud SDK Command Line Tool apitools-client/1.0 gcloud/0.9.62`: // -> The request was made by the STACKIT SDK GO client or STACKIT CLI
// The request was made by the Google Cloud SDK CLI (gcloud). // "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
// + `AppEngine-Google; (+http://code.google.com/appengine; appid: // -> The request was made by a web browser.
// s~my-project`: //
// The request was made from the `my-project` App Engine app. // Required: true
string caller_supplied_user_agent = 2; string caller_supplied_user_agent = 2 [
(buf.validate.field).required = true,
(buf.validate.field).string.min_len = 1
];
// TODO check description // This field contains request attributes like request url, time, etc.
// Request attributes used in IAM condition evaluation. This field contains
// request attributes like request time and access levels associated with
// the request.
// //
// To get the whole view of the attributes used in IAM // Required: true
// condition evaluation, the user must also look into AttributeContext.Request request_attributes = 3 [
// `AuditLog.authentication_info.resource_attributes`. (buf.validate.field).required = true
AttributeContext.Request request_attributes = 7; ];
} }
// The `Status` type defines a logical error model that is suitable for // The `Status` type defines a logical error model that is suitable for
@ -388,12 +589,25 @@ message RequestMetadata {
message ResponseStatus { message ResponseStatus {
// The http or gRPC status code. // The http or gRPC status code.
int32 code = 1; //
// Examples:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
// https://grpc.github.io/grpc/core/md_doc_statuscodes.html
//
// Required: true
google.protobuf.Int32Value code = 1 [
(buf.validate.field).required = true,
(buf.validate.field).int32.gte = 0
];
// Short description of the error // Short description of the error
string message = 2; //
// Required: false
optional string message = 2;
// Error details // Error details
//
// Required: false
repeated google.protobuf.Struct details = 3; repeated google.protobuf.Struct details = 3;
} }
@ -404,25 +618,42 @@ message ServiceAccountDelegationInfo {
message SystemPrincipal { message SystemPrincipal {
// Metadata about the service that uses the service account. // Metadata about the service that uses the service account.
google.protobuf.Struct service_metadata = 3; //
// Required: false
optional google.protobuf.Struct service_metadata = 1;
} }
// STACKIT idp principal. // STACKIT idp principal.
message IdpPrincipal { message IdpPrincipal {
// STACKIT principal id // STACKIT principal id
string principal_id = 1; //
// Required: true
string principal_id = 1 [
(buf.validate.field).required = true,
(buf.validate.field).string.min_len = 1
];
// Optional email address // The email address of the authenticated user.
optional string principal_email = 2; // Service accounts have email addresses that can be used.
//
// Required: true
string principal_email = 2 [
(buf.validate.field).required = true,
(buf.validate.field).string.min_len = 1,
(buf.validate.field).string.max_len = 255
];
// Metadata about the service that uses the service account. // Metadata about the service that uses the service account.
google.protobuf.Struct service_metadata = 3; //
// Required: false
optional google.protobuf.Struct service_metadata = 3;
} }
// Entity that creates credentials for service account and assumes its // Entity that creates credentials for service account and assumes its
// identity for authentication. // identity for authentication.
oneof Authority { oneof Authority {
option (buf.validate.oneof).required = true;
// System identity // System identity
SystemPrincipal system_principal = 1; SystemPrincipal system_principal = 1;

View file

@ -44,7 +44,10 @@ message RoutableAuditEvent {
// Functional event name with pattern <TYPE>_<ACTION>, e.g. ORGANIZATION_CREATED // Functional event name with pattern <TYPE>_<ACTION>, e.g. ORGANIZATION_CREATED
// Will be copied over by the SDK from the AuditEvent // Will be copied over by the SDK from the AuditEvent
string event_name = 1 [(buf.validate.field).required = true, (buf.validate.field).string.pattern = "^[A-Z]+_[A-Z]+$"]; string event_name = 1 [
(buf.validate.field).required = true,
(buf.validate.field).string.pattern = "^stackit\\.[a-z0-9]+\\.v[1-9][0-9]*\\.[a-z0-9]+\\.[a-z0-9]+$"
];
// Visibility relevant for differentiating between internal and public events // Visibility relevant for differentiating between internal and public events
Visibility visibility = 2 [(buf.validate.field).required = true, (buf.validate.field).enum.defined_only = true]; Visibility visibility = 2 [(buf.validate.field).required = true, (buf.validate.field).enum.defined_only = true];