aboutsummaryrefslogtreecommitdiff
path: root/weed/shell
diff options
context:
space:
mode:
Diffstat (limited to 'weed/shell')
-rw-r--r--weed/shell/command_s3_bucket_create.go37
-rw-r--r--weed/shell/command_s3_bucket_list.go7
-rw-r--r--weed/shell/command_s3_bucket_owner.go150
3 files changed, 189 insertions, 5 deletions
diff --git a/weed/shell/command_s3_bucket_create.go b/weed/shell/command_s3_bucket_create.go
index becbd96e7..ee6d3ec6a 100644
--- a/weed/shell/command_s3_bucket_create.go
+++ b/weed/shell/command_s3_bucket_create.go
@@ -4,11 +4,14 @@ import (
"context"
"flag"
"fmt"
- "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
- "github.com/seaweedfs/seaweedfs/weed/s3api/s3bucket"
"io"
"os"
+ "strings"
"time"
+
+ "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
+ "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
+ "github.com/seaweedfs/seaweedfs/weed/s3api/s3bucket"
)
func init() {
@@ -27,6 +30,15 @@ func (c *commandS3BucketCreate) Help() string {
Example:
s3.bucket.create -name <bucket_name>
+ s3.bucket.create -name <bucket_name> -owner <identity_name>
+
+ The -owner flag sets the bucket owner identity. This is important when using
+ S3 IAM authentication, as non-admin users can only access buckets they own.
+ If not specified, the bucket will have no owner and will only be accessible
+ by admin users.
+
+ The -owner value should match the identity name configured in your S3 IAM
+ system (the "name" field in s3.json identities configuration).
`
}
@@ -38,6 +50,7 @@ func (c *commandS3BucketCreate) Do(args []string, commandEnv *CommandEnv, writer
bucketCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
bucketName := bucketCommand.String("name", "", "bucket name")
+ bucketOwner := bucketCommand.String("owner", "", "bucket owner identity name (for S3 IAM authentication)")
if err = bucketCommand.Parse(args); err != nil {
return nil
}
@@ -51,6 +64,9 @@ func (c *commandS3BucketCreate) Do(args []string, commandEnv *CommandEnv, writer
return err
}
+ // Trim whitespace from owner and treat whitespace-only as empty
+ owner := strings.TrimSpace(*bucketOwner)
+
err = commandEnv.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{})
@@ -59,7 +75,7 @@ func (c *commandS3BucketCreate) Do(args []string, commandEnv *CommandEnv, writer
}
filerBucketsPath := resp.DirBuckets
- println("create bucket under", filerBucketsPath)
+ fmt.Fprintln(writer, "create bucket under", filerBucketsPath)
entry := &filer_pb.Entry{
Name: *bucketName,
@@ -71,14 +87,25 @@ func (c *commandS3BucketCreate) Do(args []string, commandEnv *CommandEnv, writer
},
}
- if err := filer_pb.CreateEntry(context.Background(), client, &filer_pb.CreateEntryRequest{
+ // Set bucket owner if specified
+ if owner != "" {
+ if entry.Extended == nil {
+ entry.Extended = make(map[string][]byte)
+ }
+ entry.Extended[s3_constants.AmzIdentityId] = []byte(owner)
+ }
+
+ if _, err := client.CreateEntry(context.Background(), &filer_pb.CreateEntryRequest{
Directory: filerBucketsPath,
Entry: entry,
}); err != nil {
return err
}
- println("created bucket", *bucketName)
+ fmt.Fprintln(writer, "created bucket", *bucketName)
+ if owner != "" {
+ fmt.Fprintln(writer, "bucket owner:", owner)
+ }
return nil
diff --git a/weed/shell/command_s3_bucket_list.go b/weed/shell/command_s3_bucket_list.go
index 031b22d2d..bb55fc013 100644
--- a/weed/shell/command_s3_bucket_list.go
+++ b/weed/shell/command_s3_bucket_list.go
@@ -8,6 +8,7 @@ import (
"math"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
+ "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
)
func init() {
@@ -71,6 +72,12 @@ func (c *commandS3BucketList) Do(args []string, commandEnv *CommandEnv, writer i
if entry.Quota > 0 {
fmt.Fprintf(writer, "\tquota:%d\tusage:%.2f%%", entry.Quota, float64(collectionSize)*100/float64(entry.Quota))
}
+ // Show bucket owner (use %q to escape special characters)
+ if entry.Extended != nil {
+ if owner, ok := entry.Extended[s3_constants.AmzIdentityId]; ok && len(owner) > 0 {
+ fmt.Fprintf(writer, "\towner:%q", string(owner))
+ }
+ }
fmt.Fprintln(writer)
return nil
}, "", false, math.MaxUint32)
diff --git a/weed/shell/command_s3_bucket_owner.go b/weed/shell/command_s3_bucket_owner.go
new file mode 100644
index 000000000..881cb730c
--- /dev/null
+++ b/weed/shell/command_s3_bucket_owner.go
@@ -0,0 +1,150 @@
+package shell
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "io"
+ "strings"
+
+ "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
+ "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
+ "github.com/seaweedfs/seaweedfs/weed/util"
+)
+
+func init() {
+ Commands = append(Commands, &commandS3BucketOwner{})
+}
+
+type commandS3BucketOwner struct {
+}
+
+func (c *commandS3BucketOwner) Name() string {
+ return "s3.bucket.owner"
+}
+
+func (c *commandS3BucketOwner) Help() string {
+ return `view or change the owner of an S3 bucket
+
+ Example:
+ # View the current owner of a bucket
+ s3.bucket.owner -name <bucket_name>
+
+ # Set or change the owner of a bucket
+ s3.bucket.owner -name <bucket_name> -owner <identity_name>
+
+ # Remove the owner (make bucket admin-only)
+ s3.bucket.owner -name <bucket_name> -delete
+
+ The owner identity determines which S3 user can access the bucket.
+ Non-admin users can only access buckets they own. Admin users can
+ access all buckets regardless of ownership.
+
+ The -owner value should match the identity name configured in your
+ S3 IAM system (the "name" field in s3.json identities configuration).
+`
+}
+
+func (c *commandS3BucketOwner) HasTag(CommandTag) bool {
+ return false
+}
+
+func (c *commandS3BucketOwner) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) {
+
+ bucketCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
+ bucketName := bucketCommand.String("name", "", "bucket name")
+ bucketOwner := bucketCommand.String("owner", "", "new bucket owner identity name")
+ deleteOwner := bucketCommand.Bool("delete", false, "remove the bucket owner (make admin-only)")
+ if err = bucketCommand.Parse(args); err != nil {
+ return nil
+ }
+
+ if *bucketName == "" {
+ return fmt.Errorf("empty bucket name")
+ }
+
+ // Trim whitespace from owner
+ owner := strings.TrimSpace(*bucketOwner)
+
+ // Validate flags: can't use both -owner and -delete
+ if owner != "" && *deleteOwner {
+ return fmt.Errorf("cannot use both -owner and -delete flags together")
+ }
+
+ err = commandEnv.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
+
+ resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{})
+ if err != nil {
+ return fmt.Errorf("get filer configuration: %w", err)
+ }
+ filerBucketsPath := resp.DirBuckets
+
+ // Look up the bucket entry
+ lookupResp, err := client.LookupDirectoryEntry(context.Background(), &filer_pb.LookupDirectoryEntryRequest{
+ Directory: filerBucketsPath,
+ Name: *bucketName,
+ })
+ if err != nil {
+ return fmt.Errorf("lookup bucket %s: %w", *bucketName, err)
+ }
+
+ entry := lookupResp.Entry
+
+ // If -owner is provided, set the owner
+ if owner != "" {
+ if entry.Extended == nil {
+ entry.Extended = make(map[string][]byte)
+ }
+ entry.Extended[s3_constants.AmzIdentityId] = []byte(owner)
+ fmt.Fprintf(writer, "Setting owner of bucket %s to: %s\n", *bucketName, owner)
+
+ // Update the entry
+ if _, err := client.UpdateEntry(context.Background(), &filer_pb.UpdateEntryRequest{
+ Directory: filerBucketsPath,
+ Entry: entry,
+ }); err != nil {
+ return fmt.Errorf("failed to update bucket: %w", err)
+ }
+
+ fmt.Fprintf(writer, "Bucket owner updated successfully.\n")
+ return nil
+ }
+
+ // If -delete is provided, remove the owner
+ if *deleteOwner {
+ if entry.Extended != nil {
+ delete(entry.Extended, s3_constants.AmzIdentityId)
+ }
+ fmt.Fprintf(writer, "Removing owner from bucket %s\n", *bucketName)
+
+ // Update the entry
+ if _, err := client.UpdateEntry(context.Background(), &filer_pb.UpdateEntryRequest{
+ Directory: filerBucketsPath,
+ Entry: entry,
+ }); err != nil {
+ return fmt.Errorf("failed to update bucket: %w", err)
+ }
+
+ fmt.Fprintf(writer, "Bucket owner removed. Bucket is now admin-only.\n")
+ return nil
+ }
+
+ // Display current owner (no flags provided)
+ fmt.Fprintf(writer, "Bucket: %s\n", *bucketName)
+ fmt.Fprintf(writer, "Path: %s\n", util.NewFullPath(filerBucketsPath, *bucketName))
+
+ if entry.Extended != nil {
+ if ownerBytes, ok := entry.Extended[s3_constants.AmzIdentityId]; ok && len(ownerBytes) > 0 {
+ fmt.Fprintf(writer, "Owner: %s\n", string(ownerBytes))
+ } else {
+ fmt.Fprintf(writer, "Owner: (none - admin access only)\n")
+ }
+ } else {
+ fmt.Fprintf(writer, "Owner: (none - admin access only)\n")
+ }
+
+ return nil
+ })
+
+ return err
+}