mirror of
https://dev.azure.com/schwarzit/schwarzit.stackit-public/_git/audit-go
synced 2026-02-08 09:07:26 +00:00
1175 lines
57 KiB
Go
1175 lines
57 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"dev.azure.com/schwarzit/schwarzit.stackit-core-platform/audit-go.git/audit/utils"
|
|
auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-core-platform/audit-go.git/gen/go/audit/v1"
|
|
"fmt"
|
|
"github.com/bufbuild/protovalidate-go"
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"go.opentelemetry.io/otel"
|
|
"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: map[string][]string{"user-agent": {"custom"}, "authorization": {"Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOGJlZjc1LWRmY2QtNGE3My1hMzkxLTU0YTdhZjU3YTdkNiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsic3RhY2tpdC1wb3J0YWwtbG9naW4tZGV2LWNsaWVudC1pZCJdLCJjbGllbnRfaWQiOiJzdGFja2l0LXBvcnRhbC1sb2dpbi1kZXYtY2xpZW50LWlkIiwiZW1haWwiOiJDaHJpc3RpYW4uU2NoYWlibGVAbm92YXRlYy1nbWJoLmRlIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImV4cCI6MTcyMjU5MDM2NywiaWF0IjoxNzIyNTg2NzY3LCJpc3MiOiJodHRwczovL2FjY291bnRzLmRldi5zdGFja2l0LmNsb3VkIiwianRpIjoiZDczYTY3YWMtZDFlYy00YjU1LTk5ZDQtZTk1MzI3NWYwMjJhIiwibmJmIjoxNzIyNTg2NzY3LCJzY29wZSI6Im9wZW5pZCBlbWFpbCIsInN1YiI6ImNkOTRmMDFhLWRmMmUtNDQ1Ni05MDJlLTQ4ZjVlNTdmMGI2MyJ9.ajhjYbC5l5g7un9NSheoAwBT83YcZM91rH4DJxPTDsB78HzIVrmaKTPrK3AI_E1THlD2Z3_ot9nFr_eX7XcwWp_ZBlataKmakdXlAmeb4xSMGNYefIfzV_3w9ZZAZ66yoeTrtn8dUx5ezquenCYpctB1NcccmK4U09V0kNcq9dFcfF3Sg9YilF3orUCR0ql1d9RnOs3EiFZuUpdBEkyoVsAdSh2P-PRbNViR_FgCcAJem97TsN5CQc9RlvKYe4sYKgqQoqa2GDVi9Niiw3fe1V8SCnROYcpkOzBBWdvuzFMBUjln3uOogYVOz93xkmImV6jidgyQ70fLt-eDUmZZfg"}},
|
|
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.Nil(t, logEntry.TraceState)
|
|
assert.Nil(t, logEntry.TraceParent)
|
|
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: map[string][]string{"user-agent": {"custom"}, "authorization": {"Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOGJlZjc1LWRmY2QtNGE3My1hMzkxLTU0YTdhZjU3YTdkNiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsic3RhY2tpdC1wb3J0YWwtbG9naW4tZGV2LWNsaWVudC1pZCJdLCJjbGllbnRfaWQiOiJzdGFja2l0LXBvcnRhbC1sb2dpbi1kZXYtY2xpZW50LWlkIiwiZW1haWwiOiJDaHJpc3RpYW4uU2NoYWlibGVAbm92YXRlYy1nbWJoLmRlIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImV4cCI6MTcyMjU5MDM2NywiaWF0IjoxNzIyNTg2NzY3LCJpc3MiOiJodHRwczovL2FjY291bnRzLmRldi5zdGFja2l0LmNsb3VkIiwianRpIjoiZDczYTY3YWMtZDFlYy00YjU1LTk5ZDQtZTk1MzI3NWYwMjJhIiwibmJmIjoxNzIyNTg2NzY3LCJzY29wZSI6Im9wZW5pZCBlbWFpbCIsInN1YiI6ImNkOTRmMDFhLWRmMmUtNDQ1Ni05MDJlLTQ4ZjVlNTdmMGI2MyJ9.ajhjYbC5l5g7un9NSheoAwBT83YcZM91rH4DJxPTDsB78HzIVrmaKTPrK3AI_E1THlD2Z3_ot9nFr_eX7XcwWp_ZBlataKmakdXlAmeb4xSMGNYefIfzV_3w9ZZAZ66yoeTrtn8dUx5ezquenCYpctB1NcccmK4U09V0kNcq9dFcfF3Sg9YilF3orUCR0ql1d9RnOs3EiFZuUpdBEkyoVsAdSh2P-PRbNViR_FgCcAJem97TsN5CQc9RlvKYe4sYKgqQoqa2GDVi9Niiw3fe1V8SCnROYcpkOzBBWdvuzFMBUjln3uOogYVOz93xkmImV6jidgyQ70fLt-eDUmZZfg"}},
|
|
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.Nil(t, logEntry.TraceState)
|
|
assert.Nil(t, logEntry.TraceParent)
|
|
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.Nil(t, logEntry.TraceState)
|
|
assert.Nil(t, logEntry.TraceParent)
|
|
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: map[string][]string{"user-agent": {"custom"}, "authorization": {"Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOGJlZjc1LWRmY2QtNGE3My1hMzkxLTU0YTdhZjU3YTdkNiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsic3RhY2tpdC1wb3J0YWwtbG9naW4tZGV2LWNsaWVudC1pZCJdLCJjbGllbnRfaWQiOiJzdGFja2l0LXBvcnRhbC1sb2dpbi1kZXYtY2xpZW50LWlkIiwiZW1haWwiOiJDaHJpc3RpYW4uU2NoYWlibGVAbm92YXRlYy1nbWJoLmRlIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImV4cCI6MTcyMjU5MDM2NywiaWF0IjoxNzIyNTg2NzY3LCJpc3MiOiJodHRwczovL2FjY291bnRzLmRldi5zdGFja2l0LmNsb3VkIiwianRpIjoiZDczYTY3YWMtZDFlYy00YjU1LTk5ZDQtZTk1MzI3NWYwMjJhIiwibmJmIjoxNzIyNTg2NzY3LCJzY29wZSI6Im9wZW5pZCBlbWFpbCIsInN1YiI6ImNkOTRmMDFhLWRmMmUtNDQ1Ni05MDJlLTQ4ZjVlNTdmMGI2MyJ9.ajhjYbC5l5g7un9NSheoAwBT83YcZM91rH4DJxPTDsB78HzIVrmaKTPrK3AI_E1THlD2Z3_ot9nFr_eX7XcwWp_ZBlataKmakdXlAmeb4xSMGNYefIfzV_3w9ZZAZ66yoeTrtn8dUx5ezquenCYpctB1NcccmK4U09V0kNcq9dFcfF3Sg9YilF3orUCR0ql1d9RnOs3EiFZuUpdBEkyoVsAdSh2P-PRbNViR_FgCcAJem97TsN5CQc9RlvKYe4sYKgqQoqa2GDVi9Niiw3fe1V8SCnROYcpkOzBBWdvuzFMBUjln3uOogYVOz93xkmImV6jidgyQ70fLt-eDUmZZfg"}},
|
|
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: map[string][]string{"user-agent": {"custom"}, "authorization": {"Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOGJlZjc1LWRmY2QtNGE3My1hMzkxLTU0YTdhZjU3YTdkNiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsic3RhY2tpdC1wb3J0YWwtbG9naW4tZGV2LWNsaWVudC1pZCJdLCJjbGllbnRfaWQiOiJzdGFja2l0LXBvcnRhbC1sb2dpbi1kZXYtY2xpZW50LWlkIiwiZW1haWwiOiJDaHJpc3RpYW4uU2NoYWlibGVAbm92YXRlYy1nbWJoLmRlIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImV4cCI6MTcyMjU5MDM2NywiaWF0IjoxNzIyNTg2NzY3LCJpc3MiOiJodHRwczovL2FjY291bnRzLmRldi5zdGFja2l0LmNsb3VkIiwianRpIjoiZDczYTY3YWMtZDFlYy00YjU1LTk5ZDQtZTk1MzI3NWYwMjJhIiwibmJmIjoxNzIyNTg2NzY3LCJzY29wZSI6Im9wZW5pZCBlbWFpbCIsInN1YiI6ImNkOTRmMDFhLWRmMmUtNDQ1Ni05MDJlLTQ4ZjVlNTdmMGI2MyJ9.ajhjYbC5l5g7un9NSheoAwBT83YcZM91rH4DJxPTDsB78HzIVrmaKTPrK3AI_E1THlD2Z3_ot9nFr_eX7XcwWp_ZBlataKmakdXlAmeb4xSMGNYefIfzV_3w9ZZAZ66yoeTrtn8dUx5ezquenCYpctB1NcccmK4U09V0kNcq9dFcfF3Sg9YilF3orUCR0ql1d9RnOs3EiFZuUpdBEkyoVsAdSh2P-PRbNViR_FgCcAJem97TsN5CQc9RlvKYe4sYKgqQoqa2GDVi9Niiw3fe1V8SCnROYcpkOzBBWdvuzFMBUjln3uOogYVOz93xkmImV6jidgyQ70fLt-eDUmZZfg"}},
|
|
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: map[string][]string{"user-agent": {"custom"}, "authorization": {"Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOGJlZjc1LWRmY2QtNGE3My1hMzkxLTU0YTdhZjU3YTdkNiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsic3RhY2tpdC1wb3J0YWwtbG9naW4tZGV2LWNsaWVudC1pZCJdLCJjbGllbnRfaWQiOiJzdGFja2l0LXBvcnRhbC1sb2dpbi1kZXYtY2xpZW50LWlkIiwiZW1haWwiOiJDaHJpc3RpYW4uU2NoYWlibGVAbm92YXRlYy1nbWJoLmRlIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImV4cCI6MTcyMjU5MDM2NywiaWF0IjoxNzIyNTg2NzY3LCJpc3MiOiJodHRwczovL2FjY291bnRzLmRldi5zdGFja2l0LmNsb3VkIiwianRpIjoiZDczYTY3YWMtZDFlYy00YjU1LTk5ZDQtZTk1MzI3NWYwMjJhIiwibmJmIjoxNzIyNTg2NzY3LCJzY29wZSI6Im9wZW5pZCBlbWFpbCIsInN1YiI6ImNkOTRmMDFhLWRmMmUtNDQ1Ni05MDJlLTQ4ZjVlNTdmMGI2MyJ9.ajhjYbC5l5g7un9NSheoAwBT83YcZM91rH4DJxPTDsB78HzIVrmaKTPrK3AI_E1THlD2Z3_ot9nFr_eX7XcwWp_ZBlataKmakdXlAmeb4xSMGNYefIfzV_3w9ZZAZ66yoeTrtn8dUx5ezquenCYpctB1NcccmK4U09V0kNcq9dFcfF3Sg9YilF3orUCR0ql1d9RnOs3EiFZuUpdBEkyoVsAdSh2P-PRbNViR_FgCcAJem97TsN5CQc9RlvKYe4sYKgqQoqa2GDVi9Niiw3fe1V8SCnROYcpkOzBBWdvuzFMBUjln3uOogYVOz93xkmImV6jidgyQ70fLt-eDUmZZfg"}},
|
|
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()
|
|
tracer := otel.Tracer("test")
|
|
|
|
cloudEvent, routingIdentifier, op, err := NewAuditEventBuilder(api, sequenceNumberGenerator, tracer, "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)
|
|
assert.Equal(t, "", op)
|
|
})
|
|
|
|
t.Run("details missing", func(t *testing.T) {
|
|
api, _ := NewMockAuditApi()
|
|
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
|
|
tracer := otel.Tracer("test")
|
|
|
|
cloudEvent, routingIdentifier, op, err := NewAuditEventBuilder(api, sequenceNumberGenerator, tracer, "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)
|
|
assert.Equal(t, "", op)
|
|
})
|
|
|
|
t.Run("required only", func(t *testing.T) {
|
|
api, _ := NewMockAuditApi()
|
|
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
|
|
tracer := otel.Tracer("test")
|
|
|
|
objectId := uuid.NewString()
|
|
operation := "stackit.demo-service.v1.operation"
|
|
builder := NewAuditEventBuilder(api, sequenceNumberGenerator, tracer, "demo-service", "worker-id", "eu01").
|
|
WithRequiredObjectId(objectId).
|
|
WithRequiredObjectType(ObjectTypeProject).
|
|
WithRequiredOperation(operation).
|
|
WithRequiredApiRequest(ApiRequest{
|
|
Body: nil,
|
|
Header: map[string][]string{"user-agent": {"custom"}, "authorization": {"Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOGJlZjc1LWRmY2QtNGE3My1hMzkxLTU0YTdhZjU3YTdkNiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsic3RhY2tpdC1wb3J0YWwtbG9naW4tZGV2LWNsaWVudC1pZCJdLCJjbGllbnRfaWQiOiJzdGFja2l0LXBvcnRhbC1sb2dpbi1kZXYtY2xpZW50LWlkIiwiZW1haWwiOiJDaHJpc3RpYW4uU2NoYWlibGVAbm92YXRlYy1nbWJoLmRlIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImV4cCI6MTcyMjU5MDM2NywiaWF0IjoxNzIyNTg2NzY3LCJpc3MiOiJodHRwczovL2FjY291bnRzLmRldi5zdGFja2l0LmNsb3VkIiwianRpIjoiZDczYTY3YWMtZDFlYy00YjU1LTk5ZDQtZTk1MzI3NWYwMjJhIiwibmJmIjoxNzIyNTg2NzY3LCJzY29wZSI6Im9wZW5pZCBlbWFpbCIsInN1YiI6ImNkOTRmMDFhLWRmMmUtNDQ1Ni05MDJlLTQ4ZjVlNTdmMGI2MyJ9.ajhjYbC5l5g7un9NSheoAwBT83YcZM91rH4DJxPTDsB78HzIVrmaKTPrK3AI_E1THlD2Z3_ot9nFr_eX7XcwWp_ZBlataKmakdXlAmeb4xSMGNYefIfzV_3w9ZZAZ66yoeTrtn8dUx5ezquenCYpctB1NcccmK4U09V0kNcq9dFcfF3Sg9YilF3orUCR0ql1d9RnOs3EiFZuUpdBEkyoVsAdSh2P-PRbNViR_FgCcAJem97TsN5CQc9RlvKYe4sYKgqQoqa2GDVi9Niiw3fe1V8SCnROYcpkOzBBWdvuzFMBUjln3uOogYVOz93xkmImV6jidgyQ70fLt-eDUmZZfg"}},
|
|
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, op, err := builder.Build(context.Background(), SequenceNumber(1))
|
|
assert.NoError(t, err)
|
|
assert.True(t, builder.IsBuilt())
|
|
|
|
assert.Equal(t, &routableIdentifier, routingIdentifier)
|
|
assert.Equal(t, operation, op)
|
|
|
|
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, "00-00000000000000000000000000000000-0000000000000000-00", *cloudEvent.TraceParent)
|
|
assert.Nil(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.Nil(t, logEntry.TraceState)
|
|
assert.Nil(t, logEntry.TraceParent)
|
|
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()
|
|
tracer := otel.Tracer("test")
|
|
|
|
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, tracer, "demo-service", "worker-id", "eu01").
|
|
WithRequiredObjectId(objectId).
|
|
WithRequiredObjectType(ObjectTypeProject).
|
|
WithRequiredOperation(operation).
|
|
WithRequiredApiRequest(ApiRequest{
|
|
Body: nil,
|
|
Header: map[string][]string{"user-agent": {"custom"}, "authorization": {"Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOGJlZjc1LWRmY2QtNGE3My1hMzkxLTU0YTdhZjU3YTdkNiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsic3RhY2tpdC1wb3J0YWwtbG9naW4tZGV2LWNsaWVudC1pZCJdLCJjbGllbnRfaWQiOiJzdGFja2l0LXBvcnRhbC1sb2dpbi1kZXYtY2xpZW50LWlkIiwiZW1haWwiOiJDaHJpc3RpYW4uU2NoYWlibGVAbm92YXRlYy1nbWJoLmRlIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImV4cCI6MTcyMjU5MDM2NywiaWF0IjoxNzIyNTg2NzY3LCJpc3MiOiJodHRwczovL2FjY291bnRzLmRldi5zdGFja2l0LmNsb3VkIiwianRpIjoiZDczYTY3YWMtZDFlYy00YjU1LTk5ZDQtZTk1MzI3NWYwMjJhIiwibmJmIjoxNzIyNTg2NzY3LCJzY29wZSI6Im9wZW5pZCBlbWFpbCIsInN1YiI6ImNkOTRmMDFhLWRmMmUtNDQ1Ni05MDJlLTQ4ZjVlNTdmMGI2MyJ9.ajhjYbC5l5g7un9NSheoAwBT83YcZM91rH4DJxPTDsB78HzIVrmaKTPrK3AI_E1THlD2Z3_ot9nFr_eX7XcwWp_ZBlataKmakdXlAmeb4xSMGNYefIfzV_3w9ZZAZ66yoeTrtn8dUx5ezquenCYpctB1NcccmK4U09V0kNcq9dFcfF3Sg9YilF3orUCR0ql1d9RnOs3EiFZuUpdBEkyoVsAdSh2P-PRbNViR_FgCcAJem97TsN5CQc9RlvKYe4sYKgqQoqa2GDVi9Niiw3fe1V8SCnROYcpkOzBBWdvuzFMBUjln3uOogYVOz93xkmImV6jidgyQ70fLt-eDUmZZfg"}},
|
|
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, op, err := builder.Build(context.Background(), SequenceNumber(1))
|
|
assert.NoError(t, err)
|
|
assert.True(t, builder.IsBuilt())
|
|
|
|
assert.Equal(t, &routableIdentifier, routingIdentifier)
|
|
assert.Equal(t, operation, op)
|
|
|
|
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, "00-00000000000000000000000000000000-0000000000000000-00", *cloudEvent.TraceParent)
|
|
assert.Nil(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.Nil(t, logEntry.TraceState)
|
|
assert.Nil(t, logEntry.TraceParent)
|
|
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()
|
|
tracer := otel.Tracer("test")
|
|
|
|
objectId := uuid.NewString()
|
|
operation := "stackit.demo-service.v1.operation"
|
|
builder := NewAuditEventBuilder(api, sequenceNumberGenerator, tracer, "demo-service", "worker-id", "eu01").
|
|
WithRequiredObjectId(objectId).
|
|
WithRequiredObjectType(ObjectTypeProject).
|
|
WithRequiredOperation(operation).
|
|
AsSystemEvent()
|
|
|
|
cloudEvent, routingIdentifier, op, 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.Equal(t, operation, op)
|
|
|
|
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, "00-00000000000000000000000000000000-0000000000000000-00", *cloudEvent.TraceParent)
|
|
assert.Nil(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.Nil(t, logEntry.TraceState)
|
|
assert.Nil(t, logEntry.TraceParent)
|
|
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()
|
|
tracer := otel.Tracer("test")
|
|
|
|
operation := "stackit.demo-service.v1.operation"
|
|
builder := NewAuditEventBuilder(api, sequenceNumberGenerator, tracer, "demo-service", "worker-id", "eu01").
|
|
WithRequiredOperation(operation).
|
|
AsSystemEvent()
|
|
|
|
cloudEvent, routingIdentifier, op, 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.Equal(t, operation, op)
|
|
|
|
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, "00-00000000000000000000000000000000-0000000000000000-00", *cloudEvent.TraceParent)
|
|
assert.Nil(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.Nil(t, logEntry.TraceState)
|
|
assert.Nil(t, logEntry.TraceParent)
|
|
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()
|
|
tracer := otel.Tracer("test")
|
|
|
|
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, tracer, "demo-service", "worker-id", "eu01").
|
|
WithRequiredObjectId(objectId).
|
|
WithRequiredObjectType(ObjectTypeProject).
|
|
WithRequiredOperation(operation).
|
|
WithRequiredApiRequest(ApiRequest{
|
|
Body: nil,
|
|
Header: map[string][]string{"user-agent": {"custom"}, "authorization": {"Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOGJlZjc1LWRmY2QtNGE3My1hMzkxLTU0YTdhZjU3YTdkNiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsic3RhY2tpdC1wb3J0YWwtbG9naW4tZGV2LWNsaWVudC1pZCJdLCJjbGllbnRfaWQiOiJzdGFja2l0LXBvcnRhbC1sb2dpbi1kZXYtY2xpZW50LWlkIiwiZW1haWwiOiJDaHJpc3RpYW4uU2NoYWlibGVAbm92YXRlYy1nbWJoLmRlIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImV4cCI6MTcyMjU5MDM2NywiaWF0IjoxNzIyNTg2NzY3LCJpc3MiOiJodHRwczovL2FjY291bnRzLmRldi5zdGFja2l0LmNsb3VkIiwianRpIjoiZDczYTY3YWMtZDFlYy00YjU1LTk5ZDQtZTk1MzI3NWYwMjJhIiwibmJmIjoxNzIyNTg2NzY3LCJzY29wZSI6Im9wZW5pZCBlbWFpbCIsInN1YiI6ImNkOTRmMDFhLWRmMmUtNDQ1Ni05MDJlLTQ4ZjVlNTdmMGI2MyJ9.ajhjYbC5l5g7un9NSheoAwBT83YcZM91rH4DJxPTDsB78HzIVrmaKTPrK3AI_E1THlD2Z3_ot9nFr_eX7XcwWp_ZBlataKmakdXlAmeb4xSMGNYefIfzV_3w9ZZAZ66yoeTrtn8dUx5ezquenCYpctB1NcccmK4U09V0kNcq9dFcfF3Sg9YilF3orUCR0ql1d9RnOs3EiFZuUpdBEkyoVsAdSh2P-PRbNViR_FgCcAJem97TsN5CQc9RlvKYe4sYKgqQoqa2GDVi9Niiw3fe1V8SCnROYcpkOzBBWdvuzFMBUjln3uOogYVOz93xkmImV6jidgyQ70fLt-eDUmZZfg"}},
|
|
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, op, err := builder.Build(context.Background(), SequenceNumber(1))
|
|
assert.NoError(t, err)
|
|
assert.True(t, builder.IsBuilt())
|
|
|
|
assert.Equal(t, &routableIdentifier, routingIdentifier)
|
|
assert.Equal(t, operation, op)
|
|
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()
|
|
tracer := otel.Tracer("test")
|
|
|
|
cloudEvent, routingIdentifier, operation, err := NewAuditEventBuilder(api, sequenceNumberGenerator, tracer, "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)
|
|
assert.Equal(t, "", operation)
|
|
})
|
|
|
|
t.Run("next sequence number", func(t *testing.T) {
|
|
api, _ := NewMockAuditApi()
|
|
sequenceNumberGenerator := utils.NewDefaultSequenceNumberGenerator()
|
|
tracer := otel.Tracer("test")
|
|
|
|
builder := NewAuditEventBuilder(api, sequenceNumberGenerator, tracer, "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()
|
|
tracer := otel.Tracer("test")
|
|
|
|
builder := NewAuditEventBuilder(api, sequenceNumberGenerator, tracer, "demo-service", "worker-id", "eu01")
|
|
assert.Equal(t, SequenceNumber(0), builder.NextSequenceNumber())
|
|
builder.RevertSequenceNumber()
|
|
assert.Equal(t, SequenceNumber(0), builder.NextSequenceNumber())
|
|
})
|
|
}
|