feat: implements tagging support for CreateBucket

Closes #1595

This implementation diverges from AWS S3 behavior. The `CreateBucket` request body is no longer ignored. Based on the S3 request body schema, the gateway parses only the `LocationConstraint` and `Tags` fields. If the `LocationConstraint` does not match the gateway’s region, it returns an `InvalidLocationConstraint` error.

In AWS S3, tagging during bucket creation is supported only for directory buckets. The gateway extends this support to general-purpose buckets.

If the request body is malformed, the gateway returns a `MalformedXML` error.
This commit is contained in:
niksis02
2025-10-31 00:59:56 +04:00
parent 8a733b8cbf
commit 9bde1ddb3a
10 changed files with 326 additions and 3 deletions
+29
View File
@@ -533,6 +533,32 @@ func (c S3ApiController) CreateBucket(ctx *fiber.Ctx) (*Response, error) {
}, s3err.GetAPIError(s3err.ErrBothCannedAndHeaderGrants)
}
var body s3response.CreateBucketConfiguration
if len(ctx.Body()) != 0 {
// request body is optional for CreateBucket
err := xml.Unmarshal(ctx.Body(), &body)
if err != nil {
debuglogger.Logf("failed to parse the request body: %v", err)
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: acct.Access,
},
}, s3err.GetAPIError(s3err.ErrMalformedXML)
}
if body.LocationConstraint != "" {
region := utils.ContextKeyRegion.Get(ctx).(string)
if body.LocationConstraint != region {
debuglogger.Logf("invalid location constraint: %s", body.LocationConstraint)
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: acct.Access,
},
}, s3err.GetAPIError(s3err.ErrInvalidLocationConstraint)
}
}
}
defACL := auth.ACL{
Owner: acct.Access,
}
@@ -562,6 +588,9 @@ func (c S3ApiController) CreateBucket(ctx *fiber.Ctx) (*Response, error) {
Bucket: &bucket,
ObjectOwnership: objectOwnership,
ObjectLockEnabledForBucket: &lockEnabled,
CreateBucketConfiguration: &types.CreateBucketConfiguration{
Tags: body.TagSet,
},
}, updAcl)
return &Response{
MetaOpts: &MetaOptions{
+36
View File
@@ -695,6 +695,11 @@ func TestS3ApiController_CreateBucket(t *testing.T) {
Role: auth.RoleUser,
}
invLocConstBody, err := xml.Marshal(s3response.CreateBucketConfiguration{
LocationConstraint: "us-west-1",
})
assert.NoError(t, err)
tests := []struct {
name string
input testInput
@@ -729,6 +734,37 @@ func TestS3ApiController_CreateBucket(t *testing.T) {
err: s3err.GetAPIError(s3err.ErrInvalidBucketName),
},
},
{
name: "malformed body",
input: testInput{
locals: map[utils.ContextKey]any{
utils.ContextKeyAccount: adminAcc,
},
body: []byte("invalid_body"),
},
output: testOutput{
response: &Response{
MetaOpts: &MetaOptions{BucketOwner: adminAcc.Access},
},
err: s3err.GetAPIError(s3err.ErrMalformedXML),
},
},
{
name: "invalid location constraint",
input: testInput{
locals: map[utils.ContextKey]any{
utils.ContextKeyAccount: adminAcc,
utils.ContextKeyRegion: "us-east-1",
},
body: invLocConstBody,
},
output: testOutput{
response: &Response{
MetaOpts: &MetaOptions{BucketOwner: adminAcc.Access},
},
err: s3err.GetAPIError(s3err.ErrInvalidLocationConstraint),
},
},
{
name: "invalid ownership",
input: testInput{