diff --git a/gen/go/audit/v1/audit_event.pb.go b/gen/go/audit/v1/audit_event.pb.go index 92154bb..fa4721e 100644 --- a/gen/go/audit/v1/audit_event.pb.go +++ b/gen/go/audit/v1/audit_event.pb.go @@ -521,8 +521,8 @@ type AuthenticationInfo struct { // The email address of the authenticated user. // Service accounts have email addresses that can be used. // - // Required: true - PrincipalEmail string `protobuf:"bytes,2,opt,name=principal_email,json=principalEmail,proto3" json:"principal_email,omitempty"` + // Required: false + PrincipalEmail *string `protobuf:"bytes,2,opt,name=principal_email,json=principalEmail,proto3,oneof" json:"principal_email,omitempty"` // The name of the service account used to create or exchange // credentials for authenticating the service account making the request. // @@ -584,8 +584,8 @@ func (x *AuthenticationInfo) GetPrincipalId() string { } func (x *AuthenticationInfo) GetPrincipalEmail() string { - if x != nil { - return x.PrincipalEmail + if x != nil && x.PrincipalEmail != nil { + return *x.PrincipalEmail } return "" } @@ -1480,10 +1480,10 @@ const file_audit_v1_audit_event_proto_rawDesc = "" + "\vLabelsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x11\n" + - "\x0f_correlation_id\"\xab\x06\n" + - "\bAuditLog\x12-\n" + - "\fservice_name\x18\x01 \x01(\tB\n" + - "\xbaH\a\xc8\x01\x01r\x02\x10\x01R\vserviceName\x12w\n" + + "\x0f_correlation_id\"\xb3\x06\n" + + "\bAuditLog\x125\n" + + "\fservice_name\x18\x01 \x01(\tB\x12\xbaH\x0f\xc8\x01\x01r\n" + + "\x10\x012\x06.*\\S.*R\vserviceName\x12w\n" + "\x0eoperation_name\x18\x02 \x01(\tBP\xbaHM\xc8\x01\x01rH\x10\x01\x18\xff\x012A^stackit\\.[a-z0-9-]+\\.(?:v[0-9]+\\.)?(?:[a-z0-9-.]+\\.)?[a-z0-9-]+$R\roperationName\x12c\n" + "\rresource_name\x18\x03 \x01(\tB>\xbaH;\xc8\x01\x01r6\x10\x01\x18\xff\x012/^[a-z]+/[a-z0-9-]+(?:/[a-z0-9-]+/[a-z0-9-_]+)*$R\fresourceName\x12U\n" + "\x13authentication_info\x18\x04 \x01(\v2\x1c.audit.v1.AuthenticationInfoB\x06\xbaH\x03\xc8\x01\x01R\x12authenticationInfo\x12J\n" + @@ -1497,14 +1497,14 @@ const file_audit_v1_audit_event_proto_rawDesc = "" + "\n" + "\b_requestB\v\n" + "\t_responseB\v\n" + - "\t_metadata\"\xf3\x02\n" + - "\x12AuthenticationInfo\x12-\n" + - "\fprincipal_id\x18\x01 \x01(\tB\n" + - "\xbaH\a\xc8\x01\x01r\x02\x10\x01R\vprincipalId\x126\n" + - "\x0fprincipal_email\x18\x02 \x01(\tB\r\xbaH\n" + - "\xc8\x01\x01r\x05\x10\x01\x18\xff\x01R\x0eprincipalEmail\x12n\n" + - "\x14service_account_name\x18\x03 \x01(\tB7\xbaH4r220^[a-z-]+/[a-z0-9-]+/service-accounts/[a-z0-9-]+$H\x00R\x12serviceAccountName\x88\x01\x01\x12m\n" + - "\x1fservice_account_delegation_info\x18\x04 \x03(\v2&.audit.v1.ServiceAccountDelegationInfoR\x1cserviceAccountDelegationInfoB\x17\n" + + "\t_metadata\"\x93\x03\n" + + "\x12AuthenticationInfo\x125\n" + + "\fprincipal_id\x18\x01 \x01(\tB\x12\xbaH\x0f\xc8\x01\x01r\n" + + "\x10\x012\x06.*\\S.*R\vprincipalId\x12:\n" + + "\x0fprincipal_email\x18\x02 \x01(\tB\f\xbaH\tr\a\x10\x05\x18\xff\x01`\x01H\x00R\x0eprincipalEmail\x88\x01\x01\x12n\n" + + "\x14service_account_name\x18\x03 \x01(\tB7\xbaH4r220^[a-z-]+/[a-z0-9-]+/service-accounts/[a-z0-9-]+$H\x01R\x12serviceAccountName\x88\x01\x01\x12m\n" + + "\x1fservice_account_delegation_info\x18\x04 \x03(\v2&.audit.v1.ServiceAccountDelegationInfoR\x1cserviceAccountDelegationInfoB\x12\n" + + "\x10_principal_emailB\x17\n" + "\x15_service_account_name\"\xf2\x01\n" + "\x11AuthorizationInfo\x12U\n" + "\bresource\x18\x01 \x01(\tB9\xbaH6\xc8\x01\x01r12/^[a-z]+/[a-z0-9-]+(?:/[a-z0-9-]+/[a-z0-9-_]+)*$R\bresource\x12L\n" + @@ -1514,26 +1514,25 @@ const file_audit_v1_audit_event_proto_rawDesc = "" + "\agranted\x18\x03 \x01(\bH\x01R\agranted\x88\x01\x01B\r\n" + "\v_permissionB\n" + "\n" + - "\b_granted\"\x8a\v\n" + + "\b_granted\"\xaa\v\n" + "\x10AttributeContext\x1a\xa9\x01\n" + "\x04Auth\x12J\n" + "\tprincipal\x18\x01 \x01(\tB,\xbaH)\xc8\x01\x01r$2\"^[a-zA-Z0-9-%._]+/[a-zA-Z0-9-%.]+$R\tprincipal\x12\x1c\n" + "\taudiences\x18\x02 \x03(\tR\taudiences\x127\n" + - "\x06claims\x18\x03 \x01(\v2\x17.google.protobuf.StructB\x06\xbaH\x03\xc8\x01\x01R\x06claims\x1a\xae\x04\n" + + "\x06claims\x18\x03 \x01(\v2\x17.google.protobuf.StructB\x06\xbaH\x03\xc8\x01\x01R\x06claims\x1a\xce\x04\n" + "\aRequest\x12\x13\n" + "\x02id\x18\x01 \x01(\tH\x00R\x02id\x88\x01\x01\x12J\n" + "\x06method\x18\x02 \x01(\x0e2%.audit.v1.AttributeContext.HttpMethodB\v\xbaH\b\xc8\x01\x01\x82\x01\x02\x10\x01R\x06method\x12Q\n" + - "\aheaders\x18\x03 \x03(\v2/.audit.v1.AttributeContext.Request.HeadersEntryB\x06\xbaH\x03\xc8\x01\x01R\aheaders\x12!\n" + - "\x04path\x18\x04 \x01(\tB\r\xbaH\n" + - "\xc8\x01\x01r\x05\x10\x01\x18\xff\x01R\x04path\x12\x1e\n" + - "\x04host\x18\x05 \x01(\tB\n" + - "\xbaH\a\xc8\x01\x01r\x02\x10\x01R\x04host\x12\"\n" + - "\x06scheme\x18\x06 \x01(\tB\n" + - "\xbaH\a\xc8\x01\x01r\x02\x10\x01R\x06scheme\x12\x19\n" + + "\aheaders\x18\x03 \x03(\v2/.audit.v1.AttributeContext.Request.HeadersEntryB\x06\xbaH\x03\xc8\x01\x01R\aheaders\x12)\n" + + "\x04path\x18\x04 \x01(\tB\x15\xbaH\x12\xc8\x01\x01r\r\x10\x01\x18\xff\x012\x06.*\\S.*R\x04path\x12&\n" + + "\x04host\x18\x05 \x01(\tB\x12\xbaH\x0f\xc8\x01\x01r\n" + + "\x10\x012\x06.*\\S.*R\x04host\x12*\n" + + "\x06scheme\x18\x06 \x01(\tB\x12\xbaH\x0f\xc8\x01\x01r\n" + + "\x10\x012\x06.*\\S.*R\x06scheme\x12\x19\n" + "\x05query\x18\a \x01(\tH\x01R\x05query\x88\x01\x01\x12;\n" + - "\x04time\x18\b \x01(\v2\x1a.google.protobuf.TimestampB\v\xbaH\b\xc8\x01\x01\xb2\x01\x028\x01R\x04time\x12&\n" + - "\bprotocol\x18\t \x01(\tB\n" + - "\xbaH\a\xc8\x01\x01r\x02\x10\x01R\bprotocol\x12;\n" + + "\x04time\x18\b \x01(\v2\x1a.google.protobuf.TimestampB\v\xbaH\b\xc8\x01\x01\xb2\x01\x028\x01R\x04time\x12.\n" + + "\bprotocol\x18\t \x01(\tB\x12\xbaH\x0f\xc8\x01\x01r\n" + + "\x10\x012\x06.*\\S.*R\bprotocol\x12;\n" + "\x04auth\x18\n" + " \x01(\v2\x1f.audit.v1.AttributeContext.AuthB\x06\xbaH\x03\xc8\x01\x01R\x04auth\x1a:\n" + "\fHeadersEntry\x12\x10\n" + @@ -1564,12 +1563,11 @@ const file_audit_v1_audit_event_proto_rawDesc = "" + "\x13HTTP_METHOD_OPTIONS\x10\b\x12\x15\n" + "\x11HTTP_METHOD_TRACE\x10\t\x12\x15\n" + "\x11HTTP_METHOD_PATCH\x10\n" + - "\"\xe1\x01\n" + + "\"\xe9\x01\n" + "\x0fRequestMetadata\x12'\n" + "\tcaller_ip\x18\x01 \x01(\tB\n" + - "\xbaH\a\xc8\x01\x01r\x02p\x01R\bcallerIp\x12J\n" + - "\x1acaller_supplied_user_agent\x18\x02 \x01(\tB\r\xbaH\n" + - "\xc8\x01\x01r\x05\x10\x01\x18\xff\x01R\x17callerSuppliedUserAgent\x12Y\n" + + "\xbaH\a\xc8\x01\x01r\x02p\x01R\bcallerIp\x12R\n" + + "\x1acaller_supplied_user_agent\x18\x02 \x01(\tB\x15\xbaH\x12\xc8\x01\x01r\r\x10\x01\x18\xff\x012\x06.*\\S.*R\x17callerSuppliedUserAgent\x12Y\n" + "\x12request_attributes\x18\x03 \x01(\v2\".audit.v1.AttributeContext.RequestB\x06\xbaH\x03\xc8\x01\x01R\x11requestAttributes\"\xb4\x02\n" + "\x10ResponseMetadata\x12H\n" + "\vstatus_code\x18\x01 \x01(\v2\x1b.google.protobuf.Int32ValueB\n" + @@ -1578,18 +1576,17 @@ const file_audit_v1_audit_event_proto_rawDesc = "" + "\rerror_message\x18\x02 \x01(\tH\x00R\ferrorMessage\x88\x01\x01\x12<\n" + "\rerror_details\x18\x03 \x03(\v2\x17.google.protobuf.StructR\ferrorDetails\x12\\\n" + "\x13response_attributes\x18\x04 \x01(\v2#.audit.v1.AttributeContext.ResponseB\x06\xbaH\x03\xc8\x01\x01R\x12responseAttributesB\x10\n" + - "\x0e_error_message\"\xba\x04\n" + + "\x0e_error_message\"\xca\x04\n" + "\x1cServiceAccountDelegationInfo\x12c\n" + "\x10system_principal\x18\x01 \x01(\v26.audit.v1.ServiceAccountDelegationInfo.SystemPrincipalH\x00R\x0fsystemPrincipal\x12Z\n" + "\ridp_principal\x18\x02 \x01(\v23.audit.v1.ServiceAccountDelegationInfo.IdpPrincipalH\x00R\fidpPrincipal\x1ao\n" + "\x0fSystemPrincipal\x12G\n" + "\x10service_metadata\x18\x01 \x01(\v2\x17.google.protobuf.StructH\x00R\x0fserviceMetadata\x88\x01\x01B\x13\n" + - "\x11_service_metadata\x1a\xd3\x01\n" + - "\fIdpPrincipal\x12-\n" + - "\fprincipal_id\x18\x01 \x01(\tB\n" + - "\xbaH\a\xc8\x01\x01r\x02\x10\x01R\vprincipalId\x126\n" + - "\x0fprincipal_email\x18\x02 \x01(\tB\r\xbaH\n" + - "\xc8\x01\x01r\x05\x10\x01\x18\xff\x01R\x0eprincipalEmail\x12G\n" + + "\x11_service_metadata\x1a\xe3\x01\n" + + "\fIdpPrincipal\x125\n" + + "\fprincipal_id\x18\x01 \x01(\tB\x12\xbaH\x0f\xc8\x01\x01r\n" + + "\x10\x012\x06.*\\S.*R\vprincipalId\x12>\n" + + "\x0fprincipal_email\x18\x02 \x01(\tB\x15\xbaH\x12\xc8\x01\x01r\r\x10\x01\x18\xff\x012\x06.*\\S.*R\x0eprincipalEmail\x12G\n" + "\x10service_metadata\x18\x03 \x01(\v2\x17.google.protobuf.StructH\x00R\x0fserviceMetadata\x88\x01\x01B\x13\n" + "\x11_service_metadataB\x12\n" + "\tauthority\x12\x05\xbaH\x02\b\x01*\x96\x02\n" + diff --git a/gen/go/audit/v1/audit_event.pb.validate.go b/gen/go/audit/v1/audit_event.pb.validate.go index 3694d8e..29f1b96 100644 --- a/gen/go/audit/v1/audit_event.pb.validate.go +++ b/gen/go/audit/v1/audit_event.pb.validate.go @@ -554,8 +554,6 @@ func (m *AuthenticationInfo) validate(all bool) error { // no validation rules for PrincipalId - // no validation rules for PrincipalEmail - for idx, item := range m.GetServiceAccountDelegationInfo() { _, _ = idx, item @@ -590,6 +588,10 @@ func (m *AuthenticationInfo) validate(all bool) error { } + if m.PrincipalEmail != nil { + // no validation rules for PrincipalEmail + } + if m.ServiceAccountName != nil { // no validation rules for ServiceAccountName } diff --git a/internal/audit/api/api_legacy_converter.go b/internal/audit/api/api_legacy_converter.go index 5711dc2..c47700b 100644 --- a/internal/audit/api/api_legacy_converter.go +++ b/internal/audit/api/api_legacy_converter.go @@ -202,7 +202,7 @@ func ConvertAndSerializeIntoLegacyFormat( UserAgent: userAgent, Initiator: LegacyAuditEventPrincipal{ Id: event.ProtoPayload.AuthenticationInfo.PrincipalId, - Email: &event.ProtoPayload.AuthenticationInfo.PrincipalEmail, + Email: event.ProtoPayload.AuthenticationInfo.PrincipalEmail, }, ServiceAccountDelegationInfo: serviceAccountDelegationInfo, Request: request, diff --git a/internal/audit/api/model.go b/internal/audit/api/model.go index 774c792..d5ee83c 100644 --- a/internal/audit/api/model.go +++ b/internal/audit/api/model.go @@ -558,7 +558,7 @@ func AuditAttributesFromAuthorizationHeader(request *pkgAuditCommon.ApiRequest) var authenticationPrincipal = "none/none" var principalId = "none" - var principalEmail = EmailAddressDoNotReplyAtStackItDotCloud + var principalEmail *string emptyClaims, err := structpb.NewStruct(make(map[string]interface{})) if err != nil { return nil, authenticationPrincipal, nil, nil, err @@ -741,14 +741,15 @@ func extractSubjectAndEmailFromActClaims(actClaim map[string]interface{}) (strin return principalId, principalEmail } -func extractSubjectAndEmail(token jwt.Token) (string, string) { - var principalEmail string +func extractSubjectAndEmail(token jwt.Token) (string, *string) { + var principalEmail *string principalId := token.Subject() emailClaim, hasEmail := token.Get("email") - if !hasEmail { - principalEmail = EmailAddressDoNotReplyAtStackItDotCloud - } else { - principalEmail = fmt.Sprintf("%s", emailClaim) + if hasEmail { + trimmedEmail := strings.TrimSpace(fmt.Sprintf("%s", emailClaim)) + if trimmedEmail != "" { + principalEmail = &trimmedEmail + } } return principalId, principalEmail } diff --git a/internal/audit/api/model_test.go b/internal/audit/api/model_test.go index cd15698..7f72579 100644 --- a/internal/audit/api/model_test.go +++ b/internal/audit/api/model_test.go @@ -404,7 +404,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { assert.Equal(t, []string{"stackit-resource-manager-dev"}, audiences) assert.Equal(t, "stackit-resource-manager-dev", authenticationInfo.PrincipalId) - assert.Equal(t, "do-not-reply@stackit.cloud", authenticationInfo.PrincipalEmail) + assert.Nil(t, authenticationInfo.PrincipalEmail) assert.Nil(t, authenticationInfo.ServiceAccountName) assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo) @@ -442,7 +442,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { assert.Equal(t, []string{"stackit", "api"}, audiences) assert.Equal(t, "10f38b01-534b-47bb-a03a-e294ca2be4de", authenticationInfo.PrincipalId) - assert.Equal(t, "my-service-yifc9e1@sa.stackit.cloud", authenticationInfo.PrincipalEmail) + assert.Equal(t, "my-service-yifc9e1@sa.stackit.cloud", *authenticationInfo.PrincipalEmail) assert.Equal(t, "projects/dacc7830-843e-4c5e-86ff-aa0fb51d636f/service-accounts/10f38b01-534b-47bb-a03a-e294ca2be4de", @@ -482,7 +482,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { assert.Equal(t, []string{"stackit", "api"}, audiences) assert.Equal(t, "10f38b01_534b_47bb_a03a_e294ca2be4de", authenticationInfo.PrincipalId) - assert.Equal(t, "my-service-yifc9e1@sa.stackit.cloud", authenticationInfo.PrincipalEmail) + assert.Equal(t, "my-service-yifc9e1@sa.stackit.cloud", *authenticationInfo.PrincipalEmail) assert.Equal(t, "projects/dacc7830-843e-4c5e-86ff-aa0fb51d636f/service-accounts/10f38b01-534b-47bb-a03a-e294ca2be4de", @@ -526,7 +526,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { assert.Equal(t, []string{"stackit", "api"}, audiences) assert.Equal(t, "f45009b2-6433-43c1-b6c7-618c44359e71", authenticationInfo.PrincipalId) - assert.Equal(t, "service-account-2-tj9srt1@sa.stackit.cloud", authenticationInfo.PrincipalEmail) + assert.Equal(t, "service-account-2-tj9srt1@sa.stackit.cloud", *authenticationInfo.PrincipalEmail) assert.Equal(t, "projects/dacc7830-843e-4c5e-86ff-aa0fb51d636f/service-accounts/f45009b2-6433-43c1-b6c7-618c44359e71", @@ -577,7 +577,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { assert.Equal(t, []string{"stackit", "api"}, audiences) assert.Equal(t, "1734b4b6-1d5e-4819-9b50-29917a1b9ad5", authenticationInfo.PrincipalId) - assert.Equal(t, "service-account-3-fghsxw1@sa.stackit.cloud", authenticationInfo.PrincipalEmail) + assert.Equal(t, "service-account-3-fghsxw1@sa.stackit.cloud", *authenticationInfo.PrincipalEmail) assert.Equal(t, "projects/dacc7830-843e-4c5e-86ff-aa0fb51d636f/service-accounts/1734b4b6-1d5e-4819-9b50-29917a1b9ad5", @@ -622,7 +622,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { assert.Equal(t, []string{"stackit-portal-login-dev-client-id"}, audiences) assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", authenticationInfo.PrincipalId) - assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail) + assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", *authenticationInfo.PrincipalEmail) assert.Nil(t, authenticationInfo.ServiceAccountName) assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo) @@ -657,7 +657,7 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { assert.Equal(t, []string{"https://stackit-service-account-dev.apps.01.cf.eu01.stackit.cloud"}, audiences) assert.Equal(t, "5e426aed-c487-4c48-af25-87f69cf9cdd4", authenticationInfo.PrincipalId) - assert.Equal(t, "Lukas.Schmitt@stackit.cloud", authenticationInfo.PrincipalEmail) + assert.Equal(t, "Lukas.Schmitt@stackit.cloud", *authenticationInfo.PrincipalEmail) assert.Nil(t, authenticationInfo.ServiceAccountName) assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo) @@ -756,7 +756,7 @@ func Test_NewAuditLogEntry(t *testing.T) { authenticationInfo := payload.AuthenticationInfo assert.NotNil(t, authenticationInfo) assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", authenticationInfo.PrincipalId) - assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail) + assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", *authenticationInfo.PrincipalEmail) assert.Nil(t, authenticationInfo.ServiceAccountName) assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo) @@ -889,7 +889,7 @@ func Test_NewAuditLogEntry(t *testing.T) { authenticationInfo := payload.AuthenticationInfo assert.NotNil(t, authenticationInfo) assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", authenticationInfo.PrincipalId) - assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail) + assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", *authenticationInfo.PrincipalEmail) assert.Nil(t, authenticationInfo.ServiceAccountName) assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo) diff --git a/internal/audit/api/schema_validation_test.go b/internal/audit/api/schema_validation_test.go index 24952be..013d085 100644 --- a/internal/audit/api/schema_validation_test.go +++ b/internal/audit/api/schema_validation_test.go @@ -129,3 +129,56 @@ func Test_RoutableAuditEvent(t *testing.T) { assert.EqualError(t, err, "validation error:\n - unencrypted_data.protobuf_type: value is required [required]") }) } + +func Test_AuthenticationInfo(t *testing.T) { + + validator, err := protovalidate.New() + assert.NoError(t, err) + + email := "x@x.x" + newEvent := func() auditV1.AuthenticationInfo { + return auditV1.AuthenticationInfo{ + PrincipalId: "1234567890", + PrincipalEmail: &email, + ServiceAccountName: nil, + ServiceAccountDelegationInfo: nil, + } + } + + t.Run("valid event", func(t *testing.T) { + event := newEvent() + err := validator.Validate(&event) + assert.NoError(t, err) + }) + + t.Run("valid event without email", func(t *testing.T) { + event := newEvent() + event.PrincipalEmail = nil + err := validator.Validate(&event) + assert.NoError(t, err) + }) + + t.Run("principal id contains only whitespace", func(t *testing.T) { + event := newEvent() + event.PrincipalId = " " + err := validator.Validate(&event) + assert.EqualError(t, err, "validation error:\n - principal_id: value does not match regex pattern `.*\\S.*` [string.pattern]") + }) + + t.Run("principal email contains only whitespace", func(t *testing.T) { + event := newEvent() + whitespaceEmail := " " + event.PrincipalEmail = &whitespaceEmail + err := validator.Validate(&event) + assert.EqualError(t, err, "validation error:\n - principal_email: value must be a valid email address [string.email]") + }) + + t.Run("missing host in email", func(t *testing.T) { + event := newEvent() + invalidEmail := "@test.com" + event.PrincipalEmail = &invalidEmail + err := validator.Validate(&event) + assert.EqualError(t, err, "validation error:\n - principal_email: value must be a valid email address [string.email]") + }) + +} diff --git a/internal/audit/api/test_data.go b/internal/audit/api/test_data.go index 8338305..b84ae50 100644 --- a/internal/audit/api/test_data.go +++ b/internal/audit/api/test_data.go @@ -43,6 +43,7 @@ func NewOrganizationAuditEvent( headers["Content-Type"] = "application/json" labels := make(map[string]string) labels["label1"] = "value1" + email := "user@example.com" auditEvent := &auditV1.AuditLogEntry{ LogName: fmt.Sprintf("%s/%s/logs/%s", pkgAuditCommon.ObjectTypeOrganization.Plural(), identifier, pkgAuditCommon.EventTypeAdminActivity), ProtoPayload: &auditV1.AuditLog{ @@ -51,7 +52,7 @@ func NewOrganizationAuditEvent( ResourceName: fmt.Sprintf("%s/%s", pkgAuditCommon.ObjectTypeOrganization.Plural(), identifier), AuthenticationInfo: &auditV1.AuthenticationInfo{ PrincipalId: uuid.NewString(), - PrincipalEmail: "user@example.com", + PrincipalEmail: &email, ServiceAccountName: nil, ServiceAccountDelegationInfo: nil, }, @@ -133,6 +134,7 @@ func NewFolderAuditEvent( headers["Content-Type"] = "application/json" labels := make(map[string]string) labels["label1"] = "value1" + email := "user@example.com" auditEvent := &auditV1.AuditLogEntry{ LogName: fmt.Sprintf("%s/%s/logs/%s", pkgAuditCommon.ObjectTypeFolder.Plural(), identifier, pkgAuditCommon.EventTypeAdminActivity), ProtoPayload: &auditV1.AuditLog{ @@ -141,7 +143,7 @@ func NewFolderAuditEvent( ResourceName: fmt.Sprintf("%s/%s", pkgAuditCommon.ObjectTypeFolder.Plural(), identifier), AuthenticationInfo: &auditV1.AuthenticationInfo{ PrincipalId: uuid.NewString(), - PrincipalEmail: "user@example.com", + PrincipalEmail: &email, ServiceAccountName: nil, ServiceAccountDelegationInfo: nil, }, @@ -223,6 +225,7 @@ func NewProjectAuditEvent( headers["Content-Type"] = "application/json" labels := make(map[string]string) labels["label1"] = "value1" + email := "user@example.com" auditEvent := &auditV1.AuditLogEntry{ LogName: fmt.Sprintf("%s/%s/logs/%s", pkgAuditCommon.ObjectTypeProject.Plural(), identifier, pkgAuditCommon.EventTypeAdminActivity), ProtoPayload: &auditV1.AuditLog{ @@ -231,7 +234,7 @@ func NewProjectAuditEvent( ResourceName: fmt.Sprintf("%s/%s", pkgAuditCommon.ObjectTypeProject.Plural(), identifier), AuthenticationInfo: &auditV1.AuthenticationInfo{ PrincipalId: uuid.NewString(), - PrincipalEmail: "user@example.com", + PrincipalEmail: &email, ServiceAccountName: nil, ServiceAccountDelegationInfo: nil, }, @@ -308,6 +311,7 @@ func NewProjectSystemAuditEvent( serviceAccountId := uuid.NewString() serviceAccountName := fmt.Sprintf("projects/%s/service-accounts/%s", identifier, serviceAccountId) delegationPrincipal := auditV1.ServiceAccountDelegationInfo{Authority: &auditV1.ServiceAccountDelegationInfo_SystemPrincipal_{}} + email := "service-account@sa.stackit.cloud" auditEvent := &auditV1.AuditLogEntry{ LogName: fmt.Sprintf("%s/%s/logs/%s", pkgAuditCommon.SystemIdentifier.Type, pkgAuditCommon.SystemIdentifier.Identifier, pkgAuditCommon.EventTypeSystemEvent), ProtoPayload: &auditV1.AuditLog{ @@ -316,7 +320,7 @@ func NewProjectSystemAuditEvent( ResourceName: fmt.Sprintf("%s/%s", pkgAuditCommon.ObjectTypeProject.Plural(), identifier), AuthenticationInfo: &auditV1.AuthenticationInfo{ PrincipalId: serviceAccountId, - PrincipalEmail: "service-account@sa.stackit.cloud", + PrincipalEmail: &email, ServiceAccountName: &serviceAccountName, ServiceAccountDelegationInfo: []*auditV1.ServiceAccountDelegationInfo{&delegationPrincipal}, }, @@ -388,6 +392,7 @@ func NewSystemAuditEvent( serviceAccountId := uuid.NewString() serviceAccountName := fmt.Sprintf("projects/%s/service-accounts/%s", identifier, serviceAccountId) delegationPrincipal := auditV1.ServiceAccountDelegationInfo{Authority: &auditV1.ServiceAccountDelegationInfo_SystemPrincipal_{}} + email := "service-account@sa.stackit.cloud" auditEvent := &auditV1.AuditLogEntry{ LogName: fmt.Sprintf("%s/%s/logs/%s", pkgAuditCommon.ObjectTypeSystem.Plural(), identifier, pkgAuditCommon.EventTypeSystemEvent), ProtoPayload: &auditV1.AuditLog{ @@ -396,7 +401,7 @@ func NewSystemAuditEvent( ResourceName: fmt.Sprintf("%s/%s", pkgAuditCommon.ObjectTypeSystem.Plural(), identifier), AuthenticationInfo: &auditV1.AuthenticationInfo{ PrincipalId: serviceAccountId, - PrincipalEmail: "service-account@sa.stackit.cloud", + PrincipalEmail: &email, ServiceAccountName: &serviceAccountName, ServiceAccountDelegationInfo: []*auditV1.ServiceAccountDelegationInfo{&delegationPrincipal}, }, diff --git a/pkg/audit/api/builder_test.go b/pkg/audit/api/builder_test.go index 5a765ae..eaf53e2 100644 --- a/pkg/audit/api/builder_test.go +++ b/pkg/audit/api/builder_test.go @@ -130,7 +130,7 @@ func Test_AuditLogEntryBuilder(t *testing.T) { authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo assert.NotNil(t, authenticationInfo) - assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail) + 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) @@ -246,7 +246,7 @@ func Test_AuditLogEntryBuilder(t *testing.T) { authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo assert.NotNil(t, authenticationInfo) - assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail) + 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) @@ -347,7 +347,7 @@ func Test_AuditLogEntryBuilder(t *testing.T) { authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo assert.NotNil(t, authenticationInfo) - assert.Equal(t, "my-service-yifc9e1@sa.stackit.cloud", authenticationInfo.PrincipalEmail) + assert.Equal(t, "my-service-yifc9e1@sa.stackit.cloud", *authenticationInfo.PrincipalEmail) assert.Equal(t, "10f38b01_534b_47bb_a03a_e294ca2be4de", authenticationInfo.PrincipalId) assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo) assert.Equal(t, "projects/dacc7830-843e-4c5e-86ff-aa0fb51d636f/service-accounts/10f38b01-534b-47bb-a03a-e294ca2be4de", *authenticationInfo.ServiceAccountName) @@ -429,7 +429,7 @@ func Test_AuditLogEntryBuilder(t *testing.T) { authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo assert.NotNil(t, authenticationInfo) - assert.Equal(t, internalAuditApi.EmailAddressDoNotReplyAtStackItDotCloud, authenticationInfo.PrincipalEmail) + assert.Nil(t, authenticationInfo.PrincipalEmail) assert.Equal(t, "none", authenticationInfo.PrincipalId) assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo) assert.Nil(t, authenticationInfo.ServiceAccountName) @@ -830,7 +830,7 @@ func Test_AuditEventBuilder(t *testing.T) { authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo assert.NotNil(t, authenticationInfo) - assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail) + 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) @@ -977,7 +977,7 @@ func Test_AuditEventBuilder(t *testing.T) { authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo assert.NotNil(t, authenticationInfo) - assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", authenticationInfo.PrincipalEmail) + 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) @@ -1095,7 +1095,7 @@ func Test_AuditEventBuilder(t *testing.T) { authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo assert.NotNil(t, authenticationInfo) - assert.Equal(t, internalAuditApi.EmailAddressDoNotReplyAtStackItDotCloud, authenticationInfo.PrincipalEmail) + assert.Nil(t, authenticationInfo.PrincipalEmail) assert.Equal(t, "none", authenticationInfo.PrincipalId) assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo) assert.Nil(t, authenticationInfo.ServiceAccountName) @@ -1204,7 +1204,7 @@ func Test_AuditEventBuilder(t *testing.T) { authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo assert.NotNil(t, authenticationInfo) - assert.Equal(t, internalAuditApi.EmailAddressDoNotReplyAtStackItDotCloud, authenticationInfo.PrincipalEmail) + assert.Nil(t, authenticationInfo.PrincipalEmail) assert.Equal(t, "none", authenticationInfo.PrincipalId) assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo) assert.Nil(t, authenticationInfo.ServiceAccountName) diff --git a/proto/audit/v1/audit_event.proto b/proto/audit/v1/audit_event.proto index c70f93d..56f5dfd 100644 --- a/proto/audit/v1/audit_event.proto +++ b/proto/audit/v1/audit_event.proto @@ -131,7 +131,8 @@ message AuditLog { // Required: true string service_name = 1 [ (buf.validate.field).required = true, - (buf.validate.field).string.min_len = 1 + (buf.validate.field).string.min_len = 1, + (buf.validate.field).string.pattern = ".*\\S.*" ]; // The name of the service method or operation. @@ -232,17 +233,18 @@ message AuthenticationInfo { // Required: true string principal_id = 1 [ (buf.validate.field).required = true, - (buf.validate.field).string.min_len = 1 + (buf.validate.field).string.min_len = 1, + (buf.validate.field).string.pattern = ".*\\S.*" ]; // The email address of the authenticated user. // Service accounts have email addresses that can be used. // - // Required: true - string principal_email = 2 [ - (buf.validate.field).required = true, - (buf.validate.field).string.min_len = 1, - (buf.validate.field).string.max_len = 255 + // Required: false + optional string principal_email = 2 [ + (buf.validate.field).string.min_len = 5, + (buf.validate.field).string.max_len = 255, + (buf.validate.field).string.email = true ]; // The name of the service account used to create or exchange @@ -414,7 +416,8 @@ message AttributeContext { string path = 4 [ (buf.validate.field).required = true, (buf.validate.field).string.min_len = 1, - (buf.validate.field).string.max_len = 255 + (buf.validate.field).string.max_len = 255, + (buf.validate.field).string.pattern = ".*\\S.*" ]; // The HTTP request `Host` header value. @@ -422,7 +425,8 @@ message AttributeContext { // Required: true string host = 5 [ (buf.validate.field).required = true, - (buf.validate.field).string.min_len = 1 + (buf.validate.field).string.min_len = 1, + (buf.validate.field).string.pattern = ".*\\S.*" ]; // The URL scheme, such as `http`, `https` or `gRPC`. @@ -430,7 +434,8 @@ message AttributeContext { // Required: true string scheme = 6 [ (buf.validate.field).required = true, - (buf.validate.field).string.min_len = 1 + (buf.validate.field).string.min_len = 1, + (buf.validate.field).string.pattern = ".*\\S.*" ]; // The HTTP URL query in the format of "name1=value1&name2=value2", as it @@ -457,7 +462,8 @@ message AttributeContext { // Required: true string protocol = 9 [ (buf.validate.field).required = true, - (buf.validate.field).string.min_len = 1 + (buf.validate.field).string.min_len = 1, + (buf.validate.field).string.pattern = ".*\\S.*" ]; // The request authentication. @@ -521,7 +527,8 @@ message RequestMetadata { string caller_supplied_user_agent = 2 [ (buf.validate.field).required = true, (buf.validate.field).string.min_len = 1, - (buf.validate.field).string.max_len = 255 + (buf.validate.field).string.max_len = 255, + (buf.validate.field).string.pattern = ".*\\S.*" ]; // This field contains request attributes like request url, time, etc. @@ -577,7 +584,8 @@ message ServiceAccountDelegationInfo { // Required: true string principal_id = 1 [ (buf.validate.field).required = true, - (buf.validate.field).string.min_len = 1 + (buf.validate.field).string.min_len = 1, + (buf.validate.field).string.pattern = ".*\\S.*" ]; // The email address of the authenticated user. @@ -587,7 +595,8 @@ message ServiceAccountDelegationInfo { string principal_email = 2 [ (buf.validate.field).required = true, (buf.validate.field).string.min_len = 1, - (buf.validate.field).string.max_len = 255 + (buf.validate.field).string.max_len = 255, + (buf.validate.field).string.pattern = ".*\\S.*" ]; // Metadata about the service that uses the service account.