diff --git a/weed/s3api/s3acl/acl_helper.go b/weed/s3api/s3acl/acl_helper.go index e54e67556..4387e64ca 100644 --- a/weed/s3api/s3acl/acl_helper.go +++ b/weed/s3api/s3acl/acl_helper.go @@ -503,3 +503,13 @@ func GrantEquals(a, b *s3.Grant) bool { } return true } + +func GrantWithFullControl(accountId string) *s3.Grant { + return &s3.Grant{ + Permission: &s3_constants.PermissionFullControl, + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeCanonicalUser, + ID: &accountId, + }, + } +} diff --git a/weed/s3api/s3acl/acl_helper_test.go b/weed/s3api/s3acl/acl_helper_test.go index efc137989..3c5b09ff7 100644 --- a/weed/s3api/s3acl/acl_helper_test.go +++ b/weed/s3api/s3acl/acl_helper_test.go @@ -706,3 +706,20 @@ func TestSetAcpGrantsHeader(t *testing.T) { t.Fatalf("owner unexpect") } } + +func TestGrantWithFullControl(t *testing.T) { + accountId := "Accountaskdfj" + expect := &s3.Grant{ + Permission: &s3_constants.PermissionFullControl, + Grantee: &s3.Grantee{ + Type: &s3_constants.GrantTypeCanonicalUser, + ID: &accountId, + }, + } + + result := GrantWithFullControl(accountId) + + if !GrantEquals(result, expect) { + t.Fatal("GrantWithFullControl not expect") + } +} diff --git a/weed/s3api/s3api_bucket_handlers.go b/weed/s3api/s3api_bucket_handlers.go index 9e215db9e..c4a84dcdd 100644 --- a/weed/s3api/s3api_bucket_handlers.go +++ b/weed/s3api/s3api_bucket_handlers.go @@ -6,6 +6,8 @@ import ( "errors" "fmt" "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil" + "github.com/seaweedfs/seaweedfs/weed/s3api/s3account" + "github.com/seaweedfs/seaweedfs/weed/s3api/s3acl" "github.com/seaweedfs/seaweedfs/weed/util" "math" "net/http" @@ -400,6 +402,24 @@ func (s3a *S3ApiServer) PutBucketOwnershipControls(w http.ResponseWriter, r *htt oldOwnership, ok := bucketEntry.Extended[s3_constants.ExtOwnershipKey] if !ok || string(oldOwnership) != ownership { + + // must reset bucket acl to default(bucket owner with full control permission) before setting ownership + // to `OwnershipBucketOwnerEnforced` (bucket cannot have ACLs set with ObjectOwnership's BucketOwnerEnforced setting) + if ownership == s3_constants.OwnershipBucketOwnerEnforced { + acpGrants := s3acl.GetAcpGrants(bucketEntry.Extended) + if len(acpGrants) != 1 { + s3err.WriteErrorResponse(w, r, s3err.InvalidBucketAclWithObjectOwnership) + return + } + + bucketOwner := s3acl.GetAcpOwner(bucketEntry.Extended, s3account.AccountAdmin.Id) + expectGrant := s3acl.GrantWithFullControl(bucketOwner) + if s3acl.GrantEquals(acpGrants[0], expectGrant) { + s3err.WriteErrorResponse(w, r, s3err.InvalidBucketAclWithObjectOwnership) + return + } + } + if bucketEntry.Extended == nil { bucketEntry.Extended = make(map[string][]byte) } diff --git a/weed/s3api/s3err/s3api_errors.go b/weed/s3api/s3err/s3api_errors.go index 0348d4ddc..24d432c42 100644 --- a/weed/s3api/s3err/s3api_errors.go +++ b/weed/s3api/s3err/s3api_errors.go @@ -109,6 +109,7 @@ const ( ErrRequestBytesExceed OwnershipControlsNotFoundError + InvalidBucketAclWithObjectOwnership ) // error code to APIError structure, these fields carry respective @@ -422,6 +423,11 @@ var errorCodeResponse = map[ErrorCode]APIError{ Description: "The bucket ownership controls were not found", HTTPStatusCode: http.StatusNotFound, }, + InvalidBucketAclWithObjectOwnership: { + Code: "InvalidBucketAclWithObjectOwnership", + Description: "Bucket cannot have ACLs set with ObjectOwnership's BucketOwnerEnforced setting", + HTTPStatusCode: http.StatusNotFound, + }, } // GetAPIError provides API Error for input API error code.