diff --git a/weed/s3api/s3api_embedded_iam.go b/weed/s3api/s3api_embedded_iam.go index 0774fa9c8..3c33369fb 100644 --- a/weed/s3api/s3api_embedded_iam.go +++ b/weed/s3api/s3api_embedded_iam.go @@ -35,9 +35,7 @@ type EmbeddedIamApi struct { iam *IdentityAccessManagement policyLock sync.RWMutex // Test hook - getS3ApiConfigurationFunc func(*iam_pb.S3ApiConfiguration) error - putS3ApiConfigurationFunc func(*iam_pb.S3ApiConfiguration) error - reloadConfigurationFunc func() error + reloadConfigurationFunc func() error } // NewEmbeddedIamApi creates a new embedded IAM API handler. @@ -167,27 +165,6 @@ func (e *EmbeddedIamApi) writeIamErrorResponse(w http.ResponseWriter, r *http.Re } } -// GetS3ApiConfiguration loads the S3 API configuration from the credential manager. -func (e *EmbeddedIamApi) GetS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) error { - if e.getS3ApiConfigurationFunc != nil { - return e.getS3ApiConfigurationFunc(s3cfg) - } - config, err := e.credentialManager.LoadConfiguration(context.Background()) - if err != nil { - return fmt.Errorf("failed to load configuration: %w", err) - } - proto.Merge(s3cfg, config) - return nil -} - -// PutS3ApiConfiguration saves the S3 API configuration to the credential manager. -func (e *EmbeddedIamApi) PutS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) error { - if e.putS3ApiConfigurationFunc != nil { - return e.putS3ApiConfigurationFunc(s3cfg) - } - return e.credentialManager.SaveConfiguration(context.Background(), s3cfg) -} - // ReloadConfiguration reloads the IAM configuration from the credential manager. func (e *EmbeddedIamApi) ReloadConfiguration() error { if e.reloadConfigurationFunc != nil { @@ -1049,8 +1026,14 @@ func (e *EmbeddedIamApi) ExecuteAction(values url.Values) (interface{}, *iamErro defer e.policyLock.Unlock() s3cfg := &iam_pb.S3ApiConfiguration{} - if err := e.GetS3ApiConfiguration(s3cfg); err != nil && !errors.Is(err, filer_pb.ErrNotFound) { - return nil, &iamError{Code: s3err.GetAPIError(s3err.ErrInternalError).Code, Error: fmt.Errorf("failed to get s3 api configuration: %v", err)} + + config, err := e.credentialManager.LoadConfiguration(context.Background()) + if err != nil { + if !strings.Contains(err.Error(), filer_pb.ErrNotFound.Error()) { + return nil, &iamError{Code: s3err.GetAPIError(s3err.ErrInternalError).Code, Error: fmt.Errorf("failed to get s3 api configuration: %v", err)} + } + } else { + proto.Merge(s3cfg, config) } glog.V(4).Infof("IAM ExecuteAction: %+v", values) @@ -1165,7 +1148,7 @@ func (e *EmbeddedIamApi) ExecuteAction(values url.Values) (interface{}, *iamErro return nil, &iamError{Code: s3err.GetAPIError(s3err.ErrNotImplemented).Code, Error: errors.New(s3err.GetAPIError(s3err.ErrNotImplemented).Description)} } if changed { - if err := e.PutS3ApiConfiguration(s3cfg); err != nil { + if err := e.credentialManager.SaveConfiguration(context.Background(), s3cfg); err != nil { iamErr = &iamError{Code: iam.ErrCodeServiceFailureException, Error: err} return nil, iamErr } diff --git a/weed/s3api/s3api_embedded_iam_test.go b/weed/s3api/s3api_embedded_iam_test.go index c5dc5d949..196df7901 100644 --- a/weed/s3api/s3api_embedded_iam_test.go +++ b/weed/s3api/s3api_embedded_iam_test.go @@ -1,6 +1,7 @@ package s3api import ( + "context" "encoding/json" "encoding/xml" "fmt" @@ -16,6 +17,8 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/iam" "github.com/gorilla/mux" + "github.com/seaweedfs/seaweedfs/weed/credential" + _ "github.com/seaweedfs/seaweedfs/weed/credential/memory" "github.com/seaweedfs/seaweedfs/weed/pb/iam_pb" . "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" "github.com/seaweedfs/seaweedfs/weed/s3api/s3err" @@ -36,40 +39,18 @@ func NewEmbeddedIamApiForTest() *EmbeddedIamApiForTest { }, mockConfig: &iam_pb.S3ApiConfiguration{}, } - e.getS3ApiConfigurationFunc = func(s3cfg *iam_pb.S3ApiConfiguration) error { - if e.mockConfig != nil { - cloned := proto.Clone(e.mockConfig).(*iam_pb.S3ApiConfiguration) - proto.Merge(s3cfg, cloned) - } - return nil - } - e.putS3ApiConfigurationFunc = func(s3cfg *iam_pb.S3ApiConfiguration) error { - e.mockConfig = proto.Clone(s3cfg).(*iam_pb.S3ApiConfiguration) - return nil - } + + // Initialize credential manager with memory store for ExecuteAction tests + cm, _ := credential.NewCredentialManager("memory", nil, "") + e.credentialManager = cm + e.EmbeddedIamApi.iam.credentialManager = cm // Also set on internal IAM if needed (though EmbeddedIamApi has its own pointer) + e.reloadConfigurationFunc = func() error { return nil } return e } -// Override GetS3ApiConfiguration for testing -func (e *EmbeddedIamApiForTest) GetS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) error { - // Use proto.Clone for proper deep copy semantics - if e.mockConfig != nil { - cloned := proto.Clone(e.mockConfig).(*iam_pb.S3ApiConfiguration) - proto.Merge(s3cfg, cloned) - } - return nil -} - -// Override PutS3ApiConfiguration for testing -func (e *EmbeddedIamApiForTest) PutS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) error { - // Use proto.Clone for proper deep copy semantics - e.mockConfig = proto.Clone(s3cfg).(*iam_pb.S3ApiConfiguration) - return nil -} - // DoActions handles IAM API actions for testing func (e *EmbeddedIamApiForTest) DoActions(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { @@ -78,9 +59,10 @@ func (e *EmbeddedIamApiForTest) DoActions(w http.ResponseWriter, r *http.Request } values := r.PostForm s3cfg := &iam_pb.S3ApiConfiguration{} - if err := e.GetS3ApiConfiguration(s3cfg); err != nil { - http.Error(w, "Internal error", http.StatusInternalServerError) - return + + if e.mockConfig != nil { + cloned := proto.Clone(e.mockConfig).(*iam_pb.S3ApiConfiguration) + proto.Merge(s3cfg, cloned) } var response interface{} @@ -176,10 +158,7 @@ func (e *EmbeddedIamApiForTest) DoActions(w http.ResponseWriter, r *http.Request } if changed { - if err := e.PutS3ApiConfiguration(s3cfg); err != nil { - http.Error(w, "Internal error", http.StatusInternalServerError) - return - } + e.mockConfig = proto.Clone(s3cfg).(*iam_pb.S3ApiConfiguration) } w.Header().Set("Content-Type", "application/xml") @@ -1699,7 +1678,10 @@ func TestEmbeddedIamExecuteAction(t *testing.T) { assert.True(t, ok) assert.Equal(t, "ExecuteActionUser", *createResp.CreateUserResult.User.UserName) - // Verify persistence - assert.Len(t, api.mockConfig.Identities, 1) - assert.Equal(t, "ExecuteActionUser", api.mockConfig.Identities[0].Name) + // Verify persistence via credentialManager + // The new logic saves to credentialManager, not mockConfig (which is only for DoActions override) + config, err := api.credentialManager.LoadConfiguration(context.Background()) + assert.NoError(t, err) + assert.Len(t, config.Identities, 1) + assert.Equal(t, "ExecuteActionUser", config.Identities[0].Name) }