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.
### Open Issues
- Finalizing messaging schema
- Extraction of python / java configurations and code
- Check TODOs in the 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
- Clean up repo (delete main.go, etc. files)
- Finalizing API Design
- Decision whether serialized payload should be replaced with []byte or base64 encoded string
- Extraction of python / java configurations and code
- Clean up repo (delete main.go, etc. files)

View file

@ -119,6 +119,24 @@ type CloudEvent struct {
// The serialized payload
data []byte
// Optional W3C conform trace parent:
// https://www.w3.org/TR/trace-context/#traceparent-header
//
// Format: <version>-<trace-id>-<parent-id>-<trace-flags>
//
// Examples:
// "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
traceParent *string
// Optional W3C conform trace state header:
// https://www.w3.org/TR/trace-context/#tracestate-header
//
// Format: <key1>=<value1>[,<keyN>=<valueN>]
//
// Examples:
// "rojo=00f067aa0ba902b7,congo=t61rcWkgMzE"
traceState *string
}
// RoutingIdentifierType is an enumeration of allowed identifier types.

View file

@ -11,7 +11,8 @@ import (
"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
var ErrEventNil = errors.New("event is nil")
@ -109,7 +110,7 @@ func validateAndSerializePartially(
}
routableEvent := auditV1.RoutableAuditEvent{
EventName: event.LogName,
EventName: event.ProtoPayload.MethodName,
Visibility: visibility,
Data: &auditV1.RoutableAuditEvent_UnencryptedData{UnencryptedData: &payload},
}
@ -155,6 +156,8 @@ func send(
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["cloudEvents:specversion"] = cloudEvent.specVersion
applicationAttributes["cloudEvents:source"] = cloudEvent.source
@ -163,6 +166,12 @@ func send(
applicationAttributes["cloudEvents:datacontenttype"] = cloudEvent.dataContentType
applicationAttributes["cloudEvents:type"] = cloudEvent.dataType
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(
ctx,

View file

@ -2,14 +2,10 @@ package api
import (
"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"
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/google/uuid"
@ -69,104 +65,16 @@ func Test_ValidateAndSerializePartially_EventNil(t *testing.T) {
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) {
validator := NewValidator(t)
event, routingIdentifier, objectIdentifier := NewOrganizationAuditEvent(nil)
event.EventName = ""
event.LogName = ""
_, err := validateAndSerializePartially(
&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) {

View file

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

View file

@ -4,7 +4,9 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"log/slog"
"net/url"
"os"
"testing"
"time"
@ -320,14 +322,14 @@ func TestLegacyAuditApi(t *testing.T) {
var auditEvent LegacyAuditEvent
assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent))
assert.Equal(t, event.EventName, auditEvent.EventName)
assert.Equal(t, event.EventTimeStamp.AsTime(), auditEvent.EventTimeStamp)
assert.Equal(t, event.Initiator.Id, auditEvent.Initiator.Id)
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, "SYSTEM_EVENT", auditEvent.EventType)
assert.Equal(t, "INFO", auditEvent.Severity)
assert.Equal(t, "none", auditEvent.Request.Endpoint)
assert.Equal(t, "0.0.0.0", auditEvent.SourceIpAddress)
assert.Equal(t, "none", auditEvent.UserAgent)
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)
})
t.Run("Log event with details", func(t *testing.T) {
@ -350,7 +352,7 @@ func TestLegacyAuditApi(t *testing.T) {
assert.NoError(t, err)
// Instantiate test data
event, routingIdentifier, objectIdentifier := NewOrganizationAuditEventWithDetails()
event, routingIdentifier, objectIdentifier := NewOrganizationAuditEvent(nil)
// Log the event to solace
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
assert.Equal(t, topicName, *message.Properties.To)
@ -377,43 +379,88 @@ func validateSentMessage(t *testing.T, topicName string, message *amqp.Message,
var auditEvent LegacyAuditEvent
assert.NoError(t, json.Unmarshal(message.Data[0], &auditEvent))
assert.Equal(t, event.EventName, auditEvent.EventName)
assert.Equal(t, event.EventTimeStamp.AsTime(), auditEvent.EventTimeStamp)
assert.Equal(t, event.Initiator.Id, auditEvent.Initiator.Id)
assert.Equal(t, "ADMIN_ACTIVITY", auditEvent.EventType)
assert.Equal(t, "INFO", auditEvent.Severity)
assert.Equal(t, "none", auditEvent.Request.Endpoint)
assert.Equal(t, "0.0.0.0", auditEvent.SourceIpAddress)
assert.Equal(t, "none", auditEvent.UserAgent)
}
func validateSentMessageWithDetails(t *testing.T, topicName string, message *amqp.Message, event *auditV1.AuditEvent) {
// 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.EventName, auditEvent.EventName)
assert.Equal(t, event.EventTimeStamp.AsTime(), auditEvent.EventTimeStamp)
assert.Equal(t, event.Initiator.Id, auditEvent.Initiator.Id)
assert.Equal(t, "ADMIN_ACTIVITY", auditEvent.EventType)
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])
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:
assert.Fail(t, "unknown log severity")
}
for idx := range event.Principals {
assert.Equal(t, event.Principals[idx].Id, auditEvent.ServiceAccountDelegationInfo.Principals[idx].Id)
assert.Equal(t, event.Principals[idx].Email, auditEvent.ServiceAccountDelegationInfo.Principals[idx].Email)
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, 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) {
customization := func(event *auditV1.AuditEvent,
customization := func(event *auditV1.AuditLogEntry,
routingIdentifier *RoutingIdentifier,
objectIdentifier *auditV1.ObjectIdentifier) {
objectIdentifier.Type = 4
@ -477,7 +524,7 @@ func TestLegacyAuditApi_ConvertAndSerializeIntoLegacyFormatInvalidObjectIdentifi
func TestLegacyAuditApi_ConvertAndSerializeIntoLegacyFormat_NoResourceReference(t *testing.T) {
event, _, _ := NewProjectAuditEvent(nil)
routableEvent := auditV1.RoutableAuditEvent{
EventName: event.EventName,
EventName: event.ProtoPayload.MethodName,
Visibility: auditV1.Visibility_VISIBILITY_PUBLIC,
ResourceReference: nil,
Data: nil,

View file

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

View file

@ -31,7 +31,7 @@ func TestMockAuditApi_Log(t *testing.T) {
assert.NoError(t, err)
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) {

View file

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

View file

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

View file

@ -1,6 +1,7 @@
package api
import (
"fmt"
"google.golang.org/protobuf/types/known/wrapperspb"
"time"
@ -22,19 +23,74 @@ func NewOrganizationAuditEvent(
*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{
EventSource: "resource-manager",
Region: auditV1.Region_REGION_EU01,
SequenceNumber: wrapperspb.Int64(0),
EventName: "ORGANIZATION_CREATED",
EventTimeStamp: timestamppb.New(time.Now()),
EventTrigger: auditV1.EventTrigger_EVENT_TRIGGER_EVENT,
Initiator: &auditV1.Principal{
Id: uuid.NewString(),
LogName: fmt.Sprintf("organizations/%s/logs/admin-activity", identifier),
ProtoPayload: &auditV1.AuditLog{
ServiceName: "resource-manager",
MethodName: "stackit.resourcemanager.v2.organization.created",
ResourceName: fmt.Sprintf("organizations/%s", identifier),
AuthenticationInfo: &auditV1.AuthenticationInfo{
PrincipalId: 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{
Identifier: identifier,
Type: RoutingIdentifierTypeOrganization,
@ -44,7 +100,6 @@ func NewOrganizationAuditEvent(
Identifier: identifier.String(),
Type: auditV1.ObjectType_OBJECT_TYPE_ORGANIZATION,
}
auditEvent.ResourceContainerReference = &auditV1.AuditEvent_ObjectIdentifier{ObjectIdentifier: objectIdentifier}
if customization != nil {
(*customization)(auditEvent, routingIdentifier, objectIdentifier)
@ -53,51 +108,6 @@ func NewOrganizationAuditEvent(
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(
customization *func(
*auditV1.AuditLogEntry,
@ -109,28 +119,83 @@ func NewFolderAuditEvent(
*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{
EventSource: "resource-manager",
Region: auditV1.Region_REGION_EU01,
SequenceNumber: wrapperspb.Int64(0),
EventName: "FOLDER_CREATED",
EventTimeStamp: timestamppb.New(time.Now()),
EventTrigger: auditV1.EventTrigger_EVENT_TRIGGER_EVENT,
Initiator: &auditV1.Principal{
Id: uuid.NewString(),
LogName: fmt.Sprintf("folders/%s/logs/admin-activity", identifier),
ProtoPayload: &auditV1.AuditLog{
ServiceName: "resource-manager",
MethodName: "stackit.resourcemanager.v2.folder.created",
ResourceName: fmt.Sprintf("folders/%s", identifier),
AuthenticationInfo: &auditV1.AuthenticationInfo{
PrincipalId: 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{
Identifier: uuid.New(),
Identifier: identifier,
Type: RoutingIdentifierTypeOrganization,
}
objectIdentifier := &auditV1.ObjectIdentifier{
Identifier: uuid.New().String(),
Identifier: identifier.String(),
Type: auditV1.ObjectType_OBJECT_TYPE_FOLDER,
}
auditEvent.ResourceContainerReference = &auditV1.AuditEvent_ObjectIdentifier{ObjectIdentifier: objectIdentifier}
if customization != nil {
(*customization)(auditEvent, routingIdentifier, objectIdentifier)
@ -150,19 +215,74 @@ func NewProjectAuditEvent(
*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{
EventSource: "resource-manager",
Region: auditV1.Region_REGION_EU01,
SequenceNumber: wrapperspb.Int64(0),
EventName: "PROJECT_CREATED",
EventTimeStamp: timestamppb.New(time.Now()),
EventTrigger: auditV1.EventTrigger_EVENT_TRIGGER_EVENT,
Initiator: &auditV1.Principal{
Id: uuid.NewString(),
LogName: fmt.Sprintf("projects/%s/logs/admin-activity", identifier),
ProtoPayload: &auditV1.AuditLog{
ServiceName: "resource-manager",
MethodName: "stackit.resourcemanager.v2.project.created",
ResourceName: fmt.Sprintf("projects/%s", identifier),
AuthenticationInfo: &auditV1.AuthenticationInfo{
PrincipalId: 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{
Identifier: identifier,
Type: RoutingIdentifierTypeProject,
@ -172,7 +292,6 @@ func NewProjectAuditEvent(
Identifier: identifier.String(),
Type: auditV1.ObjectType_OBJECT_TYPE_PROJECT,
}
auditEvent.ResourceContainerReference = &auditV1.AuditEvent_ObjectIdentifier{ObjectIdentifier: objectIdentifier}
if customization != nil {
(*customization)(auditEvent, routingIdentifier, objectIdentifier)
@ -184,19 +303,74 @@ func NewProjectAuditEvent(
func NewSystemAuditEvent(
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{
EventSource: "resource-manager",
Region: auditV1.Region_REGION_EU01,
SequenceNumber: wrapperspb.Int64(0),
EventName: "SYSTEM_CHANGED",
EventTimeStamp: timestamppb.New(time.Now()),
EventTrigger: auditV1.EventTrigger_EVENT_TRIGGER_EVENT,
Initiator: &auditV1.Principal{
Id: uuid.NewString(),
LogName: fmt.Sprintf("projects/%s/logs/system-event", identifier),
ProtoPayload: &auditV1.AuditLog{
ServiceName: "resource-manager",
MethodName: "stackit.resourcemanager.v2.system.changed",
ResourceName: fmt.Sprintf("projects/%s", identifier),
AuthenticationInfo: &auditV1.AuthenticationInfo{
PrincipalId: serviceAccountId,
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 {
(*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 CorrelationId
if all {
switch v := interface{}(m.GetTimestamp()).(type) {
case interface{ ValidateAll() error }:
@ -125,9 +123,17 @@ func (m *AuditLogEntry) validate(all bool) error {
// 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 {
return AuditLogEntryMultiError(errors)
@ -231,6 +237,8 @@ func (m *AuditLog) validate(all bool) error {
// no validation rules for ServiceName
// no validation rules for MethodName
// no validation rules for ResourceName
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 {
// 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 {
@ -694,37 +731,12 @@ func (m *AuthorizationInfo) validate(all bool) error {
// 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 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 m.Granted != nil {
// no validation rules for Granted
}
if len(errors) > 0 {
@ -1062,9 +1074,34 @@ func (m *ResponseStatus) validate(all bool) error {
var errors []error
// no validation rules for Code
// no validation rules for Message
if all {
switch v := interface{}(m.GetCode()).(type) {
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() {
_, _ = 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 {
return ResponseStatusMultiError(errors)
}
@ -1392,8 +1433,6 @@ func (m *AttributeContext_Auth) validate(all bool) error {
// no validation rules for Principal
// no validation rules for Presenter
if all {
switch v := interface{}(m.GetClaims()).(type) {
case interface{ ValidateAll() error }:
@ -1525,8 +1564,6 @@ func (m *AttributeContext_Request) validate(all bool) error {
var errors []error
// no validation rules for Id
// no validation rules for Method
// 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 Query
if all {
switch v := interface{}(m.GetTime()).(type) {
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 {
return AttributeContext_RequestMultiError(errors)
}
@ -1701,10 +1744,6 @@ func (m *AttributeContext_Response) validate(all bool) error {
var errors []error
// no validation rules for Code
// no validation rules for Size
// no validation rules for Headers
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 {
return AttributeContext_ResponseMultiError(errors)
}
@ -1816,116 +1888,6 @@ var _ interface {
ErrorName() string
} = 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
// ServiceAccountDelegationInfo_SystemPrincipal with the rules defined in the
// 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
if all {
switch v := interface{}(m.GetServiceMetadata()).(type) {
case interface{ ValidateAll() error }:
if err := v.ValidateAll(); err != nil {
errors = append(errors, ServiceAccountDelegationInfo_SystemPrincipalValidationError{
field: "ServiceMetadata",
reason: "embedded message failed validation",
cause: err,
})
if m.ServiceMetadata != nil {
if all {
switch v := interface{}(m.GetServiceMetadata()).(type) {
case interface{ ValidateAll() error }:
if err := v.ValidateAll(); err != nil {
errors = append(errors, ServiceAccountDelegationInfo_SystemPrincipalValidationError{
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 {
errors = append(errors, ServiceAccountDelegationInfo_SystemPrincipalValidationError{
return ServiceAccountDelegationInfo_SystemPrincipalValidationError{
field: "ServiceMetadata",
reason: "embedded message failed validation",
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 {
@ -2088,37 +2054,39 @@ func (m *ServiceAccountDelegationInfo_IdpPrincipal) validate(all bool) error {
// no validation rules for PrincipalId
if all {
switch v := interface{}(m.GetServiceMetadata()).(type) {
case interface{ ValidateAll() error }:
if err := v.ValidateAll(); err != nil {
errors = append(errors, ServiceAccountDelegationInfo_IdpPrincipalValidationError{
field: "ServiceMetadata",
reason: "embedded message failed validation",
cause: err,
})
}
case interface{ Validate() error }:
if err := v.Validate(); err != nil {
errors = append(errors, ServiceAccountDelegationInfo_IdpPrincipalValidationError{
field: "ServiceMetadata",
reason: "embedded message failed validation",
cause: err,
})
}
}
} else if v, ok := interface{}(m.GetServiceMetadata()).(interface{ Validate() error }); ok {
if err := v.Validate(); err != nil {
return ServiceAccountDelegationInfo_IdpPrincipalValidationError{
field: "ServiceMetadata",
reason: "embedded message failed validation",
cause: err,
}
}
}
// no validation rules for PrincipalEmail
if m.ServiceMetadata != nil {
if all {
switch v := interface{}(m.GetServiceMetadata()).(type) {
case interface{ ValidateAll() error }:
if err := v.ValidateAll(); err != nil {
errors = append(errors, ServiceAccountDelegationInfo_IdpPrincipalValidationError{
field: "ServiceMetadata",
reason: "embedded message failed validation",
cause: err,
})
}
case interface{ Validate() error }:
if err := v.Validate(); err != nil {
errors = append(errors, ServiceAccountDelegationInfo_IdpPrincipalValidationError{
field: "ServiceMetadata",
reason: "embedded message failed validation",
cause: err,
})
}
}
} else if v, ok := interface{}(m.GetServiceMetadata()).(interface{ Validate() error }); ok {
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 {

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,
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,
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,
0x12, 0x38, 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,
0x5e, 0x5b, 0x41, 0x2d, 0x5a, 0x5d, 0x2b, 0x5f, 0x5b, 0x41, 0x2d, 0x5a, 0x5d, 0x2b, 0x24, 0x52,
0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x76, 0x69,
0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14,
0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69,
0x6c, 0x69, 0x74, 0x79, 0x42, 0x0b, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x82, 0x01, 0x02, 0x10,
0x01, 0x52, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x37, 0x0a,
0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01,
0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x49, 0x0a, 0x11, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52,
0x10, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65,
0x72, 0x12, 0x46, 0x0a, 0x10, 0x75, 0x6e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64,
0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x75,
0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74,
0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x48, 0x01, 0x52, 0x0f, 0x75, 0x6e, 0x65, 0x6e, 0x63, 0x72,
0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x12, 0x40, 0x0a, 0x0e, 0x65, 0x6e, 0x63,
0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x17, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x63,
0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x48, 0x01, 0x52, 0x0d, 0x65, 0x6e,
0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x42, 0x1b, 0x0a, 0x12, 0x72,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
0x65, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x42, 0x0d, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61,
0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x2a, 0x57, 0x0a, 0x0a, 0x56, 0x69, 0x73, 0x69, 0x62,
0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x1a, 0x0a, 0x16, 0x56, 0x49, 0x53, 0x49, 0x42, 0x49, 0x4c,
0x49, 0x54, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,
0x00, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x49, 0x53, 0x49, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f,
0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x49, 0x53, 0x49,
0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x10, 0x02,
0x42, 0x84, 0x01, 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x63, 0x68, 0x77, 0x61, 0x72, 0x7a,
0x2e, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x69, 0x74, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76,
0x31, 0x42, 0x12, 0x52, 0x6f, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74,
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x0f, 0x2e, 0x2f, 0x61, 0x75, 0x64, 0x69, 0x74,
0x3b, 0x61, 0x75, 0x64, 0x69, 0x74, 0x56, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02,
0x08, 0x41, 0x75, 0x64, 0x69, 0x74, 0x2e, 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,
0x12, 0x61, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x42, 0x42, 0xba, 0x48, 0x3f, 0xc8, 0x01, 0x01, 0x72, 0x3a, 0x32, 0x38,
0x5e, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x69, 0x74, 0x5c, 0x2e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d,
0x39, 0x5d, 0x2b, 0x5c, 0x2e, 0x76, 0x5b, 0x31, 0x2d, 0x39, 0x5d, 0x5b, 0x30, 0x2d, 0x39, 0x5d,
0x2a, 0x5c, 0x2e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2b, 0x5c, 0x2e, 0x5b, 0x61,
0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2b, 0x24, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4e,
0x61, 0x6d, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x76, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74,
0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e,
0x76, 0x31, 0x2e, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x42, 0x0b, 0xba,
0x48, 0x08, 0xc8, 0x01, 0x01, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x76, 0x69, 0x73, 0x69,
0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x37, 0x0a, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, 0x75,
0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d,
0x65, 0x48, 0x00, 0x52, 0x0a, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12,
0x49, 0x0a, 0x11, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x66, 0x69, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x75, 0x64,
0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x10, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x46, 0x0a, 0x10, 0x75, 0x6e,
0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e,
0x55, 0x6e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x48,
0x01, 0x52, 0x0f, 0x75, 0x6e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61,
0x74, 0x61, 0x12, 0x40, 0x0a, 0x0e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f,
0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x75, 0x64,
0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44,
0x61, 0x74, 0x61, 0x48, 0x01, 0x52, 0x0d, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64,
0x44, 0x61, 0x74, 0x61, 0x42, 0x1b, 0x0a, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08,
0x01, 0x42, 0x0d, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01,
0x2a, 0x57, 0x0a, 0x0a, 0x56, 0x69, 0x73, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x1a,
0x0a, 0x16, 0x56, 0x49, 0x53, 0x49, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x55, 0x4e, 0x53,
0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x56, 0x49,
0x53, 0x49, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10,
0x01, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x49, 0x53, 0x49, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f,
0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x10, 0x02, 0x42, 0x84, 0x01, 0x0a, 0x1c, 0x63, 0x6f,
0x6d, 0x2e, 0x73, 0x63, 0x68, 0x77, 0x61, 0x72, 0x7a, 0x2e, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x69,
0x74, 0x2e, 0x61, 0x75, 0x64, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x42, 0x12, 0x52, 0x6f, 0x75, 0x74,
0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01,
0x5a, 0x0f, 0x2e, 0x2f, 0x61, 0x75, 0x64, 0x69, 0x74, 0x3b, 0x61, 0x75, 0x64, 0x69, 0x74, 0x56,
0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x08, 0x41, 0x75, 0x64, 0x69, 0x74, 0x2e,
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 (

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/struct.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";
import "audit/v1/common.proto";
@ -13,66 +14,123 @@ option go_package = "./audit;auditV1";
option java_multiple_files = true;
option java_package = "com.schwarz.stackit.audit.v1";
// TODO update numbers of elements in messages
// TODO decide which fields should be optional
// TODO max attribute length
// 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.
message AuditLogEntry {
// 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.
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.
// Is generated and set by the SDK.
// Format:
// <timestamp>/<region>/<worker-id>/<sequence-number>
string insert_id = 4;
// Is used to check completeness of audit events over time.
//
// Format: <unix-timestamp>/<region-zone>/<worker-id>/<sequence-number>
// 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
// 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
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.
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.
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
//
// Example:
// `00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01`
string trace_parent = 22;
// Format: <version>-<trace-id>-<parent-id>-<trace-flags>
//
// 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
//
// Example:
// `rojo=00f067aa0ba902b7,congo=t61rcWkgMzE`.
string trace_state = 27;
// Format: <key1>=<value1>[,<keyN>=<valueN>]
//
// 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
// standard severity levels listed below. For your reference, the levels are
// 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
// standard severity levels listed below.
enum LogSeverity {
// (0) The log entry has no assigned severity level.
DEFAULT = 0;
UNSPECIFIED = 0;
// (1) The log entry has no assigned severity level.
// TODO check index
DEFAULT = 1;
// (100) Debug or trace information.
DEBUG = 100;
@ -103,87 +161,156 @@ enum LogSeverity {
// Common audit log format for STACKIT API operations.
message AuditLog {
// The name of the API service performing the operation. For example,
// `"resource-manager"`.
string service_name = 7;
// The name of the API service performing the operation.
//
// 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 format should is:
// stackit.<product>.<version>.<object>.<operation>
//
// For example,
// Format: stackit.<product>.<version>.<type>.<operation>
//
// "stackit.resourcemanager.v1.organization.created"
// "stackit.authorization.v2.organization.moved"
// "stackit.authorization.v2.folder.moved"
optional string method_name = 8;
// Examples:
// "stackit.resourcemanager.v1.organization.created"
// "stackit.authorization.v2.organization.moved"
// "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 name is a scheme-less URI, not including the API service name.
// For example:
//
// "projects/<id>/zones/<region-zone>/vms/<vm-id>"
// "projects/<id>/zones/<region-zone>/vms/<vm-id>/ports/<port-id>"
// "projects/<id>/zones/<region-zone>/instances/instance-20240723-184227
string resource_name = 11;
// Format: <type>/<id>[/locations/<region-zone>][/<details>]
// Where:
// Region-Zone: Optional region and zone id. If both, separated with a - (dash). Alternatively _ (underscore).
// 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.
AuthenticationInfo authentication_info = 3;
//
// Required: true
AuthenticationInfo authentication_info = 4 [
(buf.validate.field).required = true
];
// Authorization information. If there are multiple
// resources or permissions involved, then there is
// Authorization information. If there are multiple resources or permissions involved, then there is
// one AuthorizationInfo element for each {resource, permission} tuple.
repeated AuthorizationInfo authorization_info = 9;
//
// Required: false
repeated AuthorizationInfo authorization_info = 5;
// 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,
// such as those that are too large, privacy-sensitive, or duplicated
// elsewhere in the log record.
// 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.
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,
// 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,
// such as those that are too large, privacy-sensitive, or duplicated
// 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
// 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.
message AuthenticationInfo {
// Principal id
string principal_id = 1;
// STACKIT principal id
//
// 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
string principal_email = 2;
// The email address of the authenticated user.
// 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
// credentials for authenticating the service account making the request.
// Example:
//
// "projects/{PROJECT_ID}/serviceAccounts/{ACCOUNT}"
optional string service_account_name = 5;
// Format: projects/<id>/serviceAccounts/<account>
//
// 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
// the request. It contains information on the real authorities that try to
// access STACKIT resources by delegating on a service account. When multiple
// authorities present, they are guaranteed to be sorted based on the original
// 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.
@ -191,37 +318,44 @@ message AuthorizationInfo {
// The resource being accessed, as a REST-style string.
//
// For example:
// Project scoped resource: projects/test-project-123/zones/us-central1-b/instances/instance-20240723-174217
// Global Resource: projects/_/buckets/adfeaf
string resource = 1;
// Format: <type>/<id>[/locations/<region-zone>][/<details>]
// Where:
// Region-Zone: Optional region and zone id. If both, separated with a - (dash). Alternatively _ (underscore).
// 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.
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`
// was granted.
bool granted = 3;
//
// Required: false
optional bool granted = 3;
}
// TODO check description
// This message defines the standard attribute vocabulary for STACKIT APIs.
//
// 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
// 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.
// service.
message AttributeContext {
// This message defines request authentication attributes. Terminology is
@ -229,55 +363,57 @@ message AttributeContext {
// correlate to concepts in other standards.
message Auth {
// TODO check description
// The authenticated principal. Reflects the issuer (`iss`) and subject
// (`sub`) claims within a JWT. The issuer and subject should be `/`
// delimited, with `/` percent-encoded within the subject fragment. For
// Google accounts, the principal format is:
// "https://accounts.google.com/{id}"
string principal = 1;
// The authenticated principal. Reflects the issuer ("iss") and subject
// ("sub") claims within a JWT.
//
// Format: <sub-claim>/<iss-claim>
// Where:
// Sub-Claim: Sub-Claim from JWT with `/` percent-encoded (url-encoded)
//
// 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 audience (`aud`) claim within a JWT. The audience
// value(s) depends on the `issuer`, but typically include one or more of
// the following pieces of information:
// the audience ("aud") claim within a JWT, typically the services intended
// to receive the credential.
//
// * The services intended to receive the credential such as
// ["pubsub.googleapis.com", "storage.googleapis.com"]
// * 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.
// Examples:
// ["https://stackit-resource-manager-dev.apps.01.cf.eu01.stackit.cloud", "stackit", "api"]
//
// Consult the documentation for the credential issuer to determine the
// information provided.
// Required: false
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
// `{key: value}` pairs for standard and private claims. The following
// is a subset of the standard required and optional claims that would
// typically be presented for a Google-based JWT:
// {"key": <value>} pairs for standard and private claims.
//
// {'iss': 'accounts.google.com',
// 'sub': '113289723416554971153',
// 'aud': ['123456789012', 'pubsub.googleapis.com'],
// 'azp': '123456789012.apps.googleusercontent.com',
// 'email': 'jsmith@example.com',
// 'iat': 1353601026,
// 'exp': 1353604926}
// The following is a subset of the standard required and optional claims that would
// typically be presented for a STACKIT JWT:
//
// SAML assertions are similarly specified, but with an identity provider
// dependent structure.
google.protobuf.Struct claims = 4;
// {
// "aud": "https://stackit-resource-manager-dev.apps.01.cf.eu01.stackit.cloud",
// "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
@ -288,62 +424,126 @@ message AttributeContext {
// The unique ID for a request, which can be propagated to downstream
// systems. The ID should have low probability of collision
// 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`.
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
// must be merged according to the HTTP spec. All header keys must be
// 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.
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.
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`.
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.
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 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",
// "spdy/3", "h2", "h2c", "webrtc", "tcp", "udp", "quic". See
// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
// 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.
// Derived from the HTTP request `Authorization` header or equivalent.
Auth auth = 13;
// The request authentication.
//
// Required: true
Auth auth = 10 [
(buf.validate.field).required = true
];
}
// This message defines attributes for a typical network response. It
// generally models semantics of an HTTP response.
// TODO do we need another status code attribute in the Response?
message Response {
// The HTTP response status code, such as `200` and `404`.
int64 code = 1;
// The HTTP response size in bytes. If unknown, it must be -1.
int64 size = 2;
// The HTTP response size in bytes.
//
// Required: false
optional google.protobuf.Int64Value size = 1 [
(buf.validate.field).int64.gte = 0
];
// The HTTP response headers. If multiple headers share the same key, they
// must be merged according to HTTP spec. All header keys must be
// 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.
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.
// For caller from internet, this will be public IPv4 or IPv6 address.
// For caller from a VM / K8s Service / etc, this
// will be the SIT proxy's IPv4 address.
string caller_ip = 1;
// For caller from a VM / K8s Service / etc, this will be the SIT proxy's IPv4 address.
//
// 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.
// This information is not authenticated and should be treated accordingly.
// For example:
//
// + `google-api-python-client/1.4.0`:
// The request was made by the Google API client for Python.
// + `Cloud SDK Command Line Tool apitools-client/1.0 gcloud/0.9.62`:
// The request was made by the Google Cloud SDK CLI (gcloud).
// + `AppEngine-Google; (+http://code.google.com/appengine; appid:
// s~my-project`:
// The request was made from the `my-project` App Engine app.
string caller_supplied_user_agent = 2;
// Examples:
// "OpenAPI-Generator/1.0.0/go"
// -> The request was made by the STACKIT SDK GO client or STACKIT CLI
// "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
// -> The request was made by a web browser.
//
// Required: true
string caller_supplied_user_agent = 2 [
(buf.validate.field).required = true,
(buf.validate.field).string.min_len = 1
];
// TODO check description
// Request attributes used in IAM condition evaluation. This field contains
// request attributes like request time and access levels associated with
// the request.
// This field contains request attributes like request url, time, etc.
//
// To get the whole view of the attributes used in IAM
// condition evaluation, the user must also look into
// `AuditLog.authentication_info.resource_attributes`.
AttributeContext.Request request_attributes = 7;
// Required: true
AttributeContext.Request request_attributes = 3 [
(buf.validate.field).required = true
];
}
// The `Status` type defines a logical error model that is suitable for
@ -388,12 +589,25 @@ message RequestMetadata {
message ResponseStatus {
// 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
string message = 2;
//
// Required: false
optional string message = 2;
// Error details
//
// Required: false
repeated google.protobuf.Struct details = 3;
}
@ -404,25 +618,42 @@ message ServiceAccountDelegationInfo {
message SystemPrincipal {
// 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.
message IdpPrincipal {
// 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
optional string principal_email = 2;
// The email address of the authenticated user.
// 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.
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
// identity for authentication.
oneof Authority {
option (buf.validate.oneof).required = true;
// System identity
SystemPrincipal system_principal = 1;

View file

@ -44,7 +44,10 @@ message RoutableAuditEvent {
// Functional event name with pattern <TYPE>_<ACTION>, e.g. ORGANIZATION_CREATED
// 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 visibility = 2 [(buf.validate.field).required = true, (buf.validate.field).enum.defined_only = true];