mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-18 12:30:46 +02:00
iamapi: refactor handlers to use CredentialManager directly
This commit is contained in:
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/policy_engine"
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// Constants from shared package
|
||||
@@ -423,9 +424,16 @@ func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
values := r.PostForm
|
||||
s3cfg := &iam_pb.S3ApiConfiguration{}
|
||||
if err := iama.s3ApiConfig.GetS3ApiConfiguration(s3cfg); err != nil && !errors.Is(err, filer_pb.ErrNotFound) {
|
||||
s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
|
||||
return
|
||||
|
||||
// Load configuration from credential manager
|
||||
config, err := iama.iam.GetCredentialManager().LoadConfiguration(r.Context())
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), filer_pb.ErrNotFound.Error()) {
|
||||
s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
proto.Merge(s3cfg, config)
|
||||
}
|
||||
|
||||
glog.V(4).Infof("DoActions: %+v", values)
|
||||
@@ -515,7 +523,7 @@ func (iama *IamApiServer) DoActions(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
if changed {
|
||||
err := iama.s3ApiConfig.PutS3ApiConfiguration(s3cfg)
|
||||
err := iama.iam.GetCredentialManager().SaveConfiguration(r.Context(), s3cfg)
|
||||
if err != nil {
|
||||
var iamError = IamError{Code: iam.ErrCodeServiceFailureException, Error: err}
|
||||
writeIamErrorResponse(w, r, &iamError)
|
||||
|
||||
@@ -15,20 +15,15 @@ import (
|
||||
"github.com/seaweedfs/seaweedfs/weed/glog"
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb"
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api"
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/policy_engine"
|
||||
. "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
|
||||
"github.com/seaweedfs/seaweedfs/weed/util"
|
||||
"github.com/seaweedfs/seaweedfs/weed/wdclient"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type IamS3ApiConfig interface {
|
||||
GetS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error)
|
||||
PutS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error)
|
||||
GetPolicies(policies *Policies) (err error)
|
||||
PutPolicies(policies *Policies) (err error)
|
||||
}
|
||||
@@ -134,63 +129,6 @@ func (iama *IamApiServer) Shutdown() {
|
||||
}
|
||||
}
|
||||
|
||||
func (iama *IamS3ApiConfigure) GetS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error) {
|
||||
return iama.GetS3ApiConfigurationFromCredentialManager(s3cfg)
|
||||
}
|
||||
|
||||
func (iama *IamS3ApiConfigure) PutS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error) {
|
||||
return iama.PutS3ApiConfigurationToCredentialManager(s3cfg)
|
||||
}
|
||||
|
||||
func (iama *IamS3ApiConfigure) GetS3ApiConfigurationFromCredentialManager(s3cfg *iam_pb.S3ApiConfiguration) (err error) {
|
||||
config, err := iama.credentialManager.LoadConfiguration(context.Background())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load configuration from credential manager: %w", err)
|
||||
}
|
||||
// Use proto.Merge to avoid copying the sync.Mutex embedded in the message
|
||||
proto.Merge(s3cfg, config)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iama *IamS3ApiConfigure) PutS3ApiConfigurationToCredentialManager(s3cfg *iam_pb.S3ApiConfiguration) (err error) {
|
||||
return iama.credentialManager.SaveConfiguration(context.Background(), s3cfg)
|
||||
}
|
||||
|
||||
func (iama *IamS3ApiConfigure) GetS3ApiConfigurationFromFiler(s3cfg *iam_pb.S3ApiConfiguration) (err error) {
|
||||
var buf bytes.Buffer
|
||||
err = pb.WithOneOfGrpcFilerClients(false, iama.option.Filers, iama.option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
|
||||
if err = filer.ReadEntry(iama.masterClient, client, filer.IamConfigDirectory, filer.IamIdentityFile, &buf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if buf.Len() > 0 {
|
||||
if err = filer.ParseS3ConfigurationFromBytes(buf.Bytes(), s3cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iama *IamS3ApiConfigure) PutS3ApiConfigurationToFiler(s3cfg *iam_pb.S3ApiConfiguration) (err error) {
|
||||
buf := bytes.Buffer{}
|
||||
if err := filer.ProtoToText(&buf, s3cfg); err != nil {
|
||||
return fmt.Errorf("ProtoToText: %s", err)
|
||||
}
|
||||
return pb.WithOneOfGrpcFilerClients(false, iama.option.Filers, iama.option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
|
||||
err = util.Retry("saveIamIdentity", func() error {
|
||||
return filer.SaveInsideFiler(client, filer.IamConfigDirectory, filer.IamIdentityFile, buf.Bytes())
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (iama *IamS3ApiConfigure) GetPolicies(policies *Policies) (err error) {
|
||||
var buf bytes.Buffer
|
||||
err = pb.WithOneOfGrpcFilerClients(false, iama.option.Filers, iama.option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
|
||||
|
||||
+108
-13
@@ -1,6 +1,7 @@
|
||||
package iamapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"net/http"
|
||||
@@ -14,33 +15,123 @@ import (
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/seaweedfs/seaweedfs/weed/credential"
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api"
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/policy_engine"
|
||||
"github.com/seaweedfs/seaweedfs/weed/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var GetS3ApiConfiguration func(s3cfg *iam_pb.S3ApiConfiguration) (err error)
|
||||
var PutS3ApiConfiguration func(s3cfg *iam_pb.S3ApiConfiguration) (err error)
|
||||
var GetPolicies func(policies *Policies) (err error)
|
||||
var PutPolicies func(policies *Policies) (err error)
|
||||
|
||||
var s3config = iam_pb.S3ApiConfiguration{}
|
||||
var policiesFile = Policies{Policies: make(map[string]policy_engine.PolicyDocument)}
|
||||
var ias = IamApiServer{s3ApiConfig: iamS3ApiConfigureMock{}}
|
||||
|
||||
// Setup MockCredentialStore
|
||||
type MockCredentialStore struct{}
|
||||
|
||||
func (m *MockCredentialStore) GetName() credential.CredentialStoreTypeName { return "mock" }
|
||||
func (m *MockCredentialStore) Initialize(configuration util.Configuration, prefix string) error {
|
||||
return nil
|
||||
}
|
||||
func (m *MockCredentialStore) LoadConfiguration(ctx context.Context) (*iam_pb.S3ApiConfiguration, error) {
|
||||
var cfg iam_pb.S3ApiConfiguration
|
||||
_ = copier.Copy(&cfg, &s3config)
|
||||
return &cfg, nil
|
||||
}
|
||||
func (m *MockCredentialStore) SaveConfiguration(ctx context.Context, config *iam_pb.S3ApiConfiguration) error {
|
||||
_ = copier.Copy(&s3config, config)
|
||||
return nil
|
||||
}
|
||||
func (m *MockCredentialStore) CreateUser(ctx context.Context, identity *iam_pb.Identity) error {
|
||||
return nil
|
||||
}
|
||||
func (m *MockCredentialStore) GetUser(ctx context.Context, username string) (*iam_pb.Identity, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockCredentialStore) UpdateUser(ctx context.Context, username string, identity *iam_pb.Identity) error {
|
||||
return nil
|
||||
}
|
||||
func (m *MockCredentialStore) DeleteUser(ctx context.Context, username string) error { return nil }
|
||||
func (m *MockCredentialStore) ListUsers(ctx context.Context) ([]string, error) { return nil, nil }
|
||||
func (m *MockCredentialStore) GetUserByAccessKey(ctx context.Context, accessKey string) (*iam_pb.Identity, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockCredentialStore) CreateAccessKey(ctx context.Context, username string, credential *iam_pb.Credential) error {
|
||||
return nil
|
||||
}
|
||||
func (m *MockCredentialStore) DeleteAccessKey(ctx context.Context, username string, accessKey string) error {
|
||||
return nil
|
||||
}
|
||||
func (m *MockCredentialStore) GetPolicies(ctx context.Context) (map[string]policy_engine.PolicyDocument, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockCredentialStore) PutPolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error {
|
||||
return nil
|
||||
}
|
||||
func (m *MockCredentialStore) DeletePolicy(ctx context.Context, name string) error { return nil }
|
||||
func (m *MockCredentialStore) GetPolicy(ctx context.Context, name string) (*policy_engine.PolicyDocument, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockCredentialStore) CreateServiceAccount(ctx context.Context, sa *iam_pb.ServiceAccount) error {
|
||||
return nil
|
||||
}
|
||||
func (m *MockCredentialStore) UpdateServiceAccount(ctx context.Context, id string, sa *iam_pb.ServiceAccount) error {
|
||||
return nil
|
||||
}
|
||||
func (m *MockCredentialStore) DeleteServiceAccount(ctx context.Context, id string) error { return nil }
|
||||
func (m *MockCredentialStore) GetServiceAccount(ctx context.Context, id string) (*iam_pb.ServiceAccount, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockCredentialStore) ListServiceAccounts(ctx context.Context) ([]*iam_pb.ServiceAccount, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockCredentialStore) GetServiceAccountByAccessKey(ctx context.Context, accessKey string) (*iam_pb.ServiceAccount, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockCredentialStore) Shutdown() {}
|
||||
|
||||
// Initialize ias
|
||||
var ias IamApiServer
|
||||
|
||||
func init() {
|
||||
// Infect the credential manager with our mock store
|
||||
// Since NewIdentityAccessManagementWithStore creates a default one, we need to swap the store inside it
|
||||
// Accessing unexported field 'credentialManager' in 'IdentityAccessManagement' requires reflection or
|
||||
// standard setters if available.
|
||||
// However, GetCredentialManager returns *CredentialManager.
|
||||
// *CredentialManager has 'store' field which is unexported in credential package.
|
||||
// But we can create a NEW CredentialManager with our mock store and set it on iam if the field was exported.
|
||||
// It is NOT exported.
|
||||
|
||||
// Wait, credential.NewCredentialManager is a function.
|
||||
// Check if is a way to set store.
|
||||
|
||||
// Hack: register our mock store in credential.Stores?
|
||||
credential.Stores = append(credential.Stores, &MockCredentialStore{})
|
||||
|
||||
// Initialize ias after registering store
|
||||
ias = IamApiServer{
|
||||
s3ApiConfig: iamS3ApiConfigureMock{},
|
||||
iam: s3api.NewIdentityAccessManagementWithStore(&s3api.S3ApiServerOption{}, "mock"),
|
||||
}
|
||||
|
||||
_, _ = credential.NewCredentialManager("mock", nil, "")
|
||||
|
||||
// Now how to set this 'cm' into 'ias.iam'?
|
||||
// 'iam.credentialManager' is unexported in 's3api'.
|
||||
|
||||
// Maybe I should use reflection to set it, since this is a test.
|
||||
}
|
||||
|
||||
// Reflection is messy.
|
||||
// Alternative: NewIdentityAccessManagementWithStore takes 'explicitStore' string.
|
||||
// If I register my mock store with name "mock", I can pass "mock" to NewIdentityAccessManagementWithStore.
|
||||
|
||||
type iamS3ApiConfigureMock struct{}
|
||||
|
||||
func (iam iamS3ApiConfigureMock) GetS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error) {
|
||||
_ = copier.Copy(&s3cfg.Identities, &s3config.Identities)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iam iamS3ApiConfigureMock) PutS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) (err error) {
|
||||
_ = copier.Copy(&s3config.Identities, &s3cfg.Identities)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iam iamS3ApiConfigureMock) GetPolicies(policies *Policies) (err error) {
|
||||
_ = copier.Copy(&policies, &policiesFile)
|
||||
return nil
|
||||
@@ -52,6 +143,10 @@ func (iam iamS3ApiConfigureMock) PutPolicies(policies *Policies) (err error) {
|
||||
}
|
||||
|
||||
func TestCreateUser(t *testing.T) {
|
||||
// Re-init ias with mock store properly
|
||||
credential.Stores = append(credential.Stores, &MockCredentialStore{})
|
||||
ias.iam = s3api.NewIdentityAccessManagementWithStore(&s3api.S3ApiServerOption{}, "mock")
|
||||
|
||||
userName := aws.String("Test")
|
||||
params := &iam.CreateUserInput{UserName: userName}
|
||||
req, _ := iam.New(session.New()).CreateUserRequest(params)
|
||||
|
||||
Reference in New Issue
Block a user