diff --git a/weed/s3api/auth_credentials.go b/weed/s3api/auth_credentials.go index 6207d33d9..5581ffed2 100644 --- a/weed/s3api/auth_credentials.go +++ b/weed/s3api/auth_credentials.go @@ -55,7 +55,7 @@ type IdentityAccessManagement struct { grpcDialOption grpc.DialOption // IAM Integration for advanced features - iamIntegration *S3IAMIntegration + iamIntegration IAMIntegration // Bucket policy engine for evaluating bucket policies policyEngine *BucketPolicyEngine diff --git a/weed/s3api/auth_signature_v4.go b/weed/s3api/auth_signature_v4.go index d6ea5e7cc..c8127c638 100644 --- a/weed/s3api/auth_signature_v4.go +++ b/weed/s3api/auth_signature_v4.go @@ -303,15 +303,15 @@ func (iam *IdentityAccessManagement) verifyV4Signature(r *http.Request, shouldCh // validateSTSSessionToken validates an STS session token and extracts temporary credentials func (iam *IdentityAccessManagement) validateSTSSessionToken(r *http.Request, sessionToken string, accessKey string) (*Identity, *Credential, s3err.ErrorCode) { - // Check if IAM integration with STS is available - if iam.iamIntegration == nil || iam.iamIntegration.stsService == nil { - glog.V(2).Infof("STS service not available, cannot validate session token") + // Check if IAM integration is available + if iam.iamIntegration == nil { + glog.V(2).Infof("IAM integration not available, cannot validate session token") return nil, nil, s3err.ErrInvalidAccessKeyID } // Validate the session token with the STS service ctx := r.Context() - sessionInfo, err := iam.iamIntegration.stsService.ValidateSessionToken(ctx, sessionToken) + sessionInfo, err := iam.iamIntegration.ValidateSessionToken(ctx, sessionToken) if err != nil { glog.V(2).Infof("Failed to validate STS session token: %v", err) return nil, nil, s3err.ErrInvalidAccessKeyID diff --git a/weed/s3api/auth_signature_v4_sts_test.go b/weed/s3api/auth_signature_v4_sts_test.go index 19a84959d..755c85943 100644 --- a/weed/s3api/auth_signature_v4_sts_test.go +++ b/weed/s3api/auth_signature_v4_sts_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/seaweedfs/seaweedfs/weed/iam/sts" "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" "github.com/seaweedfs/seaweedfs/weed/s3api/s3err" ) @@ -28,6 +29,10 @@ func (m *MockIAMIntegration) AuthenticateJWT(ctx context.Context, r *http.Reques return nil, s3err.ErrNotImplemented } +func (m *MockIAMIntegration) ValidateSessionToken(ctx context.Context, token string) (*sts.SessionInfo, error) { + return nil, nil // Not needed for these tests +} + // TestVerifyV4SignatureWithSTSIdentity tests that verifyV4Signature properly handles STS identities // by falling back to IAM authorization when shouldCheckPermissions is true func TestVerifyV4SignatureWithSTSIdentity(t *testing.T) { @@ -224,20 +229,14 @@ func TestVerifyV4SignatureSTSStreamingUpload(t *testing.T) { action := s3_constants.ACTION_WRITE var errCode s3err.ErrorCode - if len(stsIdentity.Actions) > 0 { - if !stsIdentity.canDo(Action(action), bucket, object) { - errCode = s3err.ErrAccessDenied - } - } else if iamMock != nil { - // Use the mock IAM integration - errCode = iamMock.AuthorizeAction(req.Context(), &IAMIdentity{ - Name: stsIdentity.Name, - Account: stsIdentity.Account, - }, Action(action), bucket, object, req) - } else { - errCode = s3err.ErrAccessDenied + + // Create minimal IAM instance logic + iam := &IdentityAccessManagement{ + iamIntegration: iamMock, } + errCode = iam.VerifyActionPermission(req, stsIdentity, Action(action), bucket, object) + // Verify that the STS identity is authorized via IAM assert.Equal(t, s3err.ErrNone, errCode, "STS identity should be authorized via IAM for streaming upload") assert.True(t, iamAuthCalled, "IAM authorization should have been called for STS identity") diff --git a/weed/s3api/s3_iam_middleware.go b/weed/s3api/s3_iam_middleware.go index 70f74508b..3548b58a7 100644 --- a/weed/s3api/s3_iam_middleware.go +++ b/weed/s3api/s3_iam_middleware.go @@ -18,6 +18,13 @@ import ( "github.com/seaweedfs/seaweedfs/weed/s3api/s3err" ) +// IAMIntegration defines the interface for IAM integration +type IAMIntegration interface { + AuthenticateJWT(ctx context.Context, r *http.Request) (*IAMIdentity, s3err.ErrorCode) + AuthorizeAction(ctx context.Context, identity *IAMIdentity, action Action, bucket string, objectKey string, r *http.Request) s3err.ErrorCode + ValidateSessionToken(ctx context.Context, token string) (*sts.SessionInfo, error) +} + // S3IAMIntegration provides IAM integration for S3 API type S3IAMIntegration struct { iamManager *integration.IAMManager @@ -167,6 +174,14 @@ func (s3iam *S3IAMIntegration) AuthenticateJWT(ctx context.Context, r *http.Requ return identity, s3err.ErrNone } +// ValidateSessionToken checks the validity of an STS session token +func (s3iam *S3IAMIntegration) ValidateSessionToken(ctx context.Context, token string) (*sts.SessionInfo, error) { + if s3iam.stsService == nil { + return nil, fmt.Errorf("STS service not available") + } + return s3iam.stsService.ValidateSessionToken(ctx, token) +} + // AuthorizeAction authorizes actions using our policy engine func (s3iam *S3IAMIntegration) AuthorizeAction(ctx context.Context, identity *IAMIdentity, action Action, bucket string, objectKey string, r *http.Request) s3err.ErrorCode { if !s3iam.enabled { @@ -463,7 +478,7 @@ func (s3a *S3ApiServer) SetIAMIntegration(iamManager *integration.IAMManager) { // EnhancedS3ApiServer extends S3ApiServer with IAM integration type EnhancedS3ApiServer struct { *S3ApiServer - iamIntegration *S3IAMIntegration + iamIntegration IAMIntegration } // NewEnhancedS3ApiServer creates an S3 API server with IAM integration