aboutsummaryrefslogtreecommitdiff
path: root/weed/admin/dash/bucket_management.go
diff options
context:
space:
mode:
Diffstat (limited to 'weed/admin/dash/bucket_management.go')
-rw-r--r--weed/admin/dash/bucket_management.go112
1 files changed, 107 insertions, 5 deletions
diff --git a/weed/admin/dash/bucket_management.go b/weed/admin/dash/bucket_management.go
index 5942d5695..eb99e9fa4 100644
--- a/weed/admin/dash/bucket_management.go
+++ b/weed/admin/dash/bucket_management.go
@@ -11,8 +11,14 @@ import (
"github.com/gin-gonic/gin"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
"github.com/seaweedfs/seaweedfs/weed/s3api"
+ "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
)
+// MaxOwnerNameLength is the maximum allowed length for bucket owner identity names.
+// This is a reasonable limit to prevent abuse; AWS IAM user names are limited to 64 chars,
+// but we use 256 to allow for more complex identity formats (e.g., email addresses).
+const MaxOwnerNameLength = 256
+
// S3 Bucket management data structures for templates
type S3BucketsData struct {
Username string `json:"username"`
@@ -33,6 +39,7 @@ type CreateBucketRequest struct {
ObjectLockMode string `json:"object_lock_mode"` // Object lock mode: "GOVERNANCE" or "COMPLIANCE"
SetDefaultRetention bool `json:"set_default_retention"` // Whether to set default retention
ObjectLockDuration int32 `json:"object_lock_duration"` // Default retention duration in days
+ Owner string `json:"owner"` // Bucket owner identity (for S3 IAM authentication)
}
// S3 Bucket Management Handlers
@@ -118,7 +125,14 @@ func (s *AdminServer) CreateBucket(c *gin.Context) {
// Convert quota to bytes
quotaBytes := convertQuotaToBytes(req.QuotaSize, req.QuotaUnit)
- err := s.CreateS3BucketWithObjectLock(req.Name, quotaBytes, req.QuotaEnabled, req.VersioningEnabled, req.ObjectLockEnabled, req.ObjectLockMode, req.SetDefaultRetention, req.ObjectLockDuration)
+ // Sanitize owner: trim whitespace and enforce max length
+ owner := strings.TrimSpace(req.Owner)
+ if len(owner) > MaxOwnerNameLength {
+ c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Owner name must be %d characters or less", MaxOwnerNameLength)})
+ return
+ }
+
+ err := s.CreateS3BucketWithObjectLock(req.Name, quotaBytes, req.QuotaEnabled, req.VersioningEnabled, req.ObjectLockEnabled, req.ObjectLockMode, req.SetDefaultRetention, req.ObjectLockDuration, owner)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create bucket: " + err.Error()})
return
@@ -134,6 +148,7 @@ func (s *AdminServer) CreateBucket(c *gin.Context) {
"object_lock_enabled": req.ObjectLockEnabled,
"object_lock_mode": req.ObjectLockMode,
"object_lock_duration": req.ObjectLockDuration,
+ "owner": owner,
})
}
@@ -193,6 +208,88 @@ func (s *AdminServer) DeleteBucket(c *gin.Context) {
})
}
+// UpdateBucketOwner updates the owner of an S3 bucket
+func (s *AdminServer) UpdateBucketOwner(c *gin.Context) {
+ bucketName := c.Param("bucket")
+ if bucketName == "" {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "Bucket name is required"})
+ return
+ }
+
+ // Use pointer to detect if owner field was explicitly provided
+ var req struct {
+ Owner *string `json:"owner"`
+ }
+ if err := c.ShouldBindJSON(&req); err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request: " + err.Error()})
+ return
+ }
+
+ // Require owner field to be explicitly provided
+ if req.Owner == nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "Owner field is required (use empty string to clear owner)"})
+ return
+ }
+
+ // Trim and validate owner
+ owner := strings.TrimSpace(*req.Owner)
+ if len(owner) > MaxOwnerNameLength {
+ c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Owner name must be %d characters or less", MaxOwnerNameLength)})
+ return
+ }
+
+ err := s.SetBucketOwner(bucketName, owner)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update bucket owner: " + err.Error()})
+ return
+ }
+
+ c.JSON(http.StatusOK, gin.H{
+ "message": "Bucket owner updated successfully",
+ "bucket": bucketName,
+ "owner": owner,
+ })
+}
+
+// SetBucketOwner sets the owner of a bucket
+func (s *AdminServer) SetBucketOwner(bucketName string, owner string) error {
+ return s.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
+ // Get the current bucket entry
+ lookupResp, err := client.LookupDirectoryEntry(context.Background(), &filer_pb.LookupDirectoryEntryRequest{
+ Directory: "/buckets",
+ Name: bucketName,
+ })
+ if err != nil {
+ return fmt.Errorf("lookup bucket %s: %w", bucketName, err)
+ }
+
+ bucketEntry := lookupResp.Entry
+
+ // Initialize Extended map if nil
+ if bucketEntry.Extended == nil {
+ bucketEntry.Extended = make(map[string][]byte)
+ }
+
+ // Set or remove the owner
+ if owner == "" {
+ delete(bucketEntry.Extended, s3_constants.AmzIdentityId)
+ } else {
+ bucketEntry.Extended[s3_constants.AmzIdentityId] = []byte(owner)
+ }
+
+ // Update the entry
+ _, err = client.UpdateEntry(context.Background(), &filer_pb.UpdateEntryRequest{
+ Directory: "/buckets",
+ Entry: bucketEntry,
+ })
+ if err != nil {
+ return fmt.Errorf("failed to update bucket owner: %w", err)
+ }
+
+ return nil
+ })
+}
+
// ListBucketsAPI returns the list of buckets as JSON
func (s *AdminServer) ListBucketsAPI(c *gin.Context) {
buckets, err := s.GetS3Buckets()
@@ -288,11 +385,11 @@ func (s *AdminServer) SetBucketQuota(bucketName string, quotaBytes int64, quotaE
// CreateS3BucketWithQuota creates a new S3 bucket with quota settings
func (s *AdminServer) CreateS3BucketWithQuota(bucketName string, quotaBytes int64, quotaEnabled bool) error {
- return s.CreateS3BucketWithObjectLock(bucketName, quotaBytes, quotaEnabled, false, false, "", false, 0)
+ return s.CreateS3BucketWithObjectLock(bucketName, quotaBytes, quotaEnabled, false, false, "", false, 0, "")
}
-// CreateS3BucketWithObjectLock creates a new S3 bucket with quota, versioning, and object lock settings
-func (s *AdminServer) CreateS3BucketWithObjectLock(bucketName string, quotaBytes int64, quotaEnabled, versioningEnabled, objectLockEnabled bool, objectLockMode string, setDefaultRetention bool, objectLockDuration int32) error {
+// CreateS3BucketWithObjectLock creates a new S3 bucket with quota, versioning, object lock settings, and owner
+func (s *AdminServer) CreateS3BucketWithObjectLock(bucketName string, quotaBytes int64, quotaEnabled, versioningEnabled, objectLockEnabled bool, objectLockMode string, setDefaultRetention bool, objectLockDuration int32, owner string) error {
return s.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
// First ensure /buckets directory exists
_, err := client.CreateEntry(context.Background(), &filer_pb.CreateEntryRequest{
@@ -344,9 +441,14 @@ func (s *AdminServer) CreateS3BucketWithObjectLock(bucketName string, quotaBytes
TtlSec: 0,
}
- // Create extended attributes map for versioning
+ // Create extended attributes map for versioning and owner
extended := make(map[string][]byte)
+ // Set bucket owner if specified
+ if owner != "" {
+ extended[s3_constants.AmzIdentityId] = []byte(owner)
+ }
+
// Create bucket entry
bucketEntry := &filer_pb.Entry{
Name: bucketName,