diff options
Diffstat (limited to 'weed/shell')
| -rw-r--r-- | weed/shell/command_s3_bucket_create.go | 37 | ||||
| -rw-r--r-- | weed/shell/command_s3_bucket_list.go | 7 | ||||
| -rw-r--r-- | weed/shell/command_s3_bucket_owner.go | 150 |
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 +} |
