diff --git a/weed/s3api/auth_signature_v4.go b/weed/s3api/auth_signature_v4.go index b70b90add..adfc84653 100644 --- a/weed/s3api/auth_signature_v4.go +++ b/weed/s3api/auth_signature_v4.go @@ -434,6 +434,11 @@ func (iam *IdentityAccessManagement) validateSTSSessionToken(r *http.Request, se Claims: claims, // Populate Claims for policy variable substitution } + // Restore admin privileges if the session was created by an admin + if isAdmin, ok := claims["is_admin"].(bool); ok && isAdmin { + identity.Actions = append(identity.Actions, s3_constants.ACTION_ADMIN) + } + glog.V(2).Infof("Successfully validated STS session token for principal: %s, assumed role user: %s", sessionInfo.Principal, sessionInfo.AssumedRoleUser) return identity, cred, s3err.ErrNone diff --git a/weed/s3api/s3api_sts.go b/weed/s3api/s3api_sts.go index 1dcac3151..d8a376518 100644 --- a/weed/s3api/s3api_sts.go +++ b/weed/s3api/s3api_sts.go @@ -328,8 +328,19 @@ func (h *STSHandlers) handleAssumeRole(w http.ResponseWriter, r *http.Request) { return } + // Prepare custom claims for the session + var modifyClaims func(claims *sts.STSSessionClaims) + if identity.isAdmin() { + modifyClaims = func(claims *sts.STSSessionClaims) { + if claims.RequestContext == nil { + claims.RequestContext = make(map[string]interface{}) + } + claims.RequestContext["is_admin"] = true + } + } + // Generate common STS components - stsCreds, assumedUser, err := h.prepareSTSCredentials(roleArn, roleSessionName, durationSeconds, sessionPolicyJSON, nil) + stsCreds, assumedUser, err := h.prepareSTSCredentials(roleArn, roleSessionName, durationSeconds, sessionPolicyJSON, modifyClaims) if err != nil { h.writeSTSErrorResponse(w, r, STSErrInternalError, err) return diff --git a/weed/s3api/s3api_sts_assume_role_test.go b/weed/s3api/s3api_sts_assume_role_test.go index e3f235f53..74890f74b 100644 --- a/weed/s3api/s3api_sts_assume_role_test.go +++ b/weed/s3api/s3api_sts_assume_role_test.go @@ -7,6 +7,8 @@ import ( "net/url" "testing" + "github.com/seaweedfs/seaweedfs/weed/iam/sts" + "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" "github.com/seaweedfs/seaweedfs/weed/s3api/s3err" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -55,13 +57,25 @@ func TestAssumeRole_CallerIdentityFallback(t *testing.T) { Name: "alice", Account: &AccountAdmin, PrincipalArn: fmt.Sprintf("arn:aws:iam::%s:user/alice", defaultAccountID), + Actions: []Action{s3_constants.ACTION_ADMIN}, } // 1. Test prepareSTSCredentials with NO RoleArn (simulating the fallback logic having passed PrincipalArn) // expected RoleArn passed to prepareSTSCredentials would be the caller's PrincipalArn fallbackRoleArn := callerIdentity.PrincipalArn - stsCreds, assumedUser, err := stsHandlers.prepareSTSCredentials(fallbackRoleArn, "test-session", nil, "", nil) + // Prepare custom claims for the session (mimicking handleAssumeRole logic) + var modifyClaims func(claims *sts.STSSessionClaims) + if callerIdentity.isAdmin() { + modifyClaims = func(claims *sts.STSSessionClaims) { + if claims.RequestContext == nil { + claims.RequestContext = make(map[string]interface{}) + } + claims.RequestContext["is_admin"] = true + } + } + + stsCreds, assumedUser, err := stsHandlers.prepareSTSCredentials(fallbackRoleArn, "test-session", nil, "", modifyClaims) require.NoError(t, err) // Assertions @@ -75,6 +89,11 @@ func TestAssumeRole_CallerIdentityFallback(t *testing.T) { // The RoleArn in session info should match the fallback ARN (user ARN) assert.Equal(t, fallbackRoleArn, sessionInfo.RoleArn) + + // Verify is_admin claim is present + isAdmin, ok := sessionInfo.RequestContext["is_admin"].(bool) + assert.True(t, ok, "is_admin claim should be present") + assert.True(t, isAdmin, "is_admin claim should be true") }) // Test case 2: Caller is an STS Assumed Role, No RoleArn