From 62c4ef353659dbf87bf0ca58ec7c49129cd33047 Mon Sep 17 00:00:00 2001 From: marty Date: Tue, 27 Jan 2026 15:17:15 +0100 Subject: [PATCH] Revert "Implement IAM propagation to S3 servers (#8130)" This reverts commit 551a31e1569814292d104d7f5e10d352f72dbf59. --- test/s3/iam/Makefile | 1 - test/s3/iam/run_tests.sh | 1 - test/s3/iam/s3_iam_framework.go | 35 +- test/s3/normal/s3_integration_test.go | 35 +- weed/cluster/cluster.go | 10 - weed/command/mini.go | 5 +- weed/command/s3.go | 23 +- weed/command/server.go | 1 - weed/credential/credential_manager.go | 7 - weed/credential/propagating_store.go | 297 ------ weed/pb/iam.proto | 19 - weed/pb/iam_pb/iam.pb.go | 267 +----- weed/pb/s3.proto | 31 +- weed/pb/s3_pb/s3.pb.go | 140 ++- weed/pb/s3_pb/s3_grpc.pb.go | 614 +++++++++--- weed/s3api/auth_credentials.go | 871 ++---------------- weed/s3api/auth_credentials_test.go | 48 - weed/s3api/s3api_bucket_policy_engine.go | 10 - weed/s3api/s3api_embedded_iam.go | 30 +- weed/s3api/s3api_embedded_iam_test.go | 46 +- weed/s3api/s3api_server.go | 21 +- weed/s3api/s3api_server_grpc.go | 405 +++++++- weed/s3api/s3api_server_routing_test.go | 2 +- weed/server/filer_server.go | 4 - weed/server/filer_server_handlers_iam_grpc.go | 8 +- weed/server/master_grpc_server.go | 1 - 26 files changed, 1206 insertions(+), 1726 deletions(-) delete mode 100644 weed/credential/propagating_store.go diff --git a/test/s3/iam/Makefile b/test/s3/iam/Makefile index 6eb5b0db8..aad6d4fbd 100644 --- a/test/s3/iam/Makefile +++ b/test/s3/iam/Makefile @@ -70,7 +70,6 @@ start-services: ## Start SeaweedFS services for testing -s3.port=$(S3_PORT) \ -s3.config=test_config.json \ -s3.iam.config=$(CURDIR)/iam_config.json \ - -s3.iam.readOnly=false \ > weed-mini.log 2>&1 & \ echo $$! > $(MINI_PID_FILE) diff --git a/test/s3/iam/run_tests.sh b/test/s3/iam/run_tests.sh index 61bbecbc2..dbde18fbd 100755 --- a/test/s3/iam/run_tests.sh +++ b/test/s3/iam/run_tests.sh @@ -43,7 +43,6 @@ weed server \ -volume.max=0 \ -master.volumeSizeLimitMB=100 \ -s3.allowDeleteBucketNotEmpty=true \ - -s3.iam.readOnly=false \ > /tmp/weed_test_server.log 2>&1 & SERVER_PID=$! diff --git a/test/s3/iam/s3_iam_framework.go b/test/s3/iam/s3_iam_framework.go index cdb5a38ea..026d98373 100644 --- a/test/s3/iam/s3_iam_framework.go +++ b/test/s3/iam/s3_iam_framework.go @@ -810,7 +810,7 @@ func (f *S3IAMTestFramework) Cleanup() { } } -// WaitForS3Service waits for the S3 service to be available and checks for IAM write permissions +// WaitForS3Service waits for the S3 service to be available func (f *S3IAMTestFramework) WaitForS3Service() error { // Create a basic S3 client sess, err := session.NewSession(&aws.Config{ @@ -830,46 +830,17 @@ func (f *S3IAMTestFramework) WaitForS3Service() error { s3Client := s3.New(sess) - // Create IAM client for write permission check - iamClient := iam.New(sess) - - // Try to list buckets to check if S3 service is available + // Try to list buckets to check if service is available maxRetries := 30 for i := 0; i < maxRetries; i++ { _, err := s3Client.ListBuckets(&s3.ListBucketsInput{}) if err == nil { - // S3 is up, now check if IAM is writable - // We try to create a dummy user. If it fails with "AccessDenied: IAM write operations are disabled", - // we know we are still in read-only mode (or the flag didn't take effect). - // If it fails with other errors (e.g. invalid auth), that's fine for this connectivity check. - // Only the explicit read-only error is a blocker for our specific test scenario. - - // Note: We use a random name to avoid conflicts if it actually succeeds - dummyUser := fmt.Sprintf("check-writable-%d", time.Now().UnixNano()) - _, iamErr := iamClient.CreateUser(&iam.CreateUserInput{ - UserName: aws.String(dummyUser), - }) - - if iamErr != nil { - if reqErr, ok := iamErr.(awserr.RequestFailure); ok { - if reqErr.Code() == "AccessDenied" && strings.Contains(reqErr.Message(), "IAM write operations are disabled") { - f.t.Logf("Waiting for IAM to become writable... (attempt %d/%d)", i+1, maxRetries) - time.Sleep(1 * time.Second) - continue - } - } - // Ignore other errors (like auth errors), we just want to ensure we aren't explicitly blocked by read-only mode - } else { - // Cleanup if it actually succeeded - iamClient.DeleteUser(&iam.DeleteUserInput{UserName: aws.String(dummyUser)}) - } - return nil } time.Sleep(1 * time.Second) } - return fmt.Errorf("S3 service not available or not writable after %d retries", maxRetries) + return fmt.Errorf("S3 service not available after %d retries", maxRetries) } // PutTestObject puts a test object in the specified bucket diff --git a/test/s3/normal/s3_integration_test.go b/test/s3/normal/s3_integration_test.go index a0b432db5..77ac8c85d 100644 --- a/test/s3/normal/s3_integration_test.go +++ b/test/s3/normal/s3_integration_test.go @@ -34,18 +34,18 @@ const ( // TestCluster manages the weed mini instance for integration testing type TestCluster struct { - dataDir string - ctx context.Context - cancel context.CancelFunc - s3Client *s3.S3 - isRunning bool - startOnce sync.Once - wg sync.WaitGroup - masterPort int - volumePort int - filerPort int - s3Port int - s3Endpoint string + dataDir string + ctx context.Context + cancel context.CancelFunc + s3Client *s3.S3 + isRunning bool + startOnce sync.Once + wg sync.WaitGroup + masterPort int + volumePort int + filerPort int + s3Port int + s3Endpoint string } // TestS3Integration demonstrates basic S3 operations against a running weed mini instance @@ -172,12 +172,11 @@ func startMiniCluster(t *testing.T) (*TestCluster, error) { "-volume.port=" + strconv.Itoa(volumePort), "-filer.port=" + strconv.Itoa(filerPort), "-s3.port=" + strconv.Itoa(s3Port), - "-webdav.port=0", // Disable WebDAV - "-admin.ui=false", // Disable admin UI + "-webdav.port=0", // Disable WebDAV + "-admin.ui=false", // Disable admin UI "-master.volumeSizeLimitMB=32", // Small volumes for testing "-ip=127.0.0.1", - "-master.peers=none", // Faster startup - "-s3.iam.readOnly=false", // Enable IAM write operations for tests + "-master.peers=none", // Faster startup } // Suppress most logging during tests @@ -246,7 +245,7 @@ func (c *TestCluster) Stop() { case <-time.After(2 * time.Second): // Timeout - goroutine doesn't respond to context cancel } - + // Reset the global cmdMini flags to prevent state leakage to other tests for _, cmd := range command.Commands { if cmd.Name() == "mini" { @@ -370,7 +369,7 @@ func testGetObject(t *testing.T, cluster *TestCluster) { assert.Equal(t, int64(len(objectData)), aws.Int64Value(headResp.ContentLength)) t.Logf("✓ Got object metadata: %s/%s (verified %d bytes via HEAD)", bucketName, objectKey, len(objectData)) - + // Note: GetObject can sometimes have volume location issues in mini mode during tests // The object is correctly stored (as verified by HEAD), which demonstrates S3 functionality } diff --git a/weed/cluster/cluster.go b/weed/cluster/cluster.go index 8327065b3..a52aa2721 100644 --- a/weed/cluster/cluster.go +++ b/weed/cluster/cluster.go @@ -36,7 +36,6 @@ type ClusterNodeGroups struct { type Cluster struct { filerGroups *ClusterNodeGroups brokerGroups *ClusterNodeGroups - s3Groups *ClusterNodeGroups } func newClusterNodeGroups() *ClusterNodeGroups { @@ -91,7 +90,6 @@ func NewCluster() *Cluster { return &Cluster{ filerGroups: newClusterNodeGroups(), brokerGroups: newClusterNodeGroups(), - s3Groups: newClusterNodeGroups(), } } @@ -101,8 +99,6 @@ func (cluster *Cluster) getGroupMembers(filerGroup FilerGroupName, nodeType stri return cluster.filerGroups.getGroupMembers(filerGroup, createIfNotFound) case BrokerType: return cluster.brokerGroups.getGroupMembers(filerGroup, createIfNotFound) - case S3Type: - return cluster.s3Groups.getGroupMembers(filerGroup, createIfNotFound) } return nil } @@ -114,8 +110,6 @@ func (cluster *Cluster) AddClusterNode(ns, nodeType string, dataCenter DataCente return cluster.filerGroups.AddClusterNode(filerGroup, nodeType, dataCenter, rack, address, version) case BrokerType: return cluster.brokerGroups.AddClusterNode(filerGroup, nodeType, dataCenter, rack, address, version) - case S3Type: - return cluster.s3Groups.AddClusterNode(filerGroup, nodeType, dataCenter, rack, address, version) case MasterType: return buildClusterNodeUpdateMessage(true, filerGroup, nodeType, address) } @@ -129,8 +123,6 @@ func (cluster *Cluster) RemoveClusterNode(ns string, nodeType string, address pb return cluster.filerGroups.RemoveClusterNode(filerGroup, nodeType, address) case BrokerType: return cluster.brokerGroups.RemoveClusterNode(filerGroup, nodeType, address) - case S3Type: - return cluster.s3Groups.RemoveClusterNode(filerGroup, nodeType, address) case MasterType: return buildClusterNodeUpdateMessage(false, filerGroup, nodeType, address) } @@ -143,8 +135,6 @@ func (cluster *Cluster) ListClusterNode(filerGroup FilerGroupName, nodeType stri return cluster.filerGroups.ListClusterNode(filerGroup) case BrokerType: return cluster.brokerGroups.ListClusterNode(filerGroup) - case S3Type: - return cluster.s3Groups.ListClusterNode(filerGroup) case MasterType: } return diff --git a/weed/command/mini.go b/weed/command/mini.go index 4be16d687..000c4921a 100644 --- a/weed/command/mini.go +++ b/weed/command/mini.go @@ -58,7 +58,6 @@ var ( miniEnableWebDAV *bool miniEnableS3 *bool miniEnableAdminUI *bool - miniS3IamReadOnly *bool ) func init() { @@ -140,7 +139,6 @@ func initMiniCommonFlags() { miniEnableWebDAV = cmdMini.Flag.Bool("webdav", true, "enable WebDAV server") miniEnableS3 = cmdMini.Flag.Bool("s3", true, "enable S3 server") miniEnableAdminUI = cmdMini.Flag.Bool("admin.ui", true, "enable Admin UI") - miniS3IamReadOnly = cmdMini.Flag.Bool("s3.iam.readOnly", true, "disable IAM write operations on this server") } // initMiniMasterFlags initializes Master server flag options @@ -234,7 +232,6 @@ func initMiniS3Flags() { miniS3Options.concurrentUploadLimitMB = cmdMini.Flag.Int("s3.concurrentUploadLimitMB", 0, "limit total concurrent upload size") miniS3Options.concurrentFileUploadLimit = cmdMini.Flag.Int("s3.concurrentFileUploadLimit", 0, "limit number of concurrent file uploads") miniS3Options.enableIam = cmdMini.Flag.Bool("s3.iam", true, "enable embedded IAM API on the same port") - miniS3Options.iamReadOnly = miniS3IamReadOnly miniS3Options.dataCenter = cmdMini.Flag.String("s3.dataCenter", "", "prefer to read and write to volumes in this data center") miniS3Options.cipher = cmdMini.Flag.Bool("s3.encryptVolumeData", false, "encrypt data on volume servers for S3 uploads") miniS3Options.config = miniS3Config @@ -702,7 +699,7 @@ func runMini(cmd *Command, args []string) bool { // Capture which port flags were explicitly passed on CLI BEFORE config file is applied // This is necessary to distinguish user-specified ports from defaults or config file options explicitPortFlags = make(map[string]bool) - portFlagNames := []string{"master.port", "filer.port", "volume.port", "s3.port", "webdav.port", "admin.port", "s3.iam.readOnly"} + portFlagNames := []string{"master.port", "filer.port", "volume.port", "s3.port", "webdav.port", "admin.port"} for _, flagName := range portFlagNames { explicitPortFlags[flagName] = isFlagPassed(flagName) } diff --git a/weed/command/s3.go b/weed/command/s3.go index eb40ec230..211e51213 100644 --- a/weed/command/s3.go +++ b/weed/command/s3.go @@ -60,7 +60,6 @@ type S3Options struct { concurrentUploadLimitMB *int concurrentFileUploadLimit *int enableIam *bool - iamReadOnly *bool debug *bool debugPort *int cipher *bool @@ -93,7 +92,6 @@ func init() { s3StandaloneOptions.concurrentUploadLimitMB = cmdS3.Flag.Int("concurrentUploadLimitMB", 0, "limit total concurrent upload size, 0 means unlimited") s3StandaloneOptions.concurrentFileUploadLimit = cmdS3.Flag.Int("concurrentFileUploadLimit", 0, "limit number of concurrent file uploads, 0 means unlimited") s3StandaloneOptions.enableIam = cmdS3.Flag.Bool("iam", true, "enable embedded IAM API on the same port") - s3StandaloneOptions.iamReadOnly = cmdS3.Flag.Bool("iam.readOnly", true, "disable IAM write operations on this server") s3StandaloneOptions.debug = cmdS3.Flag.Bool("debug", false, "serves runtime profiling data via pprof on the port specified by -debug.port") s3StandaloneOptions.debugPort = cmdS3.Flag.Int("debug.port", 6060, "http port for debugging") s3StandaloneOptions.cipher = cmdS3.Flag.Bool("encryptVolumeData", false, "encrypt data on volume servers") @@ -277,13 +275,6 @@ func (s3opt *S3Options) startS3Server() bool { glog.V(0).Infof("Starting S3 API Server with standard IAM") } - if *s3opt.portGrpc == 0 { - *s3opt.portGrpc = 10000 + *s3opt.port - } - if *s3opt.bindIp == "" { - *s3opt.bindIp = "0.0.0.0" - } - s3ApiServer, s3ApiServer_err = s3api.NewS3ApiServer(router, &s3api.S3ApiServerOption{ Filers: filerAddresses, Masters: masterAddresses, @@ -301,16 +292,20 @@ func (s3opt *S3Options) startS3Server() bool { ConcurrentUploadLimit: int64(*s3opt.concurrentUploadLimitMB) * 1024 * 1024, ConcurrentFileUploadLimit: int64(*s3opt.concurrentFileUploadLimit), EnableIam: *s3opt.enableIam, // Embedded IAM API (enabled by default) - IamReadOnly: *s3opt.iamReadOnly, - Cipher: *s3opt.cipher, // encrypt data on volume servers - BindIp: *s3opt.bindIp, - GrpcPort: *s3opt.portGrpc, + Cipher: *s3opt.cipher, // encrypt data on volume servers }) if s3ApiServer_err != nil { glog.Fatalf("S3 API Server startup error: %v", s3ApiServer_err) } defer s3ApiServer.Shutdown() + if *s3opt.portGrpc == 0 { + *s3opt.portGrpc = 10000 + *s3opt.port + } + if *s3opt.bindIp == "" { + *s3opt.bindIp = "0.0.0.0" + } + if runtime.GOOS != "windows" { localSocket := *s3opt.localSocket if localSocket == "" { @@ -350,7 +345,7 @@ func (s3opt *S3Options) startS3Server() bool { glog.Fatalf("s3 failed to listen on grpc port %d: %v", grpcPort, err) } grpcS := pb.NewGrpcServer(security.LoadServerTLS(util.GetViper(), "grpc.s3")) - s3_pb.RegisterSeaweedS3IamCacheServer(grpcS, s3ApiServer) + s3_pb.RegisterSeaweedS3Server(grpcS, s3ApiServer) reflection.Register(grpcS) if grpcLocalL != nil { go grpcS.Serve(grpcLocalL) diff --git a/weed/command/server.go b/weed/command/server.go index 453604c22..ae2e421ba 100644 --- a/weed/command/server.go +++ b/weed/command/server.go @@ -175,7 +175,6 @@ func init() { s3Options.concurrentUploadLimitMB = cmdServer.Flag.Int("s3.concurrentUploadLimitMB", 0, "limit total concurrent upload size for S3, 0 means unlimited") s3Options.concurrentFileUploadLimit = cmdServer.Flag.Int("s3.concurrentFileUploadLimit", 0, "limit number of concurrent file uploads for S3, 0 means unlimited") s3Options.enableIam = cmdServer.Flag.Bool("s3.iam", true, "enable embedded IAM API on the same S3 port") - s3Options.iamReadOnly = cmdServer.Flag.Bool("s3.iam.readOnly", true, "disable IAM write operations on this server") s3Options.cipher = cmdServer.Flag.Bool("s3.encryptVolumeData", false, "encrypt data on volume servers for S3 uploads") sftpOptions.port = cmdServer.Flag.Int("sftp.port", 2022, "SFTP server listen port") diff --git a/weed/credential/credential_manager.go b/weed/credential/credential_manager.go index 01f4a744d..2a7c595fd 100644 --- a/weed/credential/credential_manager.go +++ b/weed/credential/credential_manager.go @@ -8,8 +8,6 @@ import ( "github.com/seaweedfs/seaweedfs/weed/pb/iam_pb" "github.com/seaweedfs/seaweedfs/weed/s3api/policy_engine" "github.com/seaweedfs/seaweedfs/weed/util" - "github.com/seaweedfs/seaweedfs/weed/wdclient" - "google.golang.org/grpc" ) // CredentialManager manages user credentials using a configurable store @@ -44,11 +42,6 @@ func NewCredentialManager(storeName CredentialStoreTypeName, configuration util. }, nil } -// SetMasterClient sets the master client to enable propagation of changes to S3 servers -func (cm *CredentialManager) SetMasterClient(masterClient *wdclient.MasterClient, grpcDialOption grpc.DialOption) { - cm.store = NewPropagatingCredentialStore(cm.store, masterClient, grpcDialOption) -} - // GetStore returns the underlying credential store func (cm *CredentialManager) GetStore() CredentialStore { return cm.store diff --git a/weed/credential/propagating_store.go b/weed/credential/propagating_store.go deleted file mode 100644 index 68e1c730d..000000000 --- a/weed/credential/propagating_store.go +++ /dev/null @@ -1,297 +0,0 @@ -package credential - -import ( - "context" - "encoding/json" - "sync" - "time" - - "github.com/seaweedfs/seaweedfs/weed/cluster" - "github.com/seaweedfs/seaweedfs/weed/glog" - "github.com/seaweedfs/seaweedfs/weed/pb" - "github.com/seaweedfs/seaweedfs/weed/pb/iam_pb" - "github.com/seaweedfs/seaweedfs/weed/pb/master_pb" - "github.com/seaweedfs/seaweedfs/weed/pb/s3_pb" - "github.com/seaweedfs/seaweedfs/weed/s3api/policy_engine" - "github.com/seaweedfs/seaweedfs/weed/wdclient" - "google.golang.org/grpc" -) - -var _ CredentialStore = &PropagatingCredentialStore{} -var _ PolicyManager = &PropagatingCredentialStore{} - -type PropagatingCredentialStore struct { - CredentialStore - masterClient *wdclient.MasterClient - grpcDialOption grpc.DialOption -} - -func NewPropagatingCredentialStore(upstream CredentialStore, masterClient *wdclient.MasterClient, grpcDialOption grpc.DialOption) *PropagatingCredentialStore { - return &PropagatingCredentialStore{ - CredentialStore: upstream, - masterClient: masterClient, - grpcDialOption: grpcDialOption, - } -} - -func (s *PropagatingCredentialStore) propagateChange(ctx context.Context, fn func(context.Context, s3_pb.SeaweedS3IamCacheClient) error) { - if s.masterClient == nil { - return - } - - // List S3 servers - var s3Servers []string - err := s.masterClient.WithClient(false, func(client master_pb.SeaweedClient) error { - glog.V(4).Infof("IAM: listing S3 servers (FilerGroup: '%s')", s.masterClient.FilerGroup) - resp, err := client.ListClusterNodes(ctx, &master_pb.ListClusterNodesRequest{ - ClientType: cluster.S3Type, - FilerGroup: s.masterClient.FilerGroup, - }) - if err != nil { - glog.V(1).Infof("failed to list S3 servers: %v", err) - return err - } - for _, node := range resp.ClusterNodes { - s3Servers = append(s3Servers, node.Address) - } - - return nil - }) - if err != nil { - glog.V(1).Infof("failed to list s3 servers via master client: %v", err) - return - } - glog.V(1).Infof("IAM: propagating change to %d S3 servers: %v", len(s3Servers), s3Servers) - - // Create context with timeout for the propagation process - propagateCtx, cancel := context.WithTimeout(ctx, 10*time.Second) - defer cancel() - - var wg sync.WaitGroup - for _, server := range s3Servers { - wg.Add(1) - go func(server string) { - defer wg.Done() - err := pb.WithGrpcClient(false, 0, func(conn *grpc.ClientConn) error { - glog.V(4).Infof("IAM: successfully connected to S3 server %s for propagation", server) - client := s3_pb.NewSeaweedS3IamCacheClient(conn) - return fn(propagateCtx, client) - }, server, false, s.grpcDialOption) - if err != nil { - glog.V(1).Infof("failed to propagate change to s3 server %s: %v", server, err) - } - }(server) - } - wg.Wait() -} - -func (s *PropagatingCredentialStore) CreateUser(ctx context.Context, identity *iam_pb.Identity) error { - glog.V(4).Infof("IAM: PropagatingCredentialStore.CreateUser %s", identity.Name) - if err := s.CredentialStore.CreateUser(ctx, identity); err != nil { - return err - } - s.propagateChange(ctx, func(tx context.Context, client s3_pb.SeaweedS3IamCacheClient) error { - _, err := client.PutIdentity(tx, &iam_pb.PutIdentityRequest{Identity: identity}) - return err - }) - return nil -} - -func (s *PropagatingCredentialStore) UpdateUser(ctx context.Context, username string, identity *iam_pb.Identity) error { - glog.V(4).Infof("IAM: PropagatingCredentialStore.UpdateUser %s", username) - if err := s.CredentialStore.UpdateUser(ctx, username, identity); err != nil { - return err - } - s.propagateChange(ctx, func(tx context.Context, client s3_pb.SeaweedS3IamCacheClient) error { - if _, err := client.PutIdentity(tx, &iam_pb.PutIdentityRequest{Identity: identity}); err != nil { - return err - } - if username != identity.Name { - if _, err := client.RemoveIdentity(tx, &iam_pb.RemoveIdentityRequest{Username: username}); err != nil { - return err - } - } - return nil - }) - return nil -} - -func (s *PropagatingCredentialStore) DeleteUser(ctx context.Context, username string) error { - glog.V(4).Infof("IAM: PropagatingCredentialStore.DeleteUser %s", username) - if err := s.CredentialStore.DeleteUser(ctx, username); err != nil { - return err - } - s.propagateChange(ctx, func(tx context.Context, client s3_pb.SeaweedS3IamCacheClient) error { - _, err := client.RemoveIdentity(tx, &iam_pb.RemoveIdentityRequest{Username: username}) - return err - }) - return nil -} - -func (s *PropagatingCredentialStore) CreateAccessKey(ctx context.Context, username string, credential *iam_pb.Credential) error { - if err := s.CredentialStore.CreateAccessKey(ctx, username, credential); err != nil { - return err - } - // Fetch updated identity to propagate - identity, err := s.CredentialStore.GetUser(ctx, username) - if err != nil { - glog.Warningf("failed to get user %s after creating access key: %v", username, err) - return nil - } - s.propagateChange(ctx, func(tx context.Context, client s3_pb.SeaweedS3IamCacheClient) error { - _, err := client.PutIdentity(tx, &iam_pb.PutIdentityRequest{Identity: identity}) - return err - }) - return nil -} - -func (s *PropagatingCredentialStore) DeleteAccessKey(ctx context.Context, username string, accessKey string) error { - if err := s.CredentialStore.DeleteAccessKey(ctx, username, accessKey); err != nil { - return err - } - // Fetch updated identity to propagate - identity, err := s.CredentialStore.GetUser(ctx, username) - if err != nil { - glog.Warningf("failed to get user %s after deleting access key: %v", username, err) - return nil - } - s.propagateChange(ctx, func(tx context.Context, client s3_pb.SeaweedS3IamCacheClient) error { - _, err := client.PutIdentity(tx, &iam_pb.PutIdentityRequest{Identity: identity}) - return err - }) - return nil -} - -func (s *PropagatingCredentialStore) PutPolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error { - glog.V(4).Infof("IAM: PropagatingCredentialStore.PutPolicy %s", name) - if err := s.CredentialStore.PutPolicy(ctx, name, document); err != nil { - return err - } - s.propagateChange(ctx, func(tx context.Context, client s3_pb.SeaweedS3IamCacheClient) error { - content, err := json.Marshal(document) - if err != nil { - return err - } - _, err = client.PutPolicy(tx, &iam_pb.PutPolicyRequest{Name: name, Content: string(content)}) - return err - }) - return nil -} - -func (s *PropagatingCredentialStore) DeletePolicy(ctx context.Context, name string) error { - glog.V(4).Infof("IAM: PropagatingCredentialStore.DeletePolicy %s", name) - if err := s.CredentialStore.DeletePolicy(ctx, name); err != nil { - return err - } - s.propagateChange(ctx, func(tx context.Context, client s3_pb.SeaweedS3IamCacheClient) error { - _, err := client.DeletePolicy(tx, &iam_pb.DeletePolicyRequest{Name: name}) - return err - }) - return nil -} - -func (s *PropagatingCredentialStore) CreatePolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error { - if pm, ok := s.CredentialStore.(PolicyManager); ok { - if err := pm.CreatePolicy(ctx, name, document); err != nil { - return err - } - } else { - if err := s.CredentialStore.PutPolicy(ctx, name, document); err != nil { - return err - } - } - s.propagateChange(ctx, func(tx context.Context, client s3_pb.SeaweedS3IamCacheClient) error { - content, err := json.Marshal(document) - if err != nil { - return err - } - _, err = client.PutPolicy(tx, &iam_pb.PutPolicyRequest{Name: name, Content: string(content)}) - return err - }) - return nil -} - -func (s *PropagatingCredentialStore) UpdatePolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error { - if pm, ok := s.CredentialStore.(PolicyManager); ok { - if err := pm.UpdatePolicy(ctx, name, document); err != nil { - return err - } - } else { - if err := s.CredentialStore.PutPolicy(ctx, name, document); err != nil { - return err - } - } - s.propagateChange(ctx, func(tx context.Context, client s3_pb.SeaweedS3IamCacheClient) error { - content, err := json.Marshal(document) - if err != nil { - return err - } - _, err = client.PutPolicy(tx, &iam_pb.PutPolicyRequest{Name: name, Content: string(content)}) - return err - }) - return nil -} - -func (s *PropagatingCredentialStore) CreateServiceAccount(ctx context.Context, sa *iam_pb.ServiceAccount) error { - glog.V(4).Infof("IAM: PropagatingCredentialStore.CreateServiceAccount %s (parent: %s)", sa.Id, sa.ParentUser) - if err := s.CredentialStore.CreateServiceAccount(ctx, sa); err != nil { - return err - } - // Fetch parent identity to propagate - identity, err := s.CredentialStore.GetUser(ctx, sa.ParentUser) - if err != nil { - glog.Warningf("failed to get parent user %s after creating service account: %v", sa.ParentUser, err) - return nil - } - s.propagateChange(ctx, func(tx context.Context, client s3_pb.SeaweedS3IamCacheClient) error { - _, err := client.PutIdentity(tx, &iam_pb.PutIdentityRequest{Identity: identity}) - return err - }) - return nil -} - -func (s *PropagatingCredentialStore) UpdateServiceAccount(ctx context.Context, id string, sa *iam_pb.ServiceAccount) error { - if err := s.CredentialStore.UpdateServiceAccount(ctx, id, sa); err != nil { - return err - } - // Fetch parent identity to propagate - identity, err := s.CredentialStore.GetUser(ctx, sa.ParentUser) - if err != nil { - glog.Warningf("failed to get parent user %s after updating service account: %v", sa.ParentUser, err) - return nil - } - s.propagateChange(ctx, func(tx context.Context, client s3_pb.SeaweedS3IamCacheClient) error { - _, err := client.PutIdentity(tx, &iam_pb.PutIdentityRequest{Identity: identity}) - return err - }) - return nil -} - -func (s *PropagatingCredentialStore) DeleteServiceAccount(ctx context.Context, id string) error { - // Retrieve SA first to get ParentUser - sa, err := s.CredentialStore.GetServiceAccount(ctx, id) - if err != nil { - // If accessing non-existent SA, just proceed to delete (idempotency) - // But we can't propagate to parent... - if err := s.CredentialStore.DeleteServiceAccount(ctx, id); err != nil { - return err - } - return nil - } - - if err := s.CredentialStore.DeleteServiceAccount(ctx, id); err != nil { - return err - } - - // Fetch parent identity to propagate - identity, err := s.CredentialStore.GetUser(ctx, sa.ParentUser) - if err != nil { - glog.Warningf("failed to get parent user %s after deleting service account: %v", sa.ParentUser, err) - return nil - } - s.propagateChange(ctx, func(tx context.Context, client s3_pb.SeaweedS3IamCacheClient) error { - _, err := client.PutIdentity(tx, &iam_pb.PutIdentityRequest{Identity: identity}) - return err - }) - return nil -} diff --git a/weed/pb/iam.proto b/weed/pb/iam.proto index 4972ae108..4827262b2 100644 --- a/weed/pb/iam.proto +++ b/weed/pb/iam.proto @@ -290,22 +290,3 @@ message GetServiceAccountByAccessKeyResponse { ServiceAccount service_account = 1; } - -////////////////////////////////////////////////// -// S3 IAM Cache Management -// Designed for unidirectional propagation from Filer to S3 Servers - - -message PutIdentityRequest { - Identity identity = 1; -} - -message PutIdentityResponse { -} - -message RemoveIdentityRequest { - string username = 1; -} - -message RemoveIdentityResponse { -} diff --git a/weed/pb/iam_pb/iam.pb.go b/weed/pb/iam_pb/iam.pb.go index 243abda7e..514d46660 100644 --- a/weed/pb/iam_pb/iam.pb.go +++ b/weed/pb/iam_pb/iam.pb.go @@ -2536,166 +2536,6 @@ func (x *GetServiceAccountByAccessKeyResponse) GetServiceAccount() *ServiceAccou return nil } -type PutIdentityRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Identity *Identity `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PutIdentityRequest) Reset() { - *x = PutIdentityRequest{} - mi := &file_iam_proto_msgTypes[54] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PutIdentityRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PutIdentityRequest) ProtoMessage() {} - -func (x *PutIdentityRequest) ProtoReflect() protoreflect.Message { - mi := &file_iam_proto_msgTypes[54] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PutIdentityRequest.ProtoReflect.Descriptor instead. -func (*PutIdentityRequest) Descriptor() ([]byte, []int) { - return file_iam_proto_rawDescGZIP(), []int{54} -} - -func (x *PutIdentityRequest) GetIdentity() *Identity { - if x != nil { - return x.Identity - } - return nil -} - -type PutIdentityResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PutIdentityResponse) Reset() { - *x = PutIdentityResponse{} - mi := &file_iam_proto_msgTypes[55] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PutIdentityResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PutIdentityResponse) ProtoMessage() {} - -func (x *PutIdentityResponse) ProtoReflect() protoreflect.Message { - mi := &file_iam_proto_msgTypes[55] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PutIdentityResponse.ProtoReflect.Descriptor instead. -func (*PutIdentityResponse) Descriptor() ([]byte, []int) { - return file_iam_proto_rawDescGZIP(), []int{55} -} - -type RemoveIdentityRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RemoveIdentityRequest) Reset() { - *x = RemoveIdentityRequest{} - mi := &file_iam_proto_msgTypes[56] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RemoveIdentityRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemoveIdentityRequest) ProtoMessage() {} - -func (x *RemoveIdentityRequest) ProtoReflect() protoreflect.Message { - mi := &file_iam_proto_msgTypes[56] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RemoveIdentityRequest.ProtoReflect.Descriptor instead. -func (*RemoveIdentityRequest) Descriptor() ([]byte, []int) { - return file_iam_proto_rawDescGZIP(), []int{56} -} - -func (x *RemoveIdentityRequest) GetUsername() string { - if x != nil { - return x.Username - } - return "" -} - -type RemoveIdentityResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RemoveIdentityResponse) Reset() { - *x = RemoveIdentityResponse{} - mi := &file_iam_proto_msgTypes[57] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RemoveIdentityResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemoveIdentityResponse) ProtoMessage() {} - -func (x *RemoveIdentityResponse) ProtoReflect() protoreflect.Message { - mi := &file_iam_proto_msgTypes[57] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RemoveIdentityResponse.ProtoReflect.Descriptor instead. -func (*RemoveIdentityResponse) Descriptor() ([]byte, []int) { - return file_iam_proto_rawDescGZIP(), []int{57} -} - var File_iam_proto protoreflect.FileDescriptor const file_iam_proto_rawDesc = "" + @@ -2847,13 +2687,7 @@ const file_iam_proto_rawDesc = "" + "\n" + "access_key\x18\x01 \x01(\tR\taccessKey\"g\n" + "$GetServiceAccountByAccessKeyResponse\x12?\n" + - "\x0fservice_account\x18\x01 \x01(\v2\x16.iam_pb.ServiceAccountR\x0eserviceAccount\"B\n" + - "\x12PutIdentityRequest\x12,\n" + - "\bidentity\x18\x01 \x01(\v2\x10.iam_pb.IdentityR\bidentity\"\x15\n" + - "\x13PutIdentityResponse\"3\n" + - "\x15RemoveIdentityRequest\x12\x1a\n" + - "\busername\x18\x01 \x01(\tR\busername\"\x18\n" + - "\x16RemoveIdentityResponse2\x99\r\n" + + "\x0fservice_account\x18\x01 \x01(\v2\x16.iam_pb.ServiceAccountR\x0eserviceAccount2\x99\r\n" + "\x1fSeaweedIdentityAccessManagement\x12U\n" + "\x10GetConfiguration\x12\x1f.iam_pb.GetConfigurationRequest\x1a .iam_pb.GetConfigurationResponse\x12U\n" + "\x10PutConfiguration\x12\x1f.iam_pb.PutConfigurationRequest\x1a .iam_pb.PutConfigurationResponse\x12C\n" + @@ -2892,7 +2726,7 @@ func file_iam_proto_rawDescGZIP() []byte { return file_iam_proto_rawDescData } -var file_iam_proto_msgTypes = make([]protoimpl.MessageInfo, 58) +var file_iam_proto_msgTypes = make([]protoimpl.MessageInfo, 54) var file_iam_proto_goTypes = []any{ (*GetConfigurationRequest)(nil), // 0: iam_pb.GetConfigurationRequest (*GetConfigurationResponse)(nil), // 1: iam_pb.GetConfigurationResponse @@ -2948,10 +2782,6 @@ var file_iam_proto_goTypes = []any{ (*ListServiceAccountsResponse)(nil), // 51: iam_pb.ListServiceAccountsResponse (*GetServiceAccountByAccessKeyRequest)(nil), // 52: iam_pb.GetServiceAccountByAccessKeyRequest (*GetServiceAccountByAccessKeyResponse)(nil), // 53: iam_pb.GetServiceAccountByAccessKeyResponse - (*PutIdentityRequest)(nil), // 54: iam_pb.PutIdentityRequest - (*PutIdentityResponse)(nil), // 55: iam_pb.PutIdentityResponse - (*RemoveIdentityRequest)(nil), // 56: iam_pb.RemoveIdentityRequest - (*RemoveIdentityResponse)(nil), // 57: iam_pb.RemoveIdentityResponse } var file_iam_proto_depIdxs = []int32{ 28, // 0: iam_pb.GetConfigurationResponse.configuration:type_name -> iam_pb.S3ApiConfiguration @@ -2975,52 +2805,51 @@ var file_iam_proto_depIdxs = []int32{ 32, // 18: iam_pb.GetServiceAccountResponse.service_account:type_name -> iam_pb.ServiceAccount 32, // 19: iam_pb.ListServiceAccountsResponse.service_accounts:type_name -> iam_pb.ServiceAccount 32, // 20: iam_pb.GetServiceAccountByAccessKeyResponse.service_account:type_name -> iam_pb.ServiceAccount - 29, // 21: iam_pb.PutIdentityRequest.identity:type_name -> iam_pb.Identity - 0, // 22: iam_pb.SeaweedIdentityAccessManagement.GetConfiguration:input_type -> iam_pb.GetConfigurationRequest - 2, // 23: iam_pb.SeaweedIdentityAccessManagement.PutConfiguration:input_type -> iam_pb.PutConfigurationRequest - 4, // 24: iam_pb.SeaweedIdentityAccessManagement.CreateUser:input_type -> iam_pb.CreateUserRequest - 6, // 25: iam_pb.SeaweedIdentityAccessManagement.GetUser:input_type -> iam_pb.GetUserRequest - 8, // 26: iam_pb.SeaweedIdentityAccessManagement.UpdateUser:input_type -> iam_pb.UpdateUserRequest - 10, // 27: iam_pb.SeaweedIdentityAccessManagement.DeleteUser:input_type -> iam_pb.DeleteUserRequest - 12, // 28: iam_pb.SeaweedIdentityAccessManagement.ListUsers:input_type -> iam_pb.ListUsersRequest - 14, // 29: iam_pb.SeaweedIdentityAccessManagement.CreateAccessKey:input_type -> iam_pb.CreateAccessKeyRequest - 16, // 30: iam_pb.SeaweedIdentityAccessManagement.DeleteAccessKey:input_type -> iam_pb.DeleteAccessKeyRequest - 18, // 31: iam_pb.SeaweedIdentityAccessManagement.GetUserByAccessKey:input_type -> iam_pb.GetUserByAccessKeyRequest - 33, // 32: iam_pb.SeaweedIdentityAccessManagement.PutPolicy:input_type -> iam_pb.PutPolicyRequest - 35, // 33: iam_pb.SeaweedIdentityAccessManagement.GetPolicy:input_type -> iam_pb.GetPolicyRequest - 37, // 34: iam_pb.SeaweedIdentityAccessManagement.ListPolicies:input_type -> iam_pb.ListPoliciesRequest - 39, // 35: iam_pb.SeaweedIdentityAccessManagement.DeletePolicy:input_type -> iam_pb.DeletePolicyRequest - 42, // 36: iam_pb.SeaweedIdentityAccessManagement.CreateServiceAccount:input_type -> iam_pb.CreateServiceAccountRequest - 44, // 37: iam_pb.SeaweedIdentityAccessManagement.UpdateServiceAccount:input_type -> iam_pb.UpdateServiceAccountRequest - 46, // 38: iam_pb.SeaweedIdentityAccessManagement.DeleteServiceAccount:input_type -> iam_pb.DeleteServiceAccountRequest - 48, // 39: iam_pb.SeaweedIdentityAccessManagement.GetServiceAccount:input_type -> iam_pb.GetServiceAccountRequest - 50, // 40: iam_pb.SeaweedIdentityAccessManagement.ListServiceAccounts:input_type -> iam_pb.ListServiceAccountsRequest - 52, // 41: iam_pb.SeaweedIdentityAccessManagement.GetServiceAccountByAccessKey:input_type -> iam_pb.GetServiceAccountByAccessKeyRequest - 1, // 42: iam_pb.SeaweedIdentityAccessManagement.GetConfiguration:output_type -> iam_pb.GetConfigurationResponse - 3, // 43: iam_pb.SeaweedIdentityAccessManagement.PutConfiguration:output_type -> iam_pb.PutConfigurationResponse - 5, // 44: iam_pb.SeaweedIdentityAccessManagement.CreateUser:output_type -> iam_pb.CreateUserResponse - 7, // 45: iam_pb.SeaweedIdentityAccessManagement.GetUser:output_type -> iam_pb.GetUserResponse - 9, // 46: iam_pb.SeaweedIdentityAccessManagement.UpdateUser:output_type -> iam_pb.UpdateUserResponse - 11, // 47: iam_pb.SeaweedIdentityAccessManagement.DeleteUser:output_type -> iam_pb.DeleteUserResponse - 13, // 48: iam_pb.SeaweedIdentityAccessManagement.ListUsers:output_type -> iam_pb.ListUsersResponse - 15, // 49: iam_pb.SeaweedIdentityAccessManagement.CreateAccessKey:output_type -> iam_pb.CreateAccessKeyResponse - 17, // 50: iam_pb.SeaweedIdentityAccessManagement.DeleteAccessKey:output_type -> iam_pb.DeleteAccessKeyResponse - 19, // 51: iam_pb.SeaweedIdentityAccessManagement.GetUserByAccessKey:output_type -> iam_pb.GetUserByAccessKeyResponse - 34, // 52: iam_pb.SeaweedIdentityAccessManagement.PutPolicy:output_type -> iam_pb.PutPolicyResponse - 36, // 53: iam_pb.SeaweedIdentityAccessManagement.GetPolicy:output_type -> iam_pb.GetPolicyResponse - 38, // 54: iam_pb.SeaweedIdentityAccessManagement.ListPolicies:output_type -> iam_pb.ListPoliciesResponse - 40, // 55: iam_pb.SeaweedIdentityAccessManagement.DeletePolicy:output_type -> iam_pb.DeletePolicyResponse - 43, // 56: iam_pb.SeaweedIdentityAccessManagement.CreateServiceAccount:output_type -> iam_pb.CreateServiceAccountResponse - 45, // 57: iam_pb.SeaweedIdentityAccessManagement.UpdateServiceAccount:output_type -> iam_pb.UpdateServiceAccountResponse - 47, // 58: iam_pb.SeaweedIdentityAccessManagement.DeleteServiceAccount:output_type -> iam_pb.DeleteServiceAccountResponse - 49, // 59: iam_pb.SeaweedIdentityAccessManagement.GetServiceAccount:output_type -> iam_pb.GetServiceAccountResponse - 51, // 60: iam_pb.SeaweedIdentityAccessManagement.ListServiceAccounts:output_type -> iam_pb.ListServiceAccountsResponse - 53, // 61: iam_pb.SeaweedIdentityAccessManagement.GetServiceAccountByAccessKey:output_type -> iam_pb.GetServiceAccountByAccessKeyResponse - 42, // [42:62] is the sub-list for method output_type - 22, // [22:42] is the sub-list for method input_type - 22, // [22:22] is the sub-list for extension type_name - 22, // [22:22] is the sub-list for extension extendee - 0, // [0:22] is the sub-list for field type_name + 0, // 21: iam_pb.SeaweedIdentityAccessManagement.GetConfiguration:input_type -> iam_pb.GetConfigurationRequest + 2, // 22: iam_pb.SeaweedIdentityAccessManagement.PutConfiguration:input_type -> iam_pb.PutConfigurationRequest + 4, // 23: iam_pb.SeaweedIdentityAccessManagement.CreateUser:input_type -> iam_pb.CreateUserRequest + 6, // 24: iam_pb.SeaweedIdentityAccessManagement.GetUser:input_type -> iam_pb.GetUserRequest + 8, // 25: iam_pb.SeaweedIdentityAccessManagement.UpdateUser:input_type -> iam_pb.UpdateUserRequest + 10, // 26: iam_pb.SeaweedIdentityAccessManagement.DeleteUser:input_type -> iam_pb.DeleteUserRequest + 12, // 27: iam_pb.SeaweedIdentityAccessManagement.ListUsers:input_type -> iam_pb.ListUsersRequest + 14, // 28: iam_pb.SeaweedIdentityAccessManagement.CreateAccessKey:input_type -> iam_pb.CreateAccessKeyRequest + 16, // 29: iam_pb.SeaweedIdentityAccessManagement.DeleteAccessKey:input_type -> iam_pb.DeleteAccessKeyRequest + 18, // 30: iam_pb.SeaweedIdentityAccessManagement.GetUserByAccessKey:input_type -> iam_pb.GetUserByAccessKeyRequest + 33, // 31: iam_pb.SeaweedIdentityAccessManagement.PutPolicy:input_type -> iam_pb.PutPolicyRequest + 35, // 32: iam_pb.SeaweedIdentityAccessManagement.GetPolicy:input_type -> iam_pb.GetPolicyRequest + 37, // 33: iam_pb.SeaweedIdentityAccessManagement.ListPolicies:input_type -> iam_pb.ListPoliciesRequest + 39, // 34: iam_pb.SeaweedIdentityAccessManagement.DeletePolicy:input_type -> iam_pb.DeletePolicyRequest + 42, // 35: iam_pb.SeaweedIdentityAccessManagement.CreateServiceAccount:input_type -> iam_pb.CreateServiceAccountRequest + 44, // 36: iam_pb.SeaweedIdentityAccessManagement.UpdateServiceAccount:input_type -> iam_pb.UpdateServiceAccountRequest + 46, // 37: iam_pb.SeaweedIdentityAccessManagement.DeleteServiceAccount:input_type -> iam_pb.DeleteServiceAccountRequest + 48, // 38: iam_pb.SeaweedIdentityAccessManagement.GetServiceAccount:input_type -> iam_pb.GetServiceAccountRequest + 50, // 39: iam_pb.SeaweedIdentityAccessManagement.ListServiceAccounts:input_type -> iam_pb.ListServiceAccountsRequest + 52, // 40: iam_pb.SeaweedIdentityAccessManagement.GetServiceAccountByAccessKey:input_type -> iam_pb.GetServiceAccountByAccessKeyRequest + 1, // 41: iam_pb.SeaweedIdentityAccessManagement.GetConfiguration:output_type -> iam_pb.GetConfigurationResponse + 3, // 42: iam_pb.SeaweedIdentityAccessManagement.PutConfiguration:output_type -> iam_pb.PutConfigurationResponse + 5, // 43: iam_pb.SeaweedIdentityAccessManagement.CreateUser:output_type -> iam_pb.CreateUserResponse + 7, // 44: iam_pb.SeaweedIdentityAccessManagement.GetUser:output_type -> iam_pb.GetUserResponse + 9, // 45: iam_pb.SeaweedIdentityAccessManagement.UpdateUser:output_type -> iam_pb.UpdateUserResponse + 11, // 46: iam_pb.SeaweedIdentityAccessManagement.DeleteUser:output_type -> iam_pb.DeleteUserResponse + 13, // 47: iam_pb.SeaweedIdentityAccessManagement.ListUsers:output_type -> iam_pb.ListUsersResponse + 15, // 48: iam_pb.SeaweedIdentityAccessManagement.CreateAccessKey:output_type -> iam_pb.CreateAccessKeyResponse + 17, // 49: iam_pb.SeaweedIdentityAccessManagement.DeleteAccessKey:output_type -> iam_pb.DeleteAccessKeyResponse + 19, // 50: iam_pb.SeaweedIdentityAccessManagement.GetUserByAccessKey:output_type -> iam_pb.GetUserByAccessKeyResponse + 34, // 51: iam_pb.SeaweedIdentityAccessManagement.PutPolicy:output_type -> iam_pb.PutPolicyResponse + 36, // 52: iam_pb.SeaweedIdentityAccessManagement.GetPolicy:output_type -> iam_pb.GetPolicyResponse + 38, // 53: iam_pb.SeaweedIdentityAccessManagement.ListPolicies:output_type -> iam_pb.ListPoliciesResponse + 40, // 54: iam_pb.SeaweedIdentityAccessManagement.DeletePolicy:output_type -> iam_pb.DeletePolicyResponse + 43, // 55: iam_pb.SeaweedIdentityAccessManagement.CreateServiceAccount:output_type -> iam_pb.CreateServiceAccountResponse + 45, // 56: iam_pb.SeaweedIdentityAccessManagement.UpdateServiceAccount:output_type -> iam_pb.UpdateServiceAccountResponse + 47, // 57: iam_pb.SeaweedIdentityAccessManagement.DeleteServiceAccount:output_type -> iam_pb.DeleteServiceAccountResponse + 49, // 58: iam_pb.SeaweedIdentityAccessManagement.GetServiceAccount:output_type -> iam_pb.GetServiceAccountResponse + 51, // 59: iam_pb.SeaweedIdentityAccessManagement.ListServiceAccounts:output_type -> iam_pb.ListServiceAccountsResponse + 53, // 60: iam_pb.SeaweedIdentityAccessManagement.GetServiceAccountByAccessKey:output_type -> iam_pb.GetServiceAccountByAccessKeyResponse + 41, // [41:61] is the sub-list for method output_type + 21, // [21:41] is the sub-list for method input_type + 21, // [21:21] is the sub-list for extension type_name + 21, // [21:21] is the sub-list for extension extendee + 0, // [0:21] is the sub-list for field type_name } func init() { file_iam_proto_init() } @@ -3034,7 +2863,7 @@ func file_iam_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_iam_proto_rawDesc), len(file_iam_proto_rawDesc)), NumEnums: 0, - NumMessages: 58, + NumMessages: 54, NumExtensions: 0, NumServices: 1, }, diff --git a/weed/pb/s3.proto b/weed/pb/s3.proto index 8deb87ada..097c7ba26 100644 --- a/weed/pb/s3.proto +++ b/weed/pb/s3.proto @@ -10,14 +10,29 @@ option java_outer_classname = "S3Proto"; import "iam.proto"; -// Designed for unidirectional propagation from Filer to S3 Servers -service SeaweedS3IamCache { - rpc PutIdentity (iam_pb.PutIdentityRequest) returns (iam_pb.PutIdentityResponse); - rpc RemoveIdentity (iam_pb.RemoveIdentityRequest) returns (iam_pb.RemoveIdentityResponse); - rpc PutPolicy (iam_pb.PutPolicyRequest) returns (iam_pb.PutPolicyResponse); - rpc GetPolicy (iam_pb.GetPolicyRequest) returns (iam_pb.GetPolicyResponse); - rpc ListPolicies (iam_pb.ListPoliciesRequest) returns (iam_pb.ListPoliciesResponse); - rpc DeletePolicy (iam_pb.DeletePolicyRequest) returns (iam_pb.DeletePolicyResponse); +service SeaweedS3 { + + // Explicit IAM APIs mirroring SeaweedIdentityAccessManagement + rpc ListUsers (iam_pb.ListUsersRequest) returns (iam_pb.ListUsersResponse) {} + rpc CreateUser (iam_pb.CreateUserRequest) returns (iam_pb.CreateUserResponse) {} + rpc GetUser (iam_pb.GetUserRequest) returns (iam_pb.GetUserResponse) {} + rpc UpdateUser (iam_pb.UpdateUserRequest) returns (iam_pb.UpdateUserResponse) {} + rpc DeleteUser (iam_pb.DeleteUserRequest) returns (iam_pb.DeleteUserResponse) {} + + rpc ListAccessKeys (iam_pb.ListAccessKeysRequest) returns (iam_pb.ListAccessKeysResponse) {} + rpc CreateAccessKey (iam_pb.CreateAccessKeyRequest) returns (iam_pb.CreateAccessKeyResponse) {} + rpc DeleteAccessKey (iam_pb.DeleteAccessKeyRequest) returns (iam_pb.DeleteAccessKeyResponse) {} + + rpc PutUserPolicy (iam_pb.PutUserPolicyRequest) returns (iam_pb.PutUserPolicyResponse) {} + rpc GetUserPolicy (iam_pb.GetUserPolicyRequest) returns (iam_pb.GetUserPolicyResponse) {} + rpc DeleteUserPolicy (iam_pb.DeleteUserPolicyRequest) returns (iam_pb.DeleteUserPolicyResponse) {} + + rpc ListServiceAccounts (iam_pb.ListServiceAccountsRequest) returns (iam_pb.ListServiceAccountsResponse) {} + rpc CreateServiceAccount (iam_pb.CreateServiceAccountRequest) returns (iam_pb.CreateServiceAccountResponse) {} + rpc UpdateServiceAccount (iam_pb.UpdateServiceAccountRequest) returns (iam_pb.UpdateServiceAccountResponse) {} + rpc DeleteServiceAccount (iam_pb.DeleteServiceAccountRequest) returns (iam_pb.DeleteServiceAccountResponse) {} + rpc GetServiceAccount (iam_pb.GetServiceAccountRequest) returns (iam_pb.GetServiceAccountResponse) {} + } ////////////////////////////////////////////////// diff --git a/weed/pb/s3_pb/s3.pb.go b/weed/pb/s3_pb/s3.pb.go index 308eaf95e..daf6fe912 100644 --- a/weed/pb/s3_pb/s3.pb.go +++ b/weed/pb/s3_pb/s3.pb.go @@ -414,14 +414,28 @@ const file_s3_proto_rawDesc = "" + "\rsse_algorithm\x18\x01 \x01(\tR\fsseAlgorithm\x12\x1c\n" + "\n" + "kms_key_id\x18\x02 \x01(\tR\bkmsKeyId\x12,\n" + - "\x12bucket_key_enabled\x18\x03 \x01(\bR\x10bucketKeyEnabled2\xc6\x03\n" + - "\x11SeaweedS3IamCache\x12F\n" + - "\vPutIdentity\x12\x1a.iam_pb.PutIdentityRequest\x1a\x1b.iam_pb.PutIdentityResponse\x12O\n" + - "\x0eRemoveIdentity\x12\x1d.iam_pb.RemoveIdentityRequest\x1a\x1e.iam_pb.RemoveIdentityResponse\x12@\n" + - "\tPutPolicy\x12\x18.iam_pb.PutPolicyRequest\x1a\x19.iam_pb.PutPolicyResponse\x12@\n" + - "\tGetPolicy\x12\x18.iam_pb.GetPolicyRequest\x1a\x19.iam_pb.GetPolicyResponse\x12I\n" + - "\fListPolicies\x12\x1b.iam_pb.ListPoliciesRequest\x1a\x1c.iam_pb.ListPoliciesResponse\x12I\n" + - "\fDeletePolicy\x12\x1b.iam_pb.DeletePolicyRequest\x1a\x1c.iam_pb.DeletePolicyResponseBI\n" + + "\x12bucket_key_enabled\x18\x03 \x01(\bR\x10bucketKeyEnabled2\xc7\n" + + "\n" + + "\tSeaweedS3\x12B\n" + + "\tListUsers\x12\x18.iam_pb.ListUsersRequest\x1a\x19.iam_pb.ListUsersResponse\"\x00\x12E\n" + + "\n" + + "CreateUser\x12\x19.iam_pb.CreateUserRequest\x1a\x1a.iam_pb.CreateUserResponse\"\x00\x12<\n" + + "\aGetUser\x12\x16.iam_pb.GetUserRequest\x1a\x17.iam_pb.GetUserResponse\"\x00\x12E\n" + + "\n" + + "UpdateUser\x12\x19.iam_pb.UpdateUserRequest\x1a\x1a.iam_pb.UpdateUserResponse\"\x00\x12E\n" + + "\n" + + "DeleteUser\x12\x19.iam_pb.DeleteUserRequest\x1a\x1a.iam_pb.DeleteUserResponse\"\x00\x12Q\n" + + "\x0eListAccessKeys\x12\x1d.iam_pb.ListAccessKeysRequest\x1a\x1e.iam_pb.ListAccessKeysResponse\"\x00\x12T\n" + + "\x0fCreateAccessKey\x12\x1e.iam_pb.CreateAccessKeyRequest\x1a\x1f.iam_pb.CreateAccessKeyResponse\"\x00\x12T\n" + + "\x0fDeleteAccessKey\x12\x1e.iam_pb.DeleteAccessKeyRequest\x1a\x1f.iam_pb.DeleteAccessKeyResponse\"\x00\x12N\n" + + "\rPutUserPolicy\x12\x1c.iam_pb.PutUserPolicyRequest\x1a\x1d.iam_pb.PutUserPolicyResponse\"\x00\x12N\n" + + "\rGetUserPolicy\x12\x1c.iam_pb.GetUserPolicyRequest\x1a\x1d.iam_pb.GetUserPolicyResponse\"\x00\x12W\n" + + "\x10DeleteUserPolicy\x12\x1f.iam_pb.DeleteUserPolicyRequest\x1a .iam_pb.DeleteUserPolicyResponse\"\x00\x12`\n" + + "\x13ListServiceAccounts\x12\".iam_pb.ListServiceAccountsRequest\x1a#.iam_pb.ListServiceAccountsResponse\"\x00\x12c\n" + + "\x14CreateServiceAccount\x12#.iam_pb.CreateServiceAccountRequest\x1a$.iam_pb.CreateServiceAccountResponse\"\x00\x12c\n" + + "\x14UpdateServiceAccount\x12#.iam_pb.UpdateServiceAccountRequest\x1a$.iam_pb.UpdateServiceAccountResponse\"\x00\x12c\n" + + "\x14DeleteServiceAccount\x12#.iam_pb.DeleteServiceAccountRequest\x1a$.iam_pb.DeleteServiceAccountResponse\"\x00\x12Z\n" + + "\x11GetServiceAccount\x12 .iam_pb.GetServiceAccountRequest\x1a!.iam_pb.GetServiceAccountResponse\"\x00BI\n" + "\x10seaweedfs.clientB\aS3ProtoZ,github.com/seaweedfs/seaweedfs/weed/pb/s3_pbb\x06proto3" var ( @@ -438,27 +452,47 @@ func file_s3_proto_rawDescGZIP() []byte { var file_s3_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_s3_proto_goTypes = []any{ - (*S3CircuitBreakerConfig)(nil), // 0: messaging_pb.S3CircuitBreakerConfig - (*S3CircuitBreakerOptions)(nil), // 1: messaging_pb.S3CircuitBreakerOptions - (*CORSRule)(nil), // 2: messaging_pb.CORSRule - (*CORSConfiguration)(nil), // 3: messaging_pb.CORSConfiguration - (*BucketMetadata)(nil), // 4: messaging_pb.BucketMetadata - (*EncryptionConfiguration)(nil), // 5: messaging_pb.EncryptionConfiguration - nil, // 6: messaging_pb.S3CircuitBreakerConfig.BucketsEntry - nil, // 7: messaging_pb.S3CircuitBreakerOptions.ActionsEntry - nil, // 8: messaging_pb.BucketMetadata.TagsEntry - (*iam_pb.PutIdentityRequest)(nil), // 9: iam_pb.PutIdentityRequest - (*iam_pb.RemoveIdentityRequest)(nil), // 10: iam_pb.RemoveIdentityRequest - (*iam_pb.PutPolicyRequest)(nil), // 11: iam_pb.PutPolicyRequest - (*iam_pb.GetPolicyRequest)(nil), // 12: iam_pb.GetPolicyRequest - (*iam_pb.ListPoliciesRequest)(nil), // 13: iam_pb.ListPoliciesRequest - (*iam_pb.DeletePolicyRequest)(nil), // 14: iam_pb.DeletePolicyRequest - (*iam_pb.PutIdentityResponse)(nil), // 15: iam_pb.PutIdentityResponse - (*iam_pb.RemoveIdentityResponse)(nil), // 16: iam_pb.RemoveIdentityResponse - (*iam_pb.PutPolicyResponse)(nil), // 17: iam_pb.PutPolicyResponse - (*iam_pb.GetPolicyResponse)(nil), // 18: iam_pb.GetPolicyResponse - (*iam_pb.ListPoliciesResponse)(nil), // 19: iam_pb.ListPoliciesResponse - (*iam_pb.DeletePolicyResponse)(nil), // 20: iam_pb.DeletePolicyResponse + (*S3CircuitBreakerConfig)(nil), // 0: messaging_pb.S3CircuitBreakerConfig + (*S3CircuitBreakerOptions)(nil), // 1: messaging_pb.S3CircuitBreakerOptions + (*CORSRule)(nil), // 2: messaging_pb.CORSRule + (*CORSConfiguration)(nil), // 3: messaging_pb.CORSConfiguration + (*BucketMetadata)(nil), // 4: messaging_pb.BucketMetadata + (*EncryptionConfiguration)(nil), // 5: messaging_pb.EncryptionConfiguration + nil, // 6: messaging_pb.S3CircuitBreakerConfig.BucketsEntry + nil, // 7: messaging_pb.S3CircuitBreakerOptions.ActionsEntry + nil, // 8: messaging_pb.BucketMetadata.TagsEntry + (*iam_pb.ListUsersRequest)(nil), // 9: iam_pb.ListUsersRequest + (*iam_pb.CreateUserRequest)(nil), // 10: iam_pb.CreateUserRequest + (*iam_pb.GetUserRequest)(nil), // 11: iam_pb.GetUserRequest + (*iam_pb.UpdateUserRequest)(nil), // 12: iam_pb.UpdateUserRequest + (*iam_pb.DeleteUserRequest)(nil), // 13: iam_pb.DeleteUserRequest + (*iam_pb.ListAccessKeysRequest)(nil), // 14: iam_pb.ListAccessKeysRequest + (*iam_pb.CreateAccessKeyRequest)(nil), // 15: iam_pb.CreateAccessKeyRequest + (*iam_pb.DeleteAccessKeyRequest)(nil), // 16: iam_pb.DeleteAccessKeyRequest + (*iam_pb.PutUserPolicyRequest)(nil), // 17: iam_pb.PutUserPolicyRequest + (*iam_pb.GetUserPolicyRequest)(nil), // 18: iam_pb.GetUserPolicyRequest + (*iam_pb.DeleteUserPolicyRequest)(nil), // 19: iam_pb.DeleteUserPolicyRequest + (*iam_pb.ListServiceAccountsRequest)(nil), // 20: iam_pb.ListServiceAccountsRequest + (*iam_pb.CreateServiceAccountRequest)(nil), // 21: iam_pb.CreateServiceAccountRequest + (*iam_pb.UpdateServiceAccountRequest)(nil), // 22: iam_pb.UpdateServiceAccountRequest + (*iam_pb.DeleteServiceAccountRequest)(nil), // 23: iam_pb.DeleteServiceAccountRequest + (*iam_pb.GetServiceAccountRequest)(nil), // 24: iam_pb.GetServiceAccountRequest + (*iam_pb.ListUsersResponse)(nil), // 25: iam_pb.ListUsersResponse + (*iam_pb.CreateUserResponse)(nil), // 26: iam_pb.CreateUserResponse + (*iam_pb.GetUserResponse)(nil), // 27: iam_pb.GetUserResponse + (*iam_pb.UpdateUserResponse)(nil), // 28: iam_pb.UpdateUserResponse + (*iam_pb.DeleteUserResponse)(nil), // 29: iam_pb.DeleteUserResponse + (*iam_pb.ListAccessKeysResponse)(nil), // 30: iam_pb.ListAccessKeysResponse + (*iam_pb.CreateAccessKeyResponse)(nil), // 31: iam_pb.CreateAccessKeyResponse + (*iam_pb.DeleteAccessKeyResponse)(nil), // 32: iam_pb.DeleteAccessKeyResponse + (*iam_pb.PutUserPolicyResponse)(nil), // 33: iam_pb.PutUserPolicyResponse + (*iam_pb.GetUserPolicyResponse)(nil), // 34: iam_pb.GetUserPolicyResponse + (*iam_pb.DeleteUserPolicyResponse)(nil), // 35: iam_pb.DeleteUserPolicyResponse + (*iam_pb.ListServiceAccountsResponse)(nil), // 36: iam_pb.ListServiceAccountsResponse + (*iam_pb.CreateServiceAccountResponse)(nil), // 37: iam_pb.CreateServiceAccountResponse + (*iam_pb.UpdateServiceAccountResponse)(nil), // 38: iam_pb.UpdateServiceAccountResponse + (*iam_pb.DeleteServiceAccountResponse)(nil), // 39: iam_pb.DeleteServiceAccountResponse + (*iam_pb.GetServiceAccountResponse)(nil), // 40: iam_pb.GetServiceAccountResponse } var file_s3_proto_depIdxs = []int32{ 1, // 0: messaging_pb.S3CircuitBreakerConfig.global:type_name -> messaging_pb.S3CircuitBreakerOptions @@ -469,20 +503,40 @@ var file_s3_proto_depIdxs = []int32{ 3, // 5: messaging_pb.BucketMetadata.cors:type_name -> messaging_pb.CORSConfiguration 5, // 6: messaging_pb.BucketMetadata.encryption:type_name -> messaging_pb.EncryptionConfiguration 1, // 7: messaging_pb.S3CircuitBreakerConfig.BucketsEntry.value:type_name -> messaging_pb.S3CircuitBreakerOptions - 9, // 8: messaging_pb.SeaweedS3IamCache.PutIdentity:input_type -> iam_pb.PutIdentityRequest - 10, // 9: messaging_pb.SeaweedS3IamCache.RemoveIdentity:input_type -> iam_pb.RemoveIdentityRequest - 11, // 10: messaging_pb.SeaweedS3IamCache.PutPolicy:input_type -> iam_pb.PutPolicyRequest - 12, // 11: messaging_pb.SeaweedS3IamCache.GetPolicy:input_type -> iam_pb.GetPolicyRequest - 13, // 12: messaging_pb.SeaweedS3IamCache.ListPolicies:input_type -> iam_pb.ListPoliciesRequest - 14, // 13: messaging_pb.SeaweedS3IamCache.DeletePolicy:input_type -> iam_pb.DeletePolicyRequest - 15, // 14: messaging_pb.SeaweedS3IamCache.PutIdentity:output_type -> iam_pb.PutIdentityResponse - 16, // 15: messaging_pb.SeaweedS3IamCache.RemoveIdentity:output_type -> iam_pb.RemoveIdentityResponse - 17, // 16: messaging_pb.SeaweedS3IamCache.PutPolicy:output_type -> iam_pb.PutPolicyResponse - 18, // 17: messaging_pb.SeaweedS3IamCache.GetPolicy:output_type -> iam_pb.GetPolicyResponse - 19, // 18: messaging_pb.SeaweedS3IamCache.ListPolicies:output_type -> iam_pb.ListPoliciesResponse - 20, // 19: messaging_pb.SeaweedS3IamCache.DeletePolicy:output_type -> iam_pb.DeletePolicyResponse - 14, // [14:20] is the sub-list for method output_type - 8, // [8:14] is the sub-list for method input_type + 9, // 8: messaging_pb.SeaweedS3.ListUsers:input_type -> iam_pb.ListUsersRequest + 10, // 9: messaging_pb.SeaweedS3.CreateUser:input_type -> iam_pb.CreateUserRequest + 11, // 10: messaging_pb.SeaweedS3.GetUser:input_type -> iam_pb.GetUserRequest + 12, // 11: messaging_pb.SeaweedS3.UpdateUser:input_type -> iam_pb.UpdateUserRequest + 13, // 12: messaging_pb.SeaweedS3.DeleteUser:input_type -> iam_pb.DeleteUserRequest + 14, // 13: messaging_pb.SeaweedS3.ListAccessKeys:input_type -> iam_pb.ListAccessKeysRequest + 15, // 14: messaging_pb.SeaweedS3.CreateAccessKey:input_type -> iam_pb.CreateAccessKeyRequest + 16, // 15: messaging_pb.SeaweedS3.DeleteAccessKey:input_type -> iam_pb.DeleteAccessKeyRequest + 17, // 16: messaging_pb.SeaweedS3.PutUserPolicy:input_type -> iam_pb.PutUserPolicyRequest + 18, // 17: messaging_pb.SeaweedS3.GetUserPolicy:input_type -> iam_pb.GetUserPolicyRequest + 19, // 18: messaging_pb.SeaweedS3.DeleteUserPolicy:input_type -> iam_pb.DeleteUserPolicyRequest + 20, // 19: messaging_pb.SeaweedS3.ListServiceAccounts:input_type -> iam_pb.ListServiceAccountsRequest + 21, // 20: messaging_pb.SeaweedS3.CreateServiceAccount:input_type -> iam_pb.CreateServiceAccountRequest + 22, // 21: messaging_pb.SeaweedS3.UpdateServiceAccount:input_type -> iam_pb.UpdateServiceAccountRequest + 23, // 22: messaging_pb.SeaweedS3.DeleteServiceAccount:input_type -> iam_pb.DeleteServiceAccountRequest + 24, // 23: messaging_pb.SeaweedS3.GetServiceAccount:input_type -> iam_pb.GetServiceAccountRequest + 25, // 24: messaging_pb.SeaweedS3.ListUsers:output_type -> iam_pb.ListUsersResponse + 26, // 25: messaging_pb.SeaweedS3.CreateUser:output_type -> iam_pb.CreateUserResponse + 27, // 26: messaging_pb.SeaweedS3.GetUser:output_type -> iam_pb.GetUserResponse + 28, // 27: messaging_pb.SeaweedS3.UpdateUser:output_type -> iam_pb.UpdateUserResponse + 29, // 28: messaging_pb.SeaweedS3.DeleteUser:output_type -> iam_pb.DeleteUserResponse + 30, // 29: messaging_pb.SeaweedS3.ListAccessKeys:output_type -> iam_pb.ListAccessKeysResponse + 31, // 30: messaging_pb.SeaweedS3.CreateAccessKey:output_type -> iam_pb.CreateAccessKeyResponse + 32, // 31: messaging_pb.SeaweedS3.DeleteAccessKey:output_type -> iam_pb.DeleteAccessKeyResponse + 33, // 32: messaging_pb.SeaweedS3.PutUserPolicy:output_type -> iam_pb.PutUserPolicyResponse + 34, // 33: messaging_pb.SeaweedS3.GetUserPolicy:output_type -> iam_pb.GetUserPolicyResponse + 35, // 34: messaging_pb.SeaweedS3.DeleteUserPolicy:output_type -> iam_pb.DeleteUserPolicyResponse + 36, // 35: messaging_pb.SeaweedS3.ListServiceAccounts:output_type -> iam_pb.ListServiceAccountsResponse + 37, // 36: messaging_pb.SeaweedS3.CreateServiceAccount:output_type -> iam_pb.CreateServiceAccountResponse + 38, // 37: messaging_pb.SeaweedS3.UpdateServiceAccount:output_type -> iam_pb.UpdateServiceAccountResponse + 39, // 38: messaging_pb.SeaweedS3.DeleteServiceAccount:output_type -> iam_pb.DeleteServiceAccountResponse + 40, // 39: messaging_pb.SeaweedS3.GetServiceAccount:output_type -> iam_pb.GetServiceAccountResponse + 24, // [24:40] is the sub-list for method output_type + 8, // [8:24] is the sub-list for method input_type 8, // [8:8] is the sub-list for extension type_name 8, // [8:8] is the sub-list for extension extendee 0, // [0:8] is the sub-list for field type_name diff --git a/weed/pb/s3_pb/s3_grpc.pb.go b/weed/pb/s3_pb/s3_grpc.pb.go index 7e6b46b92..7a5bbbfcf 100644 --- a/weed/pb/s3_pb/s3_grpc.pb.go +++ b/weed/pb/s3_pb/s3_grpc.pb.go @@ -20,295 +20,673 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - SeaweedS3IamCache_PutIdentity_FullMethodName = "/messaging_pb.SeaweedS3IamCache/PutIdentity" - SeaweedS3IamCache_RemoveIdentity_FullMethodName = "/messaging_pb.SeaweedS3IamCache/RemoveIdentity" - SeaweedS3IamCache_PutPolicy_FullMethodName = "/messaging_pb.SeaweedS3IamCache/PutPolicy" - SeaweedS3IamCache_GetPolicy_FullMethodName = "/messaging_pb.SeaweedS3IamCache/GetPolicy" - SeaweedS3IamCache_ListPolicies_FullMethodName = "/messaging_pb.SeaweedS3IamCache/ListPolicies" - SeaweedS3IamCache_DeletePolicy_FullMethodName = "/messaging_pb.SeaweedS3IamCache/DeletePolicy" + SeaweedS3_ListUsers_FullMethodName = "/messaging_pb.SeaweedS3/ListUsers" + SeaweedS3_CreateUser_FullMethodName = "/messaging_pb.SeaweedS3/CreateUser" + SeaweedS3_GetUser_FullMethodName = "/messaging_pb.SeaweedS3/GetUser" + SeaweedS3_UpdateUser_FullMethodName = "/messaging_pb.SeaweedS3/UpdateUser" + SeaweedS3_DeleteUser_FullMethodName = "/messaging_pb.SeaweedS3/DeleteUser" + SeaweedS3_ListAccessKeys_FullMethodName = "/messaging_pb.SeaweedS3/ListAccessKeys" + SeaweedS3_CreateAccessKey_FullMethodName = "/messaging_pb.SeaweedS3/CreateAccessKey" + SeaweedS3_DeleteAccessKey_FullMethodName = "/messaging_pb.SeaweedS3/DeleteAccessKey" + SeaweedS3_PutUserPolicy_FullMethodName = "/messaging_pb.SeaweedS3/PutUserPolicy" + SeaweedS3_GetUserPolicy_FullMethodName = "/messaging_pb.SeaweedS3/GetUserPolicy" + SeaweedS3_DeleteUserPolicy_FullMethodName = "/messaging_pb.SeaweedS3/DeleteUserPolicy" + SeaweedS3_ListServiceAccounts_FullMethodName = "/messaging_pb.SeaweedS3/ListServiceAccounts" + SeaweedS3_CreateServiceAccount_FullMethodName = "/messaging_pb.SeaweedS3/CreateServiceAccount" + SeaweedS3_UpdateServiceAccount_FullMethodName = "/messaging_pb.SeaweedS3/UpdateServiceAccount" + SeaweedS3_DeleteServiceAccount_FullMethodName = "/messaging_pb.SeaweedS3/DeleteServiceAccount" + SeaweedS3_GetServiceAccount_FullMethodName = "/messaging_pb.SeaweedS3/GetServiceAccount" ) -// SeaweedS3IamCacheClient is the client API for SeaweedS3IamCache service. +// SeaweedS3Client is the client API for SeaweedS3 service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -// -// Designed for unidirectional propagation from Filer to S3 Servers -type SeaweedS3IamCacheClient interface { - PutIdentity(ctx context.Context, in *iam_pb.PutIdentityRequest, opts ...grpc.CallOption) (*iam_pb.PutIdentityResponse, error) - RemoveIdentity(ctx context.Context, in *iam_pb.RemoveIdentityRequest, opts ...grpc.CallOption) (*iam_pb.RemoveIdentityResponse, error) - PutPolicy(ctx context.Context, in *iam_pb.PutPolicyRequest, opts ...grpc.CallOption) (*iam_pb.PutPolicyResponse, error) - GetPolicy(ctx context.Context, in *iam_pb.GetPolicyRequest, opts ...grpc.CallOption) (*iam_pb.GetPolicyResponse, error) - ListPolicies(ctx context.Context, in *iam_pb.ListPoliciesRequest, opts ...grpc.CallOption) (*iam_pb.ListPoliciesResponse, error) - DeletePolicy(ctx context.Context, in *iam_pb.DeletePolicyRequest, opts ...grpc.CallOption) (*iam_pb.DeletePolicyResponse, error) +type SeaweedS3Client interface { + // Explicit IAM APIs mirroring SeaweedIdentityAccessManagement + ListUsers(ctx context.Context, in *iam_pb.ListUsersRequest, opts ...grpc.CallOption) (*iam_pb.ListUsersResponse, error) + CreateUser(ctx context.Context, in *iam_pb.CreateUserRequest, opts ...grpc.CallOption) (*iam_pb.CreateUserResponse, error) + GetUser(ctx context.Context, in *iam_pb.GetUserRequest, opts ...grpc.CallOption) (*iam_pb.GetUserResponse, error) + UpdateUser(ctx context.Context, in *iam_pb.UpdateUserRequest, opts ...grpc.CallOption) (*iam_pb.UpdateUserResponse, error) + DeleteUser(ctx context.Context, in *iam_pb.DeleteUserRequest, opts ...grpc.CallOption) (*iam_pb.DeleteUserResponse, error) + ListAccessKeys(ctx context.Context, in *iam_pb.ListAccessKeysRequest, opts ...grpc.CallOption) (*iam_pb.ListAccessKeysResponse, error) + CreateAccessKey(ctx context.Context, in *iam_pb.CreateAccessKeyRequest, opts ...grpc.CallOption) (*iam_pb.CreateAccessKeyResponse, error) + DeleteAccessKey(ctx context.Context, in *iam_pb.DeleteAccessKeyRequest, opts ...grpc.CallOption) (*iam_pb.DeleteAccessKeyResponse, error) + PutUserPolicy(ctx context.Context, in *iam_pb.PutUserPolicyRequest, opts ...grpc.CallOption) (*iam_pb.PutUserPolicyResponse, error) + GetUserPolicy(ctx context.Context, in *iam_pb.GetUserPolicyRequest, opts ...grpc.CallOption) (*iam_pb.GetUserPolicyResponse, error) + DeleteUserPolicy(ctx context.Context, in *iam_pb.DeleteUserPolicyRequest, opts ...grpc.CallOption) (*iam_pb.DeleteUserPolicyResponse, error) + ListServiceAccounts(ctx context.Context, in *iam_pb.ListServiceAccountsRequest, opts ...grpc.CallOption) (*iam_pb.ListServiceAccountsResponse, error) + CreateServiceAccount(ctx context.Context, in *iam_pb.CreateServiceAccountRequest, opts ...grpc.CallOption) (*iam_pb.CreateServiceAccountResponse, error) + UpdateServiceAccount(ctx context.Context, in *iam_pb.UpdateServiceAccountRequest, opts ...grpc.CallOption) (*iam_pb.UpdateServiceAccountResponse, error) + DeleteServiceAccount(ctx context.Context, in *iam_pb.DeleteServiceAccountRequest, opts ...grpc.CallOption) (*iam_pb.DeleteServiceAccountResponse, error) + GetServiceAccount(ctx context.Context, in *iam_pb.GetServiceAccountRequest, opts ...grpc.CallOption) (*iam_pb.GetServiceAccountResponse, error) } -type seaweedS3IamCacheClient struct { +type seaweedS3Client struct { cc grpc.ClientConnInterface } -func NewSeaweedS3IamCacheClient(cc grpc.ClientConnInterface) SeaweedS3IamCacheClient { - return &seaweedS3IamCacheClient{cc} +func NewSeaweedS3Client(cc grpc.ClientConnInterface) SeaweedS3Client { + return &seaweedS3Client{cc} } -func (c *seaweedS3IamCacheClient) PutIdentity(ctx context.Context, in *iam_pb.PutIdentityRequest, opts ...grpc.CallOption) (*iam_pb.PutIdentityResponse, error) { +func (c *seaweedS3Client) ListUsers(ctx context.Context, in *iam_pb.ListUsersRequest, opts ...grpc.CallOption) (*iam_pb.ListUsersResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(iam_pb.PutIdentityResponse) - err := c.cc.Invoke(ctx, SeaweedS3IamCache_PutIdentity_FullMethodName, in, out, cOpts...) + out := new(iam_pb.ListUsersResponse) + err := c.cc.Invoke(ctx, SeaweedS3_ListUsers_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } -func (c *seaweedS3IamCacheClient) RemoveIdentity(ctx context.Context, in *iam_pb.RemoveIdentityRequest, opts ...grpc.CallOption) (*iam_pb.RemoveIdentityResponse, error) { +func (c *seaweedS3Client) CreateUser(ctx context.Context, in *iam_pb.CreateUserRequest, opts ...grpc.CallOption) (*iam_pb.CreateUserResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(iam_pb.RemoveIdentityResponse) - err := c.cc.Invoke(ctx, SeaweedS3IamCache_RemoveIdentity_FullMethodName, in, out, cOpts...) + out := new(iam_pb.CreateUserResponse) + err := c.cc.Invoke(ctx, SeaweedS3_CreateUser_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } -func (c *seaweedS3IamCacheClient) PutPolicy(ctx context.Context, in *iam_pb.PutPolicyRequest, opts ...grpc.CallOption) (*iam_pb.PutPolicyResponse, error) { +func (c *seaweedS3Client) GetUser(ctx context.Context, in *iam_pb.GetUserRequest, opts ...grpc.CallOption) (*iam_pb.GetUserResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(iam_pb.PutPolicyResponse) - err := c.cc.Invoke(ctx, SeaweedS3IamCache_PutPolicy_FullMethodName, in, out, cOpts...) + out := new(iam_pb.GetUserResponse) + err := c.cc.Invoke(ctx, SeaweedS3_GetUser_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } -func (c *seaweedS3IamCacheClient) GetPolicy(ctx context.Context, in *iam_pb.GetPolicyRequest, opts ...grpc.CallOption) (*iam_pb.GetPolicyResponse, error) { +func (c *seaweedS3Client) UpdateUser(ctx context.Context, in *iam_pb.UpdateUserRequest, opts ...grpc.CallOption) (*iam_pb.UpdateUserResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(iam_pb.GetPolicyResponse) - err := c.cc.Invoke(ctx, SeaweedS3IamCache_GetPolicy_FullMethodName, in, out, cOpts...) + out := new(iam_pb.UpdateUserResponse) + err := c.cc.Invoke(ctx, SeaweedS3_UpdateUser_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } -func (c *seaweedS3IamCacheClient) ListPolicies(ctx context.Context, in *iam_pb.ListPoliciesRequest, opts ...grpc.CallOption) (*iam_pb.ListPoliciesResponse, error) { +func (c *seaweedS3Client) DeleteUser(ctx context.Context, in *iam_pb.DeleteUserRequest, opts ...grpc.CallOption) (*iam_pb.DeleteUserResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(iam_pb.ListPoliciesResponse) - err := c.cc.Invoke(ctx, SeaweedS3IamCache_ListPolicies_FullMethodName, in, out, cOpts...) + out := new(iam_pb.DeleteUserResponse) + err := c.cc.Invoke(ctx, SeaweedS3_DeleteUser_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } -func (c *seaweedS3IamCacheClient) DeletePolicy(ctx context.Context, in *iam_pb.DeletePolicyRequest, opts ...grpc.CallOption) (*iam_pb.DeletePolicyResponse, error) { +func (c *seaweedS3Client) ListAccessKeys(ctx context.Context, in *iam_pb.ListAccessKeysRequest, opts ...grpc.CallOption) (*iam_pb.ListAccessKeysResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(iam_pb.DeletePolicyResponse) - err := c.cc.Invoke(ctx, SeaweedS3IamCache_DeletePolicy_FullMethodName, in, out, cOpts...) + out := new(iam_pb.ListAccessKeysResponse) + err := c.cc.Invoke(ctx, SeaweedS3_ListAccessKeys_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } -// SeaweedS3IamCacheServer is the server API for SeaweedS3IamCache service. -// All implementations must embed UnimplementedSeaweedS3IamCacheServer +func (c *seaweedS3Client) CreateAccessKey(ctx context.Context, in *iam_pb.CreateAccessKeyRequest, opts ...grpc.CallOption) (*iam_pb.CreateAccessKeyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(iam_pb.CreateAccessKeyResponse) + err := c.cc.Invoke(ctx, SeaweedS3_CreateAccessKey_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *seaweedS3Client) DeleteAccessKey(ctx context.Context, in *iam_pb.DeleteAccessKeyRequest, opts ...grpc.CallOption) (*iam_pb.DeleteAccessKeyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(iam_pb.DeleteAccessKeyResponse) + err := c.cc.Invoke(ctx, SeaweedS3_DeleteAccessKey_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *seaweedS3Client) PutUserPolicy(ctx context.Context, in *iam_pb.PutUserPolicyRequest, opts ...grpc.CallOption) (*iam_pb.PutUserPolicyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(iam_pb.PutUserPolicyResponse) + err := c.cc.Invoke(ctx, SeaweedS3_PutUserPolicy_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *seaweedS3Client) GetUserPolicy(ctx context.Context, in *iam_pb.GetUserPolicyRequest, opts ...grpc.CallOption) (*iam_pb.GetUserPolicyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(iam_pb.GetUserPolicyResponse) + err := c.cc.Invoke(ctx, SeaweedS3_GetUserPolicy_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *seaweedS3Client) DeleteUserPolicy(ctx context.Context, in *iam_pb.DeleteUserPolicyRequest, opts ...grpc.CallOption) (*iam_pb.DeleteUserPolicyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(iam_pb.DeleteUserPolicyResponse) + err := c.cc.Invoke(ctx, SeaweedS3_DeleteUserPolicy_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *seaweedS3Client) ListServiceAccounts(ctx context.Context, in *iam_pb.ListServiceAccountsRequest, opts ...grpc.CallOption) (*iam_pb.ListServiceAccountsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(iam_pb.ListServiceAccountsResponse) + err := c.cc.Invoke(ctx, SeaweedS3_ListServiceAccounts_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *seaweedS3Client) CreateServiceAccount(ctx context.Context, in *iam_pb.CreateServiceAccountRequest, opts ...grpc.CallOption) (*iam_pb.CreateServiceAccountResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(iam_pb.CreateServiceAccountResponse) + err := c.cc.Invoke(ctx, SeaweedS3_CreateServiceAccount_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *seaweedS3Client) UpdateServiceAccount(ctx context.Context, in *iam_pb.UpdateServiceAccountRequest, opts ...grpc.CallOption) (*iam_pb.UpdateServiceAccountResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(iam_pb.UpdateServiceAccountResponse) + err := c.cc.Invoke(ctx, SeaweedS3_UpdateServiceAccount_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *seaweedS3Client) DeleteServiceAccount(ctx context.Context, in *iam_pb.DeleteServiceAccountRequest, opts ...grpc.CallOption) (*iam_pb.DeleteServiceAccountResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(iam_pb.DeleteServiceAccountResponse) + err := c.cc.Invoke(ctx, SeaweedS3_DeleteServiceAccount_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *seaweedS3Client) GetServiceAccount(ctx context.Context, in *iam_pb.GetServiceAccountRequest, opts ...grpc.CallOption) (*iam_pb.GetServiceAccountResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(iam_pb.GetServiceAccountResponse) + err := c.cc.Invoke(ctx, SeaweedS3_GetServiceAccount_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// SeaweedS3Server is the server API for SeaweedS3 service. +// All implementations must embed UnimplementedSeaweedS3Server // for forward compatibility. -// -// Designed for unidirectional propagation from Filer to S3 Servers -type SeaweedS3IamCacheServer interface { - PutIdentity(context.Context, *iam_pb.PutIdentityRequest) (*iam_pb.PutIdentityResponse, error) - RemoveIdentity(context.Context, *iam_pb.RemoveIdentityRequest) (*iam_pb.RemoveIdentityResponse, error) - PutPolicy(context.Context, *iam_pb.PutPolicyRequest) (*iam_pb.PutPolicyResponse, error) - GetPolicy(context.Context, *iam_pb.GetPolicyRequest) (*iam_pb.GetPolicyResponse, error) - ListPolicies(context.Context, *iam_pb.ListPoliciesRequest) (*iam_pb.ListPoliciesResponse, error) - DeletePolicy(context.Context, *iam_pb.DeletePolicyRequest) (*iam_pb.DeletePolicyResponse, error) - mustEmbedUnimplementedSeaweedS3IamCacheServer() +type SeaweedS3Server interface { + // Explicit IAM APIs mirroring SeaweedIdentityAccessManagement + ListUsers(context.Context, *iam_pb.ListUsersRequest) (*iam_pb.ListUsersResponse, error) + CreateUser(context.Context, *iam_pb.CreateUserRequest) (*iam_pb.CreateUserResponse, error) + GetUser(context.Context, *iam_pb.GetUserRequest) (*iam_pb.GetUserResponse, error) + UpdateUser(context.Context, *iam_pb.UpdateUserRequest) (*iam_pb.UpdateUserResponse, error) + DeleteUser(context.Context, *iam_pb.DeleteUserRequest) (*iam_pb.DeleteUserResponse, error) + ListAccessKeys(context.Context, *iam_pb.ListAccessKeysRequest) (*iam_pb.ListAccessKeysResponse, error) + CreateAccessKey(context.Context, *iam_pb.CreateAccessKeyRequest) (*iam_pb.CreateAccessKeyResponse, error) + DeleteAccessKey(context.Context, *iam_pb.DeleteAccessKeyRequest) (*iam_pb.DeleteAccessKeyResponse, error) + PutUserPolicy(context.Context, *iam_pb.PutUserPolicyRequest) (*iam_pb.PutUserPolicyResponse, error) + GetUserPolicy(context.Context, *iam_pb.GetUserPolicyRequest) (*iam_pb.GetUserPolicyResponse, error) + DeleteUserPolicy(context.Context, *iam_pb.DeleteUserPolicyRequest) (*iam_pb.DeleteUserPolicyResponse, error) + ListServiceAccounts(context.Context, *iam_pb.ListServiceAccountsRequest) (*iam_pb.ListServiceAccountsResponse, error) + CreateServiceAccount(context.Context, *iam_pb.CreateServiceAccountRequest) (*iam_pb.CreateServiceAccountResponse, error) + UpdateServiceAccount(context.Context, *iam_pb.UpdateServiceAccountRequest) (*iam_pb.UpdateServiceAccountResponse, error) + DeleteServiceAccount(context.Context, *iam_pb.DeleteServiceAccountRequest) (*iam_pb.DeleteServiceAccountResponse, error) + GetServiceAccount(context.Context, *iam_pb.GetServiceAccountRequest) (*iam_pb.GetServiceAccountResponse, error) + mustEmbedUnimplementedSeaweedS3Server() } -// UnimplementedSeaweedS3IamCacheServer must be embedded to have +// UnimplementedSeaweedS3Server must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. -type UnimplementedSeaweedS3IamCacheServer struct{} +type UnimplementedSeaweedS3Server struct{} -func (UnimplementedSeaweedS3IamCacheServer) PutIdentity(context.Context, *iam_pb.PutIdentityRequest) (*iam_pb.PutIdentityResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method PutIdentity not implemented") +func (UnimplementedSeaweedS3Server) ListUsers(context.Context, *iam_pb.ListUsersRequest) (*iam_pb.ListUsersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListUsers not implemented") } -func (UnimplementedSeaweedS3IamCacheServer) RemoveIdentity(context.Context, *iam_pb.RemoveIdentityRequest) (*iam_pb.RemoveIdentityResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method RemoveIdentity not implemented") +func (UnimplementedSeaweedS3Server) CreateUser(context.Context, *iam_pb.CreateUserRequest) (*iam_pb.CreateUserResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateUser not implemented") } -func (UnimplementedSeaweedS3IamCacheServer) PutPolicy(context.Context, *iam_pb.PutPolicyRequest) (*iam_pb.PutPolicyResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method PutPolicy not implemented") +func (UnimplementedSeaweedS3Server) GetUser(context.Context, *iam_pb.GetUserRequest) (*iam_pb.GetUserResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetUser not implemented") } -func (UnimplementedSeaweedS3IamCacheServer) GetPolicy(context.Context, *iam_pb.GetPolicyRequest) (*iam_pb.GetPolicyResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetPolicy not implemented") +func (UnimplementedSeaweedS3Server) UpdateUser(context.Context, *iam_pb.UpdateUserRequest) (*iam_pb.UpdateUserResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateUser not implemented") } -func (UnimplementedSeaweedS3IamCacheServer) ListPolicies(context.Context, *iam_pb.ListPoliciesRequest) (*iam_pb.ListPoliciesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListPolicies not implemented") +func (UnimplementedSeaweedS3Server) DeleteUser(context.Context, *iam_pb.DeleteUserRequest) (*iam_pb.DeleteUserResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteUser not implemented") } -func (UnimplementedSeaweedS3IamCacheServer) DeletePolicy(context.Context, *iam_pb.DeletePolicyRequest) (*iam_pb.DeletePolicyResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method DeletePolicy not implemented") +func (UnimplementedSeaweedS3Server) ListAccessKeys(context.Context, *iam_pb.ListAccessKeysRequest) (*iam_pb.ListAccessKeysResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListAccessKeys not implemented") } -func (UnimplementedSeaweedS3IamCacheServer) mustEmbedUnimplementedSeaweedS3IamCacheServer() {} -func (UnimplementedSeaweedS3IamCacheServer) testEmbeddedByValue() {} +func (UnimplementedSeaweedS3Server) CreateAccessKey(context.Context, *iam_pb.CreateAccessKeyRequest) (*iam_pb.CreateAccessKeyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateAccessKey not implemented") +} +func (UnimplementedSeaweedS3Server) DeleteAccessKey(context.Context, *iam_pb.DeleteAccessKeyRequest) (*iam_pb.DeleteAccessKeyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteAccessKey not implemented") +} +func (UnimplementedSeaweedS3Server) PutUserPolicy(context.Context, *iam_pb.PutUserPolicyRequest) (*iam_pb.PutUserPolicyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PutUserPolicy not implemented") +} +func (UnimplementedSeaweedS3Server) GetUserPolicy(context.Context, *iam_pb.GetUserPolicyRequest) (*iam_pb.GetUserPolicyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetUserPolicy not implemented") +} +func (UnimplementedSeaweedS3Server) DeleteUserPolicy(context.Context, *iam_pb.DeleteUserPolicyRequest) (*iam_pb.DeleteUserPolicyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteUserPolicy not implemented") +} +func (UnimplementedSeaweedS3Server) ListServiceAccounts(context.Context, *iam_pb.ListServiceAccountsRequest) (*iam_pb.ListServiceAccountsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListServiceAccounts not implemented") +} +func (UnimplementedSeaweedS3Server) CreateServiceAccount(context.Context, *iam_pb.CreateServiceAccountRequest) (*iam_pb.CreateServiceAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateServiceAccount not implemented") +} +func (UnimplementedSeaweedS3Server) UpdateServiceAccount(context.Context, *iam_pb.UpdateServiceAccountRequest) (*iam_pb.UpdateServiceAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateServiceAccount not implemented") +} +func (UnimplementedSeaweedS3Server) DeleteServiceAccount(context.Context, *iam_pb.DeleteServiceAccountRequest) (*iam_pb.DeleteServiceAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteServiceAccount not implemented") +} +func (UnimplementedSeaweedS3Server) GetServiceAccount(context.Context, *iam_pb.GetServiceAccountRequest) (*iam_pb.GetServiceAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetServiceAccount not implemented") +} +func (UnimplementedSeaweedS3Server) mustEmbedUnimplementedSeaweedS3Server() {} +func (UnimplementedSeaweedS3Server) testEmbeddedByValue() {} -// UnsafeSeaweedS3IamCacheServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to SeaweedS3IamCacheServer will +// UnsafeSeaweedS3Server may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to SeaweedS3Server will // result in compilation errors. -type UnsafeSeaweedS3IamCacheServer interface { - mustEmbedUnimplementedSeaweedS3IamCacheServer() +type UnsafeSeaweedS3Server interface { + mustEmbedUnimplementedSeaweedS3Server() } -func RegisterSeaweedS3IamCacheServer(s grpc.ServiceRegistrar, srv SeaweedS3IamCacheServer) { - // If the following call pancis, it indicates UnimplementedSeaweedS3IamCacheServer was +func RegisterSeaweedS3Server(s grpc.ServiceRegistrar, srv SeaweedS3Server) { + // If the following call pancis, it indicates UnimplementedSeaweedS3Server was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } - s.RegisterService(&SeaweedS3IamCache_ServiceDesc, srv) + s.RegisterService(&SeaweedS3_ServiceDesc, srv) } -func _SeaweedS3IamCache_PutIdentity_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(iam_pb.PutIdentityRequest) +func _SeaweedS3_ListUsers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(iam_pb.ListUsersRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(SeaweedS3IamCacheServer).PutIdentity(ctx, in) + return srv.(SeaweedS3Server).ListUsers(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: SeaweedS3IamCache_PutIdentity_FullMethodName, + FullMethod: SeaweedS3_ListUsers_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SeaweedS3IamCacheServer).PutIdentity(ctx, req.(*iam_pb.PutIdentityRequest)) + return srv.(SeaweedS3Server).ListUsers(ctx, req.(*iam_pb.ListUsersRequest)) } return interceptor(ctx, in, info, handler) } -func _SeaweedS3IamCache_RemoveIdentity_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(iam_pb.RemoveIdentityRequest) +func _SeaweedS3_CreateUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(iam_pb.CreateUserRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(SeaweedS3IamCacheServer).RemoveIdentity(ctx, in) + return srv.(SeaweedS3Server).CreateUser(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: SeaweedS3IamCache_RemoveIdentity_FullMethodName, + FullMethod: SeaweedS3_CreateUser_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SeaweedS3IamCacheServer).RemoveIdentity(ctx, req.(*iam_pb.RemoveIdentityRequest)) + return srv.(SeaweedS3Server).CreateUser(ctx, req.(*iam_pb.CreateUserRequest)) } return interceptor(ctx, in, info, handler) } -func _SeaweedS3IamCache_PutPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(iam_pb.PutPolicyRequest) +func _SeaweedS3_GetUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(iam_pb.GetUserRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(SeaweedS3IamCacheServer).PutPolicy(ctx, in) + return srv.(SeaweedS3Server).GetUser(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: SeaweedS3IamCache_PutPolicy_FullMethodName, + FullMethod: SeaweedS3_GetUser_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SeaweedS3IamCacheServer).PutPolicy(ctx, req.(*iam_pb.PutPolicyRequest)) + return srv.(SeaweedS3Server).GetUser(ctx, req.(*iam_pb.GetUserRequest)) } return interceptor(ctx, in, info, handler) } -func _SeaweedS3IamCache_GetPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(iam_pb.GetPolicyRequest) +func _SeaweedS3_UpdateUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(iam_pb.UpdateUserRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(SeaweedS3IamCacheServer).GetPolicy(ctx, in) + return srv.(SeaweedS3Server).UpdateUser(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: SeaweedS3IamCache_GetPolicy_FullMethodName, + FullMethod: SeaweedS3_UpdateUser_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SeaweedS3IamCacheServer).GetPolicy(ctx, req.(*iam_pb.GetPolicyRequest)) + return srv.(SeaweedS3Server).UpdateUser(ctx, req.(*iam_pb.UpdateUserRequest)) } return interceptor(ctx, in, info, handler) } -func _SeaweedS3IamCache_ListPolicies_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(iam_pb.ListPoliciesRequest) +func _SeaweedS3_DeleteUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(iam_pb.DeleteUserRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(SeaweedS3IamCacheServer).ListPolicies(ctx, in) + return srv.(SeaweedS3Server).DeleteUser(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: SeaweedS3IamCache_ListPolicies_FullMethodName, + FullMethod: SeaweedS3_DeleteUser_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SeaweedS3IamCacheServer).ListPolicies(ctx, req.(*iam_pb.ListPoliciesRequest)) + return srv.(SeaweedS3Server).DeleteUser(ctx, req.(*iam_pb.DeleteUserRequest)) } return interceptor(ctx, in, info, handler) } -func _SeaweedS3IamCache_DeletePolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(iam_pb.DeletePolicyRequest) +func _SeaweedS3_ListAccessKeys_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(iam_pb.ListAccessKeysRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(SeaweedS3IamCacheServer).DeletePolicy(ctx, in) + return srv.(SeaweedS3Server).ListAccessKeys(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: SeaweedS3IamCache_DeletePolicy_FullMethodName, + FullMethod: SeaweedS3_ListAccessKeys_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SeaweedS3IamCacheServer).DeletePolicy(ctx, req.(*iam_pb.DeletePolicyRequest)) + return srv.(SeaweedS3Server).ListAccessKeys(ctx, req.(*iam_pb.ListAccessKeysRequest)) } return interceptor(ctx, in, info, handler) } -// SeaweedS3IamCache_ServiceDesc is the grpc.ServiceDesc for SeaweedS3IamCache service. +func _SeaweedS3_CreateAccessKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(iam_pb.CreateAccessKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SeaweedS3Server).CreateAccessKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SeaweedS3_CreateAccessKey_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SeaweedS3Server).CreateAccessKey(ctx, req.(*iam_pb.CreateAccessKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SeaweedS3_DeleteAccessKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(iam_pb.DeleteAccessKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SeaweedS3Server).DeleteAccessKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SeaweedS3_DeleteAccessKey_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SeaweedS3Server).DeleteAccessKey(ctx, req.(*iam_pb.DeleteAccessKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SeaweedS3_PutUserPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(iam_pb.PutUserPolicyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SeaweedS3Server).PutUserPolicy(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SeaweedS3_PutUserPolicy_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SeaweedS3Server).PutUserPolicy(ctx, req.(*iam_pb.PutUserPolicyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SeaweedS3_GetUserPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(iam_pb.GetUserPolicyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SeaweedS3Server).GetUserPolicy(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SeaweedS3_GetUserPolicy_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SeaweedS3Server).GetUserPolicy(ctx, req.(*iam_pb.GetUserPolicyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SeaweedS3_DeleteUserPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(iam_pb.DeleteUserPolicyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SeaweedS3Server).DeleteUserPolicy(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SeaweedS3_DeleteUserPolicy_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SeaweedS3Server).DeleteUserPolicy(ctx, req.(*iam_pb.DeleteUserPolicyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SeaweedS3_ListServiceAccounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(iam_pb.ListServiceAccountsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SeaweedS3Server).ListServiceAccounts(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SeaweedS3_ListServiceAccounts_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SeaweedS3Server).ListServiceAccounts(ctx, req.(*iam_pb.ListServiceAccountsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SeaweedS3_CreateServiceAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(iam_pb.CreateServiceAccountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SeaweedS3Server).CreateServiceAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SeaweedS3_CreateServiceAccount_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SeaweedS3Server).CreateServiceAccount(ctx, req.(*iam_pb.CreateServiceAccountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SeaweedS3_UpdateServiceAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(iam_pb.UpdateServiceAccountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SeaweedS3Server).UpdateServiceAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SeaweedS3_UpdateServiceAccount_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SeaweedS3Server).UpdateServiceAccount(ctx, req.(*iam_pb.UpdateServiceAccountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SeaweedS3_DeleteServiceAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(iam_pb.DeleteServiceAccountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SeaweedS3Server).DeleteServiceAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SeaweedS3_DeleteServiceAccount_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SeaweedS3Server).DeleteServiceAccount(ctx, req.(*iam_pb.DeleteServiceAccountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SeaweedS3_GetServiceAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(iam_pb.GetServiceAccountRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SeaweedS3Server).GetServiceAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SeaweedS3_GetServiceAccount_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SeaweedS3Server).GetServiceAccount(ctx, req.(*iam_pb.GetServiceAccountRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// SeaweedS3_ServiceDesc is the grpc.ServiceDesc for SeaweedS3 service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) -var SeaweedS3IamCache_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "messaging_pb.SeaweedS3IamCache", - HandlerType: (*SeaweedS3IamCacheServer)(nil), +var SeaweedS3_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "messaging_pb.SeaweedS3", + HandlerType: (*SeaweedS3Server)(nil), Methods: []grpc.MethodDesc{ { - MethodName: "PutIdentity", - Handler: _SeaweedS3IamCache_PutIdentity_Handler, + MethodName: "ListUsers", + Handler: _SeaweedS3_ListUsers_Handler, }, { - MethodName: "RemoveIdentity", - Handler: _SeaweedS3IamCache_RemoveIdentity_Handler, + MethodName: "CreateUser", + Handler: _SeaweedS3_CreateUser_Handler, }, { - MethodName: "PutPolicy", - Handler: _SeaweedS3IamCache_PutPolicy_Handler, + MethodName: "GetUser", + Handler: _SeaweedS3_GetUser_Handler, }, { - MethodName: "GetPolicy", - Handler: _SeaweedS3IamCache_GetPolicy_Handler, + MethodName: "UpdateUser", + Handler: _SeaweedS3_UpdateUser_Handler, }, { - MethodName: "ListPolicies", - Handler: _SeaweedS3IamCache_ListPolicies_Handler, + MethodName: "DeleteUser", + Handler: _SeaweedS3_DeleteUser_Handler, }, { - MethodName: "DeletePolicy", - Handler: _SeaweedS3IamCache_DeletePolicy_Handler, + MethodName: "ListAccessKeys", + Handler: _SeaweedS3_ListAccessKeys_Handler, + }, + { + MethodName: "CreateAccessKey", + Handler: _SeaweedS3_CreateAccessKey_Handler, + }, + { + MethodName: "DeleteAccessKey", + Handler: _SeaweedS3_DeleteAccessKey_Handler, + }, + { + MethodName: "PutUserPolicy", + Handler: _SeaweedS3_PutUserPolicy_Handler, + }, + { + MethodName: "GetUserPolicy", + Handler: _SeaweedS3_GetUserPolicy_Handler, + }, + { + MethodName: "DeleteUserPolicy", + Handler: _SeaweedS3_DeleteUserPolicy_Handler, + }, + { + MethodName: "ListServiceAccounts", + Handler: _SeaweedS3_ListServiceAccounts_Handler, + }, + { + MethodName: "CreateServiceAccount", + Handler: _SeaweedS3_CreateServiceAccount_Handler, + }, + { + MethodName: "UpdateServiceAccount", + Handler: _SeaweedS3_UpdateServiceAccount_Handler, + }, + { + MethodName: "DeleteServiceAccount", + Handler: _SeaweedS3_DeleteServiceAccount_Handler, + }, + { + MethodName: "GetServiceAccount", + Handler: _SeaweedS3_GetServiceAccount_Handler, }, }, Streams: []grpc.StreamDesc{}, diff --git a/weed/s3api/auth_credentials.go b/weed/s3api/auth_credentials.go index 679606ca0..bf7d99ece 100644 --- a/weed/s3api/auth_credentials.go +++ b/weed/s3api/auth_credentials.go @@ -7,21 +7,16 @@ import ( "fmt" "net/http" "os" - "slices" - "strings" "sync" "time" "github.com/seaweedfs/seaweedfs/weed/credential" "github.com/seaweedfs/seaweedfs/weed/filer" "github.com/seaweedfs/seaweedfs/weed/glog" - "github.com/seaweedfs/seaweedfs/weed/kms" "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/policy_engine" "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" - "github.com/seaweedfs/seaweedfs/weed/s3api/s3err" // Import KMS providers to register them _ "github.com/seaweedfs/seaweedfs/weed/kms/aws" @@ -44,7 +39,6 @@ type IdentityAccessManagement struct { identities []*Identity accessKeyIdent map[string]*Identity nameToIdentity map[string]*Identity // O(1) lookup by identity name - policies map[string]*iam_pb.Policy accounts map[string]*Account emailAccount map[string]*Account hashes map[string]*sync.Pool @@ -84,7 +78,6 @@ type Identity struct { PrincipalArn string // ARN for IAM authorization (e.g., "arn:aws:iam::account-id:user/username") Disabled bool // User status: false = enabled (default), true = disabled Claims map[string]interface{} // JWT claims for policy substitution - IsStatic bool // Whether identity was loaded from static config (immutable) } // Account represents a system user, a system user can @@ -189,7 +182,6 @@ func NewIdentityAccessManagementWithStore(option *S3ApiServerOption, explicitSto iam.staticIdentityNames = make(map[string]bool) for _, identity := range iam.identities { iam.staticIdentityNames[identity.Name] = true - identity.IsStatic = true } iam.m.Unlock() } @@ -297,7 +289,6 @@ func (iam *IdentityAccessManagement) loadEnvironmentVariableCredentials() { Actions: []Action{ s3_constants.ACTION_ADMIN, }, - IsStatic: true, } iam.m.Lock() @@ -411,20 +402,19 @@ func (iam *IdentityAccessManagement) loadS3ApiConfiguration(config *iam_pb.S3Api if hasStaticConfig { // Merge mode: preserve static identities, add/update dynamic ones - return iam.MergeS3ApiConfiguration(config) + return iam.mergeS3ApiConfiguration(config) } // Normal mode: completely replace configuration - return iam.ReplaceS3ApiConfiguration(config) + return iam.replaceS3ApiConfiguration(config) } -// ReplaceS3ApiConfiguration completely replaces the current configuration (used when no static config) -func (iam *IdentityAccessManagement) ReplaceS3ApiConfiguration(config *iam_pb.S3ApiConfiguration) error { +// replaceS3ApiConfiguration completely replaces the current configuration (used when no static config) +func (iam *IdentityAccessManagement) replaceS3ApiConfiguration(config *iam_pb.S3ApiConfiguration) error { var identities []*Identity var identityAnonymous *Identity accessKeyIdent := make(map[string]*Identity) nameToIdentity := make(map[string]*Identity) - policies := make(map[string]*iam_pb.Policy) accounts := make(map[string]*Account) emailAccount := make(map[string]*Account) foundAccountAdmin := false @@ -463,9 +453,6 @@ func (iam *IdentityAccessManagement) ReplaceS3ApiConfiguration(config *iam_pb.S3 } emailAccount[AccountAnonymous.EmailAddress] = accounts[AccountAnonymous.Id] } - for _, policy := range config.Policies { - policies[policy.Name] = policy - } for _, ident := range config.Identities { glog.V(3).Infof("loading identity %s (disabled=%v)", ident.Name, ident.Disabled) t := &Identity{ @@ -545,7 +532,6 @@ func (iam *IdentityAccessManagement) ReplaceS3ApiConfiguration(config *iam_pb.S3 iam.emailAccount = emailAccount iam.accessKeyIdent = accessKeyIdent iam.nameToIdentity = nameToIdentity - iam.policies = policies // Update authentication state based on whether identities exist // Once enabled, keep it enabled (one-way toggle) authJustEnabled := iam.updateAuthenticationState(len(identities)) @@ -575,10 +561,10 @@ func (iam *IdentityAccessManagement) ReplaceS3ApiConfiguration(config *iam_pb.S3 return nil } -// MergeS3ApiConfiguration merges dynamic configuration with existing static configuration +// mergeS3ApiConfiguration merges dynamic configuration with existing static configuration // Static identities (from file) are preserved and cannot be updated // Dynamic identities (from filer/admin) can be added or updated -func (iam *IdentityAccessManagement) MergeS3ApiConfiguration(config *iam_pb.S3ApiConfiguration) error { +func (iam *IdentityAccessManagement) mergeS3ApiConfiguration(config *iam_pb.S3ApiConfiguration) error { // Start with current configuration (which includes static identities) iam.m.RLock() identities := make([]*Identity, len(iam.identities)) @@ -592,10 +578,6 @@ func (iam *IdentityAccessManagement) MergeS3ApiConfiguration(config *iam_pb.S3Ap for k, v := range iam.nameToIdentity { nameToIdentity[k] = v } - policies := make(map[string]*iam_pb.Policy) - for k, v := range iam.policies { - policies[k] = v - } accounts := make(map[string]*Account) for k, v := range iam.accounts { accounts[k] = v @@ -768,10 +750,6 @@ func (iam *IdentityAccessManagement) MergeS3ApiConfiguration(config *iam_pb.S3Ap glog.V(3).Infof("Loaded service account %s for dynamic parent %s (expiration: %d)", sa.Id, sa.ParentUser, sa.Expiration) } - for _, policy := range config.Policies { - policies[policy.Name] = policy - } - iam.m.Lock() // atomically switch iam.identities = identities @@ -780,7 +758,6 @@ func (iam *IdentityAccessManagement) MergeS3ApiConfiguration(config *iam_pb.S3Ap iam.emailAccount = emailAccount iam.accessKeyIdent = accessKeyIdent iam.nameToIdentity = nameToIdentity - iam.policies = policies // Update authentication state based on whether identities exist // Once enabled, keep it enabled (one-way toggle) authJustEnabled := iam.updateAuthenticationState(len(identities)) @@ -810,157 +787,65 @@ func (iam *IdentityAccessManagement) MergeS3ApiConfiguration(config *iam_pb.S3Ap return nil } -func (iam *IdentityAccessManagement) RemoveIdentity(name string) { - glog.V(1).Infof("IAM: remove identity %s", name) - iam.m.Lock() - defer iam.m.Unlock() - - identity, ok := iam.nameToIdentity[name] - if !ok { - return - } - - if identity.IsStatic { - glog.V(1).Infof("IAM: skipping removal of static identity %s (immutable)", name) - return - } - - // Remove from identities slice - for i, ident := range iam.identities { - if ident.Name == name { - iam.identities = append(iam.identities[:i], iam.identities[i+1:]...) - break - } - } - - // Remove from maps - delete(iam.nameToIdentity, name) - for _, cred := range identity.Credentials { - if iam.accessKeyIdent[cred.AccessKey] == identity { - delete(iam.accessKeyIdent, cred.AccessKey) - } - } - - if identity == iam.identityAnonymous { - iam.identityAnonymous = nil - } -} - -func (iam *IdentityAccessManagement) UpsertIdentity(ident *iam_pb.Identity) error { - if ident == nil { - return fmt.Errorf("upsert identity failed: nil identity") - - } - if ident.Name == "" { - return fmt.Errorf("upsert identity failed: empty identity name") - } - glog.V(1).Infof("IAM: upsert identity %s", ident.Name) - return iam.MergeS3ApiConfiguration(&iam_pb.S3ApiConfiguration{ - Identities: []*iam_pb.Identity{ident}, - }) -} - // isEnabled reports whether S3 auth should be enforced for this server. // // Auth is considered enabled if either: // - we have any locally managed identities/credentials (iam.isAuthEnabled), or // - an external IAM integration has been configured (iam.iamIntegration != nil). -// -// The iamIntegration check is intentionally included so that when an external -// IAM provider is configured (and the server relies solely on it), auth is -// still treated as enabled even if there are no local identities yet or -// before any sync logic flips isAuthEnabled to true. Removing this check or -// relying only on isAuthEnabled would change when auth is enforced and could -// unintentionally allow unauthenticated access in integration-only setups. func (iam *IdentityAccessManagement) isEnabled() bool { + iam.m.RLock() + defer iam.m.RUnlock() return iam.isAuthEnabled || iam.iamIntegration != nil } -func (iam *IdentityAccessManagement) updateAuthenticationState(identitiesCount int) bool { - if !iam.isAuthEnabled && identitiesCount > 0 { - iam.isAuthEnabled = true - return true - } - return false -} - -func (iam *IdentityAccessManagement) IsStaticConfig() bool { - iam.m.RLock() - defer iam.m.RUnlock() - return iam.useStaticConfig -} - -// IsStaticIdentity checks if an identity was loaded from the static config file -func (iam *IdentityAccessManagement) IsStaticIdentity(identityName string) bool { - iam.m.RLock() - defer iam.m.RUnlock() - return iam.staticIdentityNames[identityName] -} - -func (iam *IdentityAccessManagement) lookupByAccessKey(accessKey string) (identity *Identity, cred *Credential, found bool) { +func (iam *IdentityAccessManagement) LookupByAccessKey(accessKey string) (identity *Identity, cred *Credential, found bool) { iam.m.RLock() defer iam.m.RUnlock() - // Helper function to truncate access key for logging to avoid credential exposure - truncate := func(key string) string { - const mask = "***" - if len(key) > 4 { - return key[:4] + mask - } - // For very short keys, never log the full key - return mask - } - - truncatedKey := truncate(accessKey) - - glog.V(4).Infof("Looking up access key: %s (len=%d, total keys registered: %d)", - truncatedKey, len(accessKey), len(iam.accessKeyIdent)) - - if ident, ok := iam.accessKeyIdent[accessKey]; ok { - // Check if user is disabled - if ident.Disabled { - glog.V(2).Infof("User %s is disabled, rejecting access key %s", ident.Name, truncatedKey) + // 1. Try local lookup first + if identity, found = iam.accessKeyIdent[accessKey]; found { + // Check if identity is disabled + if identity.Disabled { return nil, nil, false } + // Find specific credential to check expiration and status + for _, c := range identity.Credentials { + if c.AccessKey == accessKey { - for _, credential := range ident.Credentials { - if credential.AccessKey == accessKey { - // Check if access key is inactive (empty Status treated as Active for backward compatibility) - if credential.Status == iamAccessKeyStatusInactive { - glog.V(2).Infof("Access key %s for identity %s is inactive", truncatedKey, ident.Name) + // Check expiration + if c.isCredentialExpired() { + glog.V(1).Infof("Credential for %s has expired", identity.Name) return nil, nil, false } - glog.V(4).Infof("Found access key %s for identity %s", truncatedKey, ident.Name) - return ident, credential, true + + // Check status (defaults to Active if empty) + if c.Status == "Inactive" { + glog.V(1).Infof("Credential for %s is inactive", identity.Name) + return nil, nil, false + } + + return identity, c, true } } + // Should not happen if accessKeyIdent is consistent, but safe fallback + return identity, nil, true } - glog.V(2).Infof("Could not find access key %s (len=%d). Available keys: %d, Auth enabled: %v", - truncatedKey, len(accessKey), len(iam.accessKeyIdent), iam.isAuthEnabled) - - // Log all registered access keys at higher verbosity for debugging - if glog.V(3) { - glog.V(3).Infof("Registered access keys:") - for key := range iam.accessKeyIdent { - glog.V(3).Infof(" - %s (len=%d)", truncate(key), len(key)) - } + // 2. If not found locally and IAM integration is enabled, try external lookup + if iam.iamIntegration != nil { + // Note: We release the read lock before calling external integration to avoid blocking + // However, we're currently holding it. For now, since this interfaces might need locking internally, + // we should probably let them handle it or assume they are thread-safe. + // A better approach would be to unlock, call integration, then lock again if needed, + // but simple lookup should be fine. + // + // TODO: Refactor IAM integration to be more decoupled } return nil, nil, false } -// LookupByAccessKey is an exported wrapper for lookupByAccessKey. -// It returns the identity and credential associated with the given access key. -// -// WARNING: The returned pointers reference internal data structures. -// Callers MUST NOT modify the returned Identity or Credential objects. -// If mutation is needed, make a copy first. -func (iam *IdentityAccessManagement) LookupByAccessKey(accessKey string) (identity *Identity, cred *Credential, found bool) { - return iam.lookupByAccessKey(accessKey) -} - -func (iam *IdentityAccessManagement) lookupAnonymous() (identity *Identity, found bool) { +func (iam *IdentityAccessManagement) LookupAnonymous() (identity *Identity, found bool) { iam.m.RLock() defer iam.m.RUnlock() if iam.identityAnonymous != nil { @@ -969,646 +854,82 @@ func (iam *IdentityAccessManagement) lookupAnonymous() (identity *Identity, foun return nil, false } -func (iam *IdentityAccessManagement) lookupByIdentityName(name string) *Identity { +// GetUser returns the identity for the given username +func (iam *IdentityAccessManagement) GetUser(username string) (*Identity, error) { iam.m.RLock() defer iam.m.RUnlock() - return iam.nameToIdentity[name] + identity, ok := iam.nameToIdentity[username] + if !ok { + return nil, fmt.Errorf("user not found: %s", username) + } + return identity, nil } -// generatePrincipalArn generates an ARN for a user identity -func generatePrincipalArn(identityName string) string { - // Handle special cases - switch identityName { - case AccountAnonymous.Id: - return "arn:aws:iam::user/anonymous" - case AccountAdmin.Id: - return "arn:aws:iam::user/admin" - default: - return fmt.Sprintf("arn:aws:iam::user/%s", identityName) - } +// updateAuthenticationState updates the isAuthEnabled flag based on configuration +// returns true if authentication was just enabled +func (iam *IdentityAccessManagement) updateAuthenticationState(identityCount int) bool { + wasEnabled := iam.isAuthEnabled + // Enable if we have identities OR if we have external integration + // But here we only check identities count + isEnabled := identityCount > 0 + + iam.isAuthEnabled = isEnabled + return !wasEnabled && isEnabled } -func (iam *IdentityAccessManagement) GetAccountNameById(canonicalId string) string { - iam.m.RLock() - defer iam.m.RUnlock() - if account, ok := iam.accounts[canonicalId]; ok { - return account.DisplayName - } - return "" +// generatePrincipalArn generates a mock ARN for an identity +func generatePrincipalArn(name string) string { + // Format: arn:partition:service:region:account-id:resource-type/resource-id + // We use a simplified format for embedded IAM + return fmt.Sprintf("arn:aws:iam::%s:user/%s", s3_constants.AccountAdminId, name) } -func (iam *IdentityAccessManagement) GetAccountIdByEmail(email string) string { - iam.m.RLock() - defer iam.m.RUnlock() - if account, ok := iam.emailAccount[email]; ok { - return account.Id - } - return "" -} - -func (iam *IdentityAccessManagement) Auth(f http.HandlerFunc, action Action) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - if !iam.isEnabled() { - f(w, r) - return - } - - identity, errCode := iam.authRequest(r, action) - if errCode != s3err.ErrNone { - glog.V(3).Infof("auth error: %v", errCode) - } - - iam.handleAuthResult(w, r, identity, errCode, f) - } -} - -// AuthPostPolicy is a specialized authentication wrapper for PostPolicy requests. -// It allows requests with multipart/form-data to proceed even if classified as Anonymous, -// because the actual authentication (signature verification) for ALL PostPolicy requests is -// performed unconditionally in PostPolicyBucketHandler.doesPolicySignatureMatch(). -// This delegation only defers the initial authentication classification; it does NOT bypass -// signature verification, which is mandatory for all PostPolicy uploads. -func (iam *IdentityAccessManagement) AuthPostPolicy(f http.HandlerFunc, action Action) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - if !iam.isEnabled() { - f(w, r) - return - } - - // Optimization: Use authRequestWithAuthType to avoid re-parsing headers for classification - identity, errCode, authType := iam.authRequestWithAuthType(r, action) - - // Special handling for PostPolicy: if AccessDenied (likely because Anonymous to private bucket) - // AND it looks like a PostPolicy request, allow it to proceed to handler for verification. - if errCode == s3err.ErrAccessDenied { - if authType == authTypeAnonymous && - r.Method == http.MethodPost && - strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") { - - glog.V(3).Infof("Delegating PostPolicy auth to handler") - r.Header.Set(s3_constants.AmzAuthType, "PostPolicy") - f(w, r) - return - } - } - - if errCode != s3err.ErrNone { - glog.V(3).Infof("auth error: %v", errCode) - } - - iam.handleAuthResult(w, r, identity, errCode, f) - } -} - -func (iam *IdentityAccessManagement) handleAuthResult(w http.ResponseWriter, r *http.Request, identity *Identity, errCode s3err.ErrorCode, f http.HandlerFunc) { - if errCode == s3err.ErrNone { - // Store the authenticated identity in request context (secure, cannot be spoofed) - if identity != nil && identity.Name != "" { - ctx := s3_constants.SetIdentityNameInContext(r.Context(), identity.Name) - // Also store the full identity object for handlers that need it (e.g., ListBuckets) - // This is especially important for JWT users whose identity is not in the identities list - ctx = s3_constants.SetIdentityInContext(ctx, identity) - r = r.WithContext(ctx) - } - f(w, r) - return - } - s3err.WriteErrorResponse(w, r, errCode) -} - -// Wrapper to maintain backward compatibility -func (iam *IdentityAccessManagement) authRequest(r *http.Request, action Action) (*Identity, s3err.ErrorCode) { - identity, err, _ := iam.authRequestWithAuthType(r, action) - return identity, err -} - -// check whether the request has valid access keys -func (iam *IdentityAccessManagement) authRequestWithAuthType(r *http.Request, action Action) (*Identity, s3err.ErrorCode, authType) { - var identity *Identity - var s3Err s3err.ErrorCode - var found bool - var amzAuthType string - - // SECURITY: Prevent clients from spoofing internal IAM headers - // These headers are only set by the server after successful JWT authentication - // Clearing them here prevents privilege escalation via header injection - r.Header.Del("X-SeaweedFS-Principal") - r.Header.Del("X-SeaweedFS-Session-Token") - - reqAuthType := getRequestAuthType(r) - - switch reqAuthType { - case authTypeUnknown: - glog.V(4).Infof("unknown auth type") - r.Header.Set(s3_constants.AmzAuthType, "Unknown") - return identity, s3err.ErrAccessDenied, reqAuthType - case authTypePresignedV2, authTypeSignedV2: - glog.V(4).Infof("v2 auth type") - identity, s3Err = iam.isReqAuthenticatedV2(r) - amzAuthType = "SigV2" - case authTypeStreamingSigned, authTypeSigned, authTypePresigned: - glog.V(4).Infof("v4 auth type") - identity, s3Err = iam.reqSignatureV4Verify(r) - amzAuthType = "SigV4" - case authTypeStreamingUnsigned: - glog.V(4).Infof("unsigned streaming upload") - identity, s3Err = iam.reqSignatureV4Verify(r) - amzAuthType = "SigV4" - case authTypeJWT: - glog.V(4).Infof("jwt auth type detected, iamIntegration != nil? %t", iam.iamIntegration != nil) - r.Header.Set(s3_constants.AmzAuthType, "Jwt") - if iam.iamIntegration != nil { - identity, s3Err = iam.authenticateJWTWithIAM(r) - amzAuthType = "Jwt" - } else { - glog.V(2).Infof("IAM integration is nil, returning ErrNotImplemented") - return identity, s3err.ErrNotImplemented, reqAuthType - } - case authTypeAnonymous: - amzAuthType = "Anonymous" - if identity, found = iam.lookupAnonymous(); !found { - r.Header.Set(s3_constants.AmzAuthType, amzAuthType) - return identity, s3err.ErrAccessDenied, reqAuthType - } - default: - return identity, s3err.ErrNotImplemented, reqAuthType +// initializeKMSFromConfig initializes KMS providers based on configuration +func (iam *IdentityAccessManagement) initializeKMSFromConfig(content []byte) error { + // Parse a minimal struct to get KMS config + type KmsConfig struct { + Kms struct { + Type string `json:"type"` + Properties map[string]string `json:"properties"` + } `json:"kms"` } - if len(amzAuthType) > 0 { - r.Header.Set(s3_constants.AmzAuthType, amzAuthType) - } - if s3Err != s3err.ErrNone { - return identity, s3Err, reqAuthType - } - - bucket, object := s3_constants.GetBucketAndObject(r) - prefix := s3_constants.GetPrefix(r) - - // For List operations, use prefix for permission checking if available - if action == s3_constants.ACTION_LIST && object == "" && prefix != "" { - // List operation with prefix - check permission for the prefix path - object = prefix - } else if (object == "/" || object == "") && prefix != "" { - // Using the aws cli with s3, and s3api, and with boto3, the object is often set to "/" or empty - // but the prefix is set to the actual object key for permission checking - object = prefix - } - - // For ListBuckets, authorization is performed in the handler by iterating - // through buckets and checking permissions for each. Skip the global check here. - policyAllows := false - - if action == s3_constants.ACTION_LIST && bucket == "" { - // ListBuckets operation - authorization handled per-bucket in the handler - } else { - // First check bucket policy if one exists - // Bucket policies can grant or deny access to specific users/principals - // Following AWS semantics: - // - Explicit DENY in bucket policy → immediate rejection - // - Explicit ALLOW in bucket policy → grant access (bypass IAM checks) - // - No policy or indeterminate → fall through to IAM checks - if iam.policyEngine != nil && bucket != "" { - principal := buildPrincipalARN(identity, r) - // Phase 1: Evaluate bucket policy without object entry. - // Tag-based conditions (s3:ExistingObjectTag) are re-checked by handlers - // after fetching the entry, which is the Phase 2 check. - var claims map[string]interface{} - if identity != nil { - claims = identity.Claims - } - allowed, evaluated, err := iam.policyEngine.EvaluatePolicy(bucket, object, string(action), principal, r, claims, nil) - - if err != nil { - // SECURITY: Fail-close on policy evaluation errors - // If we can't evaluate the policy, deny access rather than falling through to IAM - glog.Errorf("Error evaluating bucket policy for %s/%s: %v - denying access", bucket, object, err) - return identity, s3err.ErrAccessDenied, reqAuthType - } else if evaluated { - // A bucket policy exists and was evaluated with a matching statement - if allowed { - // Policy explicitly allows this action - grant access immediately - // This bypasses IAM checks to support cross-account access and policy-only principals - glog.V(3).Infof("Bucket policy allows %s to %s on %s/%s (bypassing IAM)", identity.Name, action, bucket, object) - policyAllows = true - } else { - // Policy explicitly denies this action - deny access immediately - // Note: Explicit Deny in bucket policy overrides all other permissions - glog.V(3).Infof("Bucket policy explicitly denies %s to %s on %s/%s", identity.Name, action, bucket, object) - return identity, s3err.ErrAccessDenied, reqAuthType - } - } - // If not evaluated (no policy or no matching statements), fall through to IAM/identity checks - } - - // Only check IAM if bucket policy didn't explicitly allow - if !policyAllows { - // Use centralized permission check - if errCode := iam.VerifyActionPermission(r, identity, action, bucket, object); errCode != s3err.ErrNone { - return identity, errCode, reqAuthType - } - } - } - - r.Header.Set(s3_constants.AmzAccountId, identity.Account.Id) - - return identity, s3err.ErrNone, reqAuthType - -} - -// AuthSignatureOnly performs only signature verification without any authorization checks. -// This is used for IAM API operations where authorization is handled separately based on -// the specific IAM action (e.g., self-service vs admin operations). -// Returns the authenticated identity and any signature verification error. -func (iam *IdentityAccessManagement) AuthSignatureOnly(r *http.Request) (*Identity, s3err.ErrorCode) { - var identity *Identity - var s3Err s3err.ErrorCode - var authType string - switch getRequestAuthType(r) { - case authTypeUnknown: - glog.V(3).Infof("unknown auth type") - r.Header.Set(s3_constants.AmzAuthType, "Unknown") - return identity, s3err.ErrAccessDenied - case authTypePresignedV2, authTypeSignedV2: - glog.V(3).Infof("v2 auth type") - identity, s3Err = iam.isReqAuthenticatedV2(r) - authType = "SigV2" - case authTypeStreamingSigned, authTypeSigned, authTypePresigned: - glog.V(3).Infof("v4 auth type") - identity, s3Err = iam.reqSignatureV4Verify(r) - authType = "SigV4" - - case authTypeStreamingUnsigned: - glog.V(3).Infof("unsigned streaming upload") - identity, s3Err = iam.reqSignatureV4Verify(r) - authType = "SigV4" - case authTypeJWT: - glog.V(3).Infof("jwt auth type detected, iamIntegration != nil? %t", iam.iamIntegration != nil) - r.Header.Set(s3_constants.AmzAuthType, "Jwt") - if iam.iamIntegration != nil { - identity, s3Err = iam.authenticateJWTWithIAM(r) - authType = "Jwt" - } else { - glog.V(2).Infof("IAM integration is nil, returning ErrNotImplemented") - return identity, s3err.ErrNotImplemented - } - case authTypeAnonymous: - // Anonymous users cannot use IAM API - return identity, s3err.ErrAccessDenied - default: - return identity, s3err.ErrNotImplemented - } - - if len(authType) > 0 { - r.Header.Set(s3_constants.AmzAuthType, authType) - } - if s3Err != s3err.ErrNone { - return identity, s3Err - } - - // Set account ID header for downstream handlers - if identity != nil && identity.Account != nil { - r.Header.Set(s3_constants.AmzAccountId, identity.Account.Id) - } - - return identity, s3err.ErrNone -} - -func (identity *Identity) canDo(action Action, bucket string, objectKey string) bool { - if identity == nil { - return false - } - if identity.isAdmin() { - return true - } - for _, a := range identity.Actions { - // Case where the Resource provided is - // "Resource": [ - // "arn:aws:s3:::*" - // ] - if a == action { - return true - } - } - // Intelligent path concatenation to avoid double slashes - fullPath := bucket - if objectKey != "" && !strings.HasPrefix(objectKey, "/") { - fullPath += "/" - } - fullPath += objectKey - - if bucket == "" { - glog.V(3).Infof("identity %s is not allowed to perform action %s on %s -- bucket is empty", identity.Name, action, "/"+strings.TrimPrefix(objectKey, "/")) - return false - } - glog.V(3).Infof("checking if %s can perform %s on bucket '%s'", identity.Name, action, fullPath) - - target := string(action) + ":" + fullPath - adminTarget := s3_constants.ACTION_ADMIN + ":" + fullPath - limitedByBucket := string(action) + ":" + bucket - adminLimitedByBucket := s3_constants.ACTION_ADMIN + ":" + bucket - - for _, a := range identity.Actions { - act := string(a) - if strings.ContainsAny(act, "*?") { - // Pattern has wildcards - use smart matching - if policy_engine.MatchesWildcard(act, target) { - return true - } - if policy_engine.MatchesWildcard(act, adminTarget) { - return true - } - } else { - // No wildcards - exact match only - if act == limitedByBucket { - return true - } - if act == adminLimitedByBucket { - return true - } - } - } - //log error - glog.V(3).Infof("identity %s is not allowed to perform action %s on %s", identity.Name, action, bucket+"/"+objectKey) - return false -} - -func (identity *Identity) isAdmin() bool { - if identity == nil { - return false - } - return slices.Contains(identity.Actions, s3_constants.ACTION_ADMIN) -} - -// buildPrincipalARN builds an ARN for an identity to use in bucket policy evaluation -// It first checks if a principal ARN was set by JWT authentication in request headers -func buildPrincipalARN(identity *Identity, r *http.Request) string { - // Check if principal ARN was already set by JWT authentication - if r != nil { - if principalARN := r.Header.Get("X-SeaweedFS-Principal"); principalARN != "" { - glog.V(4).Infof("buildPrincipalARN: Using principal ARN from header: %s", principalARN) - return principalARN - } - } - - if identity == nil { - return "*" // Anonymous - } - - // Check if this is the anonymous user identity (authenticated as anonymous) - // S3 policies expect Principal: "*" for anonymous access - if identity.Name == s3_constants.AccountAnonymousId || - (identity.Account != nil && identity.Account.Id == s3_constants.AccountAnonymousId) { - return "*" // Anonymous user - } - - // Build an AWS-compatible principal ARN - // Format: arn:aws:iam::account-id:user/user-name - accountId := identity.Account.Id - if accountId == "" { - accountId = "000000000000" // Default account ID - } - - userName := identity.Name - if userName == "" { - userName = "unknown" - } - - return fmt.Sprintf("arn:aws:iam::%s:user/%s", accountId, userName) -} - -// GetCredentialManager returns the credential manager instance -func (iam *IdentityAccessManagement) GetCredentialManager() *credential.CredentialManager { - return iam.credentialManager -} - -// LoadS3ApiConfigurationFromCredentialManager loads configuration using the credential manager -func (iam *IdentityAccessManagement) LoadS3ApiConfigurationFromCredentialManager() error { - glog.V(1).Infof("IAM: reloading configuration from credential manager") - glog.V(1).Infof("Loading S3 API configuration from credential manager") - - s3ApiConfiguration, err := iam.credentialManager.LoadConfiguration(context.Background()) - if err != nil { - glog.Errorf("Failed to load configuration from credential manager: %v", err) - return fmt.Errorf("failed to load configuration from credential manager: %w", err) - } - - glog.V(2).Infof("Credential manager returned %d identities and %d accounts", - len(s3ApiConfiguration.Identities), len(s3ApiConfiguration.Accounts)) - - if err := iam.loadS3ApiConfiguration(s3ApiConfiguration); err != nil { - glog.Errorf("Failed to load S3 API configuration: %v", err) - return err - } - - glog.V(1).Infof("Successfully loaded S3 API configuration from credential manager") - return nil -} - -// initializeKMSFromConfig loads KMS configuration from TOML format -func (iam *IdentityAccessManagement) initializeKMSFromConfig(configContent []byte) error { - // JSON-only KMS configuration - if err := iam.initializeKMSFromJSON(configContent); err == nil { - glog.V(1).Infof("Successfully loaded KMS configuration from JSON format") + var config KmsConfig + // Check if content is JSON or YAML/TOML? + // The content passed to ParseS3ConfigurationFromBytes supports both via viper, + // but here we might need to parse it manually if it's JSON. + // Assuming JSON for s3 config.json + if err := json.Unmarshal(content, &config); err != nil { + // Failed to unmarshal as JSON, might be OK if it's not JSON return nil } - glog.V(2).Infof("No KMS configuration found in S3 config - SSE-KMS will not be available") + if config.Kms.Type == "" { + return nil + } + + glog.V(1).Infof("Initializing KMS provider of type: %s", config.Kms.Type) + + // In a real implementation, we would pass these properties to the KMS factory + // For now, we assume the side-effect imports have registered the providers + // and we might need to configure them. + // This part depends on how weed/kms is implemented. + return nil } -// initializeKMSFromJSON loads KMS configuration from JSON format when provided in the same file -func (iam *IdentityAccessManagement) initializeKMSFromJSON(configContent []byte) error { - // Parse as generic JSON and extract optional "kms" block - var m map[string]any - if err := json.Unmarshal([]byte(strings.TrimSpace(string(configContent))), &m); err != nil { - return err - } - kmsVal, ok := m["kms"] - if !ok { - return fmt.Errorf("no KMS section found") - } +// Accessor methods for policy engine integration - // Load KMS configuration directly from the parsed JSON data - return kms.LoadKMSFromConfig(kmsVal) -} - -// SetIAMIntegration sets the IAM integration for advanced authentication and authorization -func (iam *IdentityAccessManagement) SetIAMIntegration(integration *S3IAMIntegration) { - iam.m.Lock() - defer iam.m.Unlock() - iam.iamIntegration = integration - // When IAM integration is configured, authentication must be enabled - // to ensure requests go through proper auth checks - if integration != nil { - iam.isAuthEnabled = true - } -} - -// authenticateJWTWithIAM authenticates JWT tokens using the IAM integration -func (iam *IdentityAccessManagement) authenticateJWTWithIAM(r *http.Request) (*Identity, s3err.ErrorCode) { - ctx := r.Context() - - // Use IAM integration to authenticate JWT - iamIdentity, errCode := iam.iamIntegration.AuthenticateJWT(ctx, r) - if errCode != s3err.ErrNone { - return nil, errCode - } - - // Convert IAMIdentity to existing Identity structure - identity := &Identity{ - Name: iamIdentity.Name, - Account: iamIdentity.Account, - Actions: []Action{}, // Empty - authorization handled by policy engine - PolicyNames: iamIdentity.PolicyNames, - Claims: iamIdentity.Claims, - } - - // Store session info in request headers for later authorization - r.Header.Set("X-SeaweedFS-Session-Token", iamIdentity.SessionToken) - r.Header.Set("X-SeaweedFS-Principal", iamIdentity.Principal) - - return identity, s3err.ErrNone -} - -// IAM authorization path type constants -// iamAuthPath represents the type of IAM authorization path -type iamAuthPath string - -// IAM authorization path constants -const ( - iamAuthPathJWT iamAuthPath = "jwt" - iamAuthPathSTS_V4 iamAuthPath = "sts_v4" - iamAuthPathStatic_V4 iamAuthPath = "static_v4" - iamAuthPathNone iamAuthPath = "none" -) - -// determineIAMAuthPath determines the IAM authorization path based on available tokens and principals -func determineIAMAuthPath(sessionToken, principal, principalArn string) iamAuthPath { - if sessionToken != "" && principal != "" { - return iamAuthPathJWT - } else if sessionToken != "" && principalArn != "" { - return iamAuthPathSTS_V4 - } else if principalArn != "" { - return iamAuthPathStatic_V4 - } - return iamAuthPathNone -} - -// VerifyActionPermission checks if the identity is allowed to perform the action on the resource. -// It handles both traditional identities (via Actions) and IAM/STS identities (via Policy). -func (iam *IdentityAccessManagement) VerifyActionPermission(r *http.Request, identity *Identity, action Action, bucket, object string) s3err.ErrorCode { - // Fail closed if identity is nil - if identity == nil { - glog.V(3).Infof("VerifyActionPermission called with nil identity for action %s on %s/%s", action, bucket, object) - return s3err.ErrAccessDenied - } - - // Traditional identities (with Actions from -s3.config) use legacy auth, - // JWT/STS identities (no Actions) use IAM authorization - if len(identity.Actions) > 0 { - if !identity.canDo(action, bucket, object) { - return s3err.ErrAccessDenied +func (iam *IdentityAccessManagement) LoadS3ApiConfigurationFromCredentialManager() error { + glog.V(1).Infof("IAM: reloading configuration from credential manager") + glog.V(1).Infof("Loading S3 API configuration from credential manager") + if iam.credentialManager != nil { + config, err := iam.credentialManager.LoadConfiguration(context.Background()) + if err != nil { + return err } - return s3err.ErrNone - } else if iam.iamIntegration != nil { - return iam.authorizeWithIAM(r, identity, action, bucket, object) + return iam.loadS3ApiConfiguration(config) } - - return s3err.ErrAccessDenied -} - -// authorizeWithIAM authorizes requests using the IAM integration policy engine -func (iam *IdentityAccessManagement) authorizeWithIAM(r *http.Request, identity *Identity, action Action, bucket string, object string) s3err.ErrorCode { - ctx := r.Context() - - // Get session info from request headers - // First check for JWT-based authentication headers (X-SeaweedFS-Session-Token) - sessionToken := r.Header.Get("X-SeaweedFS-Session-Token") - principal := r.Header.Get("X-SeaweedFS-Principal") - - // Fallback to AWS Signature V4 STS token if JWT token not present - // This handles the case where STS AssumeRoleWithWebIdentity generates temporary credentials - // that include an X-Amz-Security-Token header (in addition to the access key and secret) - if sessionToken == "" { - sessionToken = r.Header.Get("X-Amz-Security-Token") - if sessionToken == "" { - // Also check query parameters for presigned URLs with STS tokens - sessionToken = r.URL.Query().Get("X-Amz-Security-Token") - } - } - - // Create IAMIdentity for authorization - iamIdentity := &IAMIdentity{ - Name: identity.Name, - Account: identity.Account, - PolicyNames: identity.PolicyNames, - Claims: identity.Claims, // Copy claims for policy variable substitution - } - - // Determine authorization path and configure identity - authPath := determineIAMAuthPath(sessionToken, principal, identity.PrincipalArn) - switch authPath { - case iamAuthPathJWT: - // JWT-based authentication - use session token and principal from headers - iamIdentity.Principal = principal - iamIdentity.SessionToken = sessionToken - glog.V(3).Infof("Using JWT-based IAM authorization for principal: %s", principal) - case iamAuthPathSTS_V4: - // STS V4 signature authentication - use session token (from X-Amz-Security-Token) with principal ARN - iamIdentity.Principal = identity.PrincipalArn - iamIdentity.SessionToken = sessionToken - glog.V(3).Infof("Using STS V4 signature IAM authorization for principal: %s with session token", identity.PrincipalArn) - case iamAuthPathStatic_V4: - // Static V4 signature authentication - use principal ARN without session token - iamIdentity.Principal = identity.PrincipalArn - iamIdentity.SessionToken = "" - glog.V(3).Infof("Using static V4 signature IAM authorization for principal: %s", identity.PrincipalArn) - default: - glog.V(3).Info("No valid principal information for IAM authorization") - return s3err.ErrAccessDenied - } - - // Use IAM integration for authorization - return iam.iamIntegration.AuthorizeAction(ctx, iamIdentity, action, bucket, object, r) -} - -// PutPolicy adds or updates a policy -func (iam *IdentityAccessManagement) PutPolicy(name string, content string) error { - iam.m.Lock() - defer iam.m.Unlock() - if iam.policies == nil { - iam.policies = make(map[string]*iam_pb.Policy) - } - iam.policies[name] = &iam_pb.Policy{Name: name, Content: content} return nil } - -// GetPolicy retrieves a policy by name -func (iam *IdentityAccessManagement) GetPolicy(name string) (*iam_pb.Policy, error) { - iam.m.RLock() - defer iam.m.RUnlock() - if policy, ok := iam.policies[name]; ok { - return policy, nil - } - return nil, fmt.Errorf("policy not found: %s", name) -} - -// DeletePolicy removes a policy -func (iam *IdentityAccessManagement) DeletePolicy(name string) error { - iam.m.Lock() - defer iam.m.Unlock() - delete(iam.policies, name) - return nil -} - -// ListPolicies lists all policies -func (iam *IdentityAccessManagement) ListPolicies() []*iam_pb.Policy { - iam.m.RLock() - defer iam.m.RUnlock() - var policies []*iam_pb.Policy - for _, p := range iam.policies { - policies = append(policies, p) - } - return policies -} diff --git a/weed/s3api/auth_credentials_test.go b/weed/s3api/auth_credentials_test.go index 1db245b88..b01d8e3ce 100644 --- a/weed/s3api/auth_credentials_test.go +++ b/weed/s3api/auth_credentials_test.go @@ -13,9 +13,6 @@ import ( "github.com/seaweedfs/seaweedfs/weed/pb/iam_pb" jsonpb "google.golang.org/protobuf/encoding/protojson" - - _ "github.com/seaweedfs/seaweedfs/weed/credential/filer_etc" - _ "github.com/seaweedfs/seaweedfs/weed/credential/memory" ) func TestIdentityListFileFormat(t *testing.T) { @@ -745,48 +742,3 @@ func TestSignatureVerificationDoesNotCheckPermissions(t *testing.T) { t.Log("Signature verification no longer checks for Write permission") t.Log("This allows list-only and read-only users to authenticate via AWS Signature V4") } - -func TestStaticIdentityProtection(t *testing.T) { - iam := NewIdentityAccessManagement(&S3ApiServerOption{}) - - // Add a static identity - staticIdent := &Identity{ - Name: "static-user", - IsStatic: true, - } - iam.m.Lock() - if iam.nameToIdentity == nil { - iam.nameToIdentity = make(map[string]*Identity) - } - iam.identities = append(iam.identities, staticIdent) - iam.nameToIdentity[staticIdent.Name] = staticIdent - iam.m.Unlock() - - // Add a dynamic identity - dynamicIdent := &Identity{ - Name: "dynamic-user", - IsStatic: false, - } - iam.m.Lock() - iam.identities = append(iam.identities, dynamicIdent) - iam.nameToIdentity[dynamicIdent.Name] = dynamicIdent - iam.m.Unlock() - - // Try to remove static identity - iam.RemoveIdentity("static-user") - - // Verify static identity still exists - iam.m.RLock() - _, ok := iam.nameToIdentity["static-user"] - iam.m.RUnlock() - assert.True(t, ok, "Static identity should not be removed") - - // Try to remove dynamic identity - iam.RemoveIdentity("dynamic-user") - - // Verify dynamic identity is removed - iam.m.RLock() - _, ok = iam.nameToIdentity["dynamic-user"] - iam.m.RUnlock() - assert.False(t, ok, "Dynamic identity should have been removed") -} diff --git a/weed/s3api/s3api_bucket_policy_engine.go b/weed/s3api/s3api_bucket_policy_engine.go index 1a014a919..3be063b35 100644 --- a/weed/s3api/s3api_bucket_policy_engine.go +++ b/weed/s3api/s3api_bucket_policy_engine.go @@ -83,16 +83,6 @@ func (bpe *BucketPolicyEngine) HasPolicyForBucket(bucket string) bool { return bpe.engine.HasPolicyForBucket(bucket) } -// GetBucketPolicy gets the policy for a bucket -func (bpe *BucketPolicyEngine) GetBucketPolicy(bucket string) (*policy_engine.PolicyDocument, error) { - return bpe.engine.GetBucketPolicy(bucket) -} - -// ListBucketPolicies returns all buckets that have policies -func (bpe *BucketPolicyEngine) ListBucketPolicies() []string { - return bpe.engine.GetAllBucketsWithPolicies() -} - // EvaluatePolicy evaluates whether an action is allowed by bucket policy // // Parameters: diff --git a/weed/s3api/s3api_embedded_iam.go b/weed/s3api/s3api_embedded_iam.go index 7181410f1..8d775e0c7 100644 --- a/weed/s3api/s3api_embedded_iam.go +++ b/weed/s3api/s3api_embedded_iam.go @@ -38,15 +38,13 @@ type EmbeddedIamApi struct { getS3ApiConfigurationFunc func(*iam_pb.S3ApiConfiguration) error putS3ApiConfigurationFunc func(*iam_pb.S3ApiConfiguration) error reloadConfigurationFunc func() error - readOnly bool } // NewEmbeddedIamApi creates a new embedded IAM API handler. -func NewEmbeddedIamApi(credentialManager *credential.CredentialManager, iam *IdentityAccessManagement, readOnly bool) *EmbeddedIamApi { +func NewEmbeddedIamApi(credentialManager *credential.CredentialManager, iam *IdentityAccessManagement) *EmbeddedIamApi { return &EmbeddedIamApi{ credentialManager: credentialManager, iam: iam, - readOnly: readOnly, } } @@ -162,8 +160,6 @@ func (e *EmbeddedIamApi) writeIamErrorResponse(w http.ResponseWriter, r *http.Re s3err.WriteXMLResponse(w, r, http.StatusConflict, errorResp) case iam.ErrCodeMalformedPolicyDocumentException, iam.ErrCodeInvalidInputException: s3err.WriteXMLResponse(w, r, http.StatusBadRequest, errorResp) - case "AccessDenied", iam.ErrCodeLimitExceededException: - s3err.WriteXMLResponse(w, r, http.StatusForbidden, errorResp) case iam.ErrCodeServiceFailureException: s3err.WriteXMLResponse(w, r, http.StatusInternalServerError, internalErrorResponse) default: @@ -194,7 +190,6 @@ func (e *EmbeddedIamApi) PutS3ApiConfiguration(s3cfg *iam_pb.S3ApiConfiguration) // ReloadConfiguration reloads the IAM configuration from the credential manager. func (e *EmbeddedIamApi) ReloadConfiguration() error { - glog.V(4).Infof("IAM: reloading configuration via EmbeddedIamApi") if e.reloadConfigurationFunc != nil { return e.reloadConfigurationFunc() } @@ -1048,22 +1043,11 @@ func (e *EmbeddedIamApi) AuthIam(f http.HandlerFunc, _ Action) http.HandlerFunc } // ExecuteAction executes an IAM action with the given values. -// If skipPersist is true, the changed configuration is not saved to the persistent store. -func (e *EmbeddedIamApi) ExecuteAction(values url.Values, skipPersist bool) (interface{}, *iamError) { +func (e *EmbeddedIamApi) ExecuteAction(values url.Values) (interface{}, *iamError) { // Lock to prevent concurrent read-modify-write race conditions e.policyLock.Lock() defer e.policyLock.Unlock() - action := values.Get("Action") - if e.readOnly { - switch action { - case "ListUsers", "ListAccessKeys", "GetUser", "GetUserPolicy", "ListServiceAccounts", "GetServiceAccount": - // Allowed read-only actions - default: - return nil, &iamError{Code: s3err.GetAPIError(s3err.ErrAccessDenied).Code, Error: fmt.Errorf("IAM write operations are disabled on this server")} - } - } - 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)} @@ -1181,11 +1165,9 @@ func (e *EmbeddedIamApi) ExecuteAction(values url.Values, skipPersist bool) (int return nil, &iamError{Code: s3err.GetAPIError(s3err.ErrNotImplemented).Code, Error: errors.New(s3err.GetAPIError(s3err.ErrNotImplemented).Description)} } if changed { - if !skipPersist { - if err := e.PutS3ApiConfiguration(s3cfg); err != nil { - iamErr = &iamError{Code: iam.ErrCodeServiceFailureException, Error: err} - return nil, iamErr - } + if err := e.PutS3ApiConfiguration(s3cfg); err != nil { + iamErr = &iamError{Code: iam.ErrCodeServiceFailureException, Error: err} + return nil, iamErr } // Reload in-memory identity maps so subsequent LookupByAccessKey calls // can see newly created or deleted keys immediately @@ -1214,7 +1196,7 @@ func (e *EmbeddedIamApi) DoActions(w http.ResponseWriter, r *http.Request) { values.Set("CreatedBy", createdBy) } - response, iamErr := e.ExecuteAction(values, false) + response, iamErr := e.ExecuteAction(values) if iamErr != nil { e.writeIamErrorResponse(w, r, iamErr) return diff --git a/weed/s3api/s3api_embedded_iam_test.go b/weed/s3api/s3api_embedded_iam_test.go index 8ac0472cc..c5dc5d949 100644 --- a/weed/s3api/s3api_embedded_iam_test.go +++ b/weed/s3api/s3api_embedded_iam_test.go @@ -87,19 +87,7 @@ func (e *EmbeddedIamApiForTest) DoActions(w http.ResponseWriter, r *http.Request var iamErr *iamError changed := true - action := r.Form.Get("Action") - - if e.readOnly { - switch action { - case "ListUsers", "ListAccessKeys", "GetUser", "GetUserPolicy", "ListServiceAccounts", "GetServiceAccount": - // Allowed read-only actions - default: - e.writeIamErrorResponse(w, r, &iamError{Code: s3err.GetAPIError(s3err.ErrAccessDenied).Code, Error: fmt.Errorf("IAM write operations are disabled on this server")}) - return - } - } - - switch action { + switch r.Form.Get("Action") { case "ListUsers": response = e.ListUsers(s3cfg, values) changed = false @@ -1703,7 +1691,7 @@ func TestEmbeddedIamExecuteAction(t *testing.T) { vals.Set("Action", "CreateUser") vals.Set("UserName", "ExecuteActionUser") - resp, iamErr := api.ExecuteAction(vals, false) + resp, iamErr := api.ExecuteAction(vals) assert.Nil(t, iamErr) // Verify response type @@ -1715,33 +1703,3 @@ func TestEmbeddedIamExecuteAction(t *testing.T) { assert.Len(t, api.mockConfig.Identities, 1) assert.Equal(t, "ExecuteActionUser", api.mockConfig.Identities[0].Name) } - -// TestEmbeddedIamReadOnly tests that write operations are blocked when readOnly is true -func TestEmbeddedIamReadOnly(t *testing.T) { - api := NewEmbeddedIamApiForTest() - api.readOnly = true - - // Try CreateUser (Write) - userName := aws.String("ReadOnlyUser") - params := &iam.CreateUserInput{UserName: userName} - req, _ := iam.New(session.New()).CreateUserRequest(params) - _ = req.Build() - - response, err := executeEmbeddedIamRequest(api, req.HTTPRequest, nil) - assert.NoError(t, err) - assert.Equal(t, http.StatusForbidden, response.Code) - - code, msg := extractEmbeddedIamErrorCodeAndMessage(response) - assert.Equal(t, "AccessDenied", code) - assert.Contains(t, msg, "IAM write operations are disabled") - - // Try ListUsers (Read) - Should succeed - paramsList := &iam.ListUsersInput{} - reqList, _ := iam.New(session.New()).ListUsersRequest(paramsList) - _ = reqList.Build() - - outList := iamListUsersResponse{} - responseList, err := executeEmbeddedIamRequest(api, reqList.HTTPRequest, &outList) - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, responseList.Code) -} diff --git a/weed/s3api/s3api_server.go b/weed/s3api/s3api_server.go index c72669795..7670773e6 100644 --- a/weed/s3api/s3api_server.go +++ b/weed/s3api/s3api_server.go @@ -52,14 +52,11 @@ type S3ApiServerOption struct { ConcurrentUploadLimit int64 ConcurrentFileUploadLimit int64 EnableIam bool // Enable embedded IAM API on the same port - IamReadOnly bool // Disable IAM write operations on this server Cipher bool // encrypt data on volume servers - BindIp string - GrpcPort int } type S3ApiServer struct { - s3_pb.UnimplementedSeaweedS3IamCacheServer + s3_pb.UnimplementedSeaweedS3Server option *S3ApiServerOption iam *IdentityAccessManagement iamIntegration *S3IAMIntegration // Advanced IAM integration for JWT authentication @@ -117,17 +114,13 @@ func NewS3ApiServerWithStore(router *mux.Router, option *S3ApiServerOption, expl // Uses the battle-tested vidMap with filer-based lookups // Supports multiple filer addresses with automatic failover for high availability var filerClient *wdclient.FilerClient - if len(option.Masters) > 0 { + if len(option.Masters) > 0 && option.FilerGroup != "" { // Enable filer discovery via master masterMap := make(map[string]pb.ServerAddress) for i, addr := range option.Masters { masterMap[fmt.Sprintf("master%d", i)] = addr } - clientHost := option.BindIp - if clientHost == "0.0.0.0" || clientHost == "" { - clientHost = util.DetectedHostAddress() - } - masterClient := wdclient.NewMasterClient(option.GrpcDialOption, option.FilerGroup, cluster.S3Type, pb.ServerAddress(util.JoinHostPort(clientHost, option.GrpcPort)), "", "", *pb.NewServiceDiscoveryFromMap(masterMap)) + masterClient := wdclient.NewMasterClient(option.GrpcDialOption, option.FilerGroup, cluster.S3Type, "", "", "", *pb.NewServiceDiscoveryFromMap(masterMap)) // Start the master client connection loop - required for GetMaster() to work go masterClient.KeepConnectedToMaster(context.Background()) @@ -210,12 +203,8 @@ func NewS3ApiServerWithStore(router *mux.Router, option *S3ApiServerOption, expl // Initialize embedded IAM API if enabled if option.EnableIam { - s3ApiServer.embeddedIam = NewEmbeddedIamApi(s3ApiServer.credentialManager, iam, option.IamReadOnly) - if option.IamReadOnly { - glog.V(1).Infof("Embedded IAM API initialized in read-only mode (use -s3.iam.readOnly=false to enable write operations)") - } else { - glog.V(1).Infof("Embedded IAM API initialized in writable mode (WARNING: updates will not be propagated to other S3 servers)") - } + s3ApiServer.embeddedIam = NewEmbeddedIamApi(s3ApiServer.credentialManager, iam) + glog.V(1).Infof("Embedded IAM API initialized (use -iam=false to disable)") } if option.Config != "" { diff --git a/weed/s3api/s3api_server_grpc.go b/weed/s3api/s3api_server_grpc.go index 1bd48b307..318cf9ce1 100644 --- a/weed/s3api/s3api_server_grpc.go +++ b/weed/s3api/s3api_server_grpc.go @@ -2,6 +2,8 @@ package s3api import ( "context" + "fmt" + "net/url" "github.com/seaweedfs/seaweedfs/weed/glog" "github.com/seaweedfs/seaweedfs/weed/pb/iam_pb" @@ -36,7 +38,92 @@ func (s3a *S3ApiServer) RemoveIdentity(ctx context.Context, req *iam_pb.RemoveId return &iam_pb.RemoveIdentityResponse{}, nil } +func (s3a *S3ApiServer) executeAction(ctx context.Context, values url.Values) (interface{}, error) { + if s3a.embeddedIam == nil { + return nil, fmt.Errorf("embedded iam is disabled") + } + response, iamErr := s3a.embeddedIam.ExecuteAction(ctx, values) + if iamErr != nil { + return nil, fmt.Errorf("IAM error: %s - %v", iamErr.Code, iamErr.Error) + } + return response, nil +} + +func (s3a *S3ApiServer) ListUsers(ctx context.Context, req *iam_pb.ListUsersRequest) (*iam_pb.ListUsersResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } + values := url.Values{} + values.Set("Action", "ListUsers") + resp, err := s3a.executeAction(ctx, values) + if err != nil { + return nil, err + } + iamResp, ok := resp.(iamListUsersResponse) + if !ok { + return nil, fmt.Errorf("unexpected IAM ListUsers response type %T", resp) + } + var usernames []string + for _, user := range iamResp.ListUsersResult.Users { + if user != nil && user.UserName != nil { + usernames = append(usernames, *user.UserName) + } + } + return &iam_pb.ListUsersResponse{Usernames: usernames}, nil +} + +func (s3a *S3ApiServer) CreateUser(ctx context.Context, req *iam_pb.CreateUserRequest) (*iam_pb.CreateUserResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } + if req.Identity == nil || req.Identity.Name == "" { + return nil, fmt.Errorf("username name is required") + } + values := url.Values{} + values.Set("Action", "CreateUser") + values.Set("UserName", req.Identity.Name) + _, err := s3a.executeAction(ctx, values) + if err != nil { + return nil, err + } + return &iam_pb.CreateUserResponse{}, nil +} + +func (s3a *S3ApiServer) GetUser(ctx context.Context, req *iam_pb.GetUserRequest) (*iam_pb.GetUserResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } + if req.Username == "" { + return nil, status.Errorf(codes.InvalidArgument, "username is required") + } + values := url.Values{} + values.Set("Action", "GetUser") + values.Set("UserName", req.Username) + resp, err := s3a.executeAction(ctx, values) + if err != nil { + return nil, err + } + iamResp, ok := resp.(iamGetUserResponse) + if !ok { + return nil, fmt.Errorf("unexpected IAM GetUser response type %T", resp) + } + + var username string + if iamResp.GetUserResult.User.UserName != nil { + username = *iamResp.GetUserResult.User.UserName + } + + return &iam_pb.GetUserResponse{ + Identity: &iam_pb.Identity{ + Name: username, + }, + }, nil +} + func (s3a *S3ApiServer) PutPolicy(ctx context.Context, req *iam_pb.PutPolicyRequest) (*iam_pb.PutPolicyResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } if req.Name == "" { return nil, status.Errorf(codes.InvalidArgument, "policy name is required") } @@ -58,6 +145,9 @@ func (s3a *S3ApiServer) PutPolicy(ctx context.Context, req *iam_pb.PutPolicyRequ } func (s3a *S3ApiServer) DeletePolicy(ctx context.Context, req *iam_pb.DeletePolicyRequest) (*iam_pb.DeletePolicyResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } if req.Name == "" { return nil, status.Errorf(codes.InvalidArgument, "policy name is required") } @@ -76,6 +166,9 @@ func (s3a *S3ApiServer) DeletePolicy(ctx context.Context, req *iam_pb.DeletePoli } func (s3a *S3ApiServer) GetPolicy(ctx context.Context, req *iam_pb.GetPolicyRequest) (*iam_pb.GetPolicyResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } if req.Name == "" { return nil, status.Errorf(codes.InvalidArgument, "policy name is required") } @@ -84,22 +177,320 @@ func (s3a *S3ApiServer) GetPolicy(ctx context.Context, req *iam_pb.GetPolicyRequ } policy, err := s3a.iam.GetPolicy(req.Name) if err != nil { - return &iam_pb.GetPolicyResponse{}, nil // Not found is fine for cache + return nil, status.Errorf(codes.NotFound, "policy not found: %s", req.Name) } return &iam_pb.GetPolicyResponse{ - Name: policy.Name, - Content: policy.Content, + Policy: &iam_pb.Policy{ + Name: req.Name, + Content: policy, + }, }, nil } func (s3a *S3ApiServer) ListPolicies(ctx context.Context, req *iam_pb.ListPoliciesRequest) (*iam_pb.ListPoliciesResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } resp := &iam_pb.ListPoliciesResponse{} if s3a.iam == nil { return nil, status.Errorf(codes.Internal, "IAM not initialized") } - policies := s3a.iam.ListPolicies() - for _, policy := range policies { - resp.Policies = append(resp.Policies, policy) - } + // TODO: Iterate over policies in map? return resp, nil } + +func (s3a *S3ApiServer) UpdateUser(ctx context.Context, req *iam_pb.UpdateUserRequest) (*iam_pb.UpdateUserResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } + if req.Username == "" { + return nil, fmt.Errorf("username is required") + } + values := url.Values{} + values.Set("Action", "UpdateUser") + values.Set("UserName", req.Username) + // UpdateUser in DoActions expects "NewUserName" if renaming, but CreateUser just takes UserName. + // Looking at s3api_embedded_iam.go, UpdateUser uses "NewUserName" to change name. + if req.Identity != nil && req.Identity.Name != "" { + values.Set("NewUserName", req.Identity.Name) + } + _, err := s3a.executeAction(ctx, values) + if err != nil { + return nil, err + } + return &iam_pb.UpdateUserResponse{}, nil +} + +func (s3a *S3ApiServer) DeleteUser(ctx context.Context, req *iam_pb.DeleteUserRequest) (*iam_pb.DeleteUserResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } + if req.Username == "" { + return nil, fmt.Errorf("username is required") + } + values := url.Values{} + values.Set("Action", "DeleteUser") + values.Set("UserName", req.Username) + _, err := s3a.executeAction(ctx, values) + if err != nil { + return nil, err + } + return &iam_pb.DeleteUserResponse{}, nil +} + +func (s3a *S3ApiServer) ListAccessKeys(ctx context.Context, req *iam_pb.ListAccessKeysRequest) (*iam_pb.ListAccessKeysResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } + if req.Username == "" { + return nil, fmt.Errorf("username is required") + } + values := url.Values{} + values.Set("Action", "ListAccessKeys") + values.Set("UserName", req.Username) + resp, err := s3a.executeAction(ctx, values) + if err != nil { + return nil, err + } + iamResp, ok := resp.(iamListAccessKeysResponse) + if !ok { + return nil, fmt.Errorf("unexpected IAM ListAccessKeys response type %T", resp) + } + var accessKeys []*iam_pb.Credential + for _, meta := range iamResp.ListAccessKeysResult.AccessKeyMetadata { + if meta != nil && meta.AccessKeyId != nil && meta.Status != nil { + accessKeys = append(accessKeys, &iam_pb.Credential{ + AccessKey: *meta.AccessKeyId, + Status: *meta.Status, + }) + } + } + return &iam_pb.ListAccessKeysResponse{AccessKeys: accessKeys}, nil +} + +func (s3a *S3ApiServer) CreateAccessKey(ctx context.Context, req *iam_pb.CreateAccessKeyRequest) (*iam_pb.CreateAccessKeyResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } + if req.Username == "" { + return nil, fmt.Errorf("username is required") + } + values := url.Values{} + values.Set("Action", "CreateAccessKey") + values.Set("UserName", req.Username) + _, err := s3a.executeAction(ctx, values) + if err != nil { + return nil, err + } + return &iam_pb.CreateAccessKeyResponse{}, nil +} + +func (s3a *S3ApiServer) DeleteAccessKey(ctx context.Context, req *iam_pb.DeleteAccessKeyRequest) (*iam_pb.DeleteAccessKeyResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } + if req.Username == "" { + return nil, fmt.Errorf("username is required") + } + if req.AccessKey == "" { + return nil, fmt.Errorf("access key is required") + } + values := url.Values{} + values.Set("Action", "DeleteAccessKey") + values.Set("UserName", req.Username) + values.Set("AccessKeyId", req.AccessKey) + _, err := s3a.executeAction(ctx, values) + if err != nil { + return nil, err + } + return &iam_pb.DeleteAccessKeyResponse{}, nil +} + +func (s3a *S3ApiServer) PutUserPolicy(ctx context.Context, req *iam_pb.PutUserPolicyRequest) (*iam_pb.PutUserPolicyResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } + if req.Username == "" { + return nil, fmt.Errorf("username is required") + } + if req.PolicyName == "" { + return nil, fmt.Errorf("policy name is required") + } + values := url.Values{} + values.Set("Action", "PutUserPolicy") + values.Set("UserName", req.Username) + values.Set("PolicyName", req.PolicyName) + values.Set("PolicyDocument", req.PolicyDocument) + _, err := s3a.executeAction(ctx, values) + if err != nil { + return nil, err + } + return &iam_pb.PutUserPolicyResponse{}, nil +} + +func (s3a *S3ApiServer) GetUserPolicy(ctx context.Context, req *iam_pb.GetUserPolicyRequest) (*iam_pb.GetUserPolicyResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } + if req.Username == "" { + return nil, fmt.Errorf("username is required") + } + if req.PolicyName == "" { + return nil, fmt.Errorf("policy name is required") + } + values := url.Values{} + values.Set("Action", "GetUserPolicy") + values.Set("UserName", req.Username) + values.Set("PolicyName", req.PolicyName) + resp, err := s3a.executeAction(ctx, values) + if err != nil { + return nil, err + } + iamResp, ok := resp.(iamGetUserPolicyResponse) + if !ok { + return nil, fmt.Errorf("unexpected IAM GetUserPolicy response type %T", resp) + } + return &iam_pb.GetUserPolicyResponse{ + Username: iamResp.GetUserPolicyResult.UserName, + PolicyName: iamResp.GetUserPolicyResult.PolicyName, + PolicyDocument: iamResp.GetUserPolicyResult.PolicyDocument, + }, nil +} + +func (s3a *S3ApiServer) DeleteUserPolicy(ctx context.Context, req *iam_pb.DeleteUserPolicyRequest) (*iam_pb.DeleteUserPolicyResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } + if req.Username == "" { + return nil, fmt.Errorf("username is required") + } + if req.PolicyName == "" { + return nil, fmt.Errorf("policy name is required") + } + values := url.Values{} + values.Set("Action", "DeleteUserPolicy") + values.Set("UserName", req.Username) + values.Set("PolicyName", req.PolicyName) + _, err := s3a.executeAction(ctx, values) + if err != nil { + return nil, err + } + return &iam_pb.DeleteUserPolicyResponse{}, nil +} + +func (s3a *S3ApiServer) ListServiceAccounts(ctx context.Context, req *iam_pb.ListServiceAccountsRequest) (*iam_pb.ListServiceAccountsResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } + values := url.Values{} + values.Set("Action", "ListServiceAccounts") + resp, err := s3a.executeAction(ctx, values) + if err != nil { + return nil, err + } + iamResp, ok := resp.(iamListServiceAccountsResponse) + if !ok { + return nil, fmt.Errorf("unexpected IAM ListServiceAccounts response type %T", resp) + } + var serviceAccounts []*iam_pb.ServiceAccount + for _, sa := range iamResp.ListServiceAccountsResult.ServiceAccounts { + if sa != nil { + serviceAccounts = append(serviceAccounts, &iam_pb.ServiceAccount{ + Id: sa.ServiceAccountId, + ParentUser: sa.ParentUser, + Description: sa.Description, + Credential: &iam_pb.Credential{ + AccessKey: sa.AccessKeyId, + Status: sa.Status, + }, + }) + } + } + return &iam_pb.ListServiceAccountsResponse{ServiceAccounts: serviceAccounts}, nil +} + +func (s3a *S3ApiServer) CreateServiceAccount(ctx context.Context, req *iam_pb.CreateServiceAccountRequest) (*iam_pb.CreateServiceAccountResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } + if req.ServiceAccount == nil || req.ServiceAccount.CreatedBy == "" { + return nil, fmt.Errorf("service account owner is required") + } + values := url.Values{} + values.Set("Action", "CreateServiceAccount") + values.Set("CreatedBy", req.ServiceAccount.CreatedBy) + _, err := s3a.executeAction(ctx, values) + if err != nil { + return nil, err + } + return &iam_pb.CreateServiceAccountResponse{}, nil +} + +func (s3a *S3ApiServer) UpdateServiceAccount(ctx context.Context, req *iam_pb.UpdateServiceAccountRequest) (*iam_pb.UpdateServiceAccountResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } + if req.Id == "" { + return nil, fmt.Errorf("service account id is required") + } + values := url.Values{} + values.Set("Action", "UpdateServiceAccount") + values.Set("ServiceAccountId", req.Id) + if req.ServiceAccount != nil && req.ServiceAccount.Disabled { + values.Set("Status", "Inactive") + } + _, err := s3a.executeAction(ctx, values) + if err != nil { + return nil, err + } + return &iam_pb.UpdateServiceAccountResponse{}, nil +} + +func (s3a *S3ApiServer) DeleteServiceAccount(ctx context.Context, req *iam_pb.DeleteServiceAccountRequest) (*iam_pb.DeleteServiceAccountResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } + if req.Id == "" { + return nil, fmt.Errorf("service account id is required") + } + values := url.Values{} + values.Set("Action", "DeleteServiceAccount") + values.Set("ServiceAccountId", req.Id) + _, err := s3a.executeAction(ctx, values) + if err != nil { + return nil, err + } + return &iam_pb.DeleteServiceAccountResponse{}, nil +} + +func (s3a *S3ApiServer) GetServiceAccount(ctx context.Context, req *iam_pb.GetServiceAccountRequest) (*iam_pb.GetServiceAccountResponse, error) { + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "request is nil") + } + if req.Id == "" { + return nil, fmt.Errorf("service account id is required") + } + values := url.Values{} + values.Set("Action", "GetServiceAccount") + values.Set("ServiceAccountId", req.Id) + resp, err := s3a.executeAction(ctx, values) + if err != nil { + return nil, err + } + iamResp, ok := resp.(iamGetServiceAccountResponse) + if !ok { + return nil, fmt.Errorf("unexpected IAM GetServiceAccount response type %T", resp) + } + + return &iam_pb.GetServiceAccountResponse{ + ServiceAccount: &iam_pb.ServiceAccount{ + Id: iamResp.GetServiceAccountResult.ServiceAccount.ServiceAccountId, + ParentUser: iamResp.GetServiceAccountResult.ServiceAccount.ParentUser, + Description: iamResp.GetServiceAccountResult.ServiceAccount.Description, + Credential: &iam_pb.Credential{ + AccessKey: iamResp.GetServiceAccountResult.ServiceAccount.AccessKeyId, + Status: iamResp.GetServiceAccountResult.ServiceAccount.Status, + }, + }, + }, nil +} diff --git a/weed/s3api/s3api_server_routing_test.go b/weed/s3api/s3api_server_routing_test.go index 52ecd20e5..2746d59fe 100644 --- a/weed/s3api/s3api_server_routing_test.go +++ b/weed/s3api/s3api_server_routing_test.go @@ -31,7 +31,7 @@ func setupRoutingTestServer(t *testing.T) *S3ApiServer { option: opt, iam: iam, credentialManager: iam.credentialManager, - embeddedIam: NewEmbeddedIamApi(iam.credentialManager, iam, false), + embeddedIam: NewEmbeddedIamApi(iam.credentialManager, iam), stsHandlers: &STSHandlers{}, } diff --git a/weed/server/filer_server.go b/weed/server/filer_server.go index 3ec247ffd..f76d215aa 100644 --- a/weed/server/filer_server.go +++ b/weed/server/filer_server.go @@ -256,10 +256,6 @@ func NewFilerServer(defaultMux, readonlyMux *http.ServeMux, option *FilerOption) fs.filer.Dlm.LockRing.SetTakeSnapshotCallback(fs.OnDlmChangeSnapshot) - if fs.CredentialManager != nil { - fs.CredentialManager.SetMasterClient(fs.filer.MasterClient, fs.grpcDialOption) - } - return fs, nil } diff --git a/weed/server/filer_server_handlers_iam_grpc.go b/weed/server/filer_server_handlers_iam_grpc.go index c77cf8914..777c9f2d4 100644 --- a/weed/server/filer_server_handlers_iam_grpc.go +++ b/weed/server/filer_server_handlers_iam_grpc.go @@ -73,7 +73,7 @@ func (s *IamGrpcServer) CreateUser(ctx context.Context, req *iam_pb.CreateUserRe if req == nil || req.Identity == nil { return nil, status.Errorf(codes.InvalidArgument, "identity is required") } - glog.V(4).Infof("IAM: Filer.CreateUser %s", req.Identity.Name) + glog.V(4).Infof("CreateUser: %s", req.Identity.Name) if s.credentialManager == nil { return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured") @@ -113,10 +113,10 @@ func (s *IamGrpcServer) GetUser(ctx context.Context, req *iam_pb.GetUserRequest) } func (s *IamGrpcServer) UpdateUser(ctx context.Context, req *iam_pb.UpdateUserRequest) (*iam_pb.UpdateUserResponse, error) { + glog.V(4).Infof("UpdateUser: %s", req.Username) if req == nil || req.Identity == nil { return nil, status.Errorf(codes.InvalidArgument, "identity is required") } - glog.V(4).Infof("IAM: Filer.UpdateUser %s", req.Username) if s.credentialManager == nil { return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured") @@ -135,7 +135,7 @@ func (s *IamGrpcServer) UpdateUser(ctx context.Context, req *iam_pb.UpdateUserRe } func (s *IamGrpcServer) DeleteUser(ctx context.Context, req *iam_pb.DeleteUserRequest) (*iam_pb.DeleteUserResponse, error) { - glog.V(4).Infof("IAM: Filer.DeleteUser %s", req.Username) + glog.V(4).Infof("DeleteUser: %s", req.Username) if s.credentialManager == nil { return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured") @@ -243,7 +243,7 @@ func (s *IamGrpcServer) GetUserByAccessKey(ctx context.Context, req *iam_pb.GetU // Policy Management func (s *IamGrpcServer) PutPolicy(ctx context.Context, req *iam_pb.PutPolicyRequest) (*iam_pb.PutPolicyResponse, error) { - glog.V(4).Infof("IAM: Filer.PutPolicy %s", req.Name) + glog.V(4).Infof("PutPolicy: %s", req.Name) if s.credentialManager == nil { return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured") diff --git a/weed/server/master_grpc_server.go b/weed/server/master_grpc_server.go index 7c6bd1e3c..255a0d7a7 100644 --- a/weed/server/master_grpc_server.go +++ b/weed/server/master_grpc_server.go @@ -276,7 +276,6 @@ func (ms *MasterServer) KeepConnected(stream master_pb.Seaweed_KeepConnectedServ clientName, messageChan := ms.addClient(req.FilerGroup, req.ClientType, peerAddress) for _, update := range ms.Cluster.AddClusterNode(req.FilerGroup, req.ClientType, cluster.DataCenter(req.DataCenter), cluster.Rack(req.Rack), peerAddress, req.Version) { - glog.V(1).Infof("Cluster: %s node %s added to group '%s'", req.ClientType, peerAddress, req.FilerGroup) ms.broadcastToClients(update) }