diff --git a/integration/action-tests.go b/integration/action-tests.go index 10c5cdb..3c010db 100644 --- a/integration/action-tests.go +++ b/integration/action-tests.go @@ -135,7 +135,9 @@ func TestPutBucketAcl(s *S3Conf) { PutBucketAcl_invalid_acl_acp_and_grants(s) PutBucketAcl_invalid_owner(s) PutBucketAcl_success_access_denied(s) - PutBucketAcl_success(s) + PutBucketAcl_success_grants(s) + PutBucketAcl_success_canned_acl(s) + PutBucketAcl_success_acp(s) } func TestGetBucketAcl(s *S3Conf) { diff --git a/integration/tests.go b/integration/tests.go index 6edc0c4..39819ce 100644 --- a/integration/tests.go +++ b/integration/tests.go @@ -2268,8 +2268,91 @@ func PutBucketAcl_success_access_denied(s *S3Conf) { }) } -func PutBucketAcl_success(s *S3Conf) { - testName := "PutBucketAcl_success" +func PutBucketAcl_success_canned_acl(s *S3Conf) { + testName := "PutBucketAcl_success_canned_acl" + actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + err := createUsers(s, []user{{"grt1", "grt1secret", "user"}}) + if err != nil { + return err + } + + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.PutBucketAcl(ctx, &s3.PutBucketAclInput{ + Bucket: &bucket, + AccessControlPolicy: &types.AccessControlPolicy{ + Owner: &types.Owner{ + ID: &s.awsID, + }, + }, + ACL: types.BucketCannedACLPublicReadWrite, + }) + cancel() + if err != nil { + return err + } + + newConf := *s + newConf.awsID = "grt1" + newConf.awsSecret = "grt1secret" + userClient := s3.NewFromConfig(newConf.Config()) + + err = putObjects(userClient, []string{"my-obj"}, bucket) + if err != nil { + return err + } + + return nil + }) +} + +func PutBucketAcl_success_acp(s *S3Conf) { + testName := "PutBucketAcl_success_acp" + actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + err := createUsers(s, []user{{"grt1", "grt1secret", "user"}}) + if err != nil { + return err + } + + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.PutBucketAcl(ctx, &s3.PutBucketAclInput{ + Bucket: &bucket, + AccessControlPolicy: &types.AccessControlPolicy{ + Owner: &types.Owner{ + ID: &s.awsID, + }, + }, + GrantRead: getPtr("grt1"), + }) + cancel() + if err != nil { + return err + } + + newConf := *s + newConf.awsID = "grt1" + newConf.awsSecret = "grt1secret" + userClient := s3.NewFromConfig(newConf.Config()) + + err = putObjects(userClient, []string{"my-obj"}, bucket) + if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrAccessDenied)); err != nil { + return err + } + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + _, err = userClient.HeadBucket(ctx, &s3.HeadBucketInput{ + Bucket: &bucket, + }) + cancel() + if err != nil { + return err + } + + return nil + }) +} + +func PutBucketAcl_success_grants(s *S3Conf) { + testName := "PutBucketAcl_success_grants" actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { err := createUsers(s, []user{{"grt1", "grt1secret", "user"}}) if err != nil { diff --git a/s3api/controllers/base.go b/s3api/controllers/base.go index 5888b34..927c6f4 100644 --- a/s3api/controllers/base.go +++ b/s3api/controllers/base.go @@ -294,9 +294,8 @@ func (c S3ApiController) ListActions(ctx *fiber.Ctx) error { } func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error { - bucket, bucketOwner, acl, grantFullControl, grantRead, grantReadACP, granWrite, grantWriteACP, access, isRoot := + bucket, acl, grantFullControl, grantRead, grantReadACP, granWrite, grantWriteACP, access, isRoot := ctx.Params("bucket"), - ctx.Get("X-Amz-Expected-Bucket-Owner"), ctx.Get("X-Amz-Acl"), ctx.Get("X-Amz-Grant-Full-Control"), ctx.Get("X-Amz-Grant-Read"), @@ -309,21 +308,21 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error { grants := grantFullControl + grantRead + grantReadACP + granWrite + grantWriteACP if ctx.Request().URI().QueryArgs().Has("acl") { - parsedAcl := ctx.Locals("parsedAcl").(auth.ACL) var input *s3.PutBucketAclInput + var accessControlPolicy auth.AccessControlPolicy + parsedAcl := ctx.Locals("parsedAcl").(auth.ACL) if err := auth.VerifyACL(parsedAcl, bucket, access, "WRITE_ACP", isRoot); err != nil { return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "PutBucketAcl", BucketOwner: parsedAcl.Owner}) } - if len(ctx.Body()) > 0 { - if grants+acl != "" { - return SendResponse(ctx, s3err.GetAPIError(s3err.ErrInvalidRequest), &MetaOpts{Logger: c.logger, Action: "PutBucketAcl", BucketOwner: parsedAcl.Owner}) - } + err := xml.Unmarshal(ctx.Body(), &accessControlPolicy) + if err != nil { + return SendResponse(ctx, s3err.GetAPIError(s3err.ErrInvalidRequest), &MetaOpts{Logger: c.logger, Action: "PutBucketAcl", BucketOwner: parsedAcl.Owner}) + } - var accessControlPolicy auth.AccessControlPolicy - err := xml.Unmarshal(ctx.Body(), &accessControlPolicy) - if err != nil { + if len(accessControlPolicy.AccessControlList.Grants) > 0 { + if grants+acl != "" { return SendResponse(ctx, s3err.GetAPIError(s3err.ErrInvalidRequest), &MetaOpts{Logger: c.logger, Action: "PutBucketAcl", BucketOwner: parsedAcl.Owner}) } @@ -337,14 +336,14 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error { if acl != "private" && acl != "public-read" && acl != "public-read-write" { return SendResponse(ctx, s3err.GetAPIError(s3err.ErrInvalidRequest), &MetaOpts{Logger: c.logger, Action: "PutBucketAcl", BucketOwner: parsedAcl.Owner}) } - if len(ctx.Body()) > 0 || grants != "" { + if len(accessControlPolicy.AccessControlList.Grants) > 0 || grants != "" { return SendResponse(ctx, s3err.GetAPIError(s3err.ErrInvalidRequest), &MetaOpts{Logger: c.logger, Action: "PutBucketAcl", BucketOwner: parsedAcl.Owner}) } input = &s3.PutBucketAclInput{ Bucket: &bucket, ACL: types.BucketCannedACL(acl), - AccessControlPolicy: &types.AccessControlPolicy{Owner: &types.Owner{ID: &bucketOwner}}, + AccessControlPolicy: &types.AccessControlPolicy{Owner: &accessControlPolicy.Owner}, } } if grants != "" { @@ -355,7 +354,7 @@ func (c S3ApiController) PutBucketActions(ctx *fiber.Ctx) error { GrantReadACP: &grantReadACP, GrantWrite: &granWrite, GrantWriteACP: &grantWriteACP, - AccessControlPolicy: &types.AccessControlPolicy{Owner: &types.Owner{ID: &bucketOwner}}, + AccessControlPolicy: &types.AccessControlPolicy{Owner: &accessControlPolicy.Owner}, ACL: "", } } diff --git a/s3api/controllers/base_test.go b/s3api/controllers/base_test.go index 9bbd48e..c9403eb 100644 --- a/s3api/controllers/base_test.go +++ b/s3api/controllers/base_test.go @@ -481,6 +481,22 @@ func TestS3ApiController_PutBucketActions(t *testing.T) { ` + invOwnerBody := ` + + + hello + + + ` + + succBody := ` + + + valid access + + + ` + s3ApiController := S3ApiController{ be: &BackendMock{ GetBucketAclFunc: func(context.Context, *s3.GetBucketAclInput) ([]byte, error) { @@ -514,14 +530,12 @@ func TestS3ApiController_PutBucketActions(t *testing.T) { errAclReq.Header.Set("X-Amz-Grant-Read", "hello") // PutBucketAcl incorrect bucket owner case - incorrectBucketOwner := httptest.NewRequest(http.MethodPut, "/my-bucket?acl", nil) + incorrectBucketOwner := httptest.NewRequest(http.MethodPut, "/my-bucket?acl", strings.NewReader(invOwnerBody)) incorrectBucketOwner.Header.Set("X-Amz-Acl", "private") - incorrectBucketOwner.Header.Set("X-Amz-Expected-Bucket-Owner", "invalid access") // PutBucketAcl acl success - aclSuccReq := httptest.NewRequest(http.MethodPut, "/my-bucket?acl", nil) + aclSuccReq := httptest.NewRequest(http.MethodPut, "/my-bucket?acl", strings.NewReader(succBody)) aclSuccReq.Header.Set("X-Amz-Acl", "private") - aclSuccReq.Header.Set("X-Amz-Expected-Bucket-Owner", "valid access") // Invalid acl body case errAclBodyReq := httptest.NewRequest(http.MethodPut, "/my-bucket?acl", strings.NewReader(body))