audit-go/audit/api/builder_test.go
2024-10-30 15:41:48 +01:00

1142 lines
48 KiB
Go

package api
import (
"context"
"dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/audit/utils"
auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1"
"fmt"
"github.com/bufbuild/protovalidate-go"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/wrapperspb"
"testing"
"time"
)
func Test_getObjectIdAndTypeFromAuditParams(t *testing.T) {
t.Run(
"object id empty", func(t *testing.T) {
objectId, objectType, err := getObjectIdAndTypeFromAuditParams(&AuditParameters{})
assert.EqualError(t, err, "object id missing")
assert.Equal(t, "", objectId)
assert.Nil(t, objectType)
},
)
t.Run(
"object type empty", func(t *testing.T) {
objectId, objectType, err := getObjectIdAndTypeFromAuditParams(&AuditParameters{ObjectId: "value"})
assert.EqualError(t, err, "object type missing")
assert.Equal(t, "", objectId)
assert.Nil(t, objectType)
},
)
t.Run(
"object id and invalid type set", func(t *testing.T) {
objectId, objectType, err := getObjectIdAndTypeFromAuditParams(
&AuditParameters{
ObjectId: "value",
ObjectType: ObjectTypeFromPluralString("invalid"),
},
)
assert.EqualError(t, err, "unknown object type")
assert.Equal(t, "", objectId)
assert.Nil(t, objectType)
},
)
t.Run(
"object id and type set", func(t *testing.T) {
objectId, objectType, err := getObjectIdAndTypeFromAuditParams(
&AuditParameters{
ObjectId: "value",
ObjectType: ObjectTypeProject,
},
)
assert.NoError(t, err)
assert.Equal(t, "value", objectId)
assert.Equal(t, ObjectTypeProject, *objectType)
},
)
}
func Test_AuditLogEntryBuilder(t *testing.T) {
t.Run("nothing set", func(t *testing.T) {
logEntry, err := NewAuditLogEntryBuilder().Build(context.Background(), SequenceNumber(1))
assert.Error(t, err)
assert.Equal(t, "object id missing", err.Error())
assert.Nil(t, logEntry)
})
t.Run("details missing", func(t *testing.T) {
logEntry, err := NewAuditLogEntryBuilder().WithRequiredLocation("eu01").
WithRequiredObjectId("1").
WithRequiredObjectType(ObjectTypeProject).
Build(context.Background(), SequenceNumber(1))
assert.NoError(t, err)
assert.NotNil(t, logEntry)
validator, err := protovalidate.New()
assert.NoError(t, err)
err = validator.Validate(logEntry)
assert.Error(t, err)
assert.Equal(t, "validation error:\n - proto_payload.service_name: value is required [required]\n - proto_payload.operation_name: value is required [required]\n - proto_payload.request_metadata.caller_supplied_user_agent: value is required [required]\n - proto_payload.request_metadata.request_attributes.method: value is required [required]\n - proto_payload.request_metadata.request_attributes.headers: value is required [required]\n - proto_payload.request_metadata.request_attributes.path: value is required [required]\n - proto_payload.request_metadata.request_attributes.host: value is required [required]\n - proto_payload.request_metadata.request_attributes.scheme: value is required [required]\n - proto_payload.request_metadata.request_attributes.protocol: value is required [required]\n - insert_id: value does not match regex pattern `^[0-9]+/[a-z0-9-]+/[a-z0-9-]+/[0-9]+$` [string.pattern]", err.Error())
})
t.Run("required only", func(t *testing.T) {
builder := NewAuditLogEntryBuilder().
WithRequiredLocation("eu01").
WithRequiredObjectId("1").
WithRequiredObjectType(ObjectTypeProject).
WithRequiredOperation("stackit.demo-service.v1.operation").
WithRequiredApiRequest(ApiRequest{
Body: nil,
Header: TestHeaders,
Host: "localhost",
Method: "POST",
Scheme: "https",
Proto: "HTTP/1.1",
URL: RequestUrl{
Path: "/",
RawQuery: nil,
},
}).
WithRequiredRequestClientIp("127.0.0.1").
WithRequiredServiceName("demo-service").
WithRequiredWorkerId("worker-id")
logEntry, err := builder.Build(context.Background(), SequenceNumber(1))
assert.NoError(t, err)
assert.NotNil(t, logEntry)
assert.Equal(t, "projects/1/logs/admin-activity", logEntry.LogName)
assert.Nil(t, logEntry.Labels)
assert.Equal(t, auditV1.LogSeverity_LOG_SEVERITY_DEFAULT, logEntry.Severity)
assert.NotNil(t, logEntry.Timestamp)
assert.Nil(t, logEntry.CorrelationId)
assert.Regexp(t, "[0-9]+/eu01/worker-id/1", logEntry.InsertId)
assert.NotNil(t, logEntry.ProtoPayload)
authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo
assert.NotNil(t, authenticationInfo)
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail)
assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", authenticationInfo.PrincipalId)
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
assert.Nil(t, authenticationInfo.ServiceAccountName)
assert.Nil(t, logEntry.ProtoPayload.AuthorizationInfo)
assert.Nil(t, logEntry.ProtoPayload.Metadata)
assert.Equal(t, "stackit.demo-service.v1.operation", logEntry.ProtoPayload.OperationName)
assert.Nil(t, logEntry.ProtoPayload.Request)
requestMetadata := logEntry.ProtoPayload.RequestMetadata
assert.NotNil(t, requestMetadata)
assert.Equal(t, "127.0.0.1", requestMetadata.CallerIp)
assert.Equal(t, "custom", requestMetadata.CallerSuppliedUserAgent)
requestAttributes := requestMetadata.RequestAttributes
assert.NotNil(t, requestAttributes)
assert.Equal(t, "/", requestAttributes.Path)
assert.NotNil(t, requestAttributes.Time)
assert.Equal(t, "localhost", requestAttributes.Host)
assert.Equal(t, auditV1.AttributeContext_HTTP_METHOD_POST, requestAttributes.Method)
assert.Nil(t, requestAttributes.Id)
assert.Equal(t, "https", requestAttributes.Scheme)
assert.Equal(t, map[string]string{"user-agent": "custom"}, requestAttributes.Headers)
assert.Nil(t, requestAttributes.Query)
assert.Equal(t, "HTTP/1.1", requestAttributes.Protocol)
requestAttributesAuth := requestAttributes.Auth
assert.NotNil(t, requestAttributesAuth)
assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63/https%3A%2F%2Faccounts.dev.stackit.cloud", requestAttributesAuth.Principal)
assert.Equal(t, []string{"stackit-portal-login-dev-client-id"}, requestAttributesAuth.Audiences)
assert.NotNil(t, requestAttributesAuth.Claims)
assert.Equal(t, "projects/1", logEntry.ProtoPayload.ResourceName)
assert.Nil(t, logEntry.ProtoPayload.Response)
responseMetadata := logEntry.ProtoPayload.ResponseMetadata
assert.NotNil(t, responseMetadata)
assert.Nil(t, responseMetadata.ErrorDetails)
assert.Nil(t, responseMetadata.ErrorMessage)
assert.Equal(t, wrapperspb.Int32(200), responseMetadata.StatusCode)
responseAttributes := responseMetadata.ResponseAttributes
assert.NotNil(t, responseAttributes)
assert.Nil(t, responseAttributes.Headers)
assert.Nil(t, responseAttributes.NumResponseItems)
assert.Nil(t, responseAttributes.Size)
assert.NotNil(t, responseAttributes.Time)
assert.Equal(t, "demo-service", logEntry.ProtoPayload.ServiceName)
validator, err := protovalidate.New()
assert.NoError(t, err)
err = validator.Validate(logEntry)
assert.NoError(t, err)
})
t.Run("with details", func(t *testing.T) {
details := map[string]interface{}{"key": "detail"}
permission := "project.edit"
permissionCheckResult := true
requestTime := time.Now().AddDate(0, 0, -2).UTC()
responseTime := time.Now().AddDate(0, 0, -1).UTC()
responseBody := map[string]interface{}{"key": "response"}
responseBodyBytes, err := ResponseBodyToBytes(responseBody)
assert.NoError(t, err)
builder := NewAuditLogEntryBuilder().
WithRequiredLocation("eu01").
WithRequiredObjectId("1").
WithRequiredObjectType(ObjectTypeProject).
WithRequiredOperation("stackit.demo-service.v1.operation").
WithRequiredApiRequest(ApiRequest{
Body: nil,
Header: TestHeaders,
Host: "localhost",
Method: "POST",
Scheme: "https",
Proto: "HTTP/1.1",
URL: RequestUrl{
Path: "/",
RawQuery: nil,
},
}).
WithRequiredRequestClientIp("127.0.0.1").
WithRequiredServiceName("demo-service").
WithRequiredWorkerId("worker-id").
WithAuditPermission(permission).
WithAuditPermissionCheckResult(permissionCheckResult).
WithDetails(details).
WithEventType(EventTypePolicyDenied).
WithLabels(map[string]string{"key": "label"}).
WithNumResponseItems(int64(10)).
WithRequestCorrelationId("correlationId").
WithRequestId("requestId").
WithRequestTime(requestTime).
WithResponseBodyBytes(responseBodyBytes).
WithResponseHeaders(map[string][]string{"key": {"header"}}).
WithResponseTime(responseTime).
WithSeverity(auditV1.LogSeverity_LOG_SEVERITY_ERROR).
WithStatusCode(400)
logEntry, err := builder.Build(context.Background(), SequenceNumber(1))
assert.NoError(t, err)
assert.NotNil(t, logEntry)
assert.Equal(t, "projects/1/logs/policy-denied", logEntry.LogName)
assert.Equal(t, map[string]string{"key": "label"}, logEntry.Labels)
assert.Equal(t, auditV1.LogSeverity_LOG_SEVERITY_ERROR, logEntry.Severity)
assert.NotNil(t, logEntry.Timestamp)
assert.Equal(t, "correlationId", *logEntry.CorrelationId)
assert.Regexp(t, "[0-9]+/eu01/worker-id/1", logEntry.InsertId)
assert.NotNil(t, logEntry.ProtoPayload)
authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo
assert.NotNil(t, authenticationInfo)
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail)
assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", authenticationInfo.PrincipalId)
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
assert.Nil(t, authenticationInfo.ServiceAccountName)
assert.Equal(t, []*auditV1.AuthorizationInfo{{
Resource: "projects/1",
Permission: &permission,
Granted: &permissionCheckResult,
}}, logEntry.ProtoPayload.AuthorizationInfo)
expectedMetadata, _ := structpb.NewStruct(details)
assert.Equal(t, expectedMetadata, logEntry.ProtoPayload.Metadata)
assert.Equal(t, "stackit.demo-service.v1.operation", logEntry.ProtoPayload.OperationName)
assert.Nil(t, logEntry.ProtoPayload.Request)
requestMetadata := logEntry.ProtoPayload.RequestMetadata
assert.NotNil(t, requestMetadata)
assert.Equal(t, "127.0.0.1", requestMetadata.CallerIp)
assert.Equal(t, "custom", requestMetadata.CallerSuppliedUserAgent)
requestAttributes := requestMetadata.RequestAttributes
assert.NotNil(t, requestAttributes)
assert.Equal(t, "/", requestAttributes.Path)
assert.NotNil(t, requestAttributes.Time)
assert.Equal(t, "localhost", requestAttributes.Host)
assert.Equal(t, auditV1.AttributeContext_HTTP_METHOD_POST, requestAttributes.Method)
assert.Equal(t, "requestId", *requestAttributes.Id)
assert.Equal(t, "https", requestAttributes.Scheme)
assert.Equal(t, map[string]string{"user-agent": "custom"}, requestAttributes.Headers)
assert.Nil(t, requestAttributes.Query)
assert.Equal(t, "HTTP/1.1", requestAttributes.Protocol)
requestAttributesAuth := requestAttributes.Auth
assert.NotNil(t, requestAttributesAuth)
assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63/https%3A%2F%2Faccounts.dev.stackit.cloud", requestAttributesAuth.Principal)
assert.Equal(t, []string{"stackit-portal-login-dev-client-id"}, requestAttributesAuth.Audiences)
assert.NotNil(t, requestAttributesAuth.Claims)
expectedResponse, _ := structpb.NewStruct(responseBody)
assert.Equal(t, "projects/1", logEntry.ProtoPayload.ResourceName)
assert.Equal(t, expectedResponse, logEntry.ProtoPayload.Response)
responseMetadata := logEntry.ProtoPayload.ResponseMetadata
assert.NotNil(t, responseMetadata)
assert.Nil(t, responseMetadata.ErrorDetails)
assert.Equal(t, "Client error", *responseMetadata.ErrorMessage)
assert.Equal(t, wrapperspb.Int32(400), responseMetadata.StatusCode)
responseAttributes := responseMetadata.ResponseAttributes
assert.NotNil(t, responseAttributes)
assert.Equal(t, map[string]string{"key": "header"}, responseAttributes.Headers)
assert.Equal(t, wrapperspb.Int64(10), responseAttributes.NumResponseItems)
assert.Equal(t, wrapperspb.Int64(18), responseAttributes.Size)
assert.NotNil(t, responseAttributes.Time)
assert.Equal(t, "demo-service", logEntry.ProtoPayload.ServiceName)
validator, err := protovalidate.New()
assert.NoError(t, err)
err = validator.Validate(logEntry)
assert.NoError(t, err)
})
t.Run("system event", func(t *testing.T) {
builder := NewAuditLogEntryBuilder().
WithRequiredLocation("eu01").
WithRequiredObjectId("1").
WithRequiredObjectType(ObjectTypeProject).
WithRequiredOperation("stackit.demo-service.v1.operation").
WithRequiredServiceName("demo-service").
WithRequiredWorkerId("worker-id").
AsSystemEvent()
logEntry, err := builder.Build(context.Background(), SequenceNumber(1))
assert.NoError(t, err)
assert.NotNil(t, logEntry)
assert.Equal(t, fmt.Sprintf("system/%s/logs/system-event", uuid.Nil.String()), logEntry.LogName)
assert.Nil(t, logEntry.Labels)
assert.Equal(t, auditV1.LogSeverity_LOG_SEVERITY_DEFAULT, logEntry.Severity)
assert.NotNil(t, logEntry.Timestamp)
assert.Nil(t, logEntry.CorrelationId)
assert.Regexp(t, "[0-9]+/eu01/worker-id/1", logEntry.InsertId)
assert.NotNil(t, logEntry.ProtoPayload)
authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo
assert.NotNil(t, authenticationInfo)
assert.Equal(t, "do-not-reply@stackit.cloud", authenticationInfo.PrincipalEmail)
assert.Equal(t, "none", authenticationInfo.PrincipalId)
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
assert.Nil(t, authenticationInfo.ServiceAccountName)
assert.Nil(t, logEntry.ProtoPayload.AuthorizationInfo)
assert.Nil(t, logEntry.ProtoPayload.Metadata)
assert.Equal(t, "stackit.demo-service.v1.operation", logEntry.ProtoPayload.OperationName)
assert.Nil(t, logEntry.ProtoPayload.Request)
requestMetadata := logEntry.ProtoPayload.RequestMetadata
assert.NotNil(t, requestMetadata)
assert.Equal(t, "0.0.0.0", requestMetadata.CallerIp)
assert.Equal(t, "none", requestMetadata.CallerSuppliedUserAgent)
requestAttributes := requestMetadata.RequestAttributes
assert.NotNil(t, requestAttributes)
assert.Equal(t, "none", requestAttributes.Path)
assert.NotNil(t, requestAttributes.Time)
assert.Equal(t, "0.0.0.0", requestAttributes.Host)
assert.Equal(t, auditV1.AttributeContext_HTTP_METHOD_OTHER, requestAttributes.Method)
assert.Nil(t, requestAttributes.Id)
assert.Equal(t, "none", requestAttributes.Scheme)
assert.Equal(t, map[string]string{"user-agent": "none"}, requestAttributes.Headers)
assert.Nil(t, requestAttributes.Query)
assert.Equal(t, "none", requestAttributes.Protocol)
requestAttributesAuth := requestAttributes.Auth
assert.NotNil(t, requestAttributesAuth)
assert.Equal(t, "none/none", requestAttributesAuth.Principal)
assert.Equal(t, []string{}, requestAttributesAuth.Audiences)
assert.NotNil(t, requestAttributesAuth.Claims)
assert.Equal(t, map[string]any{}, requestAttributesAuth.Claims.AsMap())
assert.Equal(t, "projects/1", logEntry.ProtoPayload.ResourceName)
assert.Nil(t, logEntry.ProtoPayload.Response)
responseMetadata := logEntry.ProtoPayload.ResponseMetadata
assert.NotNil(t, responseMetadata)
assert.Nil(t, responseMetadata.ErrorDetails)
assert.Nil(t, responseMetadata.ErrorMessage)
assert.Equal(t, wrapperspb.Int32(200), responseMetadata.StatusCode)
responseAttributes := responseMetadata.ResponseAttributes
assert.NotNil(t, responseAttributes)
assert.Nil(t, responseAttributes.Headers)
assert.Nil(t, responseAttributes.NumResponseItems)
assert.Nil(t, responseAttributes.Size)
assert.NotNil(t, responseAttributes.Time)
assert.Equal(t, "demo-service", logEntry.ProtoPayload.ServiceName)
validator, err := protovalidate.New()
assert.NoError(t, err)
err = validator.Validate(logEntry)
assert.NoError(t, err)
})
t.Run("with response body unserialized", func(t *testing.T) {
details := map[string]interface{}{"key": "detail"}
permission := "project.edit"
permissionCheckResult := true
requestTime := time.Now().AddDate(0, 0, -2).UTC()
responseTime := time.Now().AddDate(0, 0, -1).UTC()
responseBody := map[string]interface{}{"key": "response"}
builder := NewAuditLogEntryBuilder().
WithRequiredLocation("eu01").
WithRequiredObjectId("1").
WithRequiredObjectType(ObjectTypeProject).
WithRequiredOperation("stackit.demo-service.v1.operation").
WithRequiredApiRequest(ApiRequest{
Body: nil,
Header: TestHeaders,
Host: "localhost",
Method: "POST",
Scheme: "https",
Proto: "HTTP/1.1",
URL: RequestUrl{
Path: "/",
RawQuery: nil,
},
}).
WithRequiredRequestClientIp("127.0.0.1").
WithRequiredServiceName("demo-service").
WithRequiredWorkerId("worker-id").
WithAuditPermission(permission).
WithAuditPermissionCheckResult(permissionCheckResult).
WithDetails(details).
WithEventType(EventTypeSystemEvent).
WithLabels(map[string]string{"key": "label"}).
WithNumResponseItems(int64(10)).
WithRequestCorrelationId("correlationId").
WithRequestId("requestId").
WithRequestTime(requestTime).
WithResponseBody(responseBody).
WithResponseHeaders(map[string][]string{"key": {"header"}}).
WithResponseTime(responseTime).
WithSeverity(auditV1.LogSeverity_LOG_SEVERITY_ERROR).
WithStatusCode(400)
logEntry, err := builder.Build(context.Background(), SequenceNumber(1))
assert.NoError(t, err)
assert.NotNil(t, logEntry)
assert.NotNil(t, logEntry.ProtoPayload)
expectedResponse, _ := structpb.NewStruct(responseBody)
assert.Equal(t, "projects/1", logEntry.ProtoPayload.ResourceName)
assert.Equal(t, expectedResponse, logEntry.ProtoPayload.Response)
validator, err := protovalidate.New()
assert.NoError(t, err)
err = validator.Validate(logEntry)
assert.NoError(t, err)
})
t.Run("with response body and response body bytes set", func(t *testing.T) {
responseBody := map[string]interface{}{"key": "response"}
responseBodyBytes, err := ResponseBodyToBytes(responseBody)
assert.NoError(t, err)
builder := NewAuditLogEntryBuilder().
WithRequiredApiRequest(ApiRequest{
Body: nil,
Header: TestHeaders,
Host: "localhost",
Method: "POST",
Scheme: "https",
Proto: "HTTP/1.1",
URL: RequestUrl{
Path: "/",
RawQuery: nil,
},
}).
WithRequiredLocation("eu01").
WithRequiredObjectId("1").
WithRequiredObjectType(ObjectTypeProject).
WithRequiredOperation("stackit.demo-service.v1.operation").
WithRequiredRequestClientIp("127.0.0.1").
WithRequiredServiceName("demo-service").
WithRequiredWorkerId("worker-id").
WithResponseBody(responseBody).
WithResponseBodyBytes(responseBodyBytes)
logEntry, err := builder.Build(context.Background(), SequenceNumber(1))
assert.EqualError(t, err, "responseBodyBytes and responseBody set")
assert.Nil(t, logEntry)
})
t.Run("with invalid response body", func(t *testing.T) {
builder := NewAuditLogEntryBuilder().
WithRequiredApiRequest(ApiRequest{
Body: nil,
Header: TestHeaders,
Host: "localhost",
Method: "POST",
Scheme: "https",
Proto: "HTTP/1.1",
URL: RequestUrl{
Path: "/",
RawQuery: nil,
},
}).
WithRequiredLocation("eu01").
WithRequiredObjectId("1").
WithRequiredObjectType(ObjectTypeProject).
WithRequiredOperation("stackit.demo-service.v1.operation").
WithRequiredRequestClientIp("127.0.0.1").
WithRequiredServiceName("demo-service").
WithRequiredWorkerId("worker-id").
WithResponseBody("invalid")
logEntry, err := builder.Build(context.Background(), SequenceNumber(1))
assert.EqualError(t, err, "json: cannot unmarshal string into Go value of type map[string]interface {}\ninvalid response")
assert.Nil(t, logEntry)
})
}
func Test_AuditEventBuilder(t *testing.T) {
t.Run("nothing set", func(t *testing.T) {
api, _ := NewMockAuditApi()
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
cloudEvent, routingIdentifier, err := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01").
Build(context.Background(), SequenceNumber(1))
assert.Error(t, err)
assert.Equal(t, "object id missing", err.Error())
assert.Nil(t, cloudEvent)
assert.Nil(t, routingIdentifier)
})
t.Run("details missing", func(t *testing.T) {
api, _ := NewMockAuditApi()
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
cloudEvent, routingIdentifier, err := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01").
WithRequiredObjectId("objectId").
WithRequiredObjectType(ObjectTypeProject).
Build(context.Background(), SequenceNumber(1))
assert.Error(t, err)
assert.Equal(t, "validation error:\n - log_name: value does not match regex pattern `^[a-z-]+/[a-z0-9-]+/logs/(?:admin-activity|system-event|policy-denied|data-access)$` [string.pattern]\n - proto_payload.operation_name: value is required [required]\n - proto_payload.resource_name: value does not match regex pattern `^[a-z]+/[a-z0-9-]+(?:/[a-z0-9-]+/[a-z0-9-_]+)*$` [string.pattern]\n - proto_payload.request_metadata.caller_supplied_user_agent: value is required [required]\n - proto_payload.request_metadata.request_attributes.method: value is required [required]\n - proto_payload.request_metadata.request_attributes.headers: value is required [required]\n - proto_payload.request_metadata.request_attributes.path: value is required [required]\n - proto_payload.request_metadata.request_attributes.host: value is required [required]\n - proto_payload.request_metadata.request_attributes.scheme: value is required [required]\n - proto_payload.request_metadata.request_attributes.protocol: value is required [required]", err.Error())
assert.Nil(t, cloudEvent)
assert.Nil(t, routingIdentifier)
})
t.Run("required only", func(t *testing.T) {
api, _ := NewMockAuditApi()
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
objectId := uuid.NewString()
operation := "stackit.demo-service.v1.operation"
builder := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01").
WithRequiredObjectId(objectId).
WithRequiredObjectType(ObjectTypeProject).
WithRequiredOperation(operation).
WithRequiredApiRequest(ApiRequest{
Body: nil,
Header: TestHeaders,
Host: "localhost",
Method: "POST",
Scheme: "https",
Proto: "HTTP/1.1",
URL: RequestUrl{
Path: "/",
RawQuery: nil,
},
}).
WithRequiredRequestClientIp("127.0.0.1")
routableIdentifier := RoutableIdentifier{Identifier: objectId, Type: ObjectTypeProject}
cloudEvent, routingIdentifier, err := builder.Build(context.Background(), SequenceNumber(1))
assert.NoError(t, err)
assert.True(t, builder.IsBuilt())
assert.Equal(t, &routableIdentifier, routingIdentifier)
assert.NotNil(t, cloudEvent)
assert.Equal(t, "application/cloudevents+protobuf", cloudEvent.DataContentType)
assert.Equal(t, "audit.v1.RoutableAuditEvent", cloudEvent.DataType)
assert.Regexp(t, "[0-9]+/eu01/worker-id/1", cloudEvent.Id)
assert.Equal(t, "demo-service", cloudEvent.Source)
assert.Equal(t, "1.0", cloudEvent.SpecVersion)
assert.Equal(t, fmt.Sprintf("projects/%s", objectId), cloudEvent.Subject)
assert.NotNil(t, cloudEvent.Time)
assert.Equal(t, "", *cloudEvent.TraceParent)
assert.Equal(t, "", *cloudEvent.TraceState)
var routableAuditEvent auditV1.RoutableAuditEvent
assert.NotNil(t, cloudEvent.Data)
assert.NoError(t, proto.Unmarshal(cloudEvent.Data, &routableAuditEvent))
assert.Equal(t, routableIdentifier.ToObjectIdentifier().Identifier, routableAuditEvent.ObjectIdentifier.Identifier)
assert.Equal(t, routableIdentifier.ToObjectIdentifier().Type, routableAuditEvent.ObjectIdentifier.Type)
assert.Equal(t, auditV1.Visibility_VISIBILITY_PUBLIC, routableAuditEvent.Visibility)
assert.Equal(t, operation, routableAuditEvent.OperationName)
var logEntry auditV1.AuditLogEntry
assert.NotNil(t, routableAuditEvent.GetUnencryptedData().Data)
assert.NoError(t, proto.Unmarshal(routableAuditEvent.GetUnencryptedData().Data, &logEntry))
assert.Equal(t, fmt.Sprintf("projects/%s/logs/admin-activity", objectId), logEntry.LogName)
assert.Nil(t, logEntry.Labels)
assert.Equal(t, auditV1.LogSeverity_LOG_SEVERITY_DEFAULT, logEntry.Severity)
assert.NotNil(t, logEntry.Timestamp)
assert.Nil(t, logEntry.CorrelationId)
assert.Regexp(t, "[0-9]+/eu01/worker-id/1", logEntry.InsertId)
assert.NotNil(t, logEntry.ProtoPayload)
authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo
assert.NotNil(t, authenticationInfo)
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail)
assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", authenticationInfo.PrincipalId)
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
assert.Nil(t, authenticationInfo.ServiceAccountName)
assert.Nil(t, logEntry.ProtoPayload.AuthorizationInfo)
assert.Nil(t, logEntry.ProtoPayload.Metadata)
assert.Equal(t, operation, logEntry.ProtoPayload.OperationName)
assert.Nil(t, logEntry.ProtoPayload.Request)
requestMetadata := logEntry.ProtoPayload.RequestMetadata
assert.NotNil(t, requestMetadata)
assert.Equal(t, "127.0.0.1", requestMetadata.CallerIp)
assert.Equal(t, "custom", requestMetadata.CallerSuppliedUserAgent)
requestAttributes := requestMetadata.RequestAttributes
assert.NotNil(t, requestAttributes)
assert.Equal(t, "/", requestAttributes.Path)
assert.NotNil(t, requestAttributes.Time)
assert.Equal(t, "localhost", requestAttributes.Host)
assert.Equal(t, auditV1.AttributeContext_HTTP_METHOD_POST, requestAttributes.Method)
assert.Nil(t, requestAttributes.Id)
assert.Equal(t, "https", requestAttributes.Scheme)
assert.Equal(t, map[string]string{"user-agent": "custom"}, requestAttributes.Headers)
assert.Nil(t, requestAttributes.Query)
assert.Equal(t, "HTTP/1.1", requestAttributes.Protocol)
requestAttributesAuth := requestAttributes.Auth
assert.NotNil(t, requestAttributesAuth)
assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63/https%3A%2F%2Faccounts.dev.stackit.cloud", requestAttributesAuth.Principal)
assert.Equal(t, []string{"stackit-portal-login-dev-client-id"}, requestAttributesAuth.Audiences)
assert.NotNil(t, requestAttributesAuth.Claims)
assert.Equal(t, fmt.Sprintf("projects/%s", objectId), logEntry.ProtoPayload.ResourceName)
assert.Nil(t, logEntry.ProtoPayload.Response)
responseMetadata := logEntry.ProtoPayload.ResponseMetadata
assert.NotNil(t, responseMetadata)
assert.Nil(t, responseMetadata.ErrorDetails)
assert.Nil(t, responseMetadata.ErrorMessage)
assert.Equal(t, wrapperspb.Int32(200), responseMetadata.StatusCode)
responseAttributes := responseMetadata.ResponseAttributes
assert.NotNil(t, responseAttributes)
assert.Nil(t, responseAttributes.Headers)
assert.Nil(t, responseAttributes.NumResponseItems)
assert.Nil(t, responseAttributes.Size)
assert.NotNil(t, responseAttributes.Time)
assert.Equal(t, "demo-service", logEntry.ProtoPayload.ServiceName)
validator, err := protovalidate.New()
assert.NoError(t, err)
err = validator.Validate(&logEntry)
assert.NoError(t, err)
})
t.Run("with details", func(t *testing.T) {
api, _ := NewMockAuditApi()
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
objectId := uuid.NewString()
operation := "stackit.demo-service.v1.operation"
details := map[string]interface{}{"key": "detail"}
permission := "project.edit"
permissionCheckResult := true
requestTime := time.Now().AddDate(0, 0, -2).UTC()
responseTime := time.Now().AddDate(0, 0, -1).UTC()
responseBody := map[string]interface{}{"key": "response"}
responseBodyBytes, err := ResponseBodyToBytes(responseBody)
assert.NoError(t, err)
builder := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01").
WithRequiredObjectId(objectId).
WithRequiredObjectType(ObjectTypeProject).
WithRequiredOperation(operation).
WithRequiredApiRequest(ApiRequest{
Body: nil,
Header: TestHeaders,
Host: "localhost",
Method: "POST",
Scheme: "https",
Proto: "HTTP/1.1",
URL: RequestUrl{
Path: "/",
RawQuery: nil,
},
}).
WithRequiredRequestClientIp("127.0.0.1").
WithAuditPermission(permission).
WithAuditPermissionCheckResult(permissionCheckResult).
WithDetails(details).
WithEventType(EventTypeAdminActivity).
WithLabels(map[string]string{"key": "label"}).
WithNumResponseItems(int64(10)).
WithRequestCorrelationId("correlationId").
WithRequestId("requestId").
WithRequestTime(requestTime).
WithResponseBodyBytes(responseBodyBytes).
WithResponseHeaders(map[string][]string{"key": {"header"}}).
WithResponseTime(responseTime).
WithSeverity(auditV1.LogSeverity_LOG_SEVERITY_ERROR).
WithStatusCode(400).
WithVisibility(auditV1.Visibility_VISIBILITY_PRIVATE)
routableIdentifier := RoutableIdentifier{Identifier: objectId, Type: ObjectTypeProject}
cloudEvent, routingIdentifier, err := builder.Build(context.Background(), SequenceNumber(1))
assert.NoError(t, err)
assert.True(t, builder.IsBuilt())
assert.Equal(t, &routableIdentifier, routingIdentifier)
assert.NotNil(t, cloudEvent)
assert.Equal(t, "application/cloudevents+protobuf", cloudEvent.DataContentType)
assert.Equal(t, "audit.v1.RoutableAuditEvent", cloudEvent.DataType)
assert.Regexp(t, "[0-9]+/eu01/worker-id/1", cloudEvent.Id)
assert.Equal(t, "demo-service", cloudEvent.Source)
assert.Equal(t, "1.0", cloudEvent.SpecVersion)
assert.Equal(t, fmt.Sprintf("projects/%s", objectId), cloudEvent.Subject)
assert.NotNil(t, cloudEvent.Time)
assert.Equal(t, "", *cloudEvent.TraceParent)
assert.Equal(t, "", *cloudEvent.TraceState)
var routableAuditEvent auditV1.RoutableAuditEvent
assert.NotNil(t, cloudEvent.Data)
assert.NoError(t, proto.Unmarshal(cloudEvent.Data, &routableAuditEvent))
assert.Equal(t, routableIdentifier.ToObjectIdentifier().Identifier, routableAuditEvent.ObjectIdentifier.Identifier)
assert.Equal(t, routableIdentifier.ToObjectIdentifier().Type, routableAuditEvent.ObjectIdentifier.Type)
assert.Equal(t, auditV1.Visibility_VISIBILITY_PRIVATE, routableAuditEvent.Visibility)
assert.Equal(t, operation, routableAuditEvent.OperationName)
var logEntry auditV1.AuditLogEntry
assert.NotNil(t, routableAuditEvent.GetUnencryptedData().Data)
assert.NoError(t, proto.Unmarshal(routableAuditEvent.GetUnencryptedData().Data, &logEntry))
assert.Equal(t, fmt.Sprintf("projects/%s/logs/admin-activity", objectId), logEntry.LogName)
assert.Equal(t, map[string]string{"key": "label"}, logEntry.Labels)
assert.Equal(t, auditV1.LogSeverity_LOG_SEVERITY_ERROR, logEntry.Severity)
assert.NotNil(t, logEntry.Timestamp)
assert.Equal(t, "correlationId", *logEntry.CorrelationId)
assert.Regexp(t, "[0-9]+/eu01/worker-id/1", logEntry.InsertId)
assert.NotNil(t, logEntry.ProtoPayload)
authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo
assert.NotNil(t, authenticationInfo)
assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail)
assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", authenticationInfo.PrincipalId)
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
assert.Nil(t, authenticationInfo.ServiceAccountName)
assert.Equal(t, []*auditV1.AuthorizationInfo{{
Resource: fmt.Sprintf("projects/%s", objectId),
Permission: &permission,
Granted: &permissionCheckResult,
}}, logEntry.ProtoPayload.AuthorizationInfo)
expectedMetadata, _ := structpb.NewStruct(details)
assert.Equal(t, expectedMetadata, logEntry.ProtoPayload.Metadata)
assert.Equal(t, "stackit.demo-service.v1.operation", logEntry.ProtoPayload.OperationName)
assert.Nil(t, logEntry.ProtoPayload.Request)
requestMetadata := logEntry.ProtoPayload.RequestMetadata
assert.NotNil(t, requestMetadata)
assert.Equal(t, "127.0.0.1", requestMetadata.CallerIp)
assert.Equal(t, "custom", requestMetadata.CallerSuppliedUserAgent)
requestAttributes := requestMetadata.RequestAttributes
assert.NotNil(t, requestAttributes)
assert.Equal(t, "/", requestAttributes.Path)
assert.NotNil(t, requestAttributes.Time)
assert.Equal(t, "localhost", requestAttributes.Host)
assert.Equal(t, auditV1.AttributeContext_HTTP_METHOD_POST, requestAttributes.Method)
assert.Equal(t, "requestId", *requestAttributes.Id)
assert.Equal(t, "https", requestAttributes.Scheme)
assert.Equal(t, map[string]string{"user-agent": "custom"}, requestAttributes.Headers)
assert.Nil(t, requestAttributes.Query)
assert.Equal(t, "HTTP/1.1", requestAttributes.Protocol)
requestAttributesAuth := requestAttributes.Auth
assert.NotNil(t, requestAttributesAuth)
assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63/https%3A%2F%2Faccounts.dev.stackit.cloud", requestAttributesAuth.Principal)
assert.Equal(t, []string{"stackit-portal-login-dev-client-id"}, requestAttributesAuth.Audiences)
assert.NotNil(t, requestAttributesAuth.Claims)
expectedResponse, _ := structpb.NewStruct(responseBody)
assert.Equal(t, fmt.Sprintf("projects/%s", objectId), logEntry.ProtoPayload.ResourceName)
assert.Equal(t, expectedResponse, logEntry.ProtoPayload.Response)
responseMetadata := logEntry.ProtoPayload.ResponseMetadata
assert.NotNil(t, responseMetadata)
assert.Nil(t, responseMetadata.ErrorDetails)
assert.Equal(t, "Client error", *responseMetadata.ErrorMessage)
assert.Equal(t, wrapperspb.Int32(400), responseMetadata.StatusCode)
responseAttributes := responseMetadata.ResponseAttributes
assert.NotNil(t, responseAttributes)
assert.Equal(t, map[string]string{"key": "header"}, responseAttributes.Headers)
assert.Equal(t, wrapperspb.Int64(10), responseAttributes.NumResponseItems)
assert.Equal(t, wrapperspb.Int64(18), responseAttributes.Size)
assert.NotNil(t, responseAttributes.Time)
assert.Equal(t, "demo-service", logEntry.ProtoPayload.ServiceName)
validator, err := protovalidate.New()
assert.NoError(t, err)
err = validator.Validate(&logEntry)
assert.NoError(t, err)
})
t.Run("system event with object reference", func(t *testing.T) {
api, _ := NewMockAuditApi()
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
objectId := uuid.NewString()
operation := "stackit.demo-service.v1.operation"
builder := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01").
WithRequiredObjectId(objectId).
WithRequiredObjectType(ObjectTypeProject).
WithRequiredOperation(operation).
AsSystemEvent()
cloudEvent, routingIdentifier, err := builder.Build(context.Background(), SequenceNumber(1))
assert.NoError(t, err)
assert.True(t, builder.IsBuilt())
assert.Equal(t, SystemIdentifier.Identifier, routingIdentifier.ToObjectIdentifier().Identifier)
assert.Equal(t, SystemIdentifier.Type, routingIdentifier.ToObjectIdentifier().Type)
assert.NotNil(t, cloudEvent)
assert.Equal(t, "application/cloudevents+protobuf", cloudEvent.DataContentType)
assert.Equal(t, "audit.v1.RoutableAuditEvent", cloudEvent.DataType)
assert.Regexp(t, "[0-9]+/eu01/worker-id/1", cloudEvent.Id)
assert.Equal(t, "demo-service", cloudEvent.Source)
assert.Equal(t, "1.0", cloudEvent.SpecVersion)
assert.Equal(t, fmt.Sprintf("projects/%s", objectId), cloudEvent.Subject)
assert.NotNil(t, cloudEvent.Time)
assert.Equal(t, "", *cloudEvent.TraceParent)
assert.Equal(t, "", *cloudEvent.TraceState)
var routableAuditEvent auditV1.RoutableAuditEvent
assert.NotNil(t, cloudEvent.Data)
assert.NoError(t, proto.Unmarshal(cloudEvent.Data, &routableAuditEvent))
assert.Equal(t, SystemIdentifier.Identifier, routableAuditEvent.ObjectIdentifier.Identifier)
assert.Equal(t, SystemIdentifier.Type, routableAuditEvent.ObjectIdentifier.Type)
assert.Equal(t, auditV1.Visibility_VISIBILITY_PRIVATE, routableAuditEvent.Visibility)
assert.Equal(t, operation, routableAuditEvent.OperationName)
var logEntry auditV1.AuditLogEntry
assert.NotNil(t, routableAuditEvent.GetUnencryptedData().Data)
assert.NoError(t, proto.Unmarshal(routableAuditEvent.GetUnencryptedData().Data, &logEntry))
assert.Equal(t, fmt.Sprintf("system/%s/logs/system-event", uuid.Nil.String()), logEntry.LogName)
assert.Nil(t, logEntry.Labels)
assert.Equal(t, auditV1.LogSeverity_LOG_SEVERITY_DEFAULT, logEntry.Severity)
assert.NotNil(t, logEntry.Timestamp)
assert.Nil(t, logEntry.CorrelationId)
assert.Regexp(t, "[0-9]+/eu01/worker-id/1", logEntry.InsertId)
assert.NotNil(t, logEntry.ProtoPayload)
authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo
assert.NotNil(t, authenticationInfo)
assert.Equal(t, "do-not-reply@stackit.cloud", authenticationInfo.PrincipalEmail)
assert.Equal(t, "none", authenticationInfo.PrincipalId)
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
assert.Nil(t, authenticationInfo.ServiceAccountName)
assert.Nil(t, logEntry.ProtoPayload.AuthorizationInfo)
assert.Nil(t, logEntry.ProtoPayload.Metadata)
assert.Equal(t, operation, logEntry.ProtoPayload.OperationName)
assert.Nil(t, logEntry.ProtoPayload.Request)
requestMetadata := logEntry.ProtoPayload.RequestMetadata
assert.NotNil(t, requestMetadata)
assert.Equal(t, "0.0.0.0", requestMetadata.CallerIp)
assert.Equal(t, "none", requestMetadata.CallerSuppliedUserAgent)
requestAttributes := requestMetadata.RequestAttributes
assert.NotNil(t, requestAttributes)
assert.Equal(t, "none", requestAttributes.Path)
assert.NotNil(t, requestAttributes.Time)
assert.Equal(t, "0.0.0.0", requestAttributes.Host)
assert.Equal(t, auditV1.AttributeContext_HTTP_METHOD_OTHER, requestAttributes.Method)
assert.Nil(t, requestAttributes.Id)
assert.Equal(t, "none", requestAttributes.Scheme)
assert.Equal(t, map[string]string{"user-agent": "none"}, requestAttributes.Headers)
assert.Nil(t, requestAttributes.Query)
assert.Equal(t, "none", requestAttributes.Protocol)
requestAttributesAuth := requestAttributes.Auth
assert.NotNil(t, requestAttributesAuth)
assert.Equal(t, "none/none", requestAttributesAuth.Principal)
assert.Nil(t, requestAttributesAuth.Audiences)
assert.NotNil(t, requestAttributesAuth.Claims)
assert.Equal(t, map[string]any{}, requestAttributesAuth.Claims.AsMap())
assert.Equal(t, fmt.Sprintf("projects/%s", objectId), logEntry.ProtoPayload.ResourceName)
assert.Nil(t, logEntry.ProtoPayload.Response)
responseMetadata := logEntry.ProtoPayload.ResponseMetadata
assert.NotNil(t, responseMetadata)
assert.Nil(t, responseMetadata.ErrorDetails)
assert.Nil(t, responseMetadata.ErrorMessage)
assert.Equal(t, wrapperspb.Int32(200), responseMetadata.StatusCode)
responseAttributes := responseMetadata.ResponseAttributes
assert.NotNil(t, responseAttributes)
assert.Nil(t, responseAttributes.Headers)
assert.Nil(t, responseAttributes.NumResponseItems)
assert.Nil(t, responseAttributes.Size)
assert.NotNil(t, responseAttributes.Time)
assert.Equal(t, "demo-service", logEntry.ProtoPayload.ServiceName)
validator, err := protovalidate.New()
assert.NoError(t, err)
err = validator.Validate(&logEntry)
assert.NoError(t, err)
})
t.Run("system event", func(t *testing.T) {
api, _ := NewMockAuditApi()
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
operation := "stackit.demo-service.v1.operation"
builder := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01").
WithRequiredOperation(operation).
AsSystemEvent()
cloudEvent, routingIdentifier, err := builder.Build(context.Background(), SequenceNumber(1))
assert.NoError(t, err)
assert.True(t, builder.IsBuilt())
assert.Equal(t, SystemIdentifier.Identifier, routingIdentifier.ToObjectIdentifier().Identifier)
assert.Equal(t, SystemIdentifier.Type, routingIdentifier.ToObjectIdentifier().Type)
assert.NotNil(t, cloudEvent)
assert.Equal(t, "application/cloudevents+protobuf", cloudEvent.DataContentType)
assert.Equal(t, "audit.v1.RoutableAuditEvent", cloudEvent.DataType)
assert.Regexp(t, "[0-9]+/eu01/worker-id/1", cloudEvent.Id)
assert.Equal(t, "demo-service", cloudEvent.Source)
assert.Equal(t, "1.0", cloudEvent.SpecVersion)
assert.Equal(t, fmt.Sprintf("system/%s", uuid.Nil.String()), cloudEvent.Subject)
assert.NotNil(t, cloudEvent.Time)
assert.Equal(t, "", *cloudEvent.TraceParent)
assert.Equal(t, "", *cloudEvent.TraceState)
var routableAuditEvent auditV1.RoutableAuditEvent
assert.NotNil(t, cloudEvent.Data)
assert.NoError(t, proto.Unmarshal(cloudEvent.Data, &routableAuditEvent))
assert.Equal(t, SystemIdentifier.Identifier, routableAuditEvent.ObjectIdentifier.Identifier)
assert.Equal(t, SystemIdentifier.Type, routableAuditEvent.ObjectIdentifier.Type)
assert.Equal(t, auditV1.Visibility_VISIBILITY_PRIVATE, routableAuditEvent.Visibility)
assert.Equal(t, operation, routableAuditEvent.OperationName)
var logEntry auditV1.AuditLogEntry
assert.NotNil(t, routableAuditEvent.GetUnencryptedData().Data)
assert.NoError(t, proto.Unmarshal(routableAuditEvent.GetUnencryptedData().Data, &logEntry))
assert.Equal(t, fmt.Sprintf("system/%s/logs/system-event", uuid.Nil.String()), logEntry.LogName)
assert.Nil(t, logEntry.Labels)
assert.Equal(t, auditV1.LogSeverity_LOG_SEVERITY_DEFAULT, logEntry.Severity)
assert.NotNil(t, logEntry.Timestamp)
assert.Nil(t, logEntry.CorrelationId)
assert.Regexp(t, "[0-9]+/eu01/worker-id/1", logEntry.InsertId)
assert.NotNil(t, logEntry.ProtoPayload)
authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo
assert.NotNil(t, authenticationInfo)
assert.Equal(t, "do-not-reply@stackit.cloud", authenticationInfo.PrincipalEmail)
assert.Equal(t, "none", authenticationInfo.PrincipalId)
assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo)
assert.Nil(t, authenticationInfo.ServiceAccountName)
assert.Nil(t, logEntry.ProtoPayload.AuthorizationInfo)
assert.Nil(t, logEntry.ProtoPayload.Metadata)
assert.Equal(t, operation, logEntry.ProtoPayload.OperationName)
assert.Nil(t, logEntry.ProtoPayload.Request)
requestMetadata := logEntry.ProtoPayload.RequestMetadata
assert.NotNil(t, requestMetadata)
assert.Equal(t, "0.0.0.0", requestMetadata.CallerIp)
assert.Equal(t, "none", requestMetadata.CallerSuppliedUserAgent)
requestAttributes := requestMetadata.RequestAttributes
assert.NotNil(t, requestAttributes)
assert.Equal(t, "none", requestAttributes.Path)
assert.NotNil(t, requestAttributes.Time)
assert.Equal(t, "0.0.0.0", requestAttributes.Host)
assert.Equal(t, auditV1.AttributeContext_HTTP_METHOD_OTHER, requestAttributes.Method)
assert.Nil(t, requestAttributes.Id)
assert.Equal(t, "none", requestAttributes.Scheme)
assert.Equal(t, map[string]string{"user-agent": "none"}, requestAttributes.Headers)
assert.Nil(t, requestAttributes.Query)
assert.Equal(t, "none", requestAttributes.Protocol)
requestAttributesAuth := requestAttributes.Auth
assert.NotNil(t, requestAttributesAuth)
assert.Equal(t, "none/none", requestAttributesAuth.Principal)
assert.Nil(t, requestAttributesAuth.Audiences)
assert.NotNil(t, requestAttributesAuth.Claims)
assert.Equal(t, map[string]any{}, requestAttributesAuth.Claims.AsMap())
assert.Equal(t, fmt.Sprintf("system/%s", uuid.Nil.String()), logEntry.ProtoPayload.ResourceName)
assert.Nil(t, logEntry.ProtoPayload.Response)
responseMetadata := logEntry.ProtoPayload.ResponseMetadata
assert.NotNil(t, responseMetadata)
assert.Nil(t, responseMetadata.ErrorDetails)
assert.Nil(t, responseMetadata.ErrorMessage)
assert.Equal(t, wrapperspb.Int32(200), responseMetadata.StatusCode)
responseAttributes := responseMetadata.ResponseAttributes
assert.NotNil(t, responseAttributes)
assert.Nil(t, responseAttributes.Headers)
assert.Nil(t, responseAttributes.NumResponseItems)
assert.Nil(t, responseAttributes.Size)
assert.NotNil(t, responseAttributes.Time)
assert.Equal(t, "demo-service", logEntry.ProtoPayload.ServiceName)
validator, err := protovalidate.New()
assert.NoError(t, err)
err = validator.Validate(&logEntry)
assert.NoError(t, err)
})
t.Run("with responsebody unserialized", func(t *testing.T) {
api, _ := NewMockAuditApi()
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
objectId := uuid.NewString()
operation := "stackit.demo-service.v1.operation"
details := map[string]interface{}{"key": "detail"}
permission := "project.edit"
permissionCheckResult := true
requestTime := time.Now().AddDate(0, 0, -2).UTC()
responseTime := time.Now().AddDate(0, 0, -1).UTC()
responseBody := map[string]interface{}{"key": "response"}
builder := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01").
WithRequiredObjectId(objectId).
WithRequiredObjectType(ObjectTypeProject).
WithRequiredOperation(operation).
WithRequiredApiRequest(ApiRequest{
Body: nil,
Header: TestHeaders,
Host: "localhost",
Method: "POST",
Scheme: "https",
Proto: "HTTP/1.1",
URL: RequestUrl{
Path: "/",
RawQuery: nil,
},
}).
WithRequiredRequestClientIp("127.0.0.1").
WithAuditPermission(permission).
WithAuditPermissionCheckResult(permissionCheckResult).
WithDetails(details).
WithEventType(EventTypeAdminActivity).
WithLabels(map[string]string{"key": "label"}).
WithNumResponseItems(int64(10)).
WithRequestCorrelationId("correlationId").
WithRequestId("requestId").
WithRequestTime(requestTime).
WithResponseBody(responseBody).
WithResponseHeaders(map[string][]string{"key": {"header"}}).
WithResponseTime(responseTime).
WithSeverity(auditV1.LogSeverity_LOG_SEVERITY_ERROR).
WithStatusCode(400).
WithVisibility(auditV1.Visibility_VISIBILITY_PRIVATE)
routableIdentifier := RoutableIdentifier{Identifier: objectId, Type: ObjectTypeProject}
cloudEvent, routingIdentifier, err := builder.Build(context.Background(), SequenceNumber(1))
assert.NoError(t, err)
assert.True(t, builder.IsBuilt())
assert.Equal(t, &routableIdentifier, routingIdentifier)
assert.NotNil(t, cloudEvent)
var routableAuditEvent auditV1.RoutableAuditEvent
assert.NotNil(t, cloudEvent.Data)
assert.NoError(t, proto.Unmarshal(cloudEvent.Data, &routableAuditEvent))
var logEntry auditV1.AuditLogEntry
assert.NotNil(t, routableAuditEvent.GetUnencryptedData().Data)
assert.NoError(t, proto.Unmarshal(routableAuditEvent.GetUnencryptedData().Data, &logEntry))
assert.NotNil(t, logEntry.ProtoPayload)
expectedResponse, _ := structpb.NewStruct(responseBody)
assert.Equal(t, fmt.Sprintf("projects/%s", objectId), logEntry.ProtoPayload.ResourceName)
assert.Equal(t, expectedResponse, logEntry.ProtoPayload.Response)
validator, err := protovalidate.New()
assert.NoError(t, err)
err = validator.Validate(&logEntry)
assert.NoError(t, err)
})
t.Run("no entry builder", func(t *testing.T) {
api, _ := NewMockAuditApi()
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
cloudEvent, routingIdentifier, err := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01").
WithAuditLogEntryBuilder(nil).Build(context.Background(), SequenceNumber(1))
assert.EqualError(t, err, "audit log entry builder not set")
assert.Nil(t, cloudEvent)
assert.Nil(t, routingIdentifier)
})
t.Run("next sequence number", func(t *testing.T) {
api, _ := NewMockAuditApi()
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
builder := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01")
assert.Equal(t, SequenceNumber(0), builder.NextSequenceNumber())
assert.Equal(t, SequenceNumber(1), builder.NextSequenceNumber())
})
t.Run("revert sequence number", func(t *testing.T) {
api, _ := NewMockAuditApi()
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
builder := NewAuditEventBuilder(api, sequenceNumberGenerator, "demo-service", "worker-id", "eu01")
assert.Equal(t, SequenceNumber(0), builder.NextSequenceNumber())
builder.RevertSequenceNumber()
assert.Equal(t, SequenceNumber(0), builder.NextSequenceNumber())
})
}