diff --git a/test/s3/iam/iam_config.json b/test/s3/iam/iam_config.json index ac02184a7..0f77c8ded 100644 --- a/test/s3/iam/iam_config.json +++ b/test/s3/iam/iam_config.json @@ -217,15 +217,7 @@ { "Effect": "Allow", "Action": [ - "s3:PutObject", - "s3:PutObjectAcl", - "s3:DeleteObject", - "s3:DeleteObjectVersion", - "s3:InitiateMultipartUpload", - "s3:UploadPart", - "s3:CompleteMultipartUpload", - "s3:AbortMultipartUpload", - "s3:ListMultipartUploadParts" + "s3:*" ], "Resource": [ "arn:seaweed:s3:::*", diff --git a/weed/iam/integration/iam_manager.go b/weed/iam/integration/iam_manager.go index a963b6782..b9ec26ceb 100644 --- a/weed/iam/integration/iam_manager.go +++ b/weed/iam/integration/iam_manager.go @@ -231,10 +231,12 @@ func (m *IAMManager) IsActionAllowed(ctx context.Context, request *ActionRequest return false, fmt.Errorf("IAM manager not initialized") } - // Validate session token first - _, err := m.stsService.ValidateSessionToken(ctx, request.SessionToken) - if err != nil { - return false, fmt.Errorf("invalid session: %w", err) + // Validate session token first (skip for OIDC tokens which are already validated) + if !isOIDCToken(request.SessionToken) { + _, err := m.stsService.ValidateSessionToken(ctx, request.SessionToken) + if err != nil { + return false, fmt.Errorf("invalid session: %w", err) + } } // Extract role name from principal ARN @@ -379,17 +381,24 @@ func extractRoleNameFromArn(roleArn string) string { return "" } -// extractRoleNameFromPrincipal extracts role name from assumed role principal ARN +// extractRoleNameFromPrincipal extracts role name from principal ARN func extractRoleNameFromPrincipal(principal string) string { - // Expected format: arn:seaweed:sts::assumed-role/RoleName/SessionName - prefix := "arn:seaweed:sts::assumed-role/" - if len(principal) > len(prefix) && principal[:len(prefix)] == prefix { - remainder := principal[len(prefix):] + // Handle STS assumed role format: arn:seaweed:sts::assumed-role/RoleName/SessionName + stsPrefix := "arn:seaweed:sts::assumed-role/" + if len(principal) > len(stsPrefix) && principal[:len(stsPrefix)] == stsPrefix { + remainder := principal[len(stsPrefix):] // Split on first '/' to get role name if slashIndex := indexOf(remainder, "/"); slashIndex != -1 { return remainder[:slashIndex] } } + + // Handle IAM role format: arn:seaweed:iam::role/RoleName + iamPrefix := "arn:seaweed:iam::role/" + if len(principal) > len(iamPrefix) && principal[:len(iamPrefix)] == iamPrefix { + return principal[len(iamPrefix):] + } + return "" } @@ -601,3 +610,15 @@ func (m *IAMManager) evaluateStringConditionForTrust(block map[string]interface{ return true } + +// isOIDCToken checks if a token is an OIDC JWT token (vs STS session token) +func isOIDCToken(token string) bool { + // JWT tokens have three parts separated by dots and start with base64-encoded JSON + parts := strings.Split(token, ".") + if len(parts) != 3 { + return false + } + + // JWT tokens typically start with "eyJ" (base64 encoded JSON starting with "{") + return strings.HasPrefix(token, "eyJ") +} diff --git a/weed/s3api/s3_iam_middleware.go b/weed/s3api/s3_iam_middleware.go index 10f4eea02..a8e5b7ee8 100644 --- a/weed/s3api/s3_iam_middleware.go +++ b/weed/s3api/s3_iam_middleware.go @@ -90,7 +90,11 @@ func (s3iam *S3IAMIntegration) AuthenticateJWT(ctx context.Context, r *http.Requ Name: identity.UserID, Principal: identity.RoleArn, SessionToken: sessionToken, - Account: nil, // OIDC tokens don't have account info + Account: &Account{ + DisplayName: identity.UserID, + EmailAddress: identity.UserID + "@oidc.local", + Id: identity.UserID, + }, }, s3err.ErrNone }