From e312b83349eccf898939d9175d09a3388bb3766a Mon Sep 17 00:00:00 2001 From: chrislu Date: Sun, 24 Aug 2025 21:05:25 -0700 Subject: [PATCH] fix: extract role information from JWT token in presigned URL validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TestPresignedURLIAMValidation was failing because the presigned URL validation was hardcoding the principal ARN as 'PresignedUser' instead of extracting the actual role from the JWT session token. ### Problem: - Test used session token from S3ReadOnlyRole - ValidatePresignedURLWithIAM hardcoded principal as PresignedUser - Authorization checked wrong role permissions - PUT operation incorrectly succeeded instead of being denied ### Solution: - Extract role and session information from JWT token claims - Use parseJWTToken() to get 'role' and 'snam' claims - Build correct principal ARN from token data - Use 'principal' claim directly if available, fallback to constructed ARN ### Test Results: ✅ TestPresignedURLIAMValidation: All 4 test cases now pass ✅ GET with read permissions: ALLOWED (correct) ✅ PUT with read-only permissions: DENIED (correct - was failing before) ✅ GET without session token: Falls back to standard auth ✅ Invalid session token: Correctly rejected ### Technical Details: - Principal now correctly shows: arn:seaweed:sts::assumed-role/S3ReadOnlyRole/presigned-test-session - Authorization logic now validates against actual assumed role - Maintains compatibility with existing presigned URL generation tests - All 20+ presigned URL tests continue to pass This ensures presigned URLs respect the actual IAM role permissions from the session token, providing proper security enforcement. --- weed/s3api/s3_presigned_url_iam.go | 35 +++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/weed/s3api/s3_presigned_url_iam.go b/weed/s3api/s3_presigned_url_iam.go index 76768b5b4..86b07668b 100644 --- a/weed/s3api/s3_presigned_url_iam.go +++ b/weed/s3api/s3_presigned_url_iam.go @@ -70,9 +70,38 @@ func (iam *IdentityAccessManagement) ValidatePresignedURLWithIAM(r *http.Request return s3err.ErrNone } - // Create IAM identity for authorization - // Use a proper ARN format for the principal - principalArn := fmt.Sprintf("arn:seaweed:sts::assumed-role/PresignedUser/%s", identity.Name) + // Parse JWT token to extract role and session information + tokenClaims, err := parseJWTToken(sessionToken) + if err != nil { + glog.V(3).Infof("Failed to parse JWT token in presigned URL: %v", err) + return s3err.ErrAccessDenied + } + + // Extract role information from token claims + roleName, ok := tokenClaims["role"].(string) + if !ok || roleName == "" { + glog.V(3).Info("No role found in JWT token for presigned URL") + return s3err.ErrAccessDenied + } + + sessionName, ok := tokenClaims["snam"].(string) + if !ok || sessionName == "" { + sessionName = "presigned-session" // Default fallback + } + + // Use the principal ARN directly from token claims, or build it if not available + principalArn, ok := tokenClaims["principal"].(string) + if !ok || principalArn == "" { + // Fallback: extract role name from role ARN and build principal ARN + roleNameOnly := roleName + if strings.Contains(roleName, "/") { + parts := strings.Split(roleName, "/") + roleNameOnly = parts[len(parts)-1] + } + principalArn = fmt.Sprintf("arn:seaweed:sts::assumed-role/%s/%s", roleNameOnly, sessionName) + } + + // Create IAM identity for authorization using extracted information iamIdentity := &IAMIdentity{ Name: identity.Name, Principal: principalArn,