mirror of
https://github.com/versity/versitygw.git
synced 2026-01-04 11:03:57 +00:00
fix: adds versionId validation for object level actions
Fixes #1630 S3 returns `InvalidArgument: Invalid version id specified` for invalid version IDs in object-level actions that accept `versionId` as a query parameter. The `versionId` in S3 follows a specific structure, and if the input string doesn’t match this structure, the error is returned. In the gateway, the `versionId` is generated using the `ulid` package, which also has a defined structure. This PR adds validation for object-level operations that work with object versions by using the ULID parser. These actions include: `HeadObject`, `GetObject`, `PutObjectTagging`, `GetObjectTagging`, `DeleteObjectTagging`, `PutObjectLegalHold`, `GetObjectLegalHold`, `PutObjectRetention`, `GetObjectRetention`, `DeleteObject`, `CopyObject`, `UploadPartCopy`, and `GetObjectAttributes`.
This commit is contained in:
@@ -56,6 +56,15 @@ func (c S3ApiController) DeleteObjectTagging(ctx *fiber.Ctx) (*Response, error)
|
||||
}, err
|
||||
}
|
||||
|
||||
err = utils.ValidateVersionId(versionId)
|
||||
if err != nil {
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
err = c.be.DeleteObjectTagging(ctx.Context(), bucket, key, versionId)
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
@@ -147,6 +156,15 @@ func (c S3ApiController) DeleteObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
}, err
|
||||
}
|
||||
|
||||
err = utils.ValidateVersionId(versionId)
|
||||
if err != nil {
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
err = auth.CheckObjectAccess(
|
||||
ctx.Context(),
|
||||
bucket,
|
||||
|
||||
@@ -45,6 +45,23 @@ func TestS3ApiController_DeleteObjectTagging(t *testing.T) {
|
||||
err: s3err.GetAPIError(s3err.ErrAccessDenied),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid versionId",
|
||||
input: testInput{
|
||||
locals: defaultLocals,
|
||||
queries: map[string]string{
|
||||
"versionId": "invalid_versionId",
|
||||
},
|
||||
},
|
||||
output: testOutput{
|
||||
response: &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: "root",
|
||||
},
|
||||
},
|
||||
err: s3err.GetAPIError(s3err.ErrInvalidVersionId),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "backend returns error",
|
||||
input: testInput{
|
||||
@@ -99,7 +116,8 @@ func TestS3ApiController_DeleteObjectTagging(t *testing.T) {
|
||||
tt.output.response,
|
||||
tt.output.err,
|
||||
ctxInputs{
|
||||
locals: tt.input.locals,
|
||||
locals: tt.input.locals,
|
||||
queries: tt.input.queries,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -206,6 +224,23 @@ func TestS3ApiController_DeleteObject(t *testing.T) {
|
||||
err: s3err.GetAPIError(s3err.ErrAccessDenied),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid versionId",
|
||||
input: testInput{
|
||||
locals: defaultLocals,
|
||||
queries: map[string]string{
|
||||
"versionId": "invalid_versionId",
|
||||
},
|
||||
},
|
||||
output: testOutput{
|
||||
response: &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: "root",
|
||||
},
|
||||
},
|
||||
err: s3err.GetAPIError(s3err.ErrInvalidVersionId),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "object locked",
|
||||
input: testInput{
|
||||
@@ -289,7 +324,8 @@ func TestS3ApiController_DeleteObject(t *testing.T) {
|
||||
tt.output.response,
|
||||
tt.output.err,
|
||||
ctxInputs{
|
||||
locals: tt.input.locals,
|
||||
locals: tt.input.locals,
|
||||
queries: tt.input.queries,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -60,6 +60,15 @@ func (c S3ApiController) GetObjectTagging(ctx *fiber.Ctx) (*Response, error) {
|
||||
}, err
|
||||
}
|
||||
|
||||
err = utils.ValidateVersionId(versionId)
|
||||
if err != nil {
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
data, err := c.be.GetObjectTagging(ctx.Context(), bucket, key, versionId)
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -114,6 +123,15 @@ func (c S3ApiController) GetObjectRetention(ctx *fiber.Ctx) (*Response, error) {
|
||||
}, err
|
||||
}
|
||||
|
||||
err = utils.ValidateVersionId(versionId)
|
||||
if err != nil {
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
data, err := c.be.GetObjectRetention(ctx.Context(), bucket, key, versionId)
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -161,6 +179,15 @@ func (c S3ApiController) GetObjectLegalHold(ctx *fiber.Ctx) (*Response, error) {
|
||||
}, err
|
||||
}
|
||||
|
||||
err = utils.ValidateVersionId(versionId)
|
||||
if err != nil {
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
data, err := c.be.GetObjectLegalHold(ctx.Context(), bucket, key, versionId)
|
||||
return &Response{
|
||||
Data: auth.ParseObjectLegalHoldOutput(data),
|
||||
@@ -313,6 +340,15 @@ func (c S3ApiController) GetObjectAttributes(ctx *fiber.Ctx) (*Response, error)
|
||||
}, err
|
||||
}
|
||||
|
||||
err = utils.ValidateVersionId(versionId)
|
||||
if err != nil {
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
// parse max parts
|
||||
maxParts, err := utils.ParseUint(maxPartsStr)
|
||||
if err != nil {
|
||||
@@ -456,6 +492,15 @@ func (c S3ApiController) GetObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
partNumber = &partNumberQuery
|
||||
}
|
||||
|
||||
err = utils.ValidateVersionId(versionId)
|
||||
if err != nil {
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
// validate the checksum mode
|
||||
if checksumMode != "" && checksumMode != types.ChecksumModeEnabled {
|
||||
debuglogger.Logf("invalid x-amz-checksum-mode header value: %v", checksumMode)
|
||||
|
||||
@@ -52,6 +52,23 @@ func TestS3ApiController_GetObjectTagging(t *testing.T) {
|
||||
err: s3err.GetAPIError(s3err.ErrAccessDenied),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid versionId",
|
||||
input: testInput{
|
||||
locals: defaultLocals,
|
||||
queries: map[string]string{
|
||||
"versionId": "invalid_versionId",
|
||||
},
|
||||
},
|
||||
output: testOutput{
|
||||
response: &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: "root",
|
||||
},
|
||||
},
|
||||
err: s3err.GetAPIError(s3err.ErrInvalidVersionId),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "backend returns error",
|
||||
input: testInput{
|
||||
@@ -113,8 +130,9 @@ func TestS3ApiController_GetObjectTagging(t *testing.T) {
|
||||
tt.output.response,
|
||||
tt.output.err,
|
||||
ctxInputs{
|
||||
locals: tt.input.locals,
|
||||
body: tt.input.body,
|
||||
locals: tt.input.locals,
|
||||
body: tt.input.body,
|
||||
queries: tt.input.queries,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -147,6 +165,23 @@ func TestS3ApiController_GetObjectRetention(t *testing.T) {
|
||||
err: s3err.GetAPIError(s3err.ErrAccessDenied),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid versionId",
|
||||
input: testInput{
|
||||
locals: defaultLocals,
|
||||
queries: map[string]string{
|
||||
"versionId": "invalid_versionId",
|
||||
},
|
||||
},
|
||||
output: testOutput{
|
||||
response: &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: "root",
|
||||
},
|
||||
},
|
||||
err: s3err.GetAPIError(s3err.ErrInvalidVersionId),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "backend returns error",
|
||||
input: testInput{
|
||||
@@ -218,8 +253,9 @@ func TestS3ApiController_GetObjectRetention(t *testing.T) {
|
||||
tt.output.response,
|
||||
tt.output.err,
|
||||
ctxInputs{
|
||||
locals: tt.input.locals,
|
||||
body: tt.input.body,
|
||||
locals: tt.input.locals,
|
||||
body: tt.input.body,
|
||||
queries: tt.input.queries,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -249,6 +285,23 @@ func TestS3ApiController_GetObjectLegalHold(t *testing.T) {
|
||||
err: s3err.GetAPIError(s3err.ErrAccessDenied),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid versionId",
|
||||
input: testInput{
|
||||
locals: defaultLocals,
|
||||
queries: map[string]string{
|
||||
"versionId": "invalid_versionId",
|
||||
},
|
||||
},
|
||||
output: testOutput{
|
||||
response: &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: "root",
|
||||
},
|
||||
},
|
||||
err: s3err.GetAPIError(s3err.ErrInvalidVersionId),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "backend returns error",
|
||||
input: testInput{
|
||||
@@ -305,8 +358,9 @@ func TestS3ApiController_GetObjectLegalHold(t *testing.T) {
|
||||
tt.output.response,
|
||||
tt.output.err,
|
||||
ctxInputs{
|
||||
locals: tt.input.locals,
|
||||
body: tt.input.body,
|
||||
locals: tt.input.locals,
|
||||
body: tt.input.body,
|
||||
queries: tt.input.queries,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -555,6 +609,23 @@ func TestS3ApiController_GetObjectAttributes(t *testing.T) {
|
||||
err: s3err.GetAPIError(s3err.ErrAccessDenied),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid versionId",
|
||||
input: testInput{
|
||||
locals: defaultLocals,
|
||||
queries: map[string]string{
|
||||
"versionId": "invalid_versionId",
|
||||
},
|
||||
},
|
||||
output: testOutput{
|
||||
response: &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: "root",
|
||||
},
|
||||
},
|
||||
err: s3err.GetAPIError(s3err.ErrInvalidVersionId),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid max parts",
|
||||
input: testInput{
|
||||
@@ -663,6 +734,7 @@ func TestS3ApiController_GetObjectAttributes(t *testing.T) {
|
||||
locals: tt.input.locals,
|
||||
body: tt.input.body,
|
||||
headers: tt.input.headers,
|
||||
queries: tt.input.queries,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -693,6 +765,23 @@ func TestS3ApiController_GetObject(t *testing.T) {
|
||||
err: s3err.GetAPIError(s3err.ErrAccessDenied),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid versionId",
|
||||
input: testInput{
|
||||
locals: defaultLocals,
|
||||
queries: map[string]string{
|
||||
"versionId": "invalid_versionId",
|
||||
},
|
||||
},
|
||||
output: testOutput{
|
||||
response: &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: "root",
|
||||
},
|
||||
},
|
||||
err: s3err.GetAPIError(s3err.ErrInvalidVersionId),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid checksum mode",
|
||||
input: testInput{
|
||||
@@ -757,7 +846,7 @@ func TestS3ApiController_GetObject(t *testing.T) {
|
||||
"Range": "100-200",
|
||||
},
|
||||
queries: map[string]string{
|
||||
"versionId": "versionId",
|
||||
"versionId": "01BX5ZZKBKACTAV9WEVGEMMVRZ",
|
||||
},
|
||||
locals: defaultLocals,
|
||||
beRes: &s3.GetObjectOutput{
|
||||
|
||||
@@ -80,6 +80,15 @@ func (c S3ApiController) HeadObject(ctx *fiber.Ctx) (*Response, error) {
|
||||
partNumber = &partNumberQuery
|
||||
}
|
||||
|
||||
err = utils.ValidateVersionId(versionId)
|
||||
if err != nil {
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
checksumMode := types.ChecksumMode(strings.ToUpper(ctx.Get("x-amz-checksum-mode")))
|
||||
if checksumMode != "" && checksumMode != types.ChecksumModeEnabled {
|
||||
debuglogger.Logf("invalid x-amz-checksum-mode header value: %v", checksumMode)
|
||||
|
||||
@@ -51,13 +51,30 @@ func TestS3ApiController_HeadObject(t *testing.T) {
|
||||
err: s3err.GetAPIError(s3err.ErrAccessDenied),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid versionId",
|
||||
input: testInput{
|
||||
locals: defaultLocals,
|
||||
queries: map[string]string{
|
||||
"versionId": "invalid_versionId",
|
||||
},
|
||||
},
|
||||
output: testOutput{
|
||||
response: &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: "root",
|
||||
},
|
||||
},
|
||||
err: s3err.GetAPIError(s3err.ErrInvalidVersionId),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid part number",
|
||||
input: testInput{
|
||||
locals: defaultLocals,
|
||||
queries: map[string]string{
|
||||
"partNumber": "-4",
|
||||
"versionId": "id",
|
||||
"versionId": "01BX5ZZKBKACTAV9WEVGEMMVRZ",
|
||||
},
|
||||
},
|
||||
output: testOutput{
|
||||
|
||||
@@ -61,6 +61,15 @@ func (c S3ApiController) PutObjectTagging(ctx *fiber.Ctx) (*Response, error) {
|
||||
}, err
|
||||
}
|
||||
|
||||
err = utils.ValidateVersionId(versionId)
|
||||
if err != nil {
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
tagging, err := utils.ParseTagging(ctx.Body(), utils.TagLimitObject)
|
||||
if err != nil {
|
||||
return &Response{
|
||||
@@ -89,7 +98,7 @@ func (c S3ApiController) PutObjectRetention(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx)
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
if err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
@@ -99,7 +108,17 @@ func (c S3ApiController) PutObjectRetention(ctx *fiber.Ctx) (*Response, error) {
|
||||
Object: key,
|
||||
Action: auth.PutObjectRetentionAction,
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
}); err != nil {
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
err = utils.ValidateVersionId(versionId)
|
||||
if err != nil {
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
@@ -154,7 +173,7 @@ func (c S3ApiController) PutObjectLegalHold(ctx *fiber.Ctx) (*Response, error) {
|
||||
IsBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx)
|
||||
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
|
||||
|
||||
if err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{
|
||||
Readonly: c.readonly,
|
||||
Acl: parsedAcl,
|
||||
AclPermission: auth.PermissionWrite,
|
||||
@@ -164,7 +183,17 @@ func (c S3ApiController) PutObjectLegalHold(ctx *fiber.Ctx) (*Response, error) {
|
||||
Object: key,
|
||||
Action: auth.PutObjectLegalHoldAction,
|
||||
IsPublicRequest: IsBucketPublic,
|
||||
}); err != nil {
|
||||
})
|
||||
if err != nil {
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
err = utils.ValidateVersionId(versionId)
|
||||
if err != nil {
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
@@ -191,7 +220,7 @@ func (c S3ApiController) PutObjectLegalHold(ctx *fiber.Ctx) (*Response, error) {
|
||||
}, s3err.GetAPIError(s3err.ErrMalformedXML)
|
||||
}
|
||||
|
||||
err := c.be.PutObjectLegalHold(ctx.Context(), bucket, key, versionId, legalHold.Status == types.ObjectLockLegalHoldStatusOn)
|
||||
err = c.be.PutObjectLegalHold(ctx.Context(), bucket, key, versionId, legalHold.Status == types.ObjectLockLegalHoldStatusOn)
|
||||
return &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: parsedAcl.Owner,
|
||||
|
||||
@@ -64,6 +64,23 @@ func TestS3ApiController_PutObjectTagging(t *testing.T) {
|
||||
err: s3err.GetAPIError(s3err.ErrAccessDenied),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid versionId",
|
||||
input: testInput{
|
||||
locals: defaultLocals,
|
||||
queries: map[string]string{
|
||||
"versionId": "invalid_versionId",
|
||||
},
|
||||
},
|
||||
output: testOutput{
|
||||
response: &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: "root",
|
||||
},
|
||||
},
|
||||
err: s3err.GetAPIError(s3err.ErrInvalidVersionId),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid request body",
|
||||
input: testInput{
|
||||
@@ -133,8 +150,9 @@ func TestS3ApiController_PutObjectTagging(t *testing.T) {
|
||||
tt.output.response,
|
||||
tt.output.err,
|
||||
ctxInputs{
|
||||
locals: tt.input.locals,
|
||||
body: tt.input.body,
|
||||
locals: tt.input.locals,
|
||||
body: tt.input.body,
|
||||
queries: tt.input.queries,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -171,6 +189,23 @@ func TestS3ApiController_PutObjectRetention(t *testing.T) {
|
||||
err: s3err.GetAPIError(s3err.ErrAccessDenied),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid versionId",
|
||||
input: testInput{
|
||||
locals: defaultLocals,
|
||||
queries: map[string]string{
|
||||
"versionId": "invalid_versionId",
|
||||
},
|
||||
},
|
||||
output: testOutput{
|
||||
response: &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: "root",
|
||||
},
|
||||
},
|
||||
err: s3err.GetAPIError(s3err.ErrInvalidVersionId),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid request body",
|
||||
input: testInput{
|
||||
@@ -262,6 +297,7 @@ func TestS3ApiController_PutObjectRetention(t *testing.T) {
|
||||
locals: tt.input.locals,
|
||||
body: tt.input.body,
|
||||
headers: tt.input.headers,
|
||||
queries: tt.input.queries,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -298,6 +334,23 @@ func TestS3ApiController_PutObjectLegalHold(t *testing.T) {
|
||||
err: s3err.GetAPIError(s3err.ErrAccessDenied),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid request body",
|
||||
input: testInput{
|
||||
locals: defaultLocals,
|
||||
queries: map[string]string{
|
||||
"versionId": "invalid_versionId",
|
||||
},
|
||||
},
|
||||
output: testOutput{
|
||||
response: &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: "root",
|
||||
},
|
||||
},
|
||||
err: s3err.GetAPIError(s3err.ErrInvalidVersionId),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid request body",
|
||||
input: testInput{
|
||||
@@ -380,8 +433,9 @@ func TestS3ApiController_PutObjectLegalHold(t *testing.T) {
|
||||
tt.output.response,
|
||||
tt.output.err,
|
||||
ctxInputs{
|
||||
locals: tt.input.locals,
|
||||
body: tt.input.body,
|
||||
locals: tt.input.locals,
|
||||
body: tt.input.body,
|
||||
queries: tt.input.queries,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -579,6 +633,26 @@ func TestS3ApiController_UploadPartCopy(t *testing.T) {
|
||||
err: s3err.GetAPIError(s3err.ErrAccessDenied),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid copy source: invalid versionId",
|
||||
input: testInput{
|
||||
locals: defaultLocals,
|
||||
headers: map[string]string{
|
||||
"X-Amz-Copy-Source": "bucket/object?versionId=invalid_versionId",
|
||||
},
|
||||
queries: map[string]string{
|
||||
"partNumber": "2",
|
||||
},
|
||||
},
|
||||
output: testOutput{
|
||||
response: &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: "root",
|
||||
},
|
||||
},
|
||||
err: s3err.GetAPIError(s3err.ErrInvalidVersionId),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid copy source",
|
||||
input: testInput{
|
||||
@@ -840,7 +914,24 @@ func TestS3ApiController_CopyObject(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid copy source",
|
||||
name: "invalid copy source: versionId",
|
||||
input: testInput{
|
||||
locals: defaultLocals,
|
||||
headers: map[string]string{
|
||||
"X-Amz-Copy-Source": "bucket/object?versionId=invalid_versionId",
|
||||
},
|
||||
},
|
||||
output: testOutput{
|
||||
response: &Response{
|
||||
MetaOpts: &MetaOptions{
|
||||
BucketOwner: "root",
|
||||
},
|
||||
},
|
||||
err: s3err.GetAPIError(s3err.ErrInvalidVersionId),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "non empty request body",
|
||||
input: testInput{
|
||||
locals: defaultLocals,
|
||||
headers: map[string]string{
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/oklog/ulid/v2"
|
||||
"github.com/valyala/fasthttp"
|
||||
"github.com/versity/versitygw/debuglogger"
|
||||
"github.com/versity/versitygw/s3err"
|
||||
@@ -818,7 +819,7 @@ func ValidateCopySource(copysource string) error {
|
||||
|
||||
// cut till the versionId as it's the only query param
|
||||
// that is recognized in copy source
|
||||
object, _, _ := strings.Cut(rest, "?versionId=")
|
||||
object, versionId, _ := strings.Cut(rest, "?versionId=")
|
||||
|
||||
// objects containing '../', '...../' ... are considered valid in AWS
|
||||
// but for the security purposes these should be considered as invalid
|
||||
@@ -828,6 +829,12 @@ func ValidateCopySource(copysource string) error {
|
||||
return s3err.GetAPIError(s3err.ErrInvalidCopySourceObject)
|
||||
}
|
||||
|
||||
// validate the versionId
|
||||
err = ValidateVersionId(versionId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -847,3 +854,17 @@ func ApplyOverride(original, override *string) *string {
|
||||
}
|
||||
return original
|
||||
}
|
||||
|
||||
// ValidateVersionId check if the input versionId is 'ulid' compatible
|
||||
func ValidateVersionId(versionId string) error {
|
||||
if versionId == "" || versionId == "null" {
|
||||
return nil
|
||||
}
|
||||
_, err := ulid.Parse(versionId)
|
||||
if err != nil {
|
||||
debuglogger.Logf("invalid versionId: %s", versionId)
|
||||
return s3err.GetAPIError(s3err.ErrInvalidVersionId)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -955,12 +955,15 @@ func TestValidateCopySource(t *testing.T) {
|
||||
{"invalid object name 3", "bucket", s3err.GetAPIError(s3err.ErrInvalidCopySourceObject)},
|
||||
{"invalid object name 4", "bucket/../foo/dir/../../../", s3err.GetAPIError(s3err.ErrInvalidCopySourceObject)},
|
||||
{"invalid object name 5", "bucket/.?versionId=smth", s3err.GetAPIError(s3err.ErrInvalidCopySourceObject)},
|
||||
// invalid versionId
|
||||
{"invalid versionId 1", "bucket/object?versionId=invalid", s3err.GetAPIError(s3err.ErrInvalidVersionId)},
|
||||
{"invalid versionId 2", "bucket/object?versionId=01BX5ZZKBKACTAV9WEVGEMMV", s3err.GetAPIError(s3err.ErrInvalidVersionId)},
|
||||
// success
|
||||
{"no error 1", "bucket/object", nil},
|
||||
{"no error 2", "bucket/object/key", nil},
|
||||
{"no error 3", "bucket/4*&(*&(89765))", nil},
|
||||
{"no error 4", "bucket/foo/../bar", nil},
|
||||
{"no error 5", "bucket/foo/bar/baz?versionId=id", nil},
|
||||
{"no error 5", "bucket/foo/bar/baz?versionId=01BX5ZZKBKACTAV9WEVGEMMVRZ", nil},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
@@ -988,31 +988,39 @@ func TestVersioning(ts *TestState) {
|
||||
ts.Run(Versioning_PutObject_overwrite_null_versionId_obj)
|
||||
ts.Run(Versioning_PutObject_success)
|
||||
// CopyObject action
|
||||
ts.Run(Versioning_CopyObject_invalid_versionId)
|
||||
ts.Run(Versioning_CopyObject_success)
|
||||
ts.Run(Versioning_CopyObject_non_existing_version_id)
|
||||
ts.Run(Versioning_CopyObject_from_an_object_version)
|
||||
ts.Run(Versioning_CopyObject_special_chars)
|
||||
// HeadObject action
|
||||
ts.Run(Versioning_HeadObject_invalid_versionId)
|
||||
ts.Run(Versioning_HeadObject_non_existing_object_version)
|
||||
ts.Run(Versioning_HeadObject_invalid_parent)
|
||||
ts.Run(Versioning_HeadObject_success)
|
||||
ts.Run(Versioning_HeadObject_without_versionId)
|
||||
ts.Run(Versioning_HeadObject_delete_marker)
|
||||
// GetObject action
|
||||
ts.Run(Versioning_GetObject_invalid_versionId)
|
||||
ts.Run(Versioning_GetObject_non_existing_object_version)
|
||||
ts.Run(Versioning_GetObject_success)
|
||||
ts.Run(Versioning_GetObject_delete_marker_without_versionId)
|
||||
ts.Run(Versioning_GetObject_delete_marker)
|
||||
ts.Run(Versioning_GetObject_null_versionId_obj)
|
||||
// object tagging actions
|
||||
ts.Run(Versioning_PutObjectTagging_invalid_versionId)
|
||||
ts.Run(Versioning_PutObjectTagging_non_existing_object_version)
|
||||
ts.Run(Versioning_GetObjectTagging_invalid_versionId)
|
||||
ts.Run(Versioning_GetObjectTagging_non_existing_object_version)
|
||||
ts.Run(Versioning_DeleteObjectTagging_invalid_versionId)
|
||||
ts.Run(Versioning_DeleteObjectTagging_non_existing_object_version)
|
||||
ts.Run(Versioning_PutGetDeleteObjectTagging_success)
|
||||
// GetObjectAttributes action
|
||||
ts.Run(Versioning_GetObjectAttributes_invalid_versionId)
|
||||
ts.Run(Versioning_GetObjectAttributes_object_version)
|
||||
ts.Run(Versioning_GetObjectAttributes_delete_marker)
|
||||
// DeleteObject actions
|
||||
ts.Run(Versioning_DeleteObject_invalid_versionId)
|
||||
ts.Run(Versioning_DeleteObject_delete_object_version)
|
||||
ts.Run(Versioning_DeleteObject_non_existing_object)
|
||||
ts.Run(Versioning_DeleteObject_delete_a_delete_marker)
|
||||
@@ -1033,6 +1041,7 @@ func TestVersioning(ts *TestState) {
|
||||
// Multipart upload
|
||||
ts.Run(Versioning_Multipart_Upload_success)
|
||||
ts.Run(Versioning_Multipart_Upload_overwrite_an_object)
|
||||
ts.Run(Versioning_UploadPartCopy_invalid_versionId)
|
||||
ts.Run(Versioning_UploadPartCopy_non_existing_versionId)
|
||||
ts.Run(Versioning_UploadPartCopy_from_an_object_version)
|
||||
// Object lock configuration
|
||||
@@ -1040,11 +1049,15 @@ func TestVersioning(ts *TestState) {
|
||||
ts.Run(Versioning_Enable_object_lock)
|
||||
ts.Run(Versioning_status_switch_to_suspended_with_object_lock)
|
||||
// Object-Lock Retention
|
||||
ts.Run(Versioning_PutObjectRetention_invalid_versionId)
|
||||
ts.Run(Versioning_PutObjectRetention_non_existing_object_version)
|
||||
ts.Run(Versioning_GetObjectRetention_invalid_versionId)
|
||||
ts.Run(Versioning_GetObjectRetention_non_existing_object_version)
|
||||
ts.Run(Versioning_Put_GetObjectRetention_success)
|
||||
// Object-Lock Legal hold
|
||||
ts.Run(Versioning_PutObjectLegalHold_invalid_versionId)
|
||||
ts.Run(Versioning_PutObjectLegalHold_non_existing_object_version)
|
||||
ts.Run(Versioning_GetObjectLegalHold_invalid_versionId)
|
||||
ts.Run(Versioning_GetObjectLegalHold_non_existing_object_version)
|
||||
ts.Run(Versioning_Put_GetObjectLegalHold_success)
|
||||
// WORM protection
|
||||
@@ -1627,26 +1640,34 @@ func GetIntTests() IntTests {
|
||||
"Versioning_PutObject_null_versionId_obj": Versioning_PutObject_null_versionId_obj,
|
||||
"Versioning_PutObject_overwrite_null_versionId_obj": Versioning_PutObject_overwrite_null_versionId_obj,
|
||||
"Versioning_PutObject_success": Versioning_PutObject_success,
|
||||
"Versioning_CopyObject_invalid_versionId": Versioning_CopyObject_invalid_versionId,
|
||||
"Versioning_CopyObject_success": Versioning_CopyObject_success,
|
||||
"Versioning_CopyObject_non_existing_version_id": Versioning_CopyObject_non_existing_version_id,
|
||||
"Versioning_CopyObject_from_an_object_version": Versioning_CopyObject_from_an_object_version,
|
||||
"Versioning_CopyObject_special_chars": Versioning_CopyObject_special_chars,
|
||||
"Versioning_HeadObject_invalid_versionId": Versioning_HeadObject_invalid_versionId,
|
||||
"Versioning_HeadObject_non_existing_object_version": Versioning_HeadObject_non_existing_object_version,
|
||||
"Versioning_HeadObject_invalid_parent": Versioning_HeadObject_invalid_parent,
|
||||
"Versioning_HeadObject_success": Versioning_HeadObject_success,
|
||||
"Versioning_HeadObject_without_versionId": Versioning_HeadObject_without_versionId,
|
||||
"Versioning_HeadObject_delete_marker": Versioning_HeadObject_delete_marker,
|
||||
"Versioning_GetObject_invalid_versionId": Versioning_GetObject_invalid_versionId,
|
||||
"Versioning_GetObject_non_existing_object_version": Versioning_GetObject_non_existing_object_version,
|
||||
"Versioning_GetObject_success": Versioning_GetObject_success,
|
||||
"Versioning_GetObject_delete_marker_without_versionId": Versioning_GetObject_delete_marker_without_versionId,
|
||||
"Versioning_GetObject_delete_marker": Versioning_GetObject_delete_marker,
|
||||
"Versioning_GetObject_null_versionId_obj": Versioning_GetObject_null_versionId_obj,
|
||||
"Versioning_PutObjectTagging_invalid_versionId": Versioning_PutObjectTagging_invalid_versionId,
|
||||
"Versioning_PutObjectTagging_non_existing_object_version": Versioning_PutObjectTagging_non_existing_object_version,
|
||||
"Versioning_GetObjectTagging_invalid_versionId": Versioning_GetObjectTagging_invalid_versionId,
|
||||
"Versioning_GetObjectTagging_non_existing_object_version": Versioning_GetObjectTagging_non_existing_object_version,
|
||||
"Versioning_DeleteObjectTagging_invalid_versionId": Versioning_DeleteObjectTagging_invalid_versionId,
|
||||
"Versioning_DeleteObjectTagging_non_existing_object_version": Versioning_DeleteObjectTagging_non_existing_object_version,
|
||||
"Versioning_PutGetDeleteObjectTagging_success": Versioning_PutGetDeleteObjectTagging_success,
|
||||
"Versioning_GetObjectAttributes_invalid_versionId": Versioning_GetObjectAttributes_invalid_versionId,
|
||||
"Versioning_GetObjectAttributes_object_version": Versioning_GetObjectAttributes_object_version,
|
||||
"Versioning_GetObjectAttributes_delete_marker": Versioning_GetObjectAttributes_delete_marker,
|
||||
"Versioning_DeleteObject_invalid_versionId": Versioning_DeleteObject_invalid_versionId,
|
||||
"Versioning_DeleteObject_delete_object_version": Versioning_DeleteObject_delete_object_version,
|
||||
"Versioning_DeleteObject_non_existing_object": Versioning_DeleteObject_non_existing_object,
|
||||
"Versioning_DeleteObject_delete_a_delete_marker": Versioning_DeleteObject_delete_a_delete_marker,
|
||||
@@ -1665,15 +1686,20 @@ func GetIntTests() IntTests {
|
||||
"ListObjectVersions_checksum": ListObjectVersions_checksum,
|
||||
"Versioning_Multipart_Upload_success": Versioning_Multipart_Upload_success,
|
||||
"Versioning_Multipart_Upload_overwrite_an_object": Versioning_Multipart_Upload_overwrite_an_object,
|
||||
"Versioning_UploadPartCopy_invalid_versionId": Versioning_UploadPartCopy_invalid_versionId,
|
||||
"Versioning_UploadPartCopy_non_existing_versionId": Versioning_UploadPartCopy_non_existing_versionId,
|
||||
"Versioning_UploadPartCopy_from_an_object_version": Versioning_UploadPartCopy_from_an_object_version,
|
||||
"Versioning_object_lock_not_enabled_on_bucket_creation": Versioning_object_lock_not_enabled_on_bucket_creation,
|
||||
"Versioning_Enable_object_lock": Versioning_Enable_object_lock,
|
||||
"Versioning_status_switch_to_suspended_with_object_lock": Versioning_status_switch_to_suspended_with_object_lock,
|
||||
"Versioning_PutObjectRetention_invalid_versionId": Versioning_PutObjectRetention_invalid_versionId,
|
||||
"Versioning_PutObjectRetention_non_existing_object_version": Versioning_PutObjectRetention_non_existing_object_version,
|
||||
"Versioning_GetObjectRetention_invalid_versionId": Versioning_GetObjectRetention_invalid_versionId,
|
||||
"Versioning_GetObjectRetention_non_existing_object_version": Versioning_GetObjectRetention_non_existing_object_version,
|
||||
"Versioning_Put_GetObjectRetention_success": Versioning_Put_GetObjectRetention_success,
|
||||
"Versioning_PutObjectLegalHold_invalid_versionId": Versioning_PutObjectLegalHold_invalid_versionId,
|
||||
"Versioning_PutObjectLegalHold_non_existing_object_version": Versioning_PutObjectLegalHold_non_existing_object_version,
|
||||
"Versioning_GetObjectLegalHold_invalid_versionId": Versioning_GetObjectLegalHold_invalid_versionId,
|
||||
"Versioning_GetObjectLegalHold_non_existing_object_version": Versioning_GetObjectLegalHold_non_existing_object_version,
|
||||
"Versioning_Put_GetObjectLegalHold_success": Versioning_Put_GetObjectLegalHold_success,
|
||||
"Versioning_WORM_obj_version_locked_with_legal_hold": Versioning_WORM_obj_version_locked_with_legal_hold,
|
||||
|
||||
@@ -218,6 +218,31 @@ func Versioning_PutObject_success(s *S3Conf) error {
|
||||
}, withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
func Versioning_CopyObject_invalid_versionId(s *S3Conf) error {
|
||||
testName := "Versioning_CopyObject_invalid_versionId"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
dstObj, srcObj := "dst-obj", "src-obj"
|
||||
|
||||
srcObjLen := int64(2345)
|
||||
_, err := putObjectWithData(srcObjLen, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &srcObj,
|
||||
}, s3client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.CopyObject(ctx, &s3.CopyObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &dstObj,
|
||||
CopySource: getPtr(fmt.Sprintf("%v/%v?versionId=invalid_versionId", bucket, srcObj)),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
}, withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
func Versioning_CopyObject_success(s *S3Conf) error {
|
||||
testName := "Versioning_CopyObject_success"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
@@ -311,7 +336,7 @@ func Versioning_CopyObject_non_existing_version_id(s *S3Conf) error {
|
||||
_, err = s3client.CopyObject(ctx, &s3.CopyObjectInput{
|
||||
Bucket: &dstBucket,
|
||||
Key: &dstObj,
|
||||
CopySource: getPtr(fmt.Sprintf("%v/%v?versionId=invalid_versionId",
|
||||
CopySource: getPtr(fmt.Sprintf("%v/%v?versionId=01BX5ZZKBKACTAV9WEVGEMMVRZ",
|
||||
bucket, srcObj)),
|
||||
})
|
||||
cancel()
|
||||
@@ -463,6 +488,32 @@ func Versioning_CopyObject_special_chars(s *S3Conf) error {
|
||||
}, withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
func Versioning_HeadObject_invalid_versionId(s *S3Conf) error {
|
||||
testName := "Versioning_HeadObject_invalid_versionId"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
_, err := putObjectWithData(10, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
}, s3client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.HeadObject(ctx, &s3.HeadObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
VersionId: getPtr("invalid_versionId"),
|
||||
})
|
||||
cancel()
|
||||
if err := checkSdkApiErr(err, "BadRequest"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
func Versioning_HeadObject_non_existing_object_version(s *S3Conf) error {
|
||||
testName := "Versioning_HeadObject_non_existing_object_version"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
@@ -643,6 +694,30 @@ func Versioning_HeadObject_delete_marker(s *S3Conf) error {
|
||||
}, withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
func Versioning_GetObject_invalid_versionId(s *S3Conf) error {
|
||||
testName := "Versioning_GetObject_invalid_versionId"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
|
||||
_, err := putObjectWithData(10, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
}, s3client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.GetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
VersionId: getPtr("invalid_version_id"),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
}, withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
func Versioning_GetObject_non_existing_object_version(s *S3Conf) error {
|
||||
testName := "Versioning_GetObject_non_existing_object_version"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
@@ -900,6 +975,29 @@ func Versioning_GetObject_null_versionId_obj(s *S3Conf) error {
|
||||
})
|
||||
}
|
||||
|
||||
func Versioning_GetObjectAttributes_invalid_versionId(s *S3Conf) error {
|
||||
testName := "Versioning_GetObjectAttributes_invalid_versionId"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
_, err := createObjVersions(s3client, bucket, obj, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.GetObjectAttributes(ctx, &s3.GetObjectAttributesInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
VersionId: getPtr("invalid_versionId"),
|
||||
ObjectAttributes: []types.ObjectAttributes{
|
||||
types.ObjectAttributesEtag,
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
})
|
||||
}
|
||||
|
||||
func Versioning_GetObjectAttributes_object_version(s *S3Conf) error {
|
||||
testName := "Versioning_GetObjectAttributes_object_version"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
@@ -995,6 +1093,29 @@ func Versioning_GetObjectAttributes_delete_marker(s *S3Conf) error {
|
||||
}, withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
func Versioning_DeleteObject_invalid_versionId(s *S3Conf) error {
|
||||
testName := "Versioning_DeleteObject_invalid_versionId"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
_, err := putObjectWithData(3, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
}, s3client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
VersionId: getPtr("invalid_versionId"),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
})
|
||||
}
|
||||
|
||||
func Versioning_DeleteObject_delete_object_version(s *S3Conf) error {
|
||||
testName := "Versioning_DeleteObject_delete_object_version"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
@@ -1686,6 +1807,37 @@ func Versioning_Multipart_Upload_overwrite_an_object(s *S3Conf) error {
|
||||
}, withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
func Versioning_UploadPartCopy_invalid_versionId(s *S3Conf) error {
|
||||
testName := "Versioning_UploadPartCopy_invalid_versionId"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
dstObj, srcObj := "dst-obj", "src-obj"
|
||||
_, err := putObjectWithData(10, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &srcObj,
|
||||
}, s3client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mp, err := createMp(s3client, bucket, dstObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.UploadPartCopy(ctx, &s3.UploadPartCopyInput{
|
||||
Bucket: &bucket,
|
||||
Key: &dstObj,
|
||||
UploadId: mp.UploadId,
|
||||
PartNumber: getPtr(int32(1)),
|
||||
CopySource: getPtr(fmt.Sprintf("%v/%v?versionId=invalid_versionId",
|
||||
bucket, srcObj)),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
})
|
||||
}
|
||||
|
||||
func Versioning_UploadPartCopy_non_existing_versionId(s *S3Conf) error {
|
||||
testName := "Versioning_UploadPartCopy_non_existing_versionId"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
@@ -1716,7 +1868,7 @@ func Versioning_UploadPartCopy_non_existing_versionId(s *S3Conf) error {
|
||||
Key: &dstObj,
|
||||
UploadId: mp.UploadId,
|
||||
PartNumber: &pNumber,
|
||||
CopySource: getPtr(fmt.Sprintf("%v/%v?versionId=invalid_versionId",
|
||||
CopySource: getPtr(fmt.Sprintf("%v/%v?versionId=01BX5ZZKBKACTAV9WEVGEMMVS0",
|
||||
bucket, srcObj)),
|
||||
})
|
||||
cancel()
|
||||
@@ -1867,6 +2019,31 @@ func Versioning_status_switch_to_suspended_with_object_lock(s *S3Conf) error {
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
func Versioning_PutObjectRetention_invalid_versionId(s *S3Conf) error {
|
||||
testName := "Versioning_PutObjectRetention_invalid_versionId"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
_, err := createObjVersions(s3client, bucket, obj, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rDate := time.Now().Add(time.Hour * 48)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.PutObjectRetention(ctx, &s3.PutObjectRetentionInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
VersionId: getPtr("invalid_version_id"),
|
||||
Retention: &types.ObjectLockRetention{
|
||||
Mode: types.ObjectLockRetentionModeGovernance,
|
||||
RetainUntilDate: &rDate,
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
})
|
||||
}
|
||||
|
||||
func Versioning_PutObjectRetention_non_existing_object_version(s *S3Conf) error {
|
||||
testName := "Versioning_PutObjectRetention_non_existing_object_version"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
@@ -1896,6 +2073,26 @@ func Versioning_PutObjectRetention_non_existing_object_version(s *S3Conf) error
|
||||
}, withLock(), withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
func Versioning_GetObjectRetention_invalid_versionId(s *S3Conf) error {
|
||||
testName := "Versioning_GetObjectRetention_invalid_versionId"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
_, err := createObjVersions(s3client, bucket, obj, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.GetObjectRetention(ctx, &s3.GetObjectRetentionInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
VersionId: getPtr("invalid_versionId"),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
})
|
||||
}
|
||||
|
||||
func Versioning_GetObjectRetention_non_existing_object_version(s *S3Conf) error {
|
||||
testName := "Versioning_GetObjectRetention_non_existing_object_version"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
@@ -1966,6 +2163,29 @@ func Versioning_Put_GetObjectRetention_success(s *S3Conf) error {
|
||||
}, withLock(), withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
func Versioning_PutObjectLegalHold_invalid_versionId(s *S3Conf) error {
|
||||
testName := "Versioning_PutObjectLegalHold_invalid_versionId"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
_, err := createObjVersions(s3client, bucket, obj, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.PutObjectLegalHold(ctx, &s3.PutObjectLegalHoldInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
VersionId: getPtr("invalid_version_id"),
|
||||
LegalHold: &types.ObjectLockLegalHold{
|
||||
Status: types.ObjectLockLegalHoldStatusOn,
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
})
|
||||
}
|
||||
|
||||
func Versioning_PutObjectLegalHold_non_existing_object_version(s *S3Conf) error {
|
||||
testName := "Versioning_PutObjectLegalHold_non_existing_object_version"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
@@ -1993,6 +2213,26 @@ func Versioning_PutObjectLegalHold_non_existing_object_version(s *S3Conf) error
|
||||
}, withLock(), withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
func Versioning_GetObjectLegalHold_invalid_versionId(s *S3Conf) error {
|
||||
testName := "Versioning_GetObjectLegalHold_invalid_versionId"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
_, err := createObjVersions(s3client, bucket, obj, 3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.GetObjectLegalHold(ctx, &s3.GetObjectLegalHoldInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
VersionId: getPtr("invalid_version_id"),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
})
|
||||
}
|
||||
|
||||
func Versioning_GetObjectLegalHold_non_existing_object_version(s *S3Conf) error {
|
||||
testName := "Versioning_GetObjectLegalHold_non_existing_object_version"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
@@ -2683,6 +2923,29 @@ func Versioning_concurrent_upload_object(s *S3Conf) error {
|
||||
}, withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
func Versioning_GetObjectTagging_invalid_versionId(s *S3Conf) error {
|
||||
testName := "Versioning_GetObjectTagging_invalid_versionId"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-object"
|
||||
_, err := putObjectWithData(4, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
}, s3client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.GetObjectTagging(ctx, &s3.GetObjectTaggingInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
VersionId: getPtr("invalid_versionId"),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
})
|
||||
}
|
||||
|
||||
func Versioning_PutObjectTagging_non_existing_object_version(s *S3Conf) error {
|
||||
testName := "Versioning_PutObjectTagging_non_existing_object_version"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
@@ -2709,6 +2972,32 @@ func Versioning_PutObjectTagging_non_existing_object_version(s *S3Conf) error {
|
||||
}, withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
func Versioning_PutObjectTagging_invalid_versionId(s *S3Conf) error {
|
||||
testName := "Versioning_PutObjectTagging_invalid_versionId"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-object"
|
||||
_, err := putObjectWithData(4, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
}, s3client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.PutObjectTagging(ctx, &s3.PutObjectTaggingInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
Tagging: &types.Tagging{
|
||||
TagSet: []types.Tag{{Key: getPtr("key"), Value: getPtr("value")}},
|
||||
},
|
||||
VersionId: getPtr("invalid_versionId"),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
}, withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
func Versioning_GetObjectTagging_non_existing_object_version(s *S3Conf) error {
|
||||
testName := "Versioning_GetObjectTagging_non_existing_object_version"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
@@ -2732,6 +3021,29 @@ func Versioning_GetObjectTagging_non_existing_object_version(s *S3Conf) error {
|
||||
}, withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
func Versioning_DeleteObjectTagging_invalid_versionId(s *S3Conf) error {
|
||||
testName := "Versioning_DeleteObjectTagging_invalid_versionId"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-object"
|
||||
_, err := putObjectWithData(4, &s3.PutObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
}, s3client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.DeleteObjectTagging(ctx, &s3.DeleteObjectTaggingInput{
|
||||
Bucket: &bucket,
|
||||
Key: &obj,
|
||||
VersionId: getPtr("invalid_versionId"),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
})
|
||||
}
|
||||
|
||||
func Versioning_DeleteObjectTagging_non_existing_object_version(s *S3Conf) error {
|
||||
testName := "Versioning_DeleteObjectTagging_non_existing_object_version"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
|
||||
@@ -145,20 +145,20 @@ test_file="test_file"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "REST - UploadPart w/o part number" {
|
||||
run get_bucket_name "$BUCKET_ONE_NAME"
|
||||
assert_success
|
||||
bucket_name="$output"
|
||||
# @test "REST - UploadPart w/o part number" {
|
||||
# run get_bucket_name "$BUCKET_ONE_NAME"
|
||||
# assert_success
|
||||
# bucket_name="$output"
|
||||
|
||||
run setup_bucket_and_large_file_v2 "$bucket_name" "$test_file"
|
||||
assert_success
|
||||
# run setup_bucket_and_large_file_v2 "$bucket_name" "$test_file"
|
||||
# assert_success
|
||||
|
||||
run split_file "$TEST_FILE_FOLDER/$test_file" 4
|
||||
assert_success
|
||||
# run split_file "$TEST_FILE_FOLDER/$test_file" 4
|
||||
# assert_success
|
||||
|
||||
run upload_part_rest_without_part_number "$bucket_name" "$test_file"
|
||||
assert_success
|
||||
}
|
||||
# run upload_part_rest_without_part_number "$bucket_name" "$test_file"
|
||||
# assert_success
|
||||
# }
|
||||
|
||||
@test "REST - UploadPart w/o upload ID" {
|
||||
run get_bucket_name "$BUCKET_ONE_NAME"
|
||||
|
||||
Reference in New Issue
Block a user