From 5fd40d141565076a847972e5ac90ffc7ff1de44a Mon Sep 17 00:00:00 2001 From: Christian Schaible Date: Tue, 26 Nov 2024 06:27:46 +0000 Subject: [PATCH] Merged PR 684951: fix: Replace manual jwt parsing with jwx library Related work items: #701160 --- audit/api/builder_test.go | 6 +- audit/api/model.go | 185 ++++++++++++++++++++------------------ audit/api/model_test.go | 117 ++++++++++++++++++++---- audit/api/test_data.go | 1 + go.mod | 15 +++- go.sum | 35 ++++++-- 6 files changed, 242 insertions(+), 117 deletions(-) diff --git a/audit/api/builder_test.go b/audit/api/builder_test.go index dbc09fc..cf91f2f 100644 --- a/audit/api/builder_test.go +++ b/audit/api/builder_test.go @@ -331,7 +331,7 @@ func Test_AuditLogEntryBuilder(t *testing.T) { authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo assert.NotNil(t, authenticationInfo) - assert.Equal(t, "do-not-reply@stackit.cloud", authenticationInfo.PrincipalEmail) + assert.Equal(t, EmailAddressDoNotReplyAtStackItDotCloud, authenticationInfo.PrincipalEmail) assert.Equal(t, "none", authenticationInfo.PrincipalId) assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo) assert.Nil(t, authenticationInfo.ServiceAccountName) @@ -869,7 +869,7 @@ func Test_AuditEventBuilder(t *testing.T) { authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo assert.NotNil(t, authenticationInfo) - assert.Equal(t, "do-not-reply@stackit.cloud", authenticationInfo.PrincipalEmail) + assert.Equal(t, EmailAddressDoNotReplyAtStackItDotCloud, authenticationInfo.PrincipalEmail) assert.Equal(t, "none", authenticationInfo.PrincipalId) assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo) assert.Nil(t, authenticationInfo.ServiceAccountName) @@ -978,7 +978,7 @@ func Test_AuditEventBuilder(t *testing.T) { authenticationInfo := logEntry.ProtoPayload.AuthenticationInfo assert.NotNil(t, authenticationInfo) - assert.Equal(t, "do-not-reply@stackit.cloud", authenticationInfo.PrincipalEmail) + assert.Equal(t, EmailAddressDoNotReplyAtStackItDotCloud, authenticationInfo.PrincipalEmail) assert.Equal(t, "none", authenticationInfo.PrincipalId) assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo) assert.Nil(t, authenticationInfo.ServiceAccountName) diff --git a/audit/api/model.go b/audit/api/model.go index d2a6779..11778e2 100644 --- a/audit/api/model.go +++ b/audit/api/model.go @@ -1,12 +1,13 @@ package api import ( + "context" auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1" - "encoding/base64" "encoding/json" "errors" "fmt" "github.com/google/uuid" + "github.com/lestrrat-go/jwx/v2/jwt" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/structpb" @@ -20,6 +21,10 @@ import ( "time" ) +const EmailAddressDoNotReplyAtStackItDotCloud = "do-not-reply@stackit.cloud" +const TokenClaimStackitProjectId = "stackit/project/project.id" +const TokenClaimStackitServiceAccountId = "stackit/serviceaccount/service-account.uid" + var ErrInvalidRequestBody = errors.New("invalid request body") var ErrInvalidResponse = errors.New("invalid response") var ErrInvalidAuthorizationHeaderValue = errors.New("invalid authorization header value") @@ -632,7 +637,7 @@ func AuditAttributesFromAuthorizationHeader(request *ApiRequest) ( ) { var principalId = "none" - var principalEmail = "do-not-reply@stackit.cloud" + var principalEmail = EmailAddressDoNotReplyAtStackItDotCloud emptyClaims, _ := structpb.NewStruct(make(map[string]interface{})) var auditClaims = emptyClaims var authenticationPrincipal = "none/none" @@ -650,7 +655,12 @@ func AuditAttributesFromAuthorizationHeader(request *ApiRequest) ( if len(trimmedAuthorizationHeader) > 0 { // Parse claims - parsedClaims, filteredClaims, err := parseClaimsFromAuthorizationHeader(trimmedAuthorizationHeader) + token, err := parseToken(trimmedAuthorizationHeader) + if err != nil { + return nil, authenticationPrincipal, nil, nil, err + } + + filteredClaims, err := parseClaimsFromAuthorizationHeader(token) if err != nil { return nil, authenticationPrincipal, nil, nil, err } @@ -663,23 +673,24 @@ func AuditAttributesFromAuthorizationHeader(request *ApiRequest) ( auditClaims = auditClaimsStruct // Extract principal data - authenticationPrincipal = extractAuthenticationPrincipal(parsedClaims) - principalId, principalEmail = extractSubjectAndEmail(parsedClaims) + authenticationPrincipal = extractAuthenticationPrincipal(token) + principalId, principalEmail = extractSubjectAndEmail(token) // Extract service account delegation info data - delegationInfo = extractServiceAccountDelegationInfo(parsedClaims) - - // Extract audiences data - audiences, err = extractAudiences(parsedClaims) - if err != nil { - return nil, authenticationPrincipal, nil, nil, err + actClaim, hasActClaim := token.Get("act") + if hasActClaim { + actMap := map[string]interface{}{"act": actClaim} + delegationInfo = extractServiceAccountDelegationInfo(actMap) } - // Extract project id and service account id - projectId := extractServiceAccountProjectId(parsedClaims) - serviceAccountId := extractServiceAccountId(parsedClaims) + // Extract audiences data + audiences = token.Audience() - // Calculate service account name if project id and service account id are available + // Extract project id and service account id + projectId := getTokenClaim(token, TokenClaimStackitProjectId) + serviceAccountId := getTokenClaim(token, TokenClaimStackitServiceAccountId) + + // Calculate service account name if project and service account ids are available if projectId != nil && serviceAccountId != nil { accountName := fmt.Sprintf("projects/%s/service-accounts/%s", *projectId, *serviceAccountId) serviceAccountName = &accountName @@ -696,94 +707,80 @@ func AuditAttributesFromAuthorizationHeader(request *ApiRequest) ( return auditClaims, authenticationPrincipal, audiences, &authenticationInfo, nil } -func extractServiceAccountId(parsedClaims map[string]interface{}) *string { - projectId, projectIdExists := parsedClaims["stackit/serviceaccount/service-account.uid"] - if projectIdExists { - projectIdString := fmt.Sprintf("%s", projectId) - return &projectIdString +func getTokenClaim(token jwt.Token, claimName string) *string { + claim, claimExists := token.Get(claimName) + if claimExists { + claimString := fmt.Sprintf("%s", claim) + return &claimString } else { return nil } } -func extractServiceAccountProjectId(parsedClaims map[string]interface{}) *string { - projectId, projectIdExists := parsedClaims["stackit/project/project.id"] - if projectIdExists { - projectIdString := fmt.Sprintf("%s", projectId) - return &projectIdString - } else { - return nil - } -} - -func extractAudiences(parsedClaims map[string]interface{}) ([]string, error) { - audClaim, _ := json.Marshal(parsedClaims["aud"]) - - var audiences []string - if err := json.Unmarshal(audClaim, &audiences); err != nil { - return nil, err - } - return audiences, nil -} - -func extractAuthenticationPrincipal(parsedClaims map[string]interface{}) string { - subClaim, subExists := parsedClaims["sub"] - issuerClaim, issuerExists := parsedClaims["iss"] +func extractAuthenticationPrincipal(token jwt.Token) string { + subject := token.Subject() + issuer := token.Issuer() var principal = "none/none" - if subExists && issuerExists { - principal = fmt.Sprintf("%s/%s", url.QueryEscape(subClaim.(string)), url.QueryEscape(issuerClaim.(string))) + if subject != "" && issuer != "" { + principal = fmt.Sprintf("%s/%s", url.QueryEscape(subject), url.QueryEscape(issuer)) } return principal } -func parseClaimsFromAuthorizationHeader(authorizationHeader string) (map[string]interface{}, map[string]interface{}, error) { +func parseToken(authorizationHeader string) (jwt.Token, error) { parts := strings.Split(authorizationHeader, " ") if len(parts) != 2 { - return nil, nil, ErrInvalidAuthorizationHeaderValue + return nil, ErrInvalidAuthorizationHeaderValue } if !strings.EqualFold(parts[0], "Bearer") { - return nil, nil, ErrTokenIsNotBearerToken + return nil, ErrTokenIsNotBearerToken } - jwt := parts[1] - authorizationHeaderParts := strings.Split(jwt, ".") + jwtString := parts[1] + authorizationHeaderParts := strings.Split(jwtString, ".") - parsedClaims := make(map[string]interface{}) if len(authorizationHeaderParts) == 3 { - // base64 decoding - decodedString, err := base64.RawURLEncoding.DecodeString(authorizationHeaderParts[1]) + token, err := jwt.ParseInsecure([]byte(parts[1])) if err != nil { - return parsedClaims, nil, errors.Join(err, ErrInvalidBearerToken) + return nil, ErrInvalidBearerToken } - - // unmarshall claim part of token - err = json.Unmarshal(decodedString, &parsedClaims) - if err != nil { - return parsedClaims, nil, err - } - - // Collect user-friendly filtered subset of claims - filteredClaims := make(map[string]interface{}) - err = json.Unmarshal(decodedString, &filteredClaims) - if err != nil { - return parsedClaims, nil, err - } - keysToDelete := make([]string, 0) - for key := range filteredClaims { - if key != "aud" && key != "email" && key != "iss" && key != "jti" && key != "sub" { - keysToDelete = append(keysToDelete, key) - } - } - for _, key := range keysToDelete { - delete(filteredClaims, key) - } - return parsedClaims, filteredClaims, nil + return token, nil } - return parsedClaims, nil, ErrInvalidBearerToken + return nil, ErrInvalidBearerToken } -func extractServiceAccountDelegationInfoDetails(token map[string]interface{}) []*auditV1.ServiceAccountDelegationInfo { - principalId, principalEmail := extractSubjectAndEmail(token) +func parseClaimsFromAuthorizationHeader(token jwt.Token) (map[string]interface{}, error) { + claimsMap, err := token.AsMap(context.Background()) + if err != nil { + return nil, err + } + + claims := map[string]interface{}{} + if len(token.Audience()) > 0 { + var audiences []any + for _, audience := range token.Audience() { + audiences = append(audiences, audience) + } + claims["aud"] = audiences + } + + for key := range claimsMap { + if key == "aud" { + continue + } + value := claimsMap[key] + t, isTime := value.(time.Time) + if isTime { + claims[key] = t.String() + } else if value != nil && value != "" { + claims[key] = value + } + } + return claims, nil +} + +func extractServiceAccountDelegationInfoDetails(actClaims map[string]interface{}) []*auditV1.ServiceAccountDelegationInfo { + principalId, principalEmail := extractSubjectAndEmailFromActClaims(actClaims) delegation := auditV1.ServiceAccountDelegationInfo{Authority: &auditV1.ServiceAccountDelegationInfo_IdpPrincipal_{IdpPrincipal: &auditV1.ServiceAccountDelegationInfo_IdpPrincipal{ PrincipalId: principalId, @@ -792,7 +789,7 @@ func extractServiceAccountDelegationInfoDetails(token map[string]interface{}) [] }}} delegations := []*auditV1.ServiceAccountDelegationInfo{&delegation} - nestedDelegations := extractServiceAccountDelegationInfo(token) + nestedDelegations := extractServiceAccountDelegationInfo(actClaims) if len(nestedDelegations) > 0 { return append(delegations, nestedDelegations...) } else { @@ -800,9 +797,9 @@ func extractServiceAccountDelegationInfoDetails(token map[string]interface{}) [] } } -func extractServiceAccountDelegationInfo(token map[string]interface{}) []*auditV1.ServiceAccountDelegationInfo { - actor := token["act"] - if actor != nil { +func extractServiceAccountDelegationInfo(claims map[string]interface{}) []*auditV1.ServiceAccountDelegationInfo { + actor, hasActor := claims["act"] + if hasActor { actorMap, hasActorClaim := actor.(map[string]interface{}) if hasActorClaim { return extractServiceAccountDelegationInfoDetails(actorMap) @@ -811,18 +808,30 @@ func extractServiceAccountDelegationInfo(token map[string]interface{}) []*auditV return nil } -func extractSubjectAndEmail(token map[string]interface{}) (string, string) { +func extractSubjectAndEmailFromActClaims(actClaim map[string]interface{}) (string, string) { var principalEmail string - principalId := fmt.Sprintf("%s", token["sub"]) - principalEmailRaw := token["email"] + principalId := fmt.Sprintf("%s", actClaim["sub"]) + principalEmailRaw := actClaim["email"] if principalEmailRaw == nil { - principalEmail = "do-not-reply@stackit.cloud" + principalEmail = EmailAddressDoNotReplyAtStackItDotCloud } else { principalEmail = fmt.Sprintf("%s", principalEmailRaw) } return principalId, principalEmail } +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) + } + return principalId, principalEmail +} + // OperationNameFromUrlPath converts the request url path into an operation name. // UUIDs and query parameters are filtered out, slashes replaced by dots. // HTTP methods are added as suffix as follows: diff --git a/audit/api/model_test.go b/audit/api/model_test.go index b75f9be..63d58ce 100644 --- a/audit/api/model_test.go +++ b/audit/api/model_test.go @@ -419,13 +419,19 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { auditClaims, authenticationPrincipal, audiences, authenticationInfo, err := AuditAttributesFromAuthorizationHeader(&request) + assert.Nil(t, err) auditClaimsMap := auditClaims.AsMap() - assert.Nil(t, err) + assert.Len(t, auditClaimsMap, 9) + assert.Equal(t, []interface{}{"stackit-resource-manager-dev"}, auditClaimsMap["aud"]) + assert.Equal(t, "stackit-resource-manager-dev", auditClaimsMap["client_id"]) + assert.Equal(t, "2024-08-23 09:28:46 +0000 UTC", auditClaimsMap["exp"]) + assert.Equal(t, "2024-08-23 09:13:46 +0000 UTC", auditClaimsMap["iat"]) assert.Equal(t, "https://accounts.dev.stackit.cloud", auditClaimsMap["iss"]) - assert.Equal(t, "stackit-resource-manager-dev", auditClaimsMap["sub"]) - assert.Equal(t, []interface{}{"stackit-resource-manager-dev"}, auditClaimsMap["aud"].([]interface{})) assert.Equal(t, "e46eba38-dedb-4541-94f3-49f97a934d58", auditClaimsMap["jti"]) + assert.Equal(t, "2024-08-23 09:13:46 +0000 UTC", auditClaimsMap["nbf"]) + assert.Equal(t, "uaa.none", auditClaimsMap["scope"]) + assert.Equal(t, "stackit-resource-manager-dev", auditClaimsMap["sub"]) principal := fmt.Sprintf("%s/%s", url.QueryEscape("stackit-resource-manager-dev"), @@ -448,13 +454,22 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { auditClaims, authenticationPrincipal, audiences, authenticationInfo, err := AuditAttributesFromAuthorizationHeader(&request) + assert.Nil(t, err) auditClaimsMap := auditClaims.AsMap() - assert.Nil(t, err) + assert.Len(t, auditClaimsMap, 12) + assert.Equal(t, []interface{}{"stackit", "api"}, auditClaimsMap["aud"]) + assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", auditClaimsMap["azp"]) + assert.Equal(t, "my-service-yifc9e1@sa.stackit.cloud", auditClaimsMap["email"]) + assert.Equal(t, "2024-08-03 07:15:43 +0000 UTC", auditClaimsMap["exp"]) + assert.Equal(t, "2024-08-02 07:15:43 +0000 UTC", auditClaimsMap["iat"]) assert.Equal(t, "stackit/serviceaccount", auditClaimsMap["iss"]) - assert.Equal(t, "10f38b01-534b-47bb-a03a-e294ca2be4de", auditClaimsMap["sub"]) - assert.Equal(t, []interface{}{"stackit", "api"}, auditClaimsMap["aud"].([]interface{})) assert.Equal(t, "84c30a46-1001-436f-859f-89c0ba19be1e", auditClaimsMap["jti"]) + assert.Equal(t, "api", auditClaimsMap["stackit/serviceaccount/namespace"]) + assert.Equal(t, "10f38b01-534b-47bb-a03a-e294ca2be4de", auditClaimsMap[TokenClaimStackitServiceAccountId]) + assert.Equal(t, "legacy", auditClaimsMap["stackit/serviceaccount/token.source"]) + assert.Equal(t, "dacc7830-843e-4c5e-86ff-aa0fb51d636f", auditClaimsMap[TokenClaimStackitProjectId]) + assert.Equal(t, "10f38b01-534b-47bb-a03a-e294ca2be4de", auditClaimsMap["sub"]) principal := fmt.Sprintf("%s/%s", url.QueryEscape("10f38b01-534b-47bb-a03a-e294ca2be4de"), @@ -479,13 +494,26 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { auditClaims, authenticationPrincipal, audiences, authenticationInfo, err := AuditAttributesFromAuthorizationHeader(&request) + assert.Nil(t, err) auditClaimsMap := auditClaims.AsMap() - assert.Nil(t, err) + assert.Len(t, auditClaimsMap, 13) + assert.Equal(t, []interface{}{"stackit", "api"}, auditClaimsMap["aud"]) + assert.Equal(t, "02aef516-317f-4ec1-a1df-1acbd4d49fe3", auditClaimsMap["azp"]) + assert.Equal(t, "service-account-2-tj9srt1@sa.stackit.cloud", auditClaimsMap["email"]) + assert.Equal(t, "2024-08-19 10:21:47 +0000 UTC", auditClaimsMap["exp"]) + assert.Equal(t, "2024-08-19 09:21:47 +0000 UTC", auditClaimsMap["iat"]) assert.Equal(t, "stackit/serviceaccount", auditClaimsMap["iss"]) - assert.Equal(t, "f45009b2-6433-43c1-b6c7-618c44359e71", auditClaimsMap["sub"]) - assert.Equal(t, []interface{}{"stackit", "api"}, auditClaimsMap["aud"].([]interface{})) assert.Equal(t, "37555183-01b9-4270-bdc1-69b4fcfd5ee9", auditClaimsMap["jti"]) + assert.Equal(t, "api", auditClaimsMap["stackit/serviceaccount/namespace"]) + assert.Equal(t, "f45009b2-6433-43c1-b6c7-618c44359e71", auditClaimsMap[TokenClaimStackitServiceAccountId]) + assert.Equal(t, "oauth2", auditClaimsMap["stackit/serviceaccount/token.source"]) + assert.Equal(t, "dacc7830-843e-4c5e-86ff-aa0fb51d636f", auditClaimsMap[TokenClaimStackitProjectId]) + assert.Equal(t, "f45009b2-6433-43c1-b6c7-618c44359e71", auditClaimsMap["sub"]) + assert.NotNil(t, auditClaimsMap["act"]) + act := auditClaimsMap["act"].(map[string]interface{}) + assert.NotNil(t, act) + assert.Equal(t, "02aef516-317f-4ec1-a1df-1acbd4d49fe3", act["sub"]) principal := fmt.Sprintf("%s/%s", url.QueryEscape("f45009b2-6433-43c1-b6c7-618c44359e71"), @@ -514,13 +542,29 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { auditClaims, authenticationPrincipal, audiences, authenticationInfo, err := AuditAttributesFromAuthorizationHeader(&request) + assert.Nil(t, err) auditClaimsMap := auditClaims.AsMap() - assert.Nil(t, err) + assert.Len(t, auditClaimsMap, 13) + assert.Equal(t, []interface{}{"stackit", "api"}, auditClaimsMap["aud"]) + assert.Equal(t, "f45009b2-6433-43c1-b6c7-618c44359e71", auditClaimsMap["azp"]) + assert.Equal(t, "service-account-3-fghsxw1@sa.stackit.cloud", auditClaimsMap["email"]) + assert.Equal(t, "2024-08-19 10:22:43 +0000 UTC", auditClaimsMap["exp"]) + assert.Equal(t, "2024-08-19 09:22:43 +0000 UTC", auditClaimsMap["iat"]) assert.Equal(t, "stackit/serviceaccount", auditClaimsMap["iss"]) - assert.Equal(t, "1734b4b6-1d5e-4819-9b50-29917a1b9ad5", auditClaimsMap["sub"]) - assert.Equal(t, []interface{}{"stackit", "api"}, auditClaimsMap["aud"].([]interface{})) assert.Equal(t, "1f7f1efc-3349-411a-a5d7-2255e0a5a8ae", auditClaimsMap["jti"]) + assert.Equal(t, "api", auditClaimsMap["stackit/serviceaccount/namespace"]) + assert.Equal(t, "1734b4b6-1d5e-4819-9b50-29917a1b9ad5", auditClaimsMap[TokenClaimStackitServiceAccountId]) + assert.Equal(t, "oauth2", auditClaimsMap["stackit/serviceaccount/token.source"]) + assert.Equal(t, "dacc7830-843e-4c5e-86ff-aa0fb51d636f", auditClaimsMap[TokenClaimStackitProjectId]) + assert.Equal(t, "1734b4b6-1d5e-4819-9b50-29917a1b9ad5", auditClaimsMap["sub"]) + assert.NotNil(t, auditClaimsMap["act"]) + act := auditClaimsMap["act"].(map[string]interface{}) + assert.NotNil(t, act) + assert.Equal(t, "f45009b2-6433-43c1-b6c7-618c44359e71", act["sub"]) + nestedAct := act["act"].(map[string]interface{}) + assert.NotNil(t, nestedAct) + assert.Equal(t, "02aef516-317f-4ec1-a1df-1acbd4d49fe3", nestedAct["sub"]) principal := fmt.Sprintf("%s/%s", url.QueryEscape("1734b4b6-1d5e-4819-9b50-29917a1b9ad5"), @@ -551,13 +595,21 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { auditClaims, authenticationPrincipal, audiences, authenticationInfo, err := AuditAttributesFromAuthorizationHeader(&request) + assert.Nil(t, err) auditClaimsMap := auditClaims.AsMap() - assert.Nil(t, err) + assert.Len(t, auditClaimsMap, 11) + assert.Equal(t, []interface{}{"stackit-portal-login-dev-client-id"}, auditClaimsMap["aud"]) + assert.Equal(t, "stackit-portal-login-dev-client-id", auditClaimsMap["client_id"]) + assert.Equal(t, "Christian.Schaible@novatec-gmbh.de", auditClaimsMap["email"]) + assert.True(t, auditClaimsMap["email_verified"].(bool)) + assert.Equal(t, "2024-08-02 09:19:27 +0000 UTC", auditClaimsMap["exp"]) + assert.Equal(t, "2024-08-02 08:19:27 +0000 UTC", auditClaimsMap["iat"]) assert.Equal(t, "https://accounts.dev.stackit.cloud", auditClaimsMap["iss"]) - assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", auditClaimsMap["sub"]) - assert.Equal(t, []interface{}{"stackit-portal-login-dev-client-id"}, auditClaimsMap["aud"].([]interface{})) assert.Equal(t, "d73a67ac-d1ec-4b55-99d4-e953275f022a", auditClaimsMap["jti"]) + assert.Equal(t, "2024-08-02 08:19:27 +0000 UTC", auditClaimsMap["nbf"]) + assert.Equal(t, "openid email", auditClaimsMap["scope"]) + assert.Equal(t, "cd94f01a-df2e-4456-902e-48f5e57f0b63", auditClaimsMap["sub"]) principal := fmt.Sprintf("%s/%s", url.QueryEscape("cd94f01a-df2e-4456-902e-48f5e57f0b63"), @@ -572,6 +624,41 @@ func Test_AuditAttributesFromAuthorizationHeader(t *testing.T) { assert.Nil(t, authenticationInfo.ServiceAccountName) assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo) }) + + t.Run("user token with simple aud claim", func(t *testing.T) { + headers := make(map[string][]string) + headers["Authorization"] = []string{userTokenWithSimpleAudience} + request := ApiRequest{Header: headers} + + auditClaims, authenticationPrincipal, audiences, authenticationInfo, err := + AuditAttributesFromAuthorizationHeader(&request) + assert.NoError(t, err) + + auditClaimsMap := auditClaims.AsMap() + assert.Len(t, auditClaimsMap, 9) + assert.Equal(t, []interface{}{"https://stackit-service-account-dev.apps.01.cf.eu01.stackit.cloud"}, auditClaimsMap["aud"]) + assert.Equal(t, "Lukas.Schmitt@stackit.cloud", auditClaimsMap["email"]) + assert.Equal(t, "2024-11-21 09:40:35 +0000 UTC", auditClaimsMap["exp"]) + assert.Equal(t, "2024-11-21 08:40:35 +0000 UTC", auditClaimsMap["iat"]) + assert.Equal(t, "https://api.dev.stackit.cloud", auditClaimsMap["iss"]) + assert.Equal(t, "c2be1651-1e54-4e6e-bac3-ef072b3f0149", auditClaimsMap["jti"]) + assert.Equal(t, "2024-11-21 08:40:18 +0000 UTC", auditClaimsMap["nbf"]) + assert.Equal(t, "openid email portal-bff", auditClaimsMap["scope"]) + assert.Equal(t, "5e426aed-c487-4c48-af25-87f69cf9cdd4", auditClaimsMap["sub"]) + + principal := fmt.Sprintf("%s/%s", + url.QueryEscape("5e426aed-c487-4c48-af25-87f69cf9cdd4"), + url.QueryEscape("https://api.dev.stackit.cloud")) + assert.Equal(t, principal, authenticationPrincipal) + + 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.Nil(t, authenticationInfo.ServiceAccountName) + assert.Nil(t, authenticationInfo.ServiceAccountDelegationInfo) + }) } func Test_NewAuditLogEntry(t *testing.T) { diff --git a/audit/api/test_data.go b/audit/api/test_data.go index c384cc8..dc66214 100644 --- a/audit/api/test_data.go +++ b/audit/api/test_data.go @@ -18,6 +18,7 @@ const serviceAccountTokenRepeatedlyImpersonated = "Bearer eyJraWQiOiJaVFJqWlRNek const serviceAccountTokenImpersonated = "Bearer eyJraWQiOiJaVFJqWlRNek5tSmlNRGt3TldJMU5USTRZVGxpT1RjMllUWXlZVE16WldNIiwiYWxnIjoiUlM1MTIifQ.eyJzdWIiOiJmNDUwMDliMi02NDMzLTQzYzEtYjZjNy02MThjNDQzNTllNzEiLCJpc3MiOiJzdGFja2l0L3NlcnZpY2VhY2NvdW50IiwiYXVkIjpbInN0YWNraXQiLCJhcGkiXSwic3RhY2tpdC9zZXJ2aWNlYWNjb3VudC90b2tlbi5zb3VyY2UiOiJvYXV0aDIiLCJhY3QiOnsic3ViIjoiMDJhZWY1MTYtMzE3Zi00ZWMxLWExZGYtMWFjYmQ0ZDQ5ZmUzIn0sInN0YWNraXQvc2VydmljZWFjY291bnQvbmFtZXNwYWNlIjoiYXBpIiwic3RhY2tpdC9wcm9qZWN0L3Byb2plY3QuaWQiOiJkYWNjNzgzMC04NDNlLTRjNWUtODZmZi1hYTBmYjUxZDYzNmYiLCJhenAiOiIwMmFlZjUxNi0zMTdmLTRlYzEtYTFkZi0xYWNiZDRkNDlmZTMiLCJzdGFja2l0L3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJmNDUwMDliMi02NDMzLTQzYzEtYjZjNy02MThjNDQzNTllNzEiLCJleHAiOjE3MjQwNjI5MDcsImlhdCI6MTcyNDA1OTMwNywiZW1haWwiOiJzZXJ2aWNlLWFjY291bnQtMi10ajlzcnQxQHNhLnN0YWNraXQuY2xvdWQiLCJqdGkiOiIzNzU1NTE4My0wMWI5LTQyNzAtYmRjMS02OWI0ZmNmZDVlZTkifQ.auBvvsIesFMAlWOCPCPC77DrrHF7gSKZwKs_Zry5KFvu2bpZZC1BcSXOc8b9eh0SzANI9M9aGJBhOzOm39-ZZ5XOQ-6_y1aWuEenYQ6kT5D3GzCUTMDzSi1lcZ4IG5nFMa_AAlVEN_7AMv7LHGtz49bWLJnAgeTo1cvof-OgP4mCQ5O6E0iyAq-5u8V8NJL7HIZy7BDe4J1mjfYhwKagrN7QFWu4fhN4TNS7d922X_6V489BhjRFRYjLW_qDnv912JorbGRz_XwNy_dPA81EkdMyKE0BJUezguJUEKEG2_JEi9O64Flcoi6x8cFHYhaDuMMSLipzePaHdyk2lQtH7Q" const serviceAccountToken = "Bearer eyJraWQiOiJaVFJqWlRNek5tSmlNRGt3TldJMU5USTRZVGxpT1RjMllUWXlZVE16WldNIiwiYWxnIjoiUlM1MTIifQ.eyJzdWIiOiIxMGYzOGIwMS01MzRiLTQ3YmItYTAzYS1lMjk0Y2EyYmU0ZGUiLCJhdWQiOlsic3RhY2tpdCIsImFwaSJdLCJzdGFja2l0L3NlcnZpY2VhY2NvdW50L3Rva2VuLnNvdXJjZSI6ImxlZ2FjeSIsInN0YWNraXQvc2VydmljZWFjY291bnQvbmFtZXNwYWNlIjoiYXBpIiwic3RhY2tpdC9wcm9qZWN0L3Byb2plY3QuaWQiOiJkYWNjNzgzMC04NDNlLTRjNWUtODZmZi1hYTBmYjUxZDYzNmYiLCJhenAiOiJjZDk0ZjAxYS1kZjJlLTQ0NTYtOTAyZS00OGY1ZTU3ZjBiNjMiLCJpc3MiOiJzdGFja2l0L3NlcnZpY2VhY2NvdW50Iiwic3RhY2tpdC9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiMTBmMzhiMDEtNTM0Yi00N2JiLWEwM2EtZTI5NGNhMmJlNGRlIiwiZXhwIjoxNzIyNjY5MzQzLCJpYXQiOjE3MjI1ODI5NDMsImVtYWlsIjoibXktc2VydmljZS15aWZjOWUxQHNhLnN0YWNraXQuY2xvdWQiLCJqdGkiOiI4NGMzMGE0Ni0xMDAxLTQzNmYtODU5Zi04OWMwYmExOWJlMWUifQ.hb8X9VKc9xViHgNMyFHT9ePj_lyEwTV1D2es8E278WtoCJ9-4GPPQGjhcLGGrigjnvpRYV2LKzNqpQslerT5lFT_pHACsryaAE0ImYjmoe-nutA7BBpYuM_JN6pk5VIjVFLTqRKeIvFexPacqS2Vo3YoK1GvxPB8WPWBbGIsBtMl-PTm8OTwwzooBOoCRhhMR-E1lFbAymLsc1JI4yDQKLLomvhEopgmocCnQ-P1QkiKMqdkNxiD_YYLLYTOApg6d62BhqpH66ziqx493AStdZ8d5Kjvf3e1knDhaxVwNCghQj7lSo2kNAqZe__g2tiXpiZNTXBFJ_5HgQMLh67wng" const userToken = "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOGJlZjc1LWRmY2QtNGE3My1hMzkxLTU0YTdhZjU3YTdkNiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsic3RhY2tpdC1wb3J0YWwtbG9naW4tZGV2LWNsaWVudC1pZCJdLCJjbGllbnRfaWQiOiJzdGFja2l0LXBvcnRhbC1sb2dpbi1kZXYtY2xpZW50LWlkIiwiZW1haWwiOiJDaHJpc3RpYW4uU2NoYWlibGVAbm92YXRlYy1nbWJoLmRlIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImV4cCI6MTcyMjU5MDM2NywiaWF0IjoxNzIyNTg2NzY3LCJpc3MiOiJodHRwczovL2FjY291bnRzLmRldi5zdGFja2l0LmNsb3VkIiwianRpIjoiZDczYTY3YWMtZDFlYy00YjU1LTk5ZDQtZTk1MzI3NWYwMjJhIiwibmJmIjoxNzIyNTg2NzY3LCJzY29wZSI6Im9wZW5pZCBlbWFpbCIsInN1YiI6ImNkOTRmMDFhLWRmMmUtNDQ1Ni05MDJlLTQ4ZjVlNTdmMGI2MyJ9.ajhjYbC5l5g7un9NSheoAwBT83YcZM91rH4DJxPTDsB78HzIVrmaKTPrK3AI_E1THlD2Z3_ot9nFr_eX7XcwWp_ZBlataKmakdXlAmeb4xSMGNYefIfzV_3w9ZZAZ66yoeTrtn8dUx5ezquenCYpctB1NcccmK4U09V0kNcq9dFcfF3Sg9YilF3orUCR0ql1d9RnOs3EiFZuUpdBEkyoVsAdSh2P-PRbNViR_FgCcAJem97TsN5CQc9RlvKYe4sYKgqQoqa2GDVi9Niiw3fe1V8SCnROYcpkOzBBWdvuzFMBUjln3uOogYVOz93xkmImV6jidgyQ70fLt-eDUmZZfg" +const userTokenWithSimpleAudience = "Bearer eyJhbGciOiJSUzUxMiIsImtpZCI6InNlcnZpY2UtYWNjb3VudC1mMDdiZjZhOC02MjA3LTRmOGItYjNlOS03M2VkMGJlYjg4ZjUiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL3N0YWNraXQtc2VydmljZS1hY2NvdW50LWRldi5hcHBzLjAxLmNmLmV1MDEuc3RhY2tpdC5jbG91ZCIsImVtYWlsIjoiTHVrYXMuU2NobWl0dEBzdGFja2l0LmNsb3VkIiwiZXhwIjoxNzMyMTgyMDM1LCJpYXQiOjE3MzIxNzg0MzUsImlzcyI6Imh0dHBzOi8vYXBpLmRldi5zdGFja2l0LmNsb3VkIiwianRpIjoiYzJiZTE2NTEtMWU1NC00ZTZlLWJhYzMtZWYwNzJiM2YwMTQ5IiwibmJmIjoxNzMyMTc4NDE4LCJyb2xlcyI6bnVsbCwic2NvcGUiOiJvcGVuaWQgZW1haWwgcG9ydGFsLWJmZiIsInN1YiI6IjVlNDI2YWVkLWM0ODctNGM0OC1hZjI1LTg3ZjY5Y2Y5Y2RkNCIsInVzZXJfaWQiOiIiLCJ4X2NsaWVudF9pZCI6IiIsInppZCI6IiJ9.notavailable" var TestHeaders = map[string][]string{"user-agent": {"custom"}, "authorization": {userToken}} diff --git a/go.mod b/go.mod index ab6ec3b..1402481 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/Azure/go-amqp v1.2.0 github.com/bufbuild/protovalidate-go v0.7.2 github.com/google/uuid v1.6.0 + github.com/lestrrat-go/jwx/v2 v2.1.2 github.com/rs/zerolog v1.33.0 github.com/stretchr/testify v1.9.0 github.com/testcontainers/testcontainers-go v0.34.0 @@ -26,6 +27,7 @@ require ( github.com/containerd/platforms v0.2.1 // indirect github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/docker v27.1.1+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect @@ -34,9 +36,15 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect + github.com/goccy/go-json v0.10.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/cel-go v0.21.0 // indirect github.com/klauspost/compress v1.17.4 // indirect + github.com/lestrrat-go/blackmagic v1.0.2 // indirect + github.com/lestrrat-go/httpcc v1.0.1 // indirect + github.com/lestrrat-go/httprc v1.0.6 // indirect + github.com/lestrrat-go/iter v1.0.2 // indirect + github.com/lestrrat-go/option v1.0.1 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -52,6 +60,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/segmentio/asm v1.2.0 // indirect github.com/shirou/gopsutil/v3 v3.23.12 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect @@ -62,11 +71,11 @@ require ( github.com/yusufpapurcu/wmi v1.2.3 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect go.opentelemetry.io/otel/metric v1.32.0 // indirect - golang.org/x/crypto v0.24.0 // indirect + golang.org/x/crypto v0.28.0 // indirect golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect golang.org/x/net v0.26.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 995e2e8..592231a 100644 --- a/go.sum +++ b/go.sum @@ -30,6 +30,8 @@ github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= @@ -51,6 +53,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -72,6 +76,18 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= +github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= +github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= +github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= +github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k= +github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= +github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= +github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= +github.com/lestrrat-go/jwx/v2 v2.1.2 h1:6poete4MPsO8+LAEVhpdrNI4Xp2xdiafgl2RD89moBc= +github.com/lestrrat-go/jwx/v2 v2.1.2/go.mod h1:pO+Gz9whn7MPdbsqSJzG8TlEpMZCwQDXnFJ+zsUVh8Y= +github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= +github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -108,6 +124,8 @@ github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4 github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= +github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= @@ -123,6 +141,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -159,8 +178,8 @@ go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v8 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -187,14 +206,14 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=