Browse Source

s3api: refactor embedded IAM to use CredentialManager directly

pull/8126/head
Chris Lu 2 days ago
parent
commit
9012f27af8
  1. 37
      weed/s3api/s3api_embedded_iam.go
  2. 58
      weed/s3api/s3api_embedded_iam_test.go

37
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
}

58
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)
}
Loading…
Cancel
Save