Browse Source

IAM Policy Management via gRPC (#8109)

* Add IAM gRPC service definition

- Add GetConfiguration/PutConfiguration for config management
- Add CreateUser/GetUser/UpdateUser/DeleteUser/ListUsers for user management
- Add CreateAccessKey/DeleteAccessKey/GetUserByAccessKey for access key management
- Methods mirror existing IAM HTTP API functionality

* Add IAM gRPC handlers on filer server

- Implement IamGrpcServer with CredentialManager integration
- Handle configuration get/put operations
- Handle user CRUD operations
- Handle access key create/delete operations
- All methods delegate to CredentialManager for actual storage

* Wire IAM gRPC service to filer server

- Add CredentialManager field to FilerOption and FilerServer
- Import credential store implementations in filer command
- Initialize CredentialManager from credential.toml if available
- Register IAM gRPC service on filer gRPC server
- Enable credential management via gRPC alongside existing filer services

* Regenerate IAM protobuf with gRPC service methods

* iam_pb: add Policy Management to protobuf definitions

* credential: implement PolicyManager in credential stores

* filer: implement IAM Policy Management RPCs

* shell: add s3.policy command

* test: add integration test for s3.policy

* test: fix compilation errors in policy_test

* pb

* fmt

* test

* weed shell: add -policies flag to s3.configure

This allows linking/unlinking IAM policies to/from identities
directly from the s3.configure command.

* test: verify s3.configure policy linking and fix port allocation

- Added test case for linking policies to users via s3.configure
- Implemented findAvailablePortPair to ensure HTTP and gRPC ports
  are both available, avoiding conflicts with randomized port assignments.
- Updated assertion to match jsonpb output (policyNames)

* credential: add StoreTypeGrpc constant

* credential: add IAM gRPC store boilerplate

* credential: implement identity methods in gRPC store

* credential: implement policy methods in gRPC store

* admin: use gRPC credential store for AdminServer

This ensures that all IAM and policy changes made through the Admin UI
are persisted via the Filer's IAM gRPC service instead of direct file manipulation.

* shell: s3.configure use granular IAM gRPC APIs instead of full config patching

* shell: s3.configure use granular IAM gRPC APIs

* shell: replace deprecated ioutil with os in s3.policy

* filer: use gRPC FailedPrecondition for unconfigured credential manager

* test: improve s3.policy integration tests and fix error checks

* ci: add s3 policy shell integration tests to github workflow

* filer: fix LoadCredentialConfiguration error handling

* credential/grpc: propagate unmarshal errors in GetPolicies

* filer/grpc: improve error handling and validation

* shell: use gRPC status codes in s3.configure

* credential: document PutPolicy as create-or-replace

* credential/postgres: reuse CreatePolicy in PutPolicy to deduplicate logic

* shell: add timeout context and strictly enforce flags in s3.policy

* iam: standardize policy content field naming in gRPC and proto

* shell: extract slice helper functions in s3.configure

* filer: map credential store errors to gRPC status codes

* filer: add input validation for UpdateUser and CreateAccessKey

* iam: improve validation in policy and config handlers

* filer: ensure IAM service registration by defaulting credential manager

* credential: add GetStoreName method to manager

* test: verify policy deletion in integration test
pull/8043/merge
Chris Lu 4 weeks ago
committed by GitHub
parent
commit
6bf088cec9
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 35
      .github/workflows/s3-policy-tests.yml
  2. 296
      test/s3/policy/policy_test.go
  3. 4
      weed/admin/dash/admin_server.go
  4. 24
      weed/command/filer.go
  5. 29
      weed/credential/credential_manager.go
  6. 8
      weed/credential/credential_store.go
  7. 5
      weed/credential/filer_etc/filer_etc_policy.go
  8. 8
      weed/credential/filer_multiple/filer_multiple_store.go
  9. 120
      weed/credential/grpc/grpc_identity.go
  10. 69
      weed/credential/grpc/grpc_policy.go
  11. 72
      weed/credential/grpc/grpc_store.go
  12. 13
      weed/credential/memory/memory_policy.go
  13. 5
      weed/credential/postgres/postgres_policy.go
  14. 7
      weed/pb/filer_pb/filer.pb.go
  15. 3
      weed/pb/filer_pb/filer_grpc.pb.go
  16. 146
      weed/pb/iam.proto
  17. 1836
      weed/pb/iam_pb/iam.pb.go
  18. 555
      weed/pb/iam_pb/iam_grpc.pb.go
  19. 2
      weed/pb/master_pb/master.pb.go
  20. 2
      weed/pb/master_pb/master_grpc.pb.go
  21. 7
      weed/pb/mount_pb/mount.pb.go
  22. 3
      weed/pb/mount_pb/mount_grpc.pb.go
  23. 9
      weed/pb/mq_agent_pb/mq_agent.pb.go
  24. 3
      weed/pb/mq_agent_pb/mq_agent_grpc.pb.go
  25. 9
      weed/pb/mq_pb/mq_broker.pb.go
  26. 3
      weed/pb/mq_pb/mq_broker_grpc.pb.go
  27. 7
      weed/pb/remote_pb/remote.pb.go
  28. 7
      weed/pb/s3_pb/s3.pb.go
  29. 3
      weed/pb/s3_pb/s3_grpc.pb.go
  30. 7
      weed/pb/schema_pb/mq_schema.pb.go
  31. 9
      weed/pb/volume_server_pb/volume_server.pb.go
  32. 782
      weed/pb/volume_server_pb/volume_server_grpc.pb.go
  33. 7
      weed/pb/worker_pb/worker.pb.go
  34. 3
      weed/pb/worker_pb/worker_grpc.pb.go
  35. 6
      weed/server/filer_server.go
  36. 351
      weed/server/filer_server_handlers_iam_grpc.go
  37. 339
      weed/shell/command_s3_configure.go
  38. 150
      weed/shell/command_s3_policy.go

35
.github/workflows/s3-policy-tests.yml

@ -9,6 +9,7 @@ on:
- 'weed/s3api/policy/**'
- 'weed/iam/**'
- 'test/s3/iam/**'
- 'test/s3/policy/**'
- '.github/workflows/s3-policy-tests.yml'
push:
branches: [ master, main ]
@ -19,6 +20,7 @@ on:
- 'weed/s3api/policy/**'
- 'weed/iam/**'
- 'test/s3/iam/**'
- 'test/s3/policy/**'
- '.github/workflows/s3-policy-tests.yml'
concurrency:
@ -389,3 +391,36 @@ jobs:
name: trusted-proxy-test-logs
path: /tmp/weed_proxy_test.log
retention-days: 3
# S3 Policy Shell Integration Tests
s3-policy-shell-tests:
name: S3 Policy Shell Integration Tests
runs-on: ubuntu-22.04
timeout-minutes: 15
steps:
- name: Check out code
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: 'go.mod'
id: go
- name: Install SeaweedFS
run: |
go install -buildvcs=false
- name: Run S3 Policy Shell Tests
timeout-minutes: 10
working-directory: test/s3/policy
run: |
set -x
echo "=== Running S3 Policy Shell Tests ==="
# Set WEED_BINARY to use the installed version (though test uses 'weed' command)
export WEED_BINARY=$(which weed)
export PATH=$PATH:$(dirname $WEED_BINARY)
go test -v -timeout 10m ./...

296
test/s3/policy/policy_test.go

@ -0,0 +1,296 @@
package policy
import (
"context"
"fmt"
"net"
"net/http"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/seaweedfs/seaweedfs/weed/command"
"github.com/seaweedfs/seaweedfs/weed/glog"
flag "github.com/seaweedfs/seaweedfs/weed/util/fla9"
"github.com/stretchr/testify/require"
)
// TestCluster manages the weed mini instance for integration testing
type TestCluster struct {
dataDir string
ctx context.Context
cancel context.CancelFunc
isRunning bool
wg sync.WaitGroup
masterPort int
volumePort int
filerPort int
s3Port int
s3Endpoint string
}
func TestS3PolicyShellRevised(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}
cluster, err := startMiniCluster(t)
require.NoError(t, err)
defer cluster.Stop()
policyContent := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}`
tmpPolicyFile, err := os.CreateTemp("", "test_policy_*.json")
if err != nil {
t.Fatalf("Failed to create temp policy file: %v", err)
}
defer os.Remove(tmpPolicyFile.Name())
_, err = tmpPolicyFile.WriteString(policyContent)
require.NoError(t, err)
require.NoError(t, tmpPolicyFile.Close())
weedCmd := "weed"
masterAddr := fmt.Sprintf("127.0.0.1:%d", cluster.masterPort)
filerAddr := fmt.Sprintf("127.0.0.1:%d", cluster.filerPort)
// Put
execShell(t, weedCmd, masterAddr, filerAddr, fmt.Sprintf("s3.policy -put -name=testpolicy -file=%s", tmpPolicyFile.Name()))
// List
out := execShell(t, weedCmd, masterAddr, filerAddr, "s3.policy -list")
if !contains(out, "Name: testpolicy") {
t.Errorf("List failed: %s", out)
}
// Get
out = execShell(t, weedCmd, masterAddr, filerAddr, "s3.policy -get -name=testpolicy")
if !contains(out, "Statement") {
t.Errorf("Get failed: %s", out)
}
// Delete
execShell(t, weedCmd, masterAddr, filerAddr, "s3.policy -delete -name=testpolicy")
// Verify
out = execShell(t, weedCmd, masterAddr, filerAddr, "s3.policy -list")
if contains(out, "Name: testpolicy") {
t.Errorf("delete failed, policy 'testpolicy' should not be in the list: %s", out)
}
// Verify s3.configure linking policies
execShell(t, weedCmd, masterAddr, filerAddr, "s3.configure -user=test -actions=Read -policies=testpolicy -apply")
out = execShell(t, weedCmd, masterAddr, filerAddr, "s3.configure")
if !contains(out, "\"policyNames\": [\n \"testpolicy\"\n ]") {
// relaxed check
if !contains(out, "\"testpolicy\"") || !contains(out, "policyNames") {
t.Errorf("s3.configure failed to link policy: %s", out)
}
}
// 1. Update User: Add Write action
execShell(t, weedCmd, masterAddr, filerAddr, "s3.configure -user=test -actions=Write -apply")
out = execShell(t, weedCmd, masterAddr, filerAddr, "s3.configure")
if !contains(out, "Write") {
t.Errorf("s3.configure failed to add Write action: %s", out)
}
// 2. Granular Delete: Delete Read action
execShell(t, weedCmd, masterAddr, filerAddr, "s3.configure -user=test -actions=Read -delete -apply")
out = execShell(t, weedCmd, masterAddr, filerAddr, "s3.configure")
if contains(out, "\"Read\"") { // Quote to avoid matching partial words if any
t.Errorf("s3.configure failed to delete Read action: %s", out)
}
if !contains(out, "Write") {
t.Errorf("s3.configure deleted Write action unnecessarily: %s", out)
}
// 3. Access Key Management
execShell(t, weedCmd, masterAddr, filerAddr, "s3.configure -user=test -access_key=testkey -secret_key=testsecret -apply")
out = execShell(t, weedCmd, masterAddr, filerAddr, "s3.configure")
if !contains(out, "testkey") {
t.Errorf("s3.configure failed to add access key: %s", out)
}
execShell(t, weedCmd, masterAddr, filerAddr, "s3.configure -user=test -access_key=testkey -delete -apply")
out = execShell(t, weedCmd, masterAddr, filerAddr, "s3.configure")
if contains(out, "testkey") {
t.Errorf("s3.configure failed to delete access key: %s", out)
}
// 4. Delete User
execShell(t, weedCmd, masterAddr, filerAddr, "s3.configure -user=test -delete -apply")
out = execShell(t, weedCmd, masterAddr, filerAddr, "s3.configure")
if contains(out, "\"Name\": \"test\"") {
t.Errorf("s3.configure failed to delete user: %s", out)
}
}
func execShell(t *testing.T, weedCmd, master, filer, shellCmd string) string {
// weed shell -master=... -filer=...
args := []string{"shell", "-master=" + master, "-filer=" + filer}
t.Logf("Running: %s %v <<< %s", weedCmd, args, shellCmd)
cmd := exec.Command(weedCmd, args...)
cmd.Stdin = strings.NewReader(shellCmd + "\n")
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("Failed to run %s: %v\nOutput: %s", shellCmd, err, string(out))
}
return string(out)
}
// --- Test setup helpers ---
func findAvailablePort() (int, error) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return 0, err
}
defer listener.Close()
addr := listener.Addr().(*net.TCPAddr)
return addr.Port, nil
}
// findAvailablePortPair finds an available http port P such that P and P+10000 (grpc) are both available
func findAvailablePortPair() (int, int, error) {
for i := 0; i < 100; i++ {
httpPort, err := findAvailablePort()
if err != nil {
return 0, 0, err
}
grpcPort := httpPort + 10000
// check if grpc port is available
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", grpcPort))
if err == nil {
listener.Close()
return httpPort, grpcPort, nil
}
}
return 0, 0, fmt.Errorf("failed to find available port pair")
}
func startMiniCluster(t *testing.T) (*TestCluster, error) {
masterPort, masterGrpcPort, err := findAvailablePortPair()
require.NoError(t, err)
volumePort, volumeGrpcPort, err := findAvailablePortPair()
require.NoError(t, err)
filerPort, filerGrpcPort, err := findAvailablePortPair()
require.NoError(t, err)
s3Port, s3GrpcPort, err := findAvailablePortPair()
require.NoError(t, err)
testDir := t.TempDir()
ctx, cancel := context.WithCancel(context.Background())
s3Endpoint := fmt.Sprintf("http://127.0.0.1:%d", s3Port)
cluster := &TestCluster{
dataDir: testDir,
ctx: ctx,
cancel: cancel,
masterPort: masterPort,
volumePort: volumePort,
filerPort: filerPort,
s3Port: s3Port,
s3Endpoint: s3Endpoint,
}
// Disable authentication for tests
securityToml := filepath.Join(testDir, "security.toml")
err = os.WriteFile(securityToml, []byte("# Empty security config\n"), 0644)
require.NoError(t, err)
// Configure credential store for IAM tests
credentialToml := filepath.Join(testDir, "credential.toml")
credentialConfig := `
[credential.memory]
enabled = true
`
err = os.WriteFile(credentialToml, []byte(credentialConfig), 0644)
require.NoError(t, err)
cluster.wg.Add(1)
go func() {
defer cluster.wg.Done()
oldDir, _ := os.Getwd()
oldArgs := os.Args
defer func() {
os.Chdir(oldDir)
os.Args = oldArgs
}()
os.Chdir(testDir)
os.Args = []string{
"weed",
"-dir=" + testDir,
"-master.port=" + strconv.Itoa(masterPort),
"-master.port.grpc=" + strconv.Itoa(masterGrpcPort),
"-volume.port=" + strconv.Itoa(volumePort),
"-volume.port.grpc=" + strconv.Itoa(volumeGrpcPort),
"-filer.port=" + strconv.Itoa(filerPort),
"-filer.port.grpc=" + strconv.Itoa(filerGrpcPort),
"-s3.port=" + strconv.Itoa(s3Port),
"-s3.port.grpc=" + strconv.Itoa(s3GrpcPort),
"-webdav.port=0",
"-admin.ui=false",
"-master.volumeSizeLimitMB=32",
"-ip=127.0.0.1",
"-master.peers=none",
}
glog.MaxSize = 1024 * 1024
for _, cmd := range command.Commands {
if cmd.Name() == "mini" && cmd.Run != nil {
cmd.Flag.Parse(os.Args[1:])
cmd.Run(cmd, cmd.Flag.Args())
return
}
}
}()
// Wait for S3
err = waitForS3Ready(cluster.s3Endpoint, 60*time.Second)
if err != nil {
cancel()
return nil, err
}
cluster.isRunning = true
return cluster, nil
}
func waitForS3Ready(endpoint string, timeout time.Duration) error {
client := &http.Client{Timeout: 1 * time.Second}
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
resp, err := client.Get(endpoint)
if err == nil {
resp.Body.Close()
return nil
}
time.Sleep(200 * time.Millisecond)
}
return fmt.Errorf("timeout waiting for S3")
}
func (c *TestCluster) Stop() {
if c.cancel != nil {
c.cancel()
}
if c.isRunning {
time.Sleep(500 * time.Millisecond)
}
// Simplified stop
for _, cmd := range command.Commands {
if cmd.Name() == "mini" {
cmd.Flag.VisitAll(func(f *flag.Flag) {
f.Value.Set(f.DefValue)
})
break
}
}
}
func contains(s, substr string) bool {
return strings.Contains(s, substr)
}

4
weed/admin/dash/admin_server.go

@ -28,6 +28,8 @@ import (
"github.com/seaweedfs/seaweedfs/weed/s3api"
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
"github.com/seaweedfs/seaweedfs/weed/worker/tasks"
_ "github.com/seaweedfs/seaweedfs/weed/credential/grpc" // Register gRPC credential store
)
// FilerConfig holds filer configuration needed for bucket operations
@ -136,7 +138,7 @@ func NewAdminServer(masters string, templateFS http.FileSystem, dataDir string)
server.topicRetentionPurger = NewTopicRetentionPurger(server)
// Initialize credential manager with defaults
credentialManager, err := credential.NewCredentialManagerWithDefaults("")
credentialManager, err := credential.NewCredentialManagerWithDefaults(credential.StoreTypeGrpc)
if err != nil {
glog.Warningf("Failed to initialize credential manager: %v", err)
// Continue without credential manager - will fall back to legacy approach

24
weed/command/filer.go

@ -18,10 +18,15 @@ import (
"google.golang.org/grpc/credentials/tls/certprovider/pemfile"
"google.golang.org/grpc/reflection"
"github.com/seaweedfs/seaweedfs/weed/credential"
_ "github.com/seaweedfs/seaweedfs/weed/credential/filer_etc"
_ "github.com/seaweedfs/seaweedfs/weed/credential/memory"
_ "github.com/seaweedfs/seaweedfs/weed/credential/postgres"
"github.com/seaweedfs/seaweedfs/weed/filer"
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/pb"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
"github.com/seaweedfs/seaweedfs/weed/security"
weed_server "github.com/seaweedfs/seaweedfs/weed/server"
stats_collect "github.com/seaweedfs/seaweedfs/weed/stats"
@ -324,6 +329,16 @@ func (fo *FilerOptions) startFiler() {
filerAddress := pb.NewServerAddress(*fo.ip, *fo.port, *fo.portGrpc)
// Initialize credential manager for IAM gRPC service
var credentialManager *credential.CredentialManager
var err error
credentialManager, err = credential.NewCredentialManagerWithDefaults("")
if err != nil {
glog.Warningf("Failed to initialize credential manager: %v", err)
} else {
glog.V(0).Infof("Initialized credential manager: %s", credentialManager.GetStoreName())
}
fs, nfs_err := weed_server.NewFilerServer(defaultMux, publicVolumeMux, &weed_server.FilerOption{
Masters: fo.masters,
FilerGroup: *fo.filerGroup,
@ -346,6 +361,7 @@ func (fo *FilerOptions) startFiler() {
DiskType: *fo.diskType,
AllowedOrigins: strings.Split(*fo.allowedOrigins, ","),
TusBasePath: *fo.tusBasePath,
CredentialManager: credentialManager,
})
if nfs_err != nil {
glog.Fatalf("Filer startup error: %v", nfs_err)
@ -389,6 +405,14 @@ func (fo *FilerOptions) startFiler() {
}
grpcS := pb.NewGrpcServer(security.LoadServerTLS(util.GetViper(), "grpc.filer"))
filer_pb.RegisterSeaweedFilerServer(grpcS, fs)
// Register IAM gRPC service if credential manager is available
if credentialManager != nil {
iamGrpcServer := weed_server.NewIamGrpcServer(credentialManager)
iam_pb.RegisterSeaweedIdentityAccessManagementServer(grpcS, iamGrpcServer)
glog.V(0).Info("Registered IAM gRPC service on filer")
}
reflection.Register(grpcS)
if grpcLocalL != nil {
go grpcS.Serve(grpcLocalL)

29
weed/credential/credential_manager.go

@ -6,6 +6,7 @@ import (
"strings"
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
"github.com/seaweedfs/seaweedfs/weed/s3api/policy_engine"
"github.com/seaweedfs/seaweedfs/weed/util"
)
@ -46,6 +47,14 @@ func (cm *CredentialManager) GetStore() CredentialStore {
return cm.store
}
// GetStoreName returns the name of the underlying credential store
func (cm *CredentialManager) GetStoreName() string {
if cm.store != nil {
return string(cm.store.GetName())
}
return ""
}
// LoadConfiguration loads the S3 API configuration
func (cm *CredentialManager) LoadConfiguration(ctx context.Context) (*iam_pb.S3ApiConfiguration, error) {
return cm.store.LoadConfiguration(ctx)
@ -96,6 +105,26 @@ func (cm *CredentialManager) DeleteAccessKey(ctx context.Context, username strin
return cm.store.DeleteAccessKey(ctx, username, accessKey)
}
// GetPolicies returns all policies
func (cm *CredentialManager) GetPolicies(ctx context.Context) (map[string]policy_engine.PolicyDocument, error) {
return cm.store.GetPolicies(ctx)
}
// PutPolicy creates or updates a policy
func (cm *CredentialManager) PutPolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error {
return cm.store.PutPolicy(ctx, name, document)
}
// DeletePolicy removes a policy
func (cm *CredentialManager) DeletePolicy(ctx context.Context, name string) error {
return cm.store.DeletePolicy(ctx, name)
}
// GetPolicy retrieves a policy by name
func (cm *CredentialManager) GetPolicy(ctx context.Context, name string) (*policy_engine.PolicyDocument, error) {
return cm.store.GetPolicy(ctx, name)
}
// Shutdown performs cleanup
func (cm *CredentialManager) Shutdown() {
if cm.store != nil {

8
weed/credential/credential_store.go

@ -25,6 +25,7 @@ const (
StoreTypeFilerEtc CredentialStoreTypeName = "filer_etc"
StoreTypeFilerMultiple CredentialStoreTypeName = "filer_multiple"
StoreTypePostgres CredentialStoreTypeName = "postgres"
StoreTypeGrpc CredentialStoreTypeName = "grpc"
)
// CredentialStore defines the interface for user credential storage and retrieval
@ -65,6 +66,13 @@ type CredentialStore interface {
// DeleteAccessKey removes an access key for a user
DeleteAccessKey(ctx context.Context, username string, accessKey string) error
// Policy Management
GetPolicies(ctx context.Context) (map[string]policy_engine.PolicyDocument, error)
// PutPolicy creates or replaces a policy document.
PutPolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error
DeletePolicy(ctx context.Context, name string) error
GetPolicy(ctx context.Context, name string) (*policy_engine.PolicyDocument, error)
// Shutdown performs cleanup when the store is being shut down
Shutdown()
}

5
weed/credential/filer_etc/filer_etc_policy.go

@ -98,6 +98,11 @@ func (store *FilerEtcStore) UpdatePolicy(ctx context.Context, name string, docum
})
}
// PutPolicy creates or updates an IAM policy in the filer
func (store *FilerEtcStore) PutPolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error {
return store.UpdatePolicy(ctx, name, document)
}
// DeletePolicy deletes an IAM policy from the filer
func (store *FilerEtcStore) DeletePolicy(ctx context.Context, name string) error {
return store.updatePolicies(ctx, func(policies map[string]policy_engine.PolicyDocument) {

8
weed/credential/filer_multiple/filer_multiple_store.go

@ -429,6 +429,14 @@ func (store *FilerMultipleStore) CreatePolicy(ctx context.Context, name string,
})
}
func (store *FilerMultipleStore) PutPolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error {
return store.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {
// We can just overwrite. The distinction between Create and Update in filer_multiple was just checking existence.
// Put implies "create or replace".
return store.savePolicy(ctx, client, name, document)
})
}
func (store *FilerMultipleStore) UpdatePolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error {
return store.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {
filename := name + ".json"

120
weed/credential/grpc/grpc_identity.go

@ -0,0 +1,120 @@
package grpc
import (
"context"
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
)
func (store *IamGrpcStore) LoadConfiguration(ctx context.Context) (*iam_pb.S3ApiConfiguration, error) {
var config *iam_pb.S3ApiConfiguration
err := store.withIamClient(func(client iam_pb.SeaweedIdentityAccessManagementClient) error {
resp, err := client.GetConfiguration(ctx, &iam_pb.GetConfigurationRequest{})
if err != nil {
return err
}
config = resp.Configuration
return nil
})
return config, err
}
func (store *IamGrpcStore) SaveConfiguration(ctx context.Context, config *iam_pb.S3ApiConfiguration) error {
return store.withIamClient(func(client iam_pb.SeaweedIdentityAccessManagementClient) error {
_, err := client.PutConfiguration(ctx, &iam_pb.PutConfigurationRequest{
Configuration: config,
})
return err
})
}
func (store *IamGrpcStore) CreateUser(ctx context.Context, identity *iam_pb.Identity) error {
return store.withIamClient(func(client iam_pb.SeaweedIdentityAccessManagementClient) error {
_, err := client.CreateUser(ctx, &iam_pb.CreateUserRequest{
Identity: identity,
})
return err
})
}
func (store *IamGrpcStore) GetUser(ctx context.Context, username string) (*iam_pb.Identity, error) {
var identity *iam_pb.Identity
err := store.withIamClient(func(client iam_pb.SeaweedIdentityAccessManagementClient) error {
resp, err := client.GetUser(ctx, &iam_pb.GetUserRequest{
Username: username,
})
if err != nil {
return err
}
identity = resp.Identity
return nil
})
return identity, err
}
func (store *IamGrpcStore) UpdateUser(ctx context.Context, username string, identity *iam_pb.Identity) error {
return store.withIamClient(func(client iam_pb.SeaweedIdentityAccessManagementClient) error {
_, err := client.UpdateUser(ctx, &iam_pb.UpdateUserRequest{
Username: username,
Identity: identity,
})
return err
})
}
func (store *IamGrpcStore) DeleteUser(ctx context.Context, username string) error {
return store.withIamClient(func(client iam_pb.SeaweedIdentityAccessManagementClient) error {
_, err := client.DeleteUser(ctx, &iam_pb.DeleteUserRequest{
Username: username,
})
return err
})
}
func (store *IamGrpcStore) ListUsers(ctx context.Context) ([]string, error) {
var usernames []string
err := store.withIamClient(func(client iam_pb.SeaweedIdentityAccessManagementClient) error {
resp, err := client.ListUsers(ctx, &iam_pb.ListUsersRequest{})
if err != nil {
return err
}
usernames = resp.Usernames
return nil
})
return usernames, err
}
func (store *IamGrpcStore) GetUserByAccessKey(ctx context.Context, accessKey string) (*iam_pb.Identity, error) {
var identity *iam_pb.Identity
err := store.withIamClient(func(client iam_pb.SeaweedIdentityAccessManagementClient) error {
resp, err := client.GetUserByAccessKey(ctx, &iam_pb.GetUserByAccessKeyRequest{
AccessKey: accessKey,
})
if err != nil {
return err
}
identity = resp.Identity
return nil
})
return identity, err
}
func (store *IamGrpcStore) CreateAccessKey(ctx context.Context, username string, credential *iam_pb.Credential) error {
return store.withIamClient(func(client iam_pb.SeaweedIdentityAccessManagementClient) error {
_, err := client.CreateAccessKey(ctx, &iam_pb.CreateAccessKeyRequest{
Username: username,
Credential: credential,
})
return err
})
}
func (store *IamGrpcStore) DeleteAccessKey(ctx context.Context, username string, accessKey string) error {
return store.withIamClient(func(client iam_pb.SeaweedIdentityAccessManagementClient) error {
_, err := client.DeleteAccessKey(ctx, &iam_pb.DeleteAccessKeyRequest{
Username: username,
AccessKey: accessKey,
})
return err
})
}

69
weed/credential/grpc/grpc_policy.go

@ -0,0 +1,69 @@
package grpc
import (
"context"
"encoding/json"
"fmt"
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
"github.com/seaweedfs/seaweedfs/weed/s3api/policy_engine"
)
func (store *IamGrpcStore) GetPolicies(ctx context.Context) (map[string]policy_engine.PolicyDocument, error) {
policies := make(map[string]policy_engine.PolicyDocument)
err := store.withIamClient(func(client iam_pb.SeaweedIdentityAccessManagementClient) error {
resp, err := client.ListPolicies(ctx, &iam_pb.ListPoliciesRequest{})
if err != nil {
return err
}
for _, p := range resp.Policies {
var doc policy_engine.PolicyDocument
if err := json.Unmarshal([]byte(p.Content), &doc); err != nil {
return fmt.Errorf("failed to unmarshal policy %s: %v", p.Name, err)
}
policies[p.Name] = doc
}
return nil
})
return policies, err
}
func (store *IamGrpcStore) PutPolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error {
content, err := json.Marshal(document)
if err != nil {
return err
}
return store.withIamClient(func(client iam_pb.SeaweedIdentityAccessManagementClient) error {
_, err := client.PutPolicy(ctx, &iam_pb.PutPolicyRequest{
Name: name,
Content: string(content),
})
return err
})
}
func (store *IamGrpcStore) DeletePolicy(ctx context.Context, name string) error {
return store.withIamClient(func(client iam_pb.SeaweedIdentityAccessManagementClient) error {
_, err := client.DeletePolicy(ctx, &iam_pb.DeletePolicyRequest{
Name: name,
})
return err
})
}
func (store *IamGrpcStore) GetPolicy(ctx context.Context, name string) (*policy_engine.PolicyDocument, error) {
var doc policy_engine.PolicyDocument
err := store.withIamClient(func(client iam_pb.SeaweedIdentityAccessManagementClient) error {
resp, err := client.GetPolicy(ctx, &iam_pb.GetPolicyRequest{
Name: name,
})
if err != nil {
return err
}
return json.Unmarshal([]byte(resp.Content), &doc)
})
if err != nil {
return nil, err
}
return &doc, nil
}

72
weed/credential/grpc/grpc_store.go

@ -0,0 +1,72 @@
package grpc
import (
"fmt"
"sync"
"github.com/seaweedfs/seaweedfs/weed/credential"
"github.com/seaweedfs/seaweedfs/weed/pb"
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
"github.com/seaweedfs/seaweedfs/weed/util"
"google.golang.org/grpc"
)
func init() {
credential.Stores = append(credential.Stores, &IamGrpcStore{})
}
// IamGrpcStore implements CredentialStore using SeaweedFS IAM gRPC service
type IamGrpcStore struct {
filerAddressFunc func() pb.ServerAddress // Function to get current active filer
grpcDialOption grpc.DialOption
mu sync.RWMutex // Protects filerAddressFunc and grpcDialOption
}
func (store *IamGrpcStore) GetName() credential.CredentialStoreTypeName {
return credential.StoreTypeGrpc
}
func (store *IamGrpcStore) Initialize(configuration util.Configuration, prefix string) error {
if configuration != nil {
filerAddr := configuration.GetString(prefix + "filer")
if filerAddr != "" {
store.mu.Lock()
store.filerAddressFunc = func() pb.ServerAddress {
return pb.ServerAddress(filerAddr)
}
store.mu.Unlock()
}
}
return nil
}
func (store *IamGrpcStore) SetFilerAddressFunc(getFiler func() pb.ServerAddress, grpcDialOption grpc.DialOption) {
store.mu.Lock()
defer store.mu.Unlock()
store.filerAddressFunc = getFiler
store.grpcDialOption = grpcDialOption
}
func (store *IamGrpcStore) withIamClient(fn func(client iam_pb.SeaweedIdentityAccessManagementClient) error) error {
store.mu.RLock()
if store.filerAddressFunc == nil {
store.mu.RUnlock()
return fmt.Errorf("iam_grpc: filer not yet available")
}
filerAddress := store.filerAddressFunc()
dialOption := store.grpcDialOption
store.mu.RUnlock()
if filerAddress == "" {
return fmt.Errorf("iam_grpc: no filer discovered yet")
}
return pb.WithGrpcClient(false, 0, func(conn *grpc.ClientConn) error {
client := iam_pb.NewSeaweedIdentityAccessManagementClient(conn)
return fn(client)
}, filerAddress.ToGrpcAddress(), false, dialOption)
}
func (store *IamGrpcStore) Shutdown() {
}

13
weed/credential/memory/memory_policy.go

@ -63,6 +63,19 @@ func (store *MemoryStore) UpdatePolicy(ctx context.Context, name string, documen
return nil
}
// PutPolicy creates or updates an IAM policy in memory
func (store *MemoryStore) PutPolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error {
store.mu.Lock()
defer store.mu.Unlock()
if !store.initialized {
return fmt.Errorf("store not initialized")
}
store.policies[name] = document
return nil
}
// DeletePolicy deletes an IAM policy from memory
func (store *MemoryStore) DeletePolicy(ctx context.Context, name string) error {
store.mu.Lock()

5
weed/credential/postgres/postgres_policy.go

@ -62,6 +62,11 @@ func (store *PostgresStore) CreatePolicy(ctx context.Context, name string, docum
return nil
}
// PutPolicy creates or updates an IAM policy in PostgreSQL
func (store *PostgresStore) PutPolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error {
return store.CreatePolicy(ctx, name, document)
}
// UpdatePolicy updates an existing IAM policy in PostgreSQL
func (store *PostgresStore) UpdatePolicy(ctx context.Context, name string, document policy_engine.PolicyDocument) error {
if !store.configured {

7
weed/pb/filer_pb/filer.pb.go

@ -1,18 +1,17 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v6.33.1
// protoc v6.33.4
// source: filer.proto
package filer_pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (

3
weed/pb/filer_pb/filer_grpc.pb.go

@ -1,14 +1,13 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v6.33.1
// - protoc v6.33.4
// source: filer.proto
package filer_pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"

146
weed/pb/iam.proto

@ -9,7 +9,111 @@ option java_outer_classname = "IamProto";
//////////////////////////////////////////////////
service SeaweedIdentityAccessManagement {
// Configuration Management
rpc GetConfiguration (GetConfigurationRequest) returns (GetConfigurationResponse);
rpc PutConfiguration (PutConfigurationRequest) returns (PutConfigurationResponse);
// User Management
rpc CreateUser (CreateUserRequest) returns (CreateUserResponse);
rpc GetUser (GetUserRequest) returns (GetUserResponse);
rpc UpdateUser (UpdateUserRequest) returns (UpdateUserResponse);
rpc DeleteUser (DeleteUserRequest) returns (DeleteUserResponse);
rpc ListUsers (ListUsersRequest) returns (ListUsersResponse);
// Access Key Management
rpc CreateAccessKey (CreateAccessKeyRequest) returns (CreateAccessKeyResponse);
rpc DeleteAccessKey (DeleteAccessKeyRequest) returns (DeleteAccessKeyResponse);
rpc GetUserByAccessKey (GetUserByAccessKeyRequest) returns (GetUserByAccessKeyResponse);
// Policy Management
rpc PutPolicy (PutPolicyRequest) returns (PutPolicyResponse);
rpc GetPolicy (GetPolicyRequest) returns (GetPolicyResponse);
rpc ListPolicies (ListPoliciesRequest) returns (ListPoliciesResponse);
rpc DeletePolicy (DeletePolicyRequest) returns (DeletePolicyResponse);
}
//////////////////////////////////////////////////
// Configuration Management Messages
message GetConfigurationRequest {
}
message GetConfigurationResponse {
S3ApiConfiguration configuration = 1;
}
message PutConfigurationRequest {
S3ApiConfiguration configuration = 1;
}
message PutConfigurationResponse {
}
//////////////////////////////////////////////////
// User Management Messages
message CreateUserRequest {
Identity identity = 1;
}
message CreateUserResponse {
}
message GetUserRequest {
string username = 1;
}
message GetUserResponse {
Identity identity = 1;
}
message UpdateUserRequest {
string username = 1;
Identity identity = 2;
}
message UpdateUserResponse {
}
message DeleteUserRequest {
string username = 1;
}
message DeleteUserResponse {
}
message ListUsersRequest {
}
message ListUsersResponse {
repeated string usernames = 1;
}
//////////////////////////////////////////////////
// Access Key Management Messages
message CreateAccessKeyRequest {
string username = 1;
Credential credential = 2;
}
message CreateAccessKeyResponse {
}
message DeleteAccessKeyRequest {
string username = 1;
string access_key = 2;
}
message DeleteAccessKeyResponse {
}
message GetUserByAccessKeyRequest {
string access_key = 1;
}
message GetUserByAccessKeyResponse {
Identity identity = 1;
}
//////////////////////////////////////////////////
@ -18,6 +122,7 @@ message S3ApiConfiguration {
repeated Identity identities = 1;
repeated Account accounts = 2;
repeated ServiceAccount service_accounts = 3;
repeated Policy policies = 4;
}
message Identity {
@ -56,21 +161,38 @@ message ServiceAccount {
string created_by = 9; // Who created this service account
}
/*
message Policy {
repeated Statement statements = 1;
message PutPolicyRequest {
string name = 1;
string content = 2;
}
message Statement {
repeated Action action = 1;
repeated Resource resource = 2;
message PutPolicyResponse {
}
message Action {
string action = 1;
message GetPolicyRequest {
string name = 1;
}
message GetPolicyResponse {
string name = 1;
string content = 2;
}
message ListPoliciesRequest {
}
message ListPoliciesResponse {
repeated Policy policies = 1;
}
message Resource {
string bucket = 1;
// string path = 2;
message DeletePolicyRequest {
string name = 1;
}
message DeletePolicyResponse {
}
message Policy {
string name = 1;
string content = 2; // JSON content of the policy
}
*/

1836
weed/pb/iam_pb/iam.pb.go
File diff suppressed because it is too large
View File

555
weed/pb/iam_pb/iam_grpc.pb.go

@ -1,13 +1,16 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v6.33.1
// - protoc v6.33.4
// source: iam.proto
package iam_pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
@ -15,10 +18,45 @@ import (
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
SeaweedIdentityAccessManagement_GetConfiguration_FullMethodName = "/iam_pb.SeaweedIdentityAccessManagement/GetConfiguration"
SeaweedIdentityAccessManagement_PutConfiguration_FullMethodName = "/iam_pb.SeaweedIdentityAccessManagement/PutConfiguration"
SeaweedIdentityAccessManagement_CreateUser_FullMethodName = "/iam_pb.SeaweedIdentityAccessManagement/CreateUser"
SeaweedIdentityAccessManagement_GetUser_FullMethodName = "/iam_pb.SeaweedIdentityAccessManagement/GetUser"
SeaweedIdentityAccessManagement_UpdateUser_FullMethodName = "/iam_pb.SeaweedIdentityAccessManagement/UpdateUser"
SeaweedIdentityAccessManagement_DeleteUser_FullMethodName = "/iam_pb.SeaweedIdentityAccessManagement/DeleteUser"
SeaweedIdentityAccessManagement_ListUsers_FullMethodName = "/iam_pb.SeaweedIdentityAccessManagement/ListUsers"
SeaweedIdentityAccessManagement_CreateAccessKey_FullMethodName = "/iam_pb.SeaweedIdentityAccessManagement/CreateAccessKey"
SeaweedIdentityAccessManagement_DeleteAccessKey_FullMethodName = "/iam_pb.SeaweedIdentityAccessManagement/DeleteAccessKey"
SeaweedIdentityAccessManagement_GetUserByAccessKey_FullMethodName = "/iam_pb.SeaweedIdentityAccessManagement/GetUserByAccessKey"
SeaweedIdentityAccessManagement_PutPolicy_FullMethodName = "/iam_pb.SeaweedIdentityAccessManagement/PutPolicy"
SeaweedIdentityAccessManagement_GetPolicy_FullMethodName = "/iam_pb.SeaweedIdentityAccessManagement/GetPolicy"
SeaweedIdentityAccessManagement_ListPolicies_FullMethodName = "/iam_pb.SeaweedIdentityAccessManagement/ListPolicies"
SeaweedIdentityAccessManagement_DeletePolicy_FullMethodName = "/iam_pb.SeaweedIdentityAccessManagement/DeletePolicy"
)
// SeaweedIdentityAccessManagementClient is the client API for SeaweedIdentityAccessManagement 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.
type SeaweedIdentityAccessManagementClient interface {
// Configuration Management
GetConfiguration(ctx context.Context, in *GetConfigurationRequest, opts ...grpc.CallOption) (*GetConfigurationResponse, error)
PutConfiguration(ctx context.Context, in *PutConfigurationRequest, opts ...grpc.CallOption) (*PutConfigurationResponse, error)
// User Management
CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*CreateUserResponse, error)
GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error)
UpdateUser(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*UpdateUserResponse, error)
DeleteUser(ctx context.Context, in *DeleteUserRequest, opts ...grpc.CallOption) (*DeleteUserResponse, error)
ListUsers(ctx context.Context, in *ListUsersRequest, opts ...grpc.CallOption) (*ListUsersResponse, error)
// Access Key Management
CreateAccessKey(ctx context.Context, in *CreateAccessKeyRequest, opts ...grpc.CallOption) (*CreateAccessKeyResponse, error)
DeleteAccessKey(ctx context.Context, in *DeleteAccessKeyRequest, opts ...grpc.CallOption) (*DeleteAccessKeyResponse, error)
GetUserByAccessKey(ctx context.Context, in *GetUserByAccessKeyRequest, opts ...grpc.CallOption) (*GetUserByAccessKeyResponse, error)
// Policy Management
PutPolicy(ctx context.Context, in *PutPolicyRequest, opts ...grpc.CallOption) (*PutPolicyResponse, error)
GetPolicy(ctx context.Context, in *GetPolicyRequest, opts ...grpc.CallOption) (*GetPolicyResponse, error)
ListPolicies(ctx context.Context, in *ListPoliciesRequest, opts ...grpc.CallOption) (*ListPoliciesResponse, error)
DeletePolicy(ctx context.Context, in *DeletePolicyRequest, opts ...grpc.CallOption) (*DeletePolicyResponse, error)
}
type seaweedIdentityAccessManagementClient struct {
@ -29,10 +67,168 @@ func NewSeaweedIdentityAccessManagementClient(cc grpc.ClientConnInterface) Seawe
return &seaweedIdentityAccessManagementClient{cc}
}
func (c *seaweedIdentityAccessManagementClient) GetConfiguration(ctx context.Context, in *GetConfigurationRequest, opts ...grpc.CallOption) (*GetConfigurationResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetConfigurationResponse)
err := c.cc.Invoke(ctx, SeaweedIdentityAccessManagement_GetConfiguration_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *seaweedIdentityAccessManagementClient) PutConfiguration(ctx context.Context, in *PutConfigurationRequest, opts ...grpc.CallOption) (*PutConfigurationResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(PutConfigurationResponse)
err := c.cc.Invoke(ctx, SeaweedIdentityAccessManagement_PutConfiguration_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *seaweedIdentityAccessManagementClient) CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*CreateUserResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreateUserResponse)
err := c.cc.Invoke(ctx, SeaweedIdentityAccessManagement_CreateUser_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *seaweedIdentityAccessManagementClient) GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetUserResponse)
err := c.cc.Invoke(ctx, SeaweedIdentityAccessManagement_GetUser_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *seaweedIdentityAccessManagementClient) UpdateUser(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*UpdateUserResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(UpdateUserResponse)
err := c.cc.Invoke(ctx, SeaweedIdentityAccessManagement_UpdateUser_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *seaweedIdentityAccessManagementClient) DeleteUser(ctx context.Context, in *DeleteUserRequest, opts ...grpc.CallOption) (*DeleteUserResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeleteUserResponse)
err := c.cc.Invoke(ctx, SeaweedIdentityAccessManagement_DeleteUser_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *seaweedIdentityAccessManagementClient) ListUsers(ctx context.Context, in *ListUsersRequest, opts ...grpc.CallOption) (*ListUsersResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListUsersResponse)
err := c.cc.Invoke(ctx, SeaweedIdentityAccessManagement_ListUsers_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *seaweedIdentityAccessManagementClient) CreateAccessKey(ctx context.Context, in *CreateAccessKeyRequest, opts ...grpc.CallOption) (*CreateAccessKeyResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreateAccessKeyResponse)
err := c.cc.Invoke(ctx, SeaweedIdentityAccessManagement_CreateAccessKey_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *seaweedIdentityAccessManagementClient) DeleteAccessKey(ctx context.Context, in *DeleteAccessKeyRequest, opts ...grpc.CallOption) (*DeleteAccessKeyResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeleteAccessKeyResponse)
err := c.cc.Invoke(ctx, SeaweedIdentityAccessManagement_DeleteAccessKey_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *seaweedIdentityAccessManagementClient) GetUserByAccessKey(ctx context.Context, in *GetUserByAccessKeyRequest, opts ...grpc.CallOption) (*GetUserByAccessKeyResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetUserByAccessKeyResponse)
err := c.cc.Invoke(ctx, SeaweedIdentityAccessManagement_GetUserByAccessKey_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *seaweedIdentityAccessManagementClient) PutPolicy(ctx context.Context, in *PutPolicyRequest, opts ...grpc.CallOption) (*PutPolicyResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(PutPolicyResponse)
err := c.cc.Invoke(ctx, SeaweedIdentityAccessManagement_PutPolicy_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *seaweedIdentityAccessManagementClient) GetPolicy(ctx context.Context, in *GetPolicyRequest, opts ...grpc.CallOption) (*GetPolicyResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetPolicyResponse)
err := c.cc.Invoke(ctx, SeaweedIdentityAccessManagement_GetPolicy_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *seaweedIdentityAccessManagementClient) ListPolicies(ctx context.Context, in *ListPoliciesRequest, opts ...grpc.CallOption) (*ListPoliciesResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListPoliciesResponse)
err := c.cc.Invoke(ctx, SeaweedIdentityAccessManagement_ListPolicies_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *seaweedIdentityAccessManagementClient) DeletePolicy(ctx context.Context, in *DeletePolicyRequest, opts ...grpc.CallOption) (*DeletePolicyResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeletePolicyResponse)
err := c.cc.Invoke(ctx, SeaweedIdentityAccessManagement_DeletePolicy_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// SeaweedIdentityAccessManagementServer is the server API for SeaweedIdentityAccessManagement service.
// All implementations must embed UnimplementedSeaweedIdentityAccessManagementServer
// for forward compatibility.
type SeaweedIdentityAccessManagementServer interface {
// Configuration Management
GetConfiguration(context.Context, *GetConfigurationRequest) (*GetConfigurationResponse, error)
PutConfiguration(context.Context, *PutConfigurationRequest) (*PutConfigurationResponse, error)
// User Management
CreateUser(context.Context, *CreateUserRequest) (*CreateUserResponse, error)
GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error)
UpdateUser(context.Context, *UpdateUserRequest) (*UpdateUserResponse, error)
DeleteUser(context.Context, *DeleteUserRequest) (*DeleteUserResponse, error)
ListUsers(context.Context, *ListUsersRequest) (*ListUsersResponse, error)
// Access Key Management
CreateAccessKey(context.Context, *CreateAccessKeyRequest) (*CreateAccessKeyResponse, error)
DeleteAccessKey(context.Context, *DeleteAccessKeyRequest) (*DeleteAccessKeyResponse, error)
GetUserByAccessKey(context.Context, *GetUserByAccessKeyRequest) (*GetUserByAccessKeyResponse, error)
// Policy Management
PutPolicy(context.Context, *PutPolicyRequest) (*PutPolicyResponse, error)
GetPolicy(context.Context, *GetPolicyRequest) (*GetPolicyResponse, error)
ListPolicies(context.Context, *ListPoliciesRequest) (*ListPoliciesResponse, error)
DeletePolicy(context.Context, *DeletePolicyRequest) (*DeletePolicyResponse, error)
mustEmbedUnimplementedSeaweedIdentityAccessManagementServer()
}
@ -43,6 +239,48 @@ type SeaweedIdentityAccessManagementServer interface {
// pointer dereference when methods are called.
type UnimplementedSeaweedIdentityAccessManagementServer struct{}
func (UnimplementedSeaweedIdentityAccessManagementServer) GetConfiguration(context.Context, *GetConfigurationRequest) (*GetConfigurationResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetConfiguration not implemented")
}
func (UnimplementedSeaweedIdentityAccessManagementServer) PutConfiguration(context.Context, *PutConfigurationRequest) (*PutConfigurationResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method PutConfiguration not implemented")
}
func (UnimplementedSeaweedIdentityAccessManagementServer) CreateUser(context.Context, *CreateUserRequest) (*CreateUserResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateUser not implemented")
}
func (UnimplementedSeaweedIdentityAccessManagementServer) GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUser not implemented")
}
func (UnimplementedSeaweedIdentityAccessManagementServer) UpdateUser(context.Context, *UpdateUserRequest) (*UpdateUserResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateUser not implemented")
}
func (UnimplementedSeaweedIdentityAccessManagementServer) DeleteUser(context.Context, *DeleteUserRequest) (*DeleteUserResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteUser not implemented")
}
func (UnimplementedSeaweedIdentityAccessManagementServer) ListUsers(context.Context, *ListUsersRequest) (*ListUsersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListUsers not implemented")
}
func (UnimplementedSeaweedIdentityAccessManagementServer) CreateAccessKey(context.Context, *CreateAccessKeyRequest) (*CreateAccessKeyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateAccessKey not implemented")
}
func (UnimplementedSeaweedIdentityAccessManagementServer) DeleteAccessKey(context.Context, *DeleteAccessKeyRequest) (*DeleteAccessKeyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteAccessKey not implemented")
}
func (UnimplementedSeaweedIdentityAccessManagementServer) GetUserByAccessKey(context.Context, *GetUserByAccessKeyRequest) (*GetUserByAccessKeyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUserByAccessKey not implemented")
}
func (UnimplementedSeaweedIdentityAccessManagementServer) PutPolicy(context.Context, *PutPolicyRequest) (*PutPolicyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method PutPolicy not implemented")
}
func (UnimplementedSeaweedIdentityAccessManagementServer) GetPolicy(context.Context, *GetPolicyRequest) (*GetPolicyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetPolicy not implemented")
}
func (UnimplementedSeaweedIdentityAccessManagementServer) ListPolicies(context.Context, *ListPoliciesRequest) (*ListPoliciesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListPolicies not implemented")
}
func (UnimplementedSeaweedIdentityAccessManagementServer) DeletePolicy(context.Context, *DeletePolicyRequest) (*DeletePolicyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeletePolicy not implemented")
}
func (UnimplementedSeaweedIdentityAccessManagementServer) mustEmbedUnimplementedSeaweedIdentityAccessManagementServer() {
}
func (UnimplementedSeaweedIdentityAccessManagementServer) testEmbeddedByValue() {}
@ -65,13 +303,322 @@ func RegisterSeaweedIdentityAccessManagementServer(s grpc.ServiceRegistrar, srv
s.RegisterService(&SeaweedIdentityAccessManagement_ServiceDesc, srv)
}
func _SeaweedIdentityAccessManagement_GetConfiguration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetConfigurationRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SeaweedIdentityAccessManagementServer).GetConfiguration(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SeaweedIdentityAccessManagement_GetConfiguration_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SeaweedIdentityAccessManagementServer).GetConfiguration(ctx, req.(*GetConfigurationRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SeaweedIdentityAccessManagement_PutConfiguration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PutConfigurationRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SeaweedIdentityAccessManagementServer).PutConfiguration(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SeaweedIdentityAccessManagement_PutConfiguration_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SeaweedIdentityAccessManagementServer).PutConfiguration(ctx, req.(*PutConfigurationRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SeaweedIdentityAccessManagement_CreateUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateUserRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SeaweedIdentityAccessManagementServer).CreateUser(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SeaweedIdentityAccessManagement_CreateUser_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SeaweedIdentityAccessManagementServer).CreateUser(ctx, req.(*CreateUserRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SeaweedIdentityAccessManagement_GetUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetUserRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SeaweedIdentityAccessManagementServer).GetUser(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SeaweedIdentityAccessManagement_GetUser_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SeaweedIdentityAccessManagementServer).GetUser(ctx, req.(*GetUserRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SeaweedIdentityAccessManagement_UpdateUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateUserRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SeaweedIdentityAccessManagementServer).UpdateUser(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SeaweedIdentityAccessManagement_UpdateUser_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SeaweedIdentityAccessManagementServer).UpdateUser(ctx, req.(*UpdateUserRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SeaweedIdentityAccessManagement_DeleteUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteUserRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SeaweedIdentityAccessManagementServer).DeleteUser(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SeaweedIdentityAccessManagement_DeleteUser_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SeaweedIdentityAccessManagementServer).DeleteUser(ctx, req.(*DeleteUserRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SeaweedIdentityAccessManagement_ListUsers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListUsersRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SeaweedIdentityAccessManagementServer).ListUsers(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SeaweedIdentityAccessManagement_ListUsers_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SeaweedIdentityAccessManagementServer).ListUsers(ctx, req.(*ListUsersRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SeaweedIdentityAccessManagement_CreateAccessKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateAccessKeyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SeaweedIdentityAccessManagementServer).CreateAccessKey(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SeaweedIdentityAccessManagement_CreateAccessKey_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SeaweedIdentityAccessManagementServer).CreateAccessKey(ctx, req.(*CreateAccessKeyRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SeaweedIdentityAccessManagement_DeleteAccessKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteAccessKeyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SeaweedIdentityAccessManagementServer).DeleteAccessKey(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SeaweedIdentityAccessManagement_DeleteAccessKey_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SeaweedIdentityAccessManagementServer).DeleteAccessKey(ctx, req.(*DeleteAccessKeyRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SeaweedIdentityAccessManagement_GetUserByAccessKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetUserByAccessKeyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SeaweedIdentityAccessManagementServer).GetUserByAccessKey(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SeaweedIdentityAccessManagement_GetUserByAccessKey_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SeaweedIdentityAccessManagementServer).GetUserByAccessKey(ctx, req.(*GetUserByAccessKeyRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SeaweedIdentityAccessManagement_PutPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PutPolicyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SeaweedIdentityAccessManagementServer).PutPolicy(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SeaweedIdentityAccessManagement_PutPolicy_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SeaweedIdentityAccessManagementServer).PutPolicy(ctx, req.(*PutPolicyRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SeaweedIdentityAccessManagement_GetPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetPolicyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SeaweedIdentityAccessManagementServer).GetPolicy(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SeaweedIdentityAccessManagement_GetPolicy_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SeaweedIdentityAccessManagementServer).GetPolicy(ctx, req.(*GetPolicyRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SeaweedIdentityAccessManagement_ListPolicies_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListPoliciesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SeaweedIdentityAccessManagementServer).ListPolicies(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SeaweedIdentityAccessManagement_ListPolicies_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SeaweedIdentityAccessManagementServer).ListPolicies(ctx, req.(*ListPoliciesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SeaweedIdentityAccessManagement_DeletePolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeletePolicyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SeaweedIdentityAccessManagementServer).DeletePolicy(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: SeaweedIdentityAccessManagement_DeletePolicy_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SeaweedIdentityAccessManagementServer).DeletePolicy(ctx, req.(*DeletePolicyRequest))
}
return interceptor(ctx, in, info, handler)
}
// SeaweedIdentityAccessManagement_ServiceDesc is the grpc.ServiceDesc for SeaweedIdentityAccessManagement service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var SeaweedIdentityAccessManagement_ServiceDesc = grpc.ServiceDesc{
ServiceName: "iam_pb.SeaweedIdentityAccessManagement",
HandlerType: (*SeaweedIdentityAccessManagementServer)(nil),
Methods: []grpc.MethodDesc{},
Streams: []grpc.StreamDesc{},
Metadata: "iam.proto",
Methods: []grpc.MethodDesc{
{
MethodName: "GetConfiguration",
Handler: _SeaweedIdentityAccessManagement_GetConfiguration_Handler,
},
{
MethodName: "PutConfiguration",
Handler: _SeaweedIdentityAccessManagement_PutConfiguration_Handler,
},
{
MethodName: "CreateUser",
Handler: _SeaweedIdentityAccessManagement_CreateUser_Handler,
},
{
MethodName: "GetUser",
Handler: _SeaweedIdentityAccessManagement_GetUser_Handler,
},
{
MethodName: "UpdateUser",
Handler: _SeaweedIdentityAccessManagement_UpdateUser_Handler,
},
{
MethodName: "DeleteUser",
Handler: _SeaweedIdentityAccessManagement_DeleteUser_Handler,
},
{
MethodName: "ListUsers",
Handler: _SeaweedIdentityAccessManagement_ListUsers_Handler,
},
{
MethodName: "CreateAccessKey",
Handler: _SeaweedIdentityAccessManagement_CreateAccessKey_Handler,
},
{
MethodName: "DeleteAccessKey",
Handler: _SeaweedIdentityAccessManagement_DeleteAccessKey_Handler,
},
{
MethodName: "GetUserByAccessKey",
Handler: _SeaweedIdentityAccessManagement_GetUserByAccessKey_Handler,
},
{
MethodName: "PutPolicy",
Handler: _SeaweedIdentityAccessManagement_PutPolicy_Handler,
},
{
MethodName: "GetPolicy",
Handler: _SeaweedIdentityAccessManagement_GetPolicy_Handler,
},
{
MethodName: "ListPolicies",
Handler: _SeaweedIdentityAccessManagement_ListPolicies_Handler,
},
{
MethodName: "DeletePolicy",
Handler: _SeaweedIdentityAccessManagement_DeletePolicy_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "iam.proto",
}

2
weed/pb/master_pb/master.pb.go

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v6.33.1
// protoc v6.33.4
// source: master.proto
package master_pb

2
weed/pb/master_pb/master_grpc.pb.go

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v6.33.1
// - protoc v6.33.4
// source: master.proto
package master_pb

7
weed/pb/mount_pb/mount.pb.go

@ -1,18 +1,17 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v6.33.1
// protoc v6.33.4
// source: mount.proto
package mount_pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (

3
weed/pb/mount_pb/mount_grpc.pb.go

@ -1,14 +1,13 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v6.33.1
// - protoc v6.33.4
// source: mount.proto
package mount_pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"

9
weed/pb/mq_agent_pb/mq_agent.pb.go

@ -1,19 +1,18 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v6.33.1
// protoc v6.33.4
// source: mq_agent.proto
package mq_agent_pb
import (
reflect "reflect"
sync "sync"
unsafe "unsafe"
schema_pb "github.com/seaweedfs/seaweedfs/weed/pb/schema_pb"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (

3
weed/pb/mq_agent_pb/mq_agent_grpc.pb.go

@ -1,14 +1,13 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v6.33.1
// - protoc v6.33.4
// source: mq_agent.proto
package mq_agent_pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"

9
weed/pb/mq_pb/mq_broker.pb.go

@ -1,20 +1,19 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v6.33.1
// protoc v6.33.4
// source: mq_broker.proto
package mq_pb
import (
reflect "reflect"
sync "sync"
unsafe "unsafe"
filer_pb "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
schema_pb "github.com/seaweedfs/seaweedfs/weed/pb/schema_pb"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (

3
weed/pb/mq_pb/mq_broker_grpc.pb.go

@ -1,14 +1,13 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v6.33.1
// - protoc v6.33.4
// source: mq_broker.proto
package mq_pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"

7
weed/pb/remote_pb/remote.pb.go

@ -1,18 +1,17 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v6.33.1
// protoc v6.33.4
// source: remote.proto
package remote_pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (

7
weed/pb/s3_pb/s3.pb.go

@ -1,18 +1,17 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v6.33.1
// protoc v6.33.4
// source: s3.proto
package s3_pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (

3
weed/pb/s3_pb/s3_grpc.pb.go

@ -1,14 +1,13 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v6.33.1
// - protoc v6.33.4
// source: s3.proto
package s3_pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"

7
weed/pb/schema_pb/mq_schema.pb.go

@ -1,18 +1,17 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v6.33.1
// protoc v6.33.4
// source: mq_schema.proto
package schema_pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (

9
weed/pb/volume_server_pb/volume_server.pb.go

@ -1,19 +1,18 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v6.33.1
// protoc v6.33.4
// source: volume_server.proto
package volume_server_pb
import (
reflect "reflect"
sync "sync"
unsafe "unsafe"
remote_pb "github.com/seaweedfs/seaweedfs/weed/pb/remote_pb"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (

782
weed/pb/volume_server_pb/volume_server_grpc.pb.go
File diff suppressed because it is too large
View File

7
weed/pb/worker_pb/worker.pb.go

@ -1,18 +1,17 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v6.33.1
// protoc v6.33.4
// source: worker.proto
package worker_pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (

3
weed/pb/worker_pb/worker_grpc.pb.go

@ -1,14 +1,13 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v6.33.1
// - protoc v6.33.4
// source: worker.proto
package worker_pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"

6
weed/server/filer_server.go

@ -10,6 +10,7 @@ import (
"sync/atomic"
"time"
"github.com/seaweedfs/seaweedfs/weed/credential"
"github.com/seaweedfs/seaweedfs/weed/stats"
"golang.org/x/sync/singleflight"
@ -81,6 +82,7 @@ type FilerOption struct {
AllowedOrigins []string
ExposeDirectoryData bool
TusBasePath string
CredentialManager *credential.CredentialManager
}
type FilerServer struct {
@ -112,6 +114,9 @@ type FilerServer struct {
// deduplicates concurrent remote object caching operations
remoteCacheGroup singleflight.Group
// credential manager for IAM operations
CredentialManager *credential.CredentialManager
}
func NewFilerServer(defaultMux, readonlyMux *http.ServeMux, option *FilerOption) (fs *FilerServer, err error) {
@ -148,6 +153,7 @@ func NewFilerServer(defaultMux, readonlyMux *http.ServeMux, option *FilerOption)
grpcDialOption: security.LoadClientTLS(util.GetViper(), "grpc.filer"),
knownListeners: make(map[int32]int32),
inFlightDataLimitCond: sync.NewCond(new(sync.Mutex)),
CredentialManager: option.CredentialManager,
}
fs.listenersCond = sync.NewCond(&fs.listenersLock)

351
weed/server/filer_server_handlers_iam_grpc.go

@ -0,0 +1,351 @@
package weed_server
import (
"context"
"encoding/json"
"github.com/seaweedfs/seaweedfs/weed/credential"
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
"github.com/seaweedfs/seaweedfs/weed/s3api/policy_engine"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// IamGrpcServer implements the IAM gRPC service on the filer
type IamGrpcServer struct {
iam_pb.UnimplementedSeaweedIdentityAccessManagementServer
credentialManager *credential.CredentialManager
}
// NewIamGrpcServer creates a new IAM gRPC server
func NewIamGrpcServer(credentialManager *credential.CredentialManager) *IamGrpcServer {
return &IamGrpcServer{
credentialManager: credentialManager,
}
}
//////////////////////////////////////////////////
// Configuration Management
func (s *IamGrpcServer) GetConfiguration(ctx context.Context, req *iam_pb.GetConfigurationRequest) (*iam_pb.GetConfigurationResponse, error) {
glog.V(4).Infof("GetConfiguration")
if s.credentialManager == nil {
return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured")
}
config, err := s.credentialManager.LoadConfiguration(ctx)
if err != nil {
glog.Errorf("Failed to load configuration: %v", err)
return nil, err
}
return &iam_pb.GetConfigurationResponse{
Configuration: config,
}, nil
}
func (s *IamGrpcServer) PutConfiguration(ctx context.Context, req *iam_pb.PutConfigurationRequest) (*iam_pb.PutConfigurationResponse, error) {
glog.V(4).Infof("PutConfiguration")
if s.credentialManager == nil {
return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured")
}
if req.Configuration == nil {
return nil, status.Errorf(codes.InvalidArgument, "configuration is nil")
}
err := s.credentialManager.SaveConfiguration(ctx, req.Configuration)
if err != nil {
glog.Errorf("Failed to save configuration: %v", err)
return nil, err
}
return &iam_pb.PutConfigurationResponse{}, nil
}
//////////////////////////////////////////////////
// User Management
func (s *IamGrpcServer) CreateUser(ctx context.Context, req *iam_pb.CreateUserRequest) (*iam_pb.CreateUserResponse, error) {
if req == nil || req.Identity == nil {
return nil, status.Errorf(codes.InvalidArgument, "identity is required")
}
glog.V(4).Infof("CreateUser: %s", req.Identity.Name)
if s.credentialManager == nil {
return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured")
}
err := s.credentialManager.CreateUser(ctx, req.Identity)
if err != nil {
if err == credential.ErrUserAlreadyExists {
return nil, status.Errorf(codes.AlreadyExists, "user %s already exists", req.Identity.Name)
}
glog.Errorf("Failed to create user %s: %v", req.Identity.Name, err)
return nil, status.Errorf(codes.Internal, "failed to create user: %v", err)
}
return &iam_pb.CreateUserResponse{}, nil
}
func (s *IamGrpcServer) GetUser(ctx context.Context, req *iam_pb.GetUserRequest) (*iam_pb.GetUserResponse, error) {
glog.V(4).Infof("GetUser: %s", req.Username)
if s.credentialManager == nil {
return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured")
}
identity, err := s.credentialManager.GetUser(ctx, req.Username)
if err != nil {
if err == credential.ErrUserNotFound {
return nil, status.Errorf(codes.NotFound, "user %s not found", req.Username)
}
glog.Errorf("Failed to get user %s: %v", req.Username, err)
return nil, status.Errorf(codes.Internal, "failed to get user: %v", err)
}
return &iam_pb.GetUserResponse{
Identity: identity,
}, nil
}
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")
}
if s.credentialManager == nil {
return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured")
}
err := s.credentialManager.UpdateUser(ctx, req.Username, req.Identity)
if err != nil {
if err == credential.ErrUserNotFound {
return nil, status.Errorf(codes.NotFound, "user %s not found", req.Username)
}
glog.Errorf("Failed to update user %s: %v", req.Username, err)
return nil, status.Errorf(codes.Internal, "failed to update user: %v", err)
}
return &iam_pb.UpdateUserResponse{}, nil
}
func (s *IamGrpcServer) DeleteUser(ctx context.Context, req *iam_pb.DeleteUserRequest) (*iam_pb.DeleteUserResponse, error) {
glog.V(4).Infof("DeleteUser: %s", req.Username)
if s.credentialManager == nil {
return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured")
}
err := s.credentialManager.DeleteUser(ctx, req.Username)
if err != nil {
if err == credential.ErrUserNotFound {
// Deleting a non-existent user is generally considered a success or Not Found depending on semantics
// In S3 API, usually idempotent. But for Admin API, often 404.
// Here we return NotFound to let client decide, but traditionally delete is idempotent.
// However, if we want strict status codes:
return nil, status.Errorf(codes.NotFound, "user %s not found", req.Username)
}
glog.Errorf("Failed to delete user %s: %v", req.Username, err)
return nil, status.Errorf(codes.Internal, "failed to delete user: %v", err)
}
return &iam_pb.DeleteUserResponse{}, nil
}
func (s *IamGrpcServer) ListUsers(ctx context.Context, req *iam_pb.ListUsersRequest) (*iam_pb.ListUsersResponse, error) {
glog.V(4).Infof("ListUsers")
if s.credentialManager == nil {
return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured")
}
usernames, err := s.credentialManager.ListUsers(ctx)
if err != nil {
glog.Errorf("Failed to list users: %v", err)
return nil, err
}
return &iam_pb.ListUsersResponse{
Usernames: usernames,
}, nil
}
//////////////////////////////////////////////////
// Access Key Management
func (s *IamGrpcServer) CreateAccessKey(ctx context.Context, req *iam_pb.CreateAccessKeyRequest) (*iam_pb.CreateAccessKeyResponse, error) {
if req == nil || req.Credential == nil {
return nil, status.Errorf(codes.InvalidArgument, "credential is required")
}
glog.V(4).Infof("CreateAccessKey for user: %s", req.Username)
if s.credentialManager == nil {
return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured")
}
err := s.credentialManager.CreateAccessKey(ctx, req.Username, req.Credential)
if err != nil {
if err == credential.ErrUserNotFound {
return nil, status.Errorf(codes.NotFound, "user %s not found", req.Username)
}
glog.Errorf("Failed to create access key for user %s: %v", req.Username, err)
return nil, status.Errorf(codes.Internal, "failed to create access key: %v", err)
}
return &iam_pb.CreateAccessKeyResponse{}, nil
}
func (s *IamGrpcServer) DeleteAccessKey(ctx context.Context, req *iam_pb.DeleteAccessKeyRequest) (*iam_pb.DeleteAccessKeyResponse, error) {
glog.V(4).Infof("DeleteAccessKey: %s for user: %s", req.AccessKey, req.Username)
if s.credentialManager == nil {
return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured")
}
err := s.credentialManager.DeleteAccessKey(ctx, req.Username, req.AccessKey)
if err != nil {
if err == credential.ErrUserNotFound {
return nil, status.Errorf(codes.NotFound, "user %s not found", req.Username)
}
if err == credential.ErrAccessKeyNotFound {
return nil, status.Errorf(codes.NotFound, "access key %s not found", req.AccessKey)
}
glog.Errorf("Failed to delete access key %s for user %s: %v", req.AccessKey, req.Username, err)
return nil, status.Errorf(codes.Internal, "failed to delete access key: %v", err)
}
return &iam_pb.DeleteAccessKeyResponse{}, nil
}
func (s *IamGrpcServer) GetUserByAccessKey(ctx context.Context, req *iam_pb.GetUserByAccessKeyRequest) (*iam_pb.GetUserByAccessKeyResponse, error) {
glog.V(4).Infof("GetUserByAccessKey: %s", req.AccessKey)
if s.credentialManager == nil {
return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured")
}
identity, err := s.credentialManager.GetUserByAccessKey(ctx, req.AccessKey)
if err != nil {
if err == credential.ErrAccessKeyNotFound {
return nil, status.Errorf(codes.NotFound, "access key %s not found", req.AccessKey)
}
glog.Errorf("Failed to get user by access key %s: %v", req.AccessKey, err)
return nil, status.Errorf(codes.Internal, "failed to get user: %v", err)
}
return &iam_pb.GetUserByAccessKeyResponse{
Identity: identity,
}, nil
}
//////////////////////////////////////////////////
// Policy Management
func (s *IamGrpcServer) PutPolicy(ctx context.Context, req *iam_pb.PutPolicyRequest) (*iam_pb.PutPolicyResponse, error) {
glog.V(4).Infof("PutPolicy: %s", req.Name)
if s.credentialManager == nil {
return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured")
}
if req.Name == "" {
return nil, status.Errorf(codes.InvalidArgument, "policy name is required")
}
if req.Content == "" {
return nil, status.Errorf(codes.InvalidArgument, "policy content is required")
}
var policy policy_engine.PolicyDocument
if err := json.Unmarshal([]byte(req.Content), &policy); err != nil {
glog.Errorf("Failed to unmarshal policy %s: %v", req.Name, err)
return nil, err
}
err := s.credentialManager.PutPolicy(ctx, req.Name, policy)
if err != nil {
glog.Errorf("Failed to put policy %s: %v", req.Name, err)
return nil, err
}
return &iam_pb.PutPolicyResponse{}, nil
}
func (s *IamGrpcServer) GetPolicy(ctx context.Context, req *iam_pb.GetPolicyRequest) (*iam_pb.GetPolicyResponse, error) {
glog.V(4).Infof("GetPolicy: %s", req.Name)
if s.credentialManager == nil {
return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured")
}
policy, err := s.credentialManager.GetPolicy(ctx, req.Name)
if err != nil {
glog.Errorf("Failed to get policy %s: %v", req.Name, err)
return nil, err
}
if policy == nil {
return nil, status.Errorf(codes.NotFound, "policy %s not found", req.Name)
}
jsonBytes, err := json.Marshal(policy)
if err != nil {
glog.Errorf("Failed to marshal policy %s: %v", req.Name, err)
return nil, err
}
return &iam_pb.GetPolicyResponse{
Name: req.Name,
Content: string(jsonBytes),
}, nil
}
func (s *IamGrpcServer) ListPolicies(ctx context.Context, req *iam_pb.ListPoliciesRequest) (*iam_pb.ListPoliciesResponse, error) {
glog.V(4).Infof("ListPolicies")
if s.credentialManager == nil {
return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured")
}
policiesData, err := s.credentialManager.GetPolicies(ctx)
if err != nil {
glog.Errorf("Failed to list policies: %v", err)
return nil, err
}
var policies []*iam_pb.Policy
for name, policy := range policiesData {
jsonBytes, err := json.Marshal(policy)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to marshal policy %s: %v", name, err)
}
policies = append(policies, &iam_pb.Policy{
Name: name,
Content: string(jsonBytes),
})
}
return &iam_pb.ListPoliciesResponse{
Policies: policies,
}, nil
}
func (s *IamGrpcServer) DeletePolicy(ctx context.Context, req *iam_pb.DeletePolicyRequest) (*iam_pb.DeletePolicyResponse, error) {
glog.V(4).Infof("DeletePolicy: %s", req.Name)
if s.credentialManager == nil {
return nil, status.Errorf(codes.FailedPrecondition, "credential manager is not configured")
}
err := s.credentialManager.DeletePolicy(ctx, req.Name)
if err != nil {
glog.Errorf("Failed to delete policy %s: %v", req.Name, err)
return nil, err
}
return &iam_pb.DeletePolicyResponse{}, nil
}

339
weed/shell/command_s3_configure.go

@ -2,16 +2,18 @@ package shell
import (
"bytes"
"context"
"flag"
"fmt"
"io"
"sort"
"strings"
"github.com/seaweedfs/seaweedfs/weed/filer"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
"github.com/seaweedfs/seaweedfs/weed/pb"
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func init() {
@ -32,7 +34,7 @@ func (c *commandS3Configure) Help() string {
s3.configure
# create a new identity with account information
s3.configure -user=username -actions=Read,Write,List,Tagging -buckets=bucket-name -access_key=key -secret_key=secret -account_id=id -account_display_name=name -account_email=email@example.com -apply
s3.configure -user=username -actions=Read,Write,List,Tagging -buckets=bucket-name -policies=policy1,policy2 -access_key=key -secret_key=secret -account_id=id -account_display_name=name -account_email=email@example.com -apply
`
}
@ -51,178 +53,229 @@ func (c *commandS3Configure) Do(args []string, commandEnv *CommandEnv, writer io
accountId := s3ConfigureCommand.String("account_id", "", "specify the account id")
accountDisplayName := s3ConfigureCommand.String("account_display_name", "", "specify the account display name")
accountEmail := s3ConfigureCommand.String("account_email", "", "specify the account email address")
isDelete := s3ConfigureCommand.Bool("delete", false, "delete users, actions or access keys")
policies := s3ConfigureCommand.String("policies", "", "comma separated policy names")
isDelete := s3ConfigureCommand.Bool("delete", false, "delete users, actions, access keys or policies")
apply := s3ConfigureCommand.Bool("apply", false, "update and apply s3 configuration")
if err = s3ConfigureCommand.Parse(args); err != nil {
return nil
}
// Check which account flags were provided and build update functions
var accountUpdates []func(*iam_pb.Account)
s3ConfigureCommand.Visit(func(f *flag.Flag) {
switch f.Name {
case "account_id":
accountUpdates = append(accountUpdates, func(a *iam_pb.Account) { a.Id = *accountId })
case "account_display_name":
accountUpdates = append(accountUpdates, func(a *iam_pb.Account) { a.DisplayName = *accountDisplayName })
case "account_email":
accountUpdates = append(accountUpdates, func(a *iam_pb.Account) { a.EmailAddress = *accountEmail })
}
})
// Helper function to update account information on an identity
updateAccountInfo := func(account **iam_pb.Account) {
if len(accountUpdates) > 0 {
if *account == nil {
*account = &iam_pb.Account{}
// Case 1: List configuration (no user specified)
if *user == "" {
return c.listConfiguration(commandEnv, writer)
}
// Case 2: Modify specific user
var identity *iam_pb.Identity
var isNewUser bool
err = pb.WithGrpcClient(false, 0, func(conn *grpc.ClientConn) error {
client := iam_pb.NewSeaweedIdentityAccessManagementClient(conn)
// Try to get existing user
resp, getErr := client.GetUser(context.Background(), &iam_pb.GetUserRequest{
Username: *user,
})
if getErr == nil {
identity = resp.Identity
if identity == nil {
// Should not happen if err is nil, but handle defensively
isNewUser = true
identity = &iam_pb.Identity{Name: *user}
}
for _, update := range accountUpdates {
update(*account)
} else {
st, ok := status.FromError(getErr)
if ok && st.Code() == codes.NotFound {
isNewUser = true
identity = &iam_pb.Identity{
Name: *user,
Credentials: []*iam_pb.Credential{},
Actions: []string{},
PolicyNames: []string{},
}
} else {
return fmt.Errorf("failed to get user %s: %v", *user, getErr)
}
}
}
var buf bytes.Buffer
if err = commandEnv.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
return filer.ReadEntry(commandEnv.MasterClient, client, filer.IamConfigDirectory, filer.IamIdentityFile, &buf)
}); err != nil && err != filer_pb.ErrNotFound {
return err
}
// Apply changes to identity object
if err := c.applyChanges(identity, isNewUser, actions, buckets, accessKey, secretKey, policies, isDelete, accountId, accountDisplayName, accountEmail); err != nil {
return err
}
s3cfg := &iam_pb.S3ApiConfiguration{}
if buf.Len() > 0 {
if err = filer.ParseS3ConfigurationFromBytes(buf.Bytes(), s3cfg); err != nil {
// Print changes (Simulation)
var buf bytes.Buffer
filer.ProtoToText(&buf, identity)
fmt.Fprint(writer, buf.String())
fmt.Fprintln(writer)
if !*apply {
infoAboutSimulationMode(writer, *apply, "-apply")
return nil
}
// Apply changes
if *isDelete && *actions == "" && *accessKey == "" && *buckets == "" && *policies == "" {
// Delete User
_, err := client.DeleteUser(context.Background(), &iam_pb.DeleteUserRequest{Username: *user})
return err
} else {
// Create or Update User
if isNewUser {
_, err := client.CreateUser(context.Background(), &iam_pb.CreateUserRequest{Identity: identity})
return err
} else {
_, err := client.UpdateUser(context.Background(), &iam_pb.UpdateUserRequest{Username: *user, Identity: identity})
return err
}
}
}, commandEnv.option.FilerAddress.ToGrpcAddress(), false, commandEnv.option.GrpcDialOption)
return err
}
func (c *commandS3Configure) listConfiguration(commandEnv *CommandEnv, writer io.Writer) error {
return pb.WithGrpcClient(false, 0, func(conn *grpc.ClientConn) error {
client := iam_pb.NewSeaweedIdentityAccessManagementClient(conn)
resp, err := client.GetConfiguration(context.Background(), &iam_pb.GetConfigurationRequest{})
if err != nil {
return err
}
var buf bytes.Buffer
filer.ProtoToText(&buf, resp.Configuration)
fmt.Fprint(writer, buf.String())
fmt.Fprintln(writer)
return nil
}, commandEnv.option.FilerAddress.ToGrpcAddress(), false, commandEnv.option.GrpcDialOption)
}
func (c *commandS3Configure) applyChanges(identity *iam_pb.Identity, isNewUser bool, actions, buckets, accessKey, secretKey, policies *string, isDelete *bool, accountId, accountDisplayName, accountEmail *string) error {
// Helper to update account info
if *accountId != "" || *accountDisplayName != "" || *accountEmail != "" {
if identity.Account == nil {
identity.Account = &iam_pb.Account{}
}
if *accountId != "" {
identity.Account.Id = *accountId
}
if *accountDisplayName != "" {
identity.Account.DisplayName = *accountDisplayName
}
if *accountEmail != "" {
identity.Account.EmailAddress = *accountEmail
}
}
idx := 0
changed := false
if *user != "" {
for i, identity := range s3cfg.Identities {
if *user == identity.Name {
idx = i
changed = true
break
// Prepare lists
var cmdActions []string
if *actions != "" {
for _, action := range strings.Split(*actions, ",") {
if *buckets == "" {
cmdActions = append(cmdActions, action)
} else {
for _, bucket := range strings.Split(*buckets, ",") {
cmdActions = append(cmdActions, fmt.Sprintf("%s:%s", action, bucket))
}
}
}
}
var cmdActions []string
for _, action := range strings.Split(*actions, ",") {
if *buckets == "" {
cmdActions = append(cmdActions, action)
} else {
for _, bucket := range strings.Split(*buckets, ",") {
cmdActions = append(cmdActions, fmt.Sprintf("%s:%s", action, bucket))
var cmdPolicies []string
if *policies != "" {
for _, policy := range strings.Split(*policies, ",") {
if policy != "" {
cmdPolicies = append(cmdPolicies, policy)
}
}
}
if changed {
infoAboutSimulationMode(writer, *apply, "-apply")
if *isDelete {
var exists []int
for _, cmdAction := range cmdActions {
for i, currentAction := range s3cfg.Identities[idx].Actions {
if cmdAction == currentAction {
exists = append(exists, i)
}
if *isDelete {
// DELETE LOGIC
// Remove Actions
if len(cmdActions) > 0 {
identity.Actions = removeFromSlice(identity.Actions, cmdActions)
}
// Remove Credentials
if *accessKey != "" {
var keepCredentials []*iam_pb.Credential
for _, cred := range identity.Credentials {
if cred.AccessKey != *accessKey {
keepCredentials = append(keepCredentials, cred)
}
}
sort.Sort(sort.Reverse(sort.IntSlice(exists)))
for _, i := range exists {
s3cfg.Identities[idx].Actions = append(
s3cfg.Identities[idx].Actions[:i],
s3cfg.Identities[idx].Actions[i+1:]...,
)
}
if *accessKey != "" {
exists = []int{}
for i, credential := range s3cfg.Identities[idx].Credentials {
if credential.AccessKey == *accessKey {
exists = append(exists, i)
}
}
sort.Sort(sort.Reverse(sort.IntSlice(exists)))
for _, i := range exists {
s3cfg.Identities[idx].Credentials = append(
s3cfg.Identities[idx].Credentials[:i],
s3cfg.Identities[idx].Credentials[i+1:]...,
)
}
identity.Credentials = keepCredentials
}
}
if *actions == "" && *accessKey == "" && *buckets == "" {
s3cfg.Identities = append(s3cfg.Identities[:idx], s3cfg.Identities[idx+1:]...)
}
} else {
if *actions != "" {
for _, cmdAction := range cmdActions {
found := false
for _, action := range s3cfg.Identities[idx].Actions {
if cmdAction == action {
found = true
break
}
}
if !found {
s3cfg.Identities[idx].Actions = append(s3cfg.Identities[idx].Actions, cmdAction)
// Remove Policies
if len(cmdPolicies) > 0 {
identity.PolicyNames = removeFromSlice(identity.PolicyNames, cmdPolicies)
}
} else {
// ADD/UPDATE LOGIC
// Add Actions
identity.Actions = addUniqueToSlice(identity.Actions, cmdActions)
// Add/Update Credentials
if *accessKey != "" && identity.Name != "anonymous" {
found := false
for _, cred := range identity.Credentials {
if cred.AccessKey == *accessKey {
found = true
if *secretKey != "" {
cred.SecretKey = *secretKey
}
break
}
}
if *accessKey != "" && *user != "anonymous" {
found := false
for _, credential := range s3cfg.Identities[idx].Credentials {
if credential.AccessKey == *accessKey {
found = true
credential.SecretKey = *secretKey
break
}
}
if !found {
s3cfg.Identities[idx].Credentials = append(s3cfg.Identities[idx].Credentials, &iam_pb.Credential{
AccessKey: *accessKey,
SecretKey: *secretKey,
})
if !found {
if *secretKey == "" {
return fmt.Errorf("secret_key is required when adding a new access_key")
}
identity.Credentials = append(identity.Credentials, &iam_pb.Credential{
AccessKey: *accessKey,
SecretKey: *secretKey,
})
}
// Update account information if provided
updateAccountInfo(&s3cfg.Identities[idx].Account)
}
} else if *user != "" && *actions != "" {
infoAboutSimulationMode(writer, *apply, "-apply")
identity := iam_pb.Identity{
Name: *user,
Actions: cmdActions,
Credentials: []*iam_pb.Credential{},
}
if *user != "anonymous" {
identity.Credentials = append(identity.Credentials,
&iam_pb.Credential{AccessKey: *accessKey, SecretKey: *secretKey})
}
// Add account information if provided
updateAccountInfo(&identity.Account)
s3cfg.Identities = append(s3cfg.Identities, &identity)
}
}
if err = filer.CheckDuplicateAccessKey(s3cfg); err != nil {
return err
// Add Policies
identity.PolicyNames = addUniqueToSlice(identity.PolicyNames, cmdPolicies)
}
buf.Reset()
filer.ProtoToText(&buf, s3cfg)
fmt.Fprint(writer, buf.String())
fmt.Fprintln(writer)
if *apply {
return nil
}
if err := commandEnv.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
return filer.SaveInsideFiler(client, filer.IamConfigDirectory, filer.IamIdentityFile, buf.Bytes())
}); err != nil {
return err
// Helper to remove items from a slice
func removeFromSlice(current []string, toRemove []string) []string {
removeSet := make(map[string]struct{}, len(toRemove))
for _, item := range toRemove {
removeSet[item] = struct{}{}
}
var result []string
for _, item := range current {
if _, found := removeSet[item]; !found {
result = append(result, item)
}
}
return result
}
return nil
// Helper to add unique items to a slice
func addUniqueToSlice(current []string, toAdd []string) []string {
existingSet := make(map[string]struct{}, len(current))
for _, item := range current {
existingSet[item] = struct{}{}
}
for _, item := range toAdd {
if _, found := existingSet[item]; !found {
current = append(current, item)
}
}
return current
}

150
weed/shell/command_s3_policy.go

@ -0,0 +1,150 @@
package shell
import (
"context"
"encoding/json"
"flag"
"fmt"
"io"
"os"
"time"
"github.com/seaweedfs/seaweedfs/weed/pb"
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb"
"github.com/seaweedfs/seaweedfs/weed/s3api/policy_engine"
"google.golang.org/grpc"
)
func init() {
Commands = append(Commands, &commandS3Policy{})
}
type commandS3Policy struct {
}
func (c *commandS3Policy) Name() string {
return "s3.policy"
}
func (c *commandS3Policy) Help() string {
return `manage s3 policies
# create or update a policy
s3.policy -put -name=mypolicy -file=policy.json
# list all policies
s3.policy -list
# get a policy
s3.policy -get -name=mypolicy
# delete a policy
s3.policy -delete -name=mypolicy
`
}
func (c *commandS3Policy) HasTag(CommandTag) bool {
return false
}
func (c *commandS3Policy) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) {
s3PolicyCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
put := s3PolicyCommand.Bool("put", false, "create or update a policy")
get := s3PolicyCommand.Bool("get", false, "get a policy")
list := s3PolicyCommand.Bool("list", false, "list all policies")
del := s3PolicyCommand.Bool("delete", false, "delete a policy")
name := s3PolicyCommand.String("name", "", "policy name")
file := s3PolicyCommand.String("file", "", "policy file (json)")
if err = s3PolicyCommand.Parse(args); err != nil {
return err
}
actionCount := 0
for _, v := range []bool{*put, *get, *list, *del} {
if v {
actionCount++
}
}
if actionCount == 0 {
return fmt.Errorf("one of -put, -get, -list, -delete must be specified")
}
if actionCount > 1 {
return fmt.Errorf("only one of -put, -get, -list, -delete can be specified")
}
return pb.WithGrpcClient(false, 0, func(conn *grpc.ClientConn) error {
client := iam_pb.NewSeaweedIdentityAccessManagementClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if *put {
if *name == "" {
return fmt.Errorf("-name is required")
}
if *file == "" {
return fmt.Errorf("-file is required")
}
data, err := os.ReadFile(*file)
if err != nil {
return fmt.Errorf("failed to read policy file: %v", err)
}
// Validate JSON
var policy policy_engine.PolicyDocument
if err := json.Unmarshal(data, &policy); err != nil {
return fmt.Errorf("invalid policy json: %v", err)
}
_, err = client.PutPolicy(ctx, &iam_pb.PutPolicyRequest{
Name: *name,
Content: string(data),
})
return err
}
if *get {
if *name == "" {
return fmt.Errorf("-name is required")
}
resp, err := client.GetPolicy(ctx, &iam_pb.GetPolicyRequest{
Name: *name,
})
if err != nil {
return err
}
if resp.Content == "" {
return fmt.Errorf("policy not found")
}
fmt.Fprintf(writer, "%s\n", resp.Content)
return nil
}
if *list {
resp, err := client.ListPolicies(ctx, &iam_pb.ListPoliciesRequest{})
if err != nil {
return err
}
for _, policy := range resp.Policies {
fmt.Fprintf(writer, "Name: %s\n", policy.Name)
fmt.Fprintf(writer, "Content: %s\n", policy.Content)
fmt.Fprintf(writer, "---\n")
}
return nil
}
if *del {
if *name == "" {
return fmt.Errorf("-name is required")
}
_, err := client.DeletePolicy(ctx, &iam_pb.DeletePolicyRequest{
Name: *name,
})
return err
}
return nil
}, commandEnv.option.FilerAddress.ToGrpcAddress(), false, commandEnv.option.GrpcDialOption)
}
Loading…
Cancel
Save