mirror of
https://github.com/versity/versitygw.git
synced 2026-08-21 22:56:28 +00:00
feat: global error refactoring
Fixes #2123 Fixes #2120 Fixes #2116 Fixes #2111 Fixes #2108 Fixes #2086 Fixes #2085 Fixes #2083 Fixes #2081 Fixes #2080 Fixes #2073 Fixes #2072 Fixes #2071 Fixes #2069 Fixes #2044 Fixes #2043 Fixes #2042 Fixes #2041 Fixes #2040 Fixes #2039 Fixes #2036 Fixes #2035 Fixes #2034 Fixes #2028 Fixes #2020 Fixes #1842 Fixes #1810 Fixes #1780 Fixes #1775 Fixes #1736 Fixes #1705 Fixes #1663 Fixes #1645 Fixes #1583 Fixes #1526 Fixes #1514 Fixes #1493 Fixes #1487 Fixes #959 Fixes #779 Closes #823 Closes #85 Refactor global S3 error handling around structured error types and centralized XML response generation. All S3 errors now share the common APIError base for the fields every error has: Code, HTTP status code, and Message. Non-traditional errors that need AWS-compatible XML fields now have dedicated typed errors in the s3err package. Each typed error implements the shared S3Error behavior so controllers and middleware can handle errors consistently while still emitting error-specific XML fields. Add a dedicated InvalidArgumentError type because InvalidArgument is used widely across request validation, auth, copy source handling, object lock validation, multipart validation, and header parsing. The new InvalidArgument path uses explicit InvalidArgErrorCode constants with predefined descriptions and ArgumentName values, keeping call sites readable while preserving the correct InvalidArgument XML shape and optional ArgumentValue. New structured errors added in s3err: - `AccessForbiddenError`: Method, ResourceType - `BadDigestError`: CalculatedDigest, ExpectedDigest - `BucketError`: BucketName - `ContentSHA256MismatchError`: ClientComputedContentSHA256, S3ComputedContentSHA256 - `EntityTooLargeError`: ProposedSize, MaxSizeAllowed - `EntityTooSmallError`: ProposedSize, MinSizeAllowed - `ExpiredPresignedURLError`: ServerTime, XAmzExpires, Expires - `InvalidAccessKeyIdError`: AWSAccessKeyId - `InvalidArgumentError`: Description, ArgumentName, ArgumentValue - `InvalidChunkSizeError`: Chunk, BadChunkSize - `InvalidDigestError`: ContentMD5 - `InvalidLocationConstraintError`: LocationConstraint - `InvalidPartError`: UploadId, PartNumber, ETag - `InvalidRangeError`: RangeRequested, ActualObjectSize - `InvalidTagError`: TagKey, TagValue - `KeyTooLongError`: Size, MaxSizeAllowed - `MetadataTooLargeError`: Size, MaxSizeAllowed - `MethodNotAllowedError`: Method, ResourceType, AllowedMethods - `NoSuchUploadError`: UploadId - `NoSuchVersionError`: Key, VersionId - `NotImplementedError`: Header, AdditionalMessage - `PreconditionFailedError`: Condition - `RequestTimeTooSkewedError`: RequestTime, ServerTime, MaxAllowedSkewMilliseconds - `SignatureDoesNotMatchError`: AWSAccessKeyId, StringToSign, SignatureProvided, StringToSignBytes, CanonicalRequest, CanonicalRequestBytes Fix CompleteMultipartUpload validation in the Azure backend so missing or empty `ETag` values return the appropriate S3 error instead of allowing a gateway panic. Fix presigned authentication expiration validation to compare server time in `UTC`, matching the `UTC` timestamp used by presigned URL signing. Add request ID and host ID support across S3 requests. Each request now receives AWS S3-like identifiers, returned in response headers as `x-amz-request-id` and `x-amz-id-2` and included in all XML error responses as RequestId and HostId. The generated ID structure is designed to resemble AWS S3 request IDs and host IDs. The request signature calculation/validation for streaming uploads was previously delayed until the request body was fully read, both for Authorization header authentication and presigned URLs. Now, the signature is validated immediately in the authorization middlewares without reading the request body, since the signature calculation itself does not depend on the request body. Instead, only the `x-amz-content-sha256` SHA-256 hash calculation is delayed.
This commit is contained in:
@@ -132,7 +132,7 @@ func AbortMultipartUpload_success_status_code(s *S3Conf) error {
|
||||
|
||||
req, err := createSignedReq(http.MethodDelete, s.endpoint,
|
||||
fmt.Sprintf("%v/%v?uploadId=%v", bucket, obj, *out.UploadId),
|
||||
s.awsID, s.awsSecret, "s3", s.awsRegion, nil, time.Now(), nil)
|
||||
s.awsID, s.awsSecret, "s3", s.awsRegion, "", nil, time.Now(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -23,9 +23,11 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
@@ -226,7 +228,7 @@ func CompleteMultipartUpload_invalid_checksum_part(s *S3Conf) error {
|
||||
ChecksumType: types.ChecksumTypeFullObject,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidChecksumPart)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgChecksumPart, "invalid_checksum")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -264,6 +266,7 @@ func CompleteMultipartUpload_multiple_checksum_part(s *S3Conf) error {
|
||||
cParts[0].ChecksumSHA1 = getPtr("Kq5sNclPz7QV2+lfQIuc6R7oRu0=")
|
||||
}
|
||||
}
|
||||
argValue := fmt.Sprintf("CRC32:%s;SHA1:%s;", getString(cParts[0].ChecksumCRC32), getString(cParts[0].ChecksumSHA1))
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
_, err = s3client.CompleteMultipartUpload(ctx, &s3.CompleteMultipartUploadInput{
|
||||
@@ -276,7 +279,7 @@ func CompleteMultipartUpload_multiple_checksum_part(s *S3Conf) error {
|
||||
ChecksumType: types.ChecksumTypeComposite,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidChecksumPart)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgChecksumPart, argValue)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1259,6 +1262,72 @@ func CompleteMultipartUpload_empty_parts(s *S3Conf) error {
|
||||
})
|
||||
}
|
||||
|
||||
func CompleteMultipartUpload_missing_part_fields(s *S3Conf) error {
|
||||
testName := "CompleteMultipartUpload_missing_part_fields"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
tests := []struct {
|
||||
name string
|
||||
body func(types.Part) []byte
|
||||
}{
|
||||
{
|
||||
name: "missing ETag",
|
||||
body: func(part types.Part) []byte {
|
||||
return fmt.Appendf(nil, `<CompleteMultipartUpload xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Part><PartNumber>%d</PartNumber></Part></CompleteMultipartUpload>`,
|
||||
getInt32(part.PartNumber))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing PartNumber",
|
||||
body: func(part types.Part) []byte {
|
||||
return fmt.Appendf(nil, `<CompleteMultipartUpload xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Part><ETag>%s</ETag></Part></CompleteMultipartUpload>`,
|
||||
getString(part.ETag))
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
obj := fmt.Sprintf("my-obj-%d", i)
|
||||
mp, err := createMp(s3client, bucket, obj)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", test.name, err)
|
||||
}
|
||||
|
||||
parts, _, err := uploadParts(s3client, 5*1024*1024, 1, bucket, obj, *mp.UploadId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", test.name, err)
|
||||
}
|
||||
|
||||
req, err := createSignedReq(
|
||||
http.MethodPost,
|
||||
s.endpoint,
|
||||
fmt.Sprintf("%s/%s?uploadId=%s", bucket, obj, url.QueryEscape(*mp.UploadId)),
|
||||
s.awsID,
|
||||
s.awsSecret,
|
||||
"s3",
|
||||
s.awsRegion,
|
||||
"",
|
||||
test.body(parts[0]),
|
||||
time.Now(),
|
||||
map[string]string{"Content-Type": "application/xml"},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", test.name, err)
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", test.name, err)
|
||||
}
|
||||
|
||||
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMalformedXML)); err != nil {
|
||||
return fmt.Errorf("%s: %w", test.name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func CompleteMultipartUpload_incorrect_parts_order(s *S3Conf) error {
|
||||
testName := "CompleteMultipartUpload_incorrect_parts_order"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
@@ -1616,7 +1685,7 @@ func CompleteMultipartUpload_invalid_part_number(s *S3Conf) error {
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidCompleteMpPartNumber)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgCompleteMpPartNumber, fmt.Sprint(invPartNumber))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ func CopyObject_copy_to_itself_invalid_directive(s *S3Conf) error {
|
||||
MetadataDirective: types.MetadataDirective("invalid"),
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidMetadataDirective)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgMetadataDirective, "invalid")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -158,7 +158,7 @@ func CopyObject_invalid_tagging_directive(s *S3Conf) error {
|
||||
TaggingDirective: types.TaggingDirective("invalid"),
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidTaggingDirective)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgTaggingDirective, "invalid")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -246,7 +246,7 @@ func CopyObject_should_replace_tagging(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
switch eErr := expectedErr.(type) {
|
||||
case s3err.APIError:
|
||||
case s3err.S3Error:
|
||||
return checkApiErr(err, eErr)
|
||||
default:
|
||||
return fmt.Errorf("invalid err provided: %w", expectedErr)
|
||||
@@ -299,10 +299,10 @@ func CopyObject_should_replace_tagging(s *S3Conf) error {
|
||||
{"key1=val1&key2=val2", map[string]string{"key1": "val1", "key2": "val2"}, nil},
|
||||
{"key@=val@", map[string]string{"key@": "val@"}, nil},
|
||||
// invalid url-encoded
|
||||
{"=", nil, s3err.GetAPIError(s3err.ErrInvalidURLEncodedTagging)},
|
||||
{"key%", nil, s3err.GetAPIError(s3err.ErrInvalidURLEncodedTagging)},
|
||||
{"=", nil, s3err.GetInvalidArgumentErr(s3err.InvalidArgURLEncodedTagging, "")},
|
||||
{"key%", nil, s3err.GetInvalidArgumentErr(s3err.InvalidArgURLEncodedTagging, "")},
|
||||
// duplicate keys
|
||||
{"key=val&key=val", nil, s3err.GetAPIError(s3err.ErrInvalidURLEncodedTagging)},
|
||||
{"key=val&key=val", nil, s3err.GetInvalidArgumentErr(s3err.InvalidArgURLEncodedTagging, "")},
|
||||
// invalid tag keys
|
||||
{"key?=val", nil, s3err.GetAPIError(s3err.ErrInvalidTagKey)},
|
||||
{"key(=val", nil, s3err.GetAPIError(s3err.ErrInvalidTagKey)},
|
||||
@@ -520,87 +520,87 @@ func CopyObject_invalid_copy_source(s *S3Conf) error {
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
for _, test := range []struct {
|
||||
copySource string
|
||||
expectedErr s3err.APIError
|
||||
expectedErr s3err.S3Error
|
||||
}{
|
||||
// invalid encoding
|
||||
{
|
||||
// Invalid hex digits
|
||||
copySource: "bucket/%ZZ",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceEncoding, "bucket/%ZZ"),
|
||||
},
|
||||
{
|
||||
// Ends with incomplete escape
|
||||
copySource: "100%/foo/bar/baz",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceEncoding, "100%/foo/bar/baz"),
|
||||
},
|
||||
{
|
||||
// Only one digit after %
|
||||
copySource: "bucket/%A/bar",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceEncoding, "bucket/%A/bar"),
|
||||
},
|
||||
{
|
||||
// 'G' is not a hex digit
|
||||
copySource: "bucket/%G1/",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceEncoding, "bucket/%G1/"),
|
||||
},
|
||||
{
|
||||
// Just a single percent sign
|
||||
copySource: "%",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceEncoding, "%"),
|
||||
},
|
||||
{
|
||||
// Only one hex digit
|
||||
copySource: "bucket/%1",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceEncoding, "bucket/%1"),
|
||||
},
|
||||
{
|
||||
// Incomplete multibyte UTF-8
|
||||
copySource: "bucket/%C3%",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceEncoding, "bucket/%C3%"),
|
||||
},
|
||||
// invalid bucket name
|
||||
{
|
||||
// ip v4 address
|
||||
copySource: "192.168.1.1/foo",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceBucket),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceBucket, "192.168.1.1/foo"),
|
||||
},
|
||||
{
|
||||
// ip v6 address
|
||||
copySource: "2001:0db8:85a3:0000:0000:8a2e:0370:7334/something",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceBucket),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceBucket, "2001:0db8:85a3:0000:0000:8a2e:0370:7334/something"),
|
||||
},
|
||||
{
|
||||
// some special chars
|
||||
copySource: "my-buc@k&()t/obj",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceBucket),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceBucket, "my-buc@k&()t/obj"),
|
||||
},
|
||||
// invalid object key
|
||||
{
|
||||
// object is missing
|
||||
copySource: "bucket",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceObject),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceObject, ""),
|
||||
},
|
||||
{
|
||||
// object is missing
|
||||
copySource: "bucket/",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceObject),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceObject, ""),
|
||||
},
|
||||
// directory navigation object keys
|
||||
{
|
||||
copySource: "bucket/.",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceObject),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceObject, "."),
|
||||
},
|
||||
{
|
||||
copySource: "bucket/..",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceObject),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceObject, ".."),
|
||||
},
|
||||
{
|
||||
copySource: "bucket/../",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceObject),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceObject, "../"),
|
||||
},
|
||||
{
|
||||
copySource: "bucket/foo/ba/../../../r/baz",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceObject),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceObject, "foo/ba/../../../r/baz"),
|
||||
},
|
||||
} {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
@@ -876,7 +876,7 @@ func CopyObject_invalid_legal_hold(s *S3Conf) error {
|
||||
ObjectLockLegalHoldStatus: types.ObjectLockLegalHoldStatus("invalid_status"),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidLegalHoldStatus))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgLegalHoldStatus, "invalid_status"))
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
@@ -902,7 +902,7 @@ func CopyObject_invalid_object_lock_mode(s *S3Conf) error {
|
||||
ObjectLockMode: types.ObjectLockMode("invalid_mode"),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidObjectLockMode))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockMode, "invalid_mode"))
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||
@@ -137,11 +136,7 @@ func CreateBucket_invalid_ownership(s *S3Conf) error {
|
||||
|
||||
invalidOwnership := types.ObjectOwnership("invalid_ownership")
|
||||
err := setup(s, getBucketName(), withOwnership(invalidOwnership))
|
||||
if err := checkApiErr(err, s3err.APIError{
|
||||
Code: "InvalidArgument",
|
||||
Description: fmt.Sprintf("Invalid x-amz-object-ownership header: %v", invalidOwnership),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
}); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgObjectOwnership(string(invalidOwnership))); err != nil {
|
||||
failF("%v: %v", testName, err)
|
||||
return fmt.Errorf("%v: %w", testName, err)
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ func CreateMultipartUpload_with_object_lock_invalid_retention(s *S3Conf) error {
|
||||
ObjectLockMode: types.ObjectLockModeGovernance,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrObjectLockInvalidHeaders)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgMissingObjectLockRetainDate, "")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ func CreateMultipartUpload_with_object_lock_invalid_retention(s *S3Conf) error {
|
||||
ObjectLockRetainUntilDate: &retentionDate,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrObjectLockInvalidHeaders)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgMissingObjectLockMode, "")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -298,7 +298,7 @@ func CreateMultipartUpload_past_retain_until_date(s *S3Conf) error {
|
||||
ObjectLockRetainUntilDate: &rDate,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrPastObjectLockRetainDate)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgPastObjectLockRetainDate, rDate.Format(time.RFC3339))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ func CreateMultipartUpload_invalid_legal_hold(s *S3Conf) error {
|
||||
ObjectLockLegalHoldStatus: types.ObjectLockLegalHoldStatus("invalid_status"),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidLegalHoldStatus))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgLegalHoldStatus, "invalid_status"))
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
@@ -332,7 +332,7 @@ func CreateMultipartUpload_invalid_object_lock_mode(s *S3Conf) error {
|
||||
ObjectLockRetainUntilDate: &rDate,
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidObjectLockMode))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockMode, "invalid_mode"))
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
@@ -468,7 +468,7 @@ func CreateMultipartUpload_with_tagging(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
switch eErr := expectedErr.(type) {
|
||||
case s3err.APIError:
|
||||
case s3err.S3Error:
|
||||
return checkApiErr(err, eErr)
|
||||
default:
|
||||
return fmt.Errorf("invalid err provided: %w", expectedErr)
|
||||
@@ -548,10 +548,10 @@ func CreateMultipartUpload_with_tagging(s *S3Conf) error {
|
||||
{"key1=val1&key2=val2", map[string]string{"key1": "val1", "key2": "val2"}, nil},
|
||||
{"key@=val@", map[string]string{"key@": "val@"}, nil},
|
||||
// invalid url-encoded
|
||||
{"=", nil, s3err.GetAPIError(s3err.ErrInvalidURLEncodedTagging)},
|
||||
{"key%", nil, s3err.GetAPIError(s3err.ErrInvalidURLEncodedTagging)},
|
||||
{"=", nil, s3err.GetInvalidArgumentErr(s3err.InvalidArgURLEncodedTagging, "")},
|
||||
{"key%", nil, s3err.GetInvalidArgumentErr(s3err.InvalidArgURLEncodedTagging, "")},
|
||||
// duplicate keys
|
||||
{"key=val&key=val", nil, s3err.GetAPIError(s3err.ErrInvalidURLEncodedTagging)},
|
||||
{"key=val&key=val", nil, s3err.GetInvalidArgumentErr(s3err.InvalidArgURLEncodedTagging, "")},
|
||||
// invalid tag keys
|
||||
{"key?=val", nil, s3err.GetAPIError(s3err.ErrInvalidTagKey)},
|
||||
{"key(=val", nil, s3err.GetAPIError(s3err.ErrInvalidTagKey)},
|
||||
|
||||
@@ -74,7 +74,7 @@ func DeleteBucket_success_status_code(s *S3Conf) error {
|
||||
return fmt.Errorf("%v: %w", testName, err)
|
||||
}
|
||||
|
||||
req, err := createSignedReq(http.MethodDelete, s.endpoint, bucket, s.awsID, s.awsSecret, "s3", s.awsRegion, nil, time.Now(), nil)
|
||||
req, err := createSignedReq(http.MethodDelete, s.endpoint, bucket, s.awsID, s.awsSecret, "s3", s.awsRegion, "", nil, time.Now(), nil)
|
||||
if err != nil {
|
||||
failF("%v: %v", testName, err)
|
||||
return fmt.Errorf("%v: %w", testName, err)
|
||||
|
||||
@@ -62,7 +62,7 @@ func DeleteBucketTagging_success_status(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := createSignedReq(http.MethodDelete, s.endpoint, fmt.Sprintf("%v?tagging", bucket), s.awsID, s.awsSecret, "s3", s.awsRegion, nil, time.Now(), nil)
|
||||
req, err := createSignedReq(http.MethodDelete, s.endpoint, fmt.Sprintf("%v?tagging", bucket), s.awsID, s.awsSecret, "s3", s.awsRegion, "", nil, time.Now(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -333,7 +333,7 @@ func DeleteObject_success_status_code(s *S3Conf) error {
|
||||
|
||||
req, err := createSignedReq(http.MethodDelete, s.endpoint,
|
||||
fmt.Sprintf("%v/%v", bucket, obj), s.awsID, s.awsSecret, "s3",
|
||||
s.awsRegion, nil, time.Now(), nil)
|
||||
s.awsRegion, "", nil, time.Now(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ func DeleteObjectTagging_success_status(s *S3Conf) error {
|
||||
|
||||
req, err := createSignedReq(http.MethodDelete, s.endpoint,
|
||||
fmt.Sprintf("%v/%v?tagging", bucket, obj), s.awsID, s.awsSecret,
|
||||
"s3", s.awsRegion, nil, time.Now(), nil)
|
||||
"s3", s.awsRegion, "", nil, time.Now(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -729,6 +729,7 @@ func GetObject_by_range_resp_status(s *S3Conf) error {
|
||||
s.awsSecret,
|
||||
"s3",
|
||||
s.awsRegion,
|
||||
"",
|
||||
nil,
|
||||
time.Now(),
|
||||
map[string]string{"Range": rng},
|
||||
@@ -1075,6 +1076,7 @@ func GetObject_overrides_presign_success(s *S3Conf) error {
|
||||
s.awsSecret,
|
||||
"s3",
|
||||
s.awsRegion,
|
||||
"",
|
||||
nil,
|
||||
time.Now(),
|
||||
nil,
|
||||
@@ -1121,6 +1123,7 @@ func GetObject_overrides_presign_success(s *S3Conf) error {
|
||||
s.awsSecret,
|
||||
"s3",
|
||||
s.awsRegion,
|
||||
"",
|
||||
nil,
|
||||
time.Now(),
|
||||
nil,
|
||||
@@ -1294,7 +1297,7 @@ func GetObject_invalid_part_number(s *S3Conf) error {
|
||||
PartNumber: getPtr(int32(-3)),
|
||||
})
|
||||
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidPartNumber))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgPartNumber, "-3"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1369,7 +1372,7 @@ func GetObject_mp_part_number_exceeds_parts_count(s *S3Conf) error {
|
||||
PartNumber: &pn,
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidPartNumberRange))
|
||||
return checkApiErr(err, s3err.GetInvalidPartNumberRangeErr(int32(partCount), pn))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1630,6 +1633,7 @@ func GetObject_mp_part_number_resp_status(s *S3Conf) error {
|
||||
s.awsSecret,
|
||||
"s3",
|
||||
s.awsRegion,
|
||||
"",
|
||||
nil,
|
||||
time.Now(),
|
||||
nil,
|
||||
|
||||
@@ -84,7 +84,7 @@ func GetObjectAttributes_invalid_attrs(s *S3Conf) error {
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidObjectAttributes)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectAttributes, "Invalid_argument")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ func GetObjectAttributes_invalid_single_attribute(s *S3Conf) error {
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidObjectAttributes)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectAttributes, "invalid_attr")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -828,6 +828,7 @@ func HeadObject_overrides_presign_success(s *S3Conf) error {
|
||||
s.awsSecret,
|
||||
"s3",
|
||||
s.awsRegion,
|
||||
"",
|
||||
nil,
|
||||
time.Now(),
|
||||
nil,
|
||||
@@ -863,6 +864,7 @@ func HeadObject_overrides_presign_success(s *S3Conf) error {
|
||||
s.awsSecret,
|
||||
"s3",
|
||||
s.awsRegion,
|
||||
"",
|
||||
nil,
|
||||
time.Now(),
|
||||
nil,
|
||||
@@ -1118,6 +1120,7 @@ func HeadObject_by_range_resp_status(s *S3Conf) error {
|
||||
s.awsSecret,
|
||||
"s3",
|
||||
s.awsRegion,
|
||||
"",
|
||||
nil,
|
||||
time.Now(),
|
||||
map[string]string{"Range": rng},
|
||||
@@ -1268,6 +1271,7 @@ func HeadObject_mp_part_number_resp_status(s *S3Conf) error {
|
||||
s.awsSecret,
|
||||
"s3",
|
||||
s.awsRegion,
|
||||
"",
|
||||
nil,
|
||||
time.Now(),
|
||||
nil,
|
||||
|
||||
@@ -226,13 +226,13 @@ func ListBuckets_invalid_max_buckets(s *S3Conf) error {
|
||||
|
||||
invMaxBuckets := int32(-3)
|
||||
err := listBuckets(invMaxBuckets)
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidMaxBuckets)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgMaxBuckets, fmt.Sprint(invMaxBuckets))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
invMaxBuckets = 2000000
|
||||
err = listBuckets(invMaxBuckets)
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidMaxBuckets)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgMaxBuckets, fmt.Sprint(invMaxBuckets))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ func ListMultipartUploads_invalid_max_uploads(s *S3Conf) error {
|
||||
MaxUploads: &maxUploads,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetNegativeMaxLimiterErr("max-uploads")); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgNegativeMaxLimiter("max-uploads", fmt.Sprint(maxUploads))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -251,19 +251,20 @@ func ListMultipartUploads_invalid_uploadId_marker(s *S3Conf) error {
|
||||
UploadIdMarker: getPtr("invalid_uploadId_marker"),
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidUploadIdMarker)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgUploadIdMarker, "invalid_uploadId_marker")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// valid UUID, but not from the list
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
uploadIDMarker := uuid.New().String()
|
||||
_, err = s3client.ListMultipartUploads(ctx, &s3.ListMultipartUploadsInput{
|
||||
Bucket: &bucket,
|
||||
KeyMarker: getPtr("obj-2"),
|
||||
UploadIdMarker: getPtr(uuid.New().String()),
|
||||
UploadIdMarker: &uploadIDMarker,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidUploadIdMarker)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgUploadIdMarker, uploadIDMarker)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -275,7 +276,7 @@ func ListMultipartUploads_invalid_uploadId_marker(s *S3Conf) error {
|
||||
UploadIdMarker: uploads[4].UploadId,
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidUploadIdMarker))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgUploadIdMarker, *uploads[4].UploadId))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ func ListObjectVersions_negative_max_keys(s *S3Conf) error {
|
||||
MaxKeys: getPtr(int32(-123)),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrNegativeMaxKeys))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgNegativeMaxKeys, "-123"))
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ func ListObjects_invalid_max_keys(s *S3Conf) error {
|
||||
MaxKeys: &maxKeys,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetNegativeMaxLimiterErr("max-keys")); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgNegativeMaxLimiter("max-keys", fmt.Sprint(maxKeys))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ func ListParts_invalid_max_parts(s *S3Conf) error {
|
||||
MaxParts: &invMaxParts,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetNegativeMaxLimiterErr("max-parts")); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgNegativeMaxLimiter("max-parts", fmt.Sprint(invMaxParts))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -113,18 +113,21 @@ func ListParts_invalid_part_number_marker(s *S3Conf) error {
|
||||
}
|
||||
|
||||
// invalid part number marker
|
||||
err = listparts("invalid")
|
||||
if err := checkApiErr(err, s3err.GetInvalidMaxLimiterErr("part-number-marker")); err != nil {
|
||||
partNumberMarker := "invalid"
|
||||
err = listparts(partNumberMarker)
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgMaxLimiter("part-number-marker", partNumberMarker)); err != nil {
|
||||
return err
|
||||
}
|
||||
// out of in range part number marker
|
||||
err = listparts("2736457823532448723")
|
||||
if err := checkApiErr(err, s3err.GetInvalidMaxLimiterErr("part-number-marker")); err != nil {
|
||||
partNumberMarker = "2736457823532448723"
|
||||
err = listparts(partNumberMarker)
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgMaxLimiter("part-number-marker", partNumberMarker)); err != nil {
|
||||
return err
|
||||
}
|
||||
// negative part number marker
|
||||
err = listparts("-14")
|
||||
if err := checkApiErr(err, s3err.GetNegativeMaxLimiterErr("part-number-marker")); err != nil {
|
||||
partNumberMarker = "-14"
|
||||
err = listparts(partNumberMarker)
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgNegativeMaxLimiter("part-number-marker", partNumberMarker)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ func PostObject_invalid_content_type(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrPreconditionFailed))
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetPreconditionFailedErr(s3err.ConditionPostBucket))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ func PostObject_invalid_algorithm(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrOnlyAws4HmacSha256))
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetInvalidArgumentErr(s3err.InvalidArgOnlyAws4HmacSha256, "invalid"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ func PostObject_invalid_date(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidDateHeader))
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetInvalidArgumentErr(s3err.InvalidArgDateHeader, "invalid_date"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ func PostObject_invalid_credential_format(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.PostAuth.MalformedCredential())
|
||||
return checkHTTPResponseApiErr(resp, s3err.PostAuth.MalformedCredential("malformed-no-slashes"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -186,18 +186,20 @@ func PostObject_incorrect_region(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.PostAuth.IncorrectRegion(s.awsRegion, wrongRegion))
|
||||
expectedCreds := fmt.Sprintf("%s/%s/%s/s3/aws4_request", s.awsID, time.Now().UTC().Format("20060102"), wrongRegion)
|
||||
return checkHTTPResponseApiErr(resp, s3err.PostAuth.IncorrectRegion(expectedCreds, s.awsRegion, wrongRegion))
|
||||
})
|
||||
}
|
||||
|
||||
func PostObject_non_existing_access_key(s *S3Conf) error {
|
||||
testName := "PostObject_non_existing_access_key"
|
||||
return actionHandlerNoSetup(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
accessKeyID := "this_access_key_id_can_not_really_exist"
|
||||
resp, err := sendPostObject(PostRequestConfig{
|
||||
bucket: bucket,
|
||||
key: "test-object",
|
||||
s3Conf: s,
|
||||
access: "this_access_key_id_can_not_really_exist",
|
||||
access: accessKeyID,
|
||||
secret: "a_very_secure_secret_access_key",
|
||||
fileContent: []byte("data"),
|
||||
})
|
||||
@@ -205,28 +207,57 @@ func PostObject_non_existing_access_key(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidAccessKeyID))
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetInvalidAccessKeyIdErr(accessKeyID))
|
||||
})
|
||||
}
|
||||
|
||||
func PostObject_signature_mismatch(s *S3Conf) error {
|
||||
testName := "PostObject_signature_mismatch"
|
||||
return actionHandlerNoSetup(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
resp, err := sendPostObject(PostRequestConfig{
|
||||
const signature = "incorrect_signature"
|
||||
req, fields, err := newPostObjectRequest(PostRequestConfig{
|
||||
bucket: bucket,
|
||||
key: "test-object",
|
||||
s3Conf: s,
|
||||
fileContent: []byte("data"),
|
||||
extraFields: map[string]string{
|
||||
"x-amz-signature": "incorrect_signature",
|
||||
"x-amz-signature": signature,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureDoesNotMatch))
|
||||
resp, err := s.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
var errResp APIErrorResponse
|
||||
err = xml.Unmarshal(body, &errResp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
expected := s3err.GetSignatureDoesNotMatchErr(s.awsID, fields["policy"], signature, hexBytes(fields["policy"]), "", "")
|
||||
|
||||
if resp.StatusCode != expected.HTTPStatusCode {
|
||||
return fmt.Errorf("expected response status code to be %v, instead got %v", expected.HTTPStatusCode, resp.StatusCode)
|
||||
}
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("AWSAccessKeyId", expected.AWSAccessKeyId, errResp.AWSAccessKeyId),
|
||||
compareErrField("StringToSign", expected.StringToSign, errResp.StringToSign),
|
||||
compareErrField("SignatureProvided", expected.SignatureProvided, errResp.SignatureProvided),
|
||||
compareErrField("StringToSignBytes", expected.StringToSignBytes, errResp.StringToSignBytes),
|
||||
checkErrFieldEmptiness("CanonicalRequest", expected.CanonicalRequest, false),
|
||||
checkErrFieldEmptiness("CanonicalRequestBytes", expected.CanonicalRequestBytes, false),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -466,7 +497,7 @@ func PostObject_invalid_policy_document(s *S3Conf) error {
|
||||
policy *string
|
||||
expiration time.Time
|
||||
conditions []any
|
||||
err s3err.APIError
|
||||
err s3err.S3Error
|
||||
}{
|
||||
// empty policy document
|
||||
{getPtr(""), time.Time{}, []any{}, s3err.InvalidPolicyDocument.EmptyPolicy()},
|
||||
@@ -616,7 +647,7 @@ func PostObject_policy_content_length_too_large(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrEntityTooLarge))
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetEntityTooLargeErr(10, 5))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -637,7 +668,7 @@ func PostObject_policy_content_length_too_small(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrEntityTooSmall))
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetEntityTooSmallErr(2, 100))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -700,7 +700,7 @@ func PutBucketPolicy_status(s *S3Conf) error {
|
||||
testname := "PutBucketPolicy_status"
|
||||
return actionHandler(s, testname, func(s3client *s3.Client, bucket string) error {
|
||||
doc := genPolicyDoc("Allow", `"*"`, `"s3:GetObject"`, fmt.Sprintf(`"arn:aws:s3:::%s/*"`, bucket))
|
||||
req, err := createSignedReq(http.MethodPut, s.endpoint, bucket+"?policy", s.awsID, s.awsSecret, "s3", s.awsRegion, []byte(doc), time.Now(), nil)
|
||||
req, err := createSignedReq(http.MethodPut, s.endpoint, bucket+"?policy", s.awsID, s.awsSecret, "s3", s.awsRegion, "", []byte(doc), time.Now(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ func PutBucketTagging_success_status(s *S3Conf) error {
|
||||
sum := hasher.Sum(nil)
|
||||
md5Sum := base64.StdEncoding.EncodeToString(sum)
|
||||
|
||||
req, err := createSignedReq(http.MethodPut, s.endpoint, fmt.Sprintf("%v?tagging=", bucket), s.awsID, s.awsSecret, "s3", s.awsRegion, taggingParsed, time.Now(), map[string]string{
|
||||
req, err := createSignedReq(http.MethodPut, s.endpoint, fmt.Sprintf("%v?tagging=", bucket), s.awsID, s.awsSecret, "s3", s.awsRegion, "", taggingParsed, time.Now(), map[string]string{
|
||||
"Content-Md5": md5Sum,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -98,7 +98,7 @@ func PutObject_tagging(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
switch eErr := expectedErr.(type) {
|
||||
case s3err.APIError:
|
||||
case s3err.S3Error:
|
||||
return checkApiErr(err, eErr)
|
||||
default:
|
||||
return fmt.Errorf("invalid err provided: %w", expectedErr)
|
||||
@@ -151,10 +151,10 @@ func PutObject_tagging(s *S3Conf) error {
|
||||
{"key1=val1&key2=val2", map[string]string{"key1": "val1", "key2": "val2"}, nil},
|
||||
{"key@=val@", map[string]string{"key@": "val@"}, nil},
|
||||
// invalid url-encoded
|
||||
{"=", nil, s3err.GetAPIError(s3err.ErrInvalidURLEncodedTagging)},
|
||||
{"key%", nil, s3err.GetAPIError(s3err.ErrInvalidURLEncodedTagging)},
|
||||
{"=", nil, s3err.GetInvalidArgumentErr(s3err.InvalidArgURLEncodedTagging, "")},
|
||||
{"key%", nil, s3err.GetInvalidArgumentErr(s3err.InvalidArgURLEncodedTagging, "")},
|
||||
// duplicate keys
|
||||
{"key=val&key=val", nil, s3err.GetAPIError(s3err.ErrInvalidURLEncodedTagging)},
|
||||
{"key=val&key=val", nil, s3err.GetInvalidArgumentErr(s3err.InvalidArgURLEncodedTagging, "")},
|
||||
// invalid tag keys
|
||||
{"key?=val", nil, s3err.GetAPIError(s3err.ErrInvalidTagKey)},
|
||||
{"key(=val", nil, s3err.GetAPIError(s3err.ErrInvalidTagKey)},
|
||||
@@ -212,13 +212,13 @@ func PutObject_missing_object_lock_retention_config(s *S3Conf) error {
|
||||
ObjectLockMode: types.ObjectLockModeCompliance,
|
||||
})
|
||||
cancel()
|
||||
if err := checkSdkApiErr(err, "InvalidRequest"); err != nil {
|
||||
if err := checkSdkApiErr(err, "InvalidArgument"); err != nil {
|
||||
return err
|
||||
}
|
||||
// client sdk regression issue prevents getting full error message,
|
||||
// change back to below once this is fixed:
|
||||
// https://github.com/aws/aws-sdk-go-v2/issues/2921
|
||||
// if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrObjectLockInvalidHeaders)); err != nil {
|
||||
// if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgMissingObjectLockRetainDate, "")); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
@@ -231,13 +231,13 @@ func PutObject_missing_object_lock_retention_config(s *S3Conf) error {
|
||||
ObjectLockRetainUntilDate: &retainDate,
|
||||
})
|
||||
cancel()
|
||||
if err := checkSdkApiErr(err, "InvalidRequest"); err != nil {
|
||||
if err := checkSdkApiErr(err, "InvalidArgument"); err != nil {
|
||||
return err
|
||||
}
|
||||
// client sdk regression issue prevents getting full error message,
|
||||
// change back to below once this is fixed:
|
||||
// https://github.com/aws/aws-sdk-go-v2/issues/2921
|
||||
// if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrObjectLockInvalidHeaders)); err != nil {
|
||||
// if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgMissingObjectLockMode, "")); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
@@ -315,7 +315,7 @@ func PutObject_invalid_legal_hold(s *S3Conf) error {
|
||||
Key: getPtr("foo"),
|
||||
ObjectLockLegalHoldStatus: types.ObjectLockLegalHoldStatus("invalid_status"),
|
||||
}, s3client)
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidLegalHoldStatus))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgLegalHoldStatus, "invalid_status"))
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
@@ -329,7 +329,7 @@ func PutObject_invalid_object_lock_mode(s *S3Conf) error {
|
||||
ObjectLockRetainUntilDate: &rDate,
|
||||
ObjectLockMode: types.ObjectLockMode("invalid_mode"),
|
||||
}, s3client)
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidObjectLockMode))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockMode, "invalid_mode"))
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ func PutObject_past_retain_until_date(s *S3Conf) error {
|
||||
ObjectLockRetainUntilDate: &rDate,
|
||||
}, s3client)
|
||||
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrPastObjectLockRetainDate))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgPastObjectLockRetainDate, rDate.Format(time.RFC3339)))
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
@@ -359,6 +359,7 @@ func PutObject_invalid_retain_until_date(s *S3Conf) error {
|
||||
s.awsSecret,
|
||||
"s3",
|
||||
s.awsRegion,
|
||||
"",
|
||||
nil,
|
||||
time.Now(),
|
||||
map[string]string{
|
||||
@@ -375,7 +376,7 @@ func PutObject_invalid_retain_until_date(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidRetainUntilDate))
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetInvalidArgumentErr(s3err.InvalidArgRetainUntilDate, "invalid_date"))
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ func PutObjectLockConfiguration_malformed_body(s *S3Conf) error {
|
||||
s.awsSecret,
|
||||
"s3",
|
||||
s.awsRegion,
|
||||
"",
|
||||
body,
|
||||
time.Now(),
|
||||
map[string]string{"Content-Md5": md5Sum},
|
||||
@@ -227,7 +228,7 @@ func PutObjectLockConfiguration_invalid_years_days(s *S3Conf) error {
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrObjectLockInvalidRetentionPeriod)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionDays, fmt.Sprint(days))); err != nil {
|
||||
return err
|
||||
}
|
||||
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
||||
@@ -244,7 +245,7 @@ func PutObjectLockConfiguration_invalid_years_days(s *S3Conf) error {
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrObjectLockInvalidRetentionPeriod)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionYears, fmt.Sprint(years))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ func PutObjectRetention_expired_retain_until_date(s *S3Conf) error {
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrPastObjectLockRetainDate)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgPastObjectLockRetainDate, date.Format(time.RFC3339))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ func UploadPart_invalid_part_number(s *S3Conf) error {
|
||||
PartNumber: &el,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidPartNumber)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgPartNumber, fmt.Sprint(el))); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ func UploadPartCopy_invalid_part_number(s *S3Conf) error {
|
||||
PartNumber: &partNumber,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidPartNumber)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgPartNumber, fmt.Sprint(partNumber))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -156,87 +156,87 @@ func UploadPartCopy_invalid_copy_source(s *S3Conf) error {
|
||||
partNumber := int32(1)
|
||||
for _, test := range []struct {
|
||||
copySource string
|
||||
expectedErr s3err.APIError
|
||||
expectedErr s3err.S3Error
|
||||
}{
|
||||
// invalid encoding
|
||||
{
|
||||
// Invalid hex digits
|
||||
copySource: "bucket/%ZZ",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceEncoding, "bucket/%ZZ"),
|
||||
},
|
||||
{
|
||||
// Ends with incomplete escape
|
||||
copySource: "100%/foo/bar/baz",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceEncoding, "100%/foo/bar/baz"),
|
||||
},
|
||||
{
|
||||
// Only one digit after %
|
||||
copySource: "bucket/%A/bar",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceEncoding, "bucket/%A/bar"),
|
||||
},
|
||||
{
|
||||
// 'G' is not a hex digit
|
||||
copySource: "bucket/%G1/",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceEncoding, "bucket/%G1/"),
|
||||
},
|
||||
{
|
||||
// Just a single percent sign
|
||||
copySource: "%",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceEncoding, "%"),
|
||||
},
|
||||
{
|
||||
// Only one hex digit
|
||||
copySource: "bucket/%1",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceEncoding, "bucket/%1"),
|
||||
},
|
||||
{
|
||||
// Incomplete multibyte UTF-8
|
||||
copySource: "bucket/%C3%",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceEncoding, "bucket/%C3%"),
|
||||
},
|
||||
// invalid bucket name
|
||||
{
|
||||
// ip v4 address
|
||||
copySource: "192.168.1.1/foo",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceBucket),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceBucket, "192.168.1.1/foo"),
|
||||
},
|
||||
{
|
||||
// ip v6 address
|
||||
copySource: "2001:0db8:85a3:0000:0000:8a2e:0370:7334/something",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceBucket),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceBucket, "2001:0db8:85a3:0000:0000:8a2e:0370:7334/something"),
|
||||
},
|
||||
{
|
||||
// some special chars
|
||||
copySource: "my-buc@k&()t/obj",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceBucket),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceBucket, "my-buc@k&()t/obj"),
|
||||
},
|
||||
// invalid object key
|
||||
{
|
||||
// object is missing
|
||||
copySource: "bucket",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceObject),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceObject, ""),
|
||||
},
|
||||
{
|
||||
// object is missing
|
||||
copySource: "bucket/",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceObject),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceObject, ""),
|
||||
},
|
||||
// directory navigation object keys
|
||||
{
|
||||
copySource: "bucket/.",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceObject),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceObject, "."),
|
||||
},
|
||||
{
|
||||
copySource: "bucket/..",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceObject),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceObject, ".."),
|
||||
},
|
||||
{
|
||||
copySource: "bucket/../",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceObject),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceObject, "../"),
|
||||
},
|
||||
{
|
||||
copySource: "bucket/foo/ba/../../../r/baz",
|
||||
expectedErr: s3err.GetAPIError(s3err.ErrInvalidCopySourceObject),
|
||||
expectedErr: s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceObject, "foo/ba/../../../r/baz"),
|
||||
},
|
||||
} {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
@@ -429,7 +429,7 @@ func UploadPartCopy_by_range_invalid_ranges(s *S3Conf) error {
|
||||
CopySourceRange: &csRange,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidCopySourceRange)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySourceRange, csRange)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -493,7 +493,7 @@ func UploadPartCopy_exceeding_copy_source_range(s *S3Conf) error {
|
||||
CopySourceRange: &csRange,
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.CreateExceedingRangeErr(objSize))
|
||||
return checkApiErr(err, s3err.GetInvalidArgExceedingRange(objSize))
|
||||
}
|
||||
|
||||
for i, rg := range []string{
|
||||
@@ -549,7 +549,7 @@ func UploadPartCopy_greater_range_than_obj_size(s *S3Conf) error {
|
||||
PartNumber: &partNumber,
|
||||
})
|
||||
cancel()
|
||||
if err := checkApiErr(err, s3err.CreateExceedingRangeErr(int64(srcObjSize))); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgExceedingRange(int64(srcObjSize))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ func RouterPutPartNumberWithoutUploadId(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMissingUploadId)); err != nil {
|
||||
if err := checkHTTPResponseApiErr(resp, s3err.GetInvalidArgumentErr(s3err.InvalidArgMissingUploadId, "partNumber")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func RouterPostRoot(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMethodNotAllowed)); err != nil {
|
||||
if err := checkHTTPResponseApiErr(resp, s3err.GetMethodNotAllowedErr(http.MethodPost, s3err.ResourceTypeService, nil)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ func RouterPostObjectWithoutQuery(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMethodNotAllowed)); err != nil {
|
||||
if err := checkHTTPResponseApiErr(resp, s3err.GetMethodNotAllowedErr(http.MethodPost, s3err.ResourceTypeService, nil)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -117,7 +117,11 @@ func RouterPUTObjectOnlyUploadId(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMethodNotAllowed)); err != nil {
|
||||
if err := checkHTTPResponseApiErr(resp, s3err.GetMethodNotAllowedErr(
|
||||
http.MethodPut,
|
||||
s3err.ResourceTypeUpload,
|
||||
[]string{http.MethodDelete, http.MethodPost, http.MethodGet},
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -178,7 +182,7 @@ func RouterCopySourceNotAllowed(s *S3Conf) error {
|
||||
return fmt.Errorf("expected 400 status code for HEAD %s request, instead got %v", path, resp.StatusCode)
|
||||
}
|
||||
} else {
|
||||
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrCopySourceNotAllowed)); err != nil {
|
||||
if err := checkHTTPResponseApiErr(resp, s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySource, "bucket/object")); err != nil {
|
||||
return fmt.Errorf("%s %s: %w", method, path, err)
|
||||
}
|
||||
}
|
||||
@@ -226,7 +230,7 @@ func CORSMiddleware_invalid_method(s *S3Conf) error {
|
||||
}
|
||||
|
||||
// create a PutObject signed request
|
||||
req, err := createSignedReq(http.MethodPut, s.endpoint, bucket+"/my-obj", s.awsID, s.awsSecret, "s3", s.awsRegion, nil, time.Now(), map[string]string{
|
||||
req, err := createSignedReq(http.MethodPut, s.endpoint, bucket+"/my-obj", s.awsID, s.awsSecret, "s3", s.awsRegion, "", nil, time.Now(), map[string]string{
|
||||
"Origin": "http://www.example.com",
|
||||
"Access-Control-Request-Method": "invalid_method",
|
||||
})
|
||||
@@ -267,7 +271,7 @@ func CORSMiddleware_invalid_headers(s *S3Conf) error {
|
||||
}
|
||||
|
||||
// create a PutObject signed request
|
||||
req, err := createSignedReq(http.MethodPut, s.endpoint, bucket+"/my-obj", s.awsID, s.awsSecret, "s3", s.awsRegion, nil, time.Now(), map[string]string{
|
||||
req, err := createSignedReq(http.MethodPut, s.endpoint, bucket+"/my-obj", s.awsID, s.awsSecret, "s3", s.awsRegion, "", nil, time.Now(), map[string]string{
|
||||
"Origin": "http://www.example.com",
|
||||
"Access-Control-Request-Headers": "invalid header",
|
||||
})
|
||||
@@ -330,7 +334,7 @@ func CORSMiddleware_access_forbidden(s *S3Conf) error {
|
||||
// origin match, method not (2nd rule)
|
||||
{"https://any-origin.com", http.MethodPost, ""},
|
||||
} {
|
||||
req, err := createSignedReq(http.MethodPut, s.endpoint, bucket+"/my-obj", s.awsID, s.awsSecret, "s3", s.awsRegion, nil, time.Now(), map[string]string{
|
||||
req, err := createSignedReq(http.MethodPut, s.endpoint, bucket+"/my-obj", s.awsID, s.awsSecret, "s3", s.awsRegion, "", nil, time.Now(), map[string]string{
|
||||
"Origin": test.origin,
|
||||
"Access-Control-Request-Headers": test.headers,
|
||||
"Access-Control-Request-Method": test.method,
|
||||
@@ -413,7 +417,7 @@ func CORSMiddleware_access_granted(s *S3Conf) error {
|
||||
{"something.net", http.MethodPut, "Authorization", PreflightResult{"something.net", "POST, PUT", "authorization", "Content-Disposition, Content-Encoding", "3000", "true", varyHdr, nil}},
|
||||
{"something.net", http.MethodPost, "", PreflightResult{"something.net", "POST, PUT", "", "Content-Disposition, Content-Encoding", "3000", "true", varyHdr, nil}},
|
||||
} {
|
||||
req, err := createSignedReq(http.MethodPut, s.endpoint, bucket+"/my-obj", s.awsID, s.awsSecret, "s3", s.awsRegion, nil, time.Now(), map[string]string{
|
||||
req, err := createSignedReq(http.MethodPut, s.endpoint, bucket+"/my-obj", s.awsID, s.awsSecret, "s3", s.awsRegion, "", nil, time.Now(), map[string]string{
|
||||
"Origin": test.origin,
|
||||
"Access-Control-Request-Headers": test.headers,
|
||||
"Access-Control-Request-Method": test.method,
|
||||
|
||||
@@ -529,6 +529,7 @@ func TestCompleteMultipartUpload(ts *TestState) {
|
||||
ts.Run(CompleteMultipartUpload_invalid_ETag)
|
||||
ts.Run(CompleteMultipartUpload_small_upload_size)
|
||||
ts.Run(CompleteMultipartUpload_empty_parts)
|
||||
ts.Run(CompleteMultipartUpload_missing_part_fields)
|
||||
ts.Run(CompleteMultipartUpload_incorrect_parts_order)
|
||||
ts.Run(CompleteMultipartUpload_mpu_object_size)
|
||||
ts.Run(CompleteMultipartUpload_conditional_writes)
|
||||
@@ -969,6 +970,7 @@ func TestScoutfs(ts *TestState) {
|
||||
ts.Run(CompleteMultipartUpload_invalid_ETag)
|
||||
ts.Run(CompleteMultipartUpload_small_upload_size)
|
||||
ts.Run(CompleteMultipartUpload_empty_parts)
|
||||
ts.Run(CompleteMultipartUpload_missing_part_fields)
|
||||
ts.Run(CompleteMultipartUpload_incorrect_parts_order)
|
||||
ts.Run(CompleteMultipartUpload_mpu_object_size)
|
||||
ts.Run(CompleteMultipartUpload_invalid_checksum_type)
|
||||
@@ -1648,6 +1650,7 @@ func GetIntTests() IntTests {
|
||||
"CompleteMultipartUpload_invalid_ETag": CompleteMultipartUpload_invalid_ETag,
|
||||
"CompleteMultipartUpload_small_upload_size": CompleteMultipartUpload_small_upload_size,
|
||||
"CompleteMultipartUpload_empty_parts": CompleteMultipartUpload_empty_parts,
|
||||
"CompleteMultipartUpload_missing_part_fields": CompleteMultipartUpload_missing_part_fields,
|
||||
"CompleteMultipartUpload_incorrect_part_number": CompleteMultipartUpload_incorrect_part_number,
|
||||
"CompleteMultipartUpload_incorrect_parts_order": CompleteMultipartUpload_incorrect_parts_order,
|
||||
"CompleteMultipartUpload_mpu_object_size": CompleteMultipartUpload_mpu_object_size,
|
||||
|
||||
@@ -189,6 +189,7 @@ func IAM_CreateBucket_empty_owner_header(s *S3Conf) error {
|
||||
s.awsSecret,
|
||||
"s3",
|
||||
s.awsRegion,
|
||||
"",
|
||||
nil,
|
||||
time.Now(),
|
||||
nil,
|
||||
@@ -217,6 +218,7 @@ func IAM_CreateBucket_non_existing_user(s *S3Conf) error {
|
||||
s.awsSecret,
|
||||
"s3",
|
||||
s.awsRegion,
|
||||
"",
|
||||
nil,
|
||||
time.Now(),
|
||||
map[string]string{
|
||||
@@ -263,6 +265,7 @@ func IAM_CreateBucket_success(s *S3Conf) error {
|
||||
s.awsSecret,
|
||||
"s3",
|
||||
s.awsRegion,
|
||||
"",
|
||||
body,
|
||||
time.Now(),
|
||||
map[string]string{
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -200,7 +201,7 @@ func PresignedAuth_malformed_creds_invalid_parts(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.MalformedCredential())
|
||||
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.MalformedCredential(""))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -229,7 +230,7 @@ func PresignedAuth_creds_invalid_terminal(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.IncorrectTerminal("aws5_request"))
|
||||
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.IncorrectTerminal("", "aws5_request"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -258,7 +259,7 @@ func PresignedAuth_creds_incorrect_service(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.IncorrectService("sns"))
|
||||
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.IncorrectService("", "sns"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -321,13 +322,14 @@ func PresignedAuth_creds_invalid_date(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.InvalidDateFormat("32234Z34"))
|
||||
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.InvalidDateFormat("", "32234Z34"))
|
||||
})
|
||||
}
|
||||
|
||||
func PresignedAuth_non_existing_access_key_id(s *S3Conf) error {
|
||||
testName := "PresignedAuth_non_existing_access_key_id"
|
||||
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
|
||||
accessKeyID := "a_rarely_existing_access_key_id890asd6f807as6ydf870say"
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
|
||||
cancel()
|
||||
@@ -335,7 +337,7 @@ func PresignedAuth_non_existing_access_key_id(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
uri, err := changeAuthCred(v4req.URL, "a_rarely_existing_access_key_id890asd6f807as6ydf870say", credAccess)
|
||||
uri, err := changeAuthCred(v4req.URL, accessKeyID, credAccess)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -350,7 +352,7 @@ func PresignedAuth_non_existing_access_key_id(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidAccessKeyID))
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetInvalidAccessKeyIdErr(accessKeyID))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -609,6 +611,10 @@ func PresignedAuth_expired_request(s *S3Conf) error {
|
||||
queries := urlParsed.Query()
|
||||
queries.Set("X-Amz-Date", expDate)
|
||||
urlParsed.RawQuery = queries.Encode()
|
||||
xAmzExpires, err := strconv.Atoi(queries.Get("X-Amz-Expires"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uri, err := changeAuthCred(urlParsed.String(), expDate[:8], credDate)
|
||||
if err != nil {
|
||||
@@ -625,7 +631,7 @@ func PresignedAuth_expired_request(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrExpiredPresignRequest)); err != nil {
|
||||
if err := checkHTTPResponseApiErr(resp, s3err.GetExpiredPresignedURLError(xAmzExpires, "", "")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -2475,13 +2475,14 @@ func PublicBucket_incorrect_sha256_hash(s *S3Conf) error {
|
||||
// in anonymous requests the sha256 hash validity is not checked
|
||||
// so for any invalid values, the server calculates the hash
|
||||
// and compares with the provided one
|
||||
req.Header.Add("x-amz-content-sha256", "incorrect_hash")
|
||||
const incorrectPayloadHash = "incorrect_hash"
|
||||
req.Header.Add("x-amz-content-sha256", incorrectPayloadHash)
|
||||
|
||||
resp, err := s.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrContentSHA256Mismatch))
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetContentSHA256MismatchErr(incorrectPayloadHash, emptySHA256Hash))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
func Server_large_http_header(s *S3Conf) error {
|
||||
testName := "Server_large_http_header"
|
||||
return actionHandlerNoSetup(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
req, err := createSignedReq(http.MethodPut, s.endpoint, "/bucket/object", s.awsID, s.awsSecret, "s3", s.awsRegion, nil, time.Now(), map[string]string{
|
||||
req, err := createSignedReq(http.MethodPut, s.endpoint, "/bucket/object", s.awsID, s.awsSecret, "s3", s.awsRegion, "", nil, time.Now(), map[string]string{
|
||||
"x-amz-custom-header": strings.Repeat("d", 1024*8),
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -42,7 +44,7 @@ func Authentication_invalid_auth_header(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidAuthHeader))
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetInvalidArgumentErr(s3err.InvalidArgAuthHeader, "invalid_header"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -64,7 +66,7 @@ func Authentication_unsupported_signature_version(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrUnsupportedAuthorizationType))
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetInvalidArgumentErr(s3err.InvalidArgAuthorizationType, "AWS2-HMAC-SHA1"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -192,7 +194,7 @@ func Authentication_malformed_credential(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.MalformedCredential())
|
||||
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.MalformedCredential(""))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -215,7 +217,7 @@ func Authentication_credentials_invalid_terminal(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.IncorrectTerminal("aws_request"))
|
||||
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.IncorrectTerminal("", "aws_request"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -238,7 +240,7 @@ func Authentication_credentials_incorrect_service(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.IncorrectService("ec2"))
|
||||
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.IncorrectService("", "ec2"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -285,7 +287,7 @@ func Authentication_credentials_invalid_date(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.InvalidDateFormat("3223423234"))
|
||||
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.InvalidDateFormat("", "3223423234"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -309,7 +311,7 @@ func Authentication_credentials_future_date(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var errResp s3err.APIErrorResponse
|
||||
var errResp APIErrorResponse
|
||||
err = xml.Unmarshal(body, &errResp)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -346,7 +348,7 @@ func Authentication_credentials_past_date(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var errResp s3err.APIErrorResponse
|
||||
var errResp APIErrorResponse
|
||||
err = xml.Unmarshal(body, &errResp)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -372,9 +374,10 @@ func Authentication_credentials_non_existing_access_key(s *S3Conf) error {
|
||||
service: "s3",
|
||||
date: time.Now(),
|
||||
}, func(req *http.Request) error {
|
||||
accessKeyID := "a_rarely_existing_access_key_id_a7s86df78as6df89790a8sd7f"
|
||||
authHdr := req.Header.Get("Authorization")
|
||||
regExp := regexp.MustCompile("Credential=([^/]+)")
|
||||
hdr := regExp.ReplaceAllString(authHdr, "Credential=a_rarely_existing_access_key_id_a7s86df78as6df89790a8sd7f")
|
||||
hdr := regExp.ReplaceAllString(authHdr, "Credential="+accessKeyID)
|
||||
req.Header.Set("Authorization", hdr)
|
||||
|
||||
resp, err := s.httpClient.Do(req)
|
||||
@@ -382,7 +385,7 @@ func Authentication_credentials_non_existing_access_key(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidAccessKeyID))
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetInvalidAccessKeyIdErr(accessKeyID))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -471,28 +474,28 @@ func Authentication_invalid_sha256_payload_hash(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidSHA256Paylod))
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetInvalidArgumentErr(s3err.InvalidArgSHA256Payload, "invalid_sha256"))
|
||||
})
|
||||
}
|
||||
|
||||
func Authentication_incorrect_payload_hash(s *S3Conf) error {
|
||||
testName := "Authentication_incorrect_payload_hash"
|
||||
const incorrectPayloadHash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b854"
|
||||
return authHandler(s, &authConfig{
|
||||
testName: testName,
|
||||
method: http.MethodPut,
|
||||
body: nil,
|
||||
service: "s3",
|
||||
date: time.Now(),
|
||||
path: "bucket/object?tagging",
|
||||
testName: testName,
|
||||
method: http.MethodPut,
|
||||
body: nil,
|
||||
service: "s3",
|
||||
date: time.Now(),
|
||||
path: "bucket/object?tagging",
|
||||
overrideSha256: incorrectPayloadHash,
|
||||
}, func(req *http.Request) error {
|
||||
req.Header.Set("X-Amz-Content-Sha256", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b854")
|
||||
|
||||
resp, err := s.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrContentSHA256Mismatch))
|
||||
return checkHTTPResponseApiErr(resp, s3err.GetContentSHA256MismatchErr(incorrectPayloadHash, emptySHA256Hash))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -512,15 +515,18 @@ func Authentication_md5(s *S3Conf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
sum := md5.Sum(nil)
|
||||
emptyMd5 := base64.StdEncoding.EncodeToString(sum[:])
|
||||
|
||||
for i, test := range []struct {
|
||||
md5 string
|
||||
err s3err.APIError
|
||||
err s3err.S3Error
|
||||
}{
|
||||
{"invalid_md5", s3err.GetAPIError(s3err.ErrInvalidDigest)},
|
||||
{"invalid_md5", s3err.GetInvalidDigestErr("invalid_md5")},
|
||||
// valid base64, but invalid md5
|
||||
{"aGVsbCBzLGRham5mamFuc2Y=", s3err.GetAPIError(s3err.ErrInvalidDigest)},
|
||||
{"aGVsbCBzLGRham5mamFuc2Y=", s3err.GetInvalidDigestErr("aGVsbCBzLGRham5mamFuc2Y=")},
|
||||
// valid md5, but incorrect
|
||||
{"XrY7u+Ae7tCTyyK7j1rNww==", s3err.GetAPIError(s3err.ErrBadDigest)},
|
||||
{"XrY7u+Ae7tCTyyK7j1rNww==", s3err.GetBadDigestErr(emptyMd5, base64ToHexString("XrY7u+Ae7tCTyyK7j1rNww=="))},
|
||||
} {
|
||||
req.Header.Set("Content-Md5", test.md5)
|
||||
|
||||
@@ -554,7 +560,6 @@ func Authentication_signature_error_incorrect_secret_key(s *S3Conf) error {
|
||||
service: "s3",
|
||||
date: time.Now(),
|
||||
}, func(req *http.Request) error {
|
||||
|
||||
resp, err := s.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
+273
-32
@@ -61,6 +61,8 @@ import (
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
|
||||
const emptySHA256Hash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
|
||||
var (
|
||||
bcktCount atomic.Uint64
|
||||
adminErrorPrefix = "XAdmin"
|
||||
@@ -296,18 +298,19 @@ func actionHandlerNoSetup(s *S3Conf, testName string, handler func(s3client *s3.
|
||||
}
|
||||
|
||||
type authConfig struct {
|
||||
testName string
|
||||
path string
|
||||
method string
|
||||
body []byte
|
||||
service string
|
||||
date time.Time
|
||||
headers map[string]string
|
||||
testName string
|
||||
path string
|
||||
method string
|
||||
overrideSha256 string
|
||||
body []byte
|
||||
service string
|
||||
date time.Time
|
||||
headers map[string]string
|
||||
}
|
||||
|
||||
func authHandler(s *S3Conf, cfg *authConfig, handler func(req *http.Request) error) error {
|
||||
runF(cfg.testName)
|
||||
req, err := createSignedReq(cfg.method, s.endpoint, cfg.path, s.awsID, s.awsSecret, cfg.service, s.awsRegion, cfg.body, cfg.date, cfg.headers)
|
||||
req, err := createSignedReq(cfg.method, s.endpoint, cfg.path, s.awsID, s.awsSecret, cfg.service, s.awsRegion, cfg.overrideSha256, cfg.body, cfg.date, cfg.headers)
|
||||
if err != nil {
|
||||
failF("%v: %v", cfg.testName, err)
|
||||
return fmt.Errorf("%v: %w", cfg.testName, err)
|
||||
@@ -348,7 +351,7 @@ func presignedAuthHandler(s *S3Conf, testName string, handler func(client *s3.Pr
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSignedReq(method, endpoint, path, access, secret, service, region string, body []byte, date time.Time, headers map[string]string) (*http.Request, error) {
|
||||
func createSignedReq(method, endpoint, path, access, secret, service, region, overrideSha256 string, body []byte, date time.Time, headers map[string]string) (*http.Request, error) {
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("%v/%v", endpoint, path), bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to send the request: %w", err)
|
||||
@@ -356,8 +359,11 @@ func createSignedReq(method, endpoint, path, access, secret, service, region str
|
||||
|
||||
signer := v4.NewSigner()
|
||||
|
||||
hashedPayload := sha256.Sum256(body)
|
||||
hexPayload := hex.EncodeToString(hashedPayload[:])
|
||||
hexPayload := overrideSha256
|
||||
if hexPayload == "" {
|
||||
hashedPayload := sha256.Sum256(body)
|
||||
hexPayload = hex.EncodeToString(hashedPayload[:])
|
||||
}
|
||||
|
||||
req.Header.Set("X-Amz-Content-Sha256", hexPayload)
|
||||
for key, val := range headers {
|
||||
@@ -372,7 +378,60 @@ func createSignedReq(method, endpoint, path, access, secret, service, region str
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func checkHTTPResponseApiErr(resp *http.Response, apiErr s3err.APIError) error {
|
||||
type APIErrorResponse struct {
|
||||
XMLName xml.Name `xml:"Error"`
|
||||
Code string
|
||||
Message string
|
||||
ArgumentName string `xml:"ArgumentName,omitempty"`
|
||||
ArgumentValue string `xml:"ArgumentValue,omitempty"`
|
||||
Method string `xml:"Method,omitempty"`
|
||||
Resource string `xml:"Resource,omitempty"`
|
||||
ResourceType s3err.ResourceType
|
||||
CalculatedDigest string `xml:"CalculatedDigest,omitempty"`
|
||||
ExpectedDigest string `xml:"ExpectedDigest,omitempty"`
|
||||
BucketName string `xml:"BucketName,omitempty"`
|
||||
ClientComputedContentSHA256 string `xml:"ClientComputedContentSHA256,omitempty"`
|
||||
S3ComputedContentSHA256 string `xml:"S3ComputedContentSHA256,omitempty"`
|
||||
ProposedSize int64 `xml:"ProposedSize,omitempty"`
|
||||
MaxSizeAllowed int64 `xml:"MaxSizeAllowed,omitempty"`
|
||||
MinSizeAllowed int64 `xml:"MinSizeAllowed,omitempty"`
|
||||
ServerTime string `xml:"ServerTime,omitempty"`
|
||||
XAmzExpires int `xml:"X-Amz-Expires,omitempty"`
|
||||
Expires string `xml:"Expires,omitempty"`
|
||||
AWSAccessKeyId string `xml:"AWSAccessKeyId,omitempty"`
|
||||
Chunk int `xml:"Chunk,omitempty"`
|
||||
BadChunkSize int64 `xml:"BadChunkSize,omitempty"`
|
||||
ContentMD5 string `xml:"Content-MD5,omitempty"`
|
||||
LocationConstraint string `xml:"LocationConstraint,omitempty"`
|
||||
UploadId string `xml:"UploadId,omitempty"`
|
||||
PartNumber int32 `xml:"PartNumber,omitempty"`
|
||||
ETag string `xml:"ETag,omitempty"`
|
||||
ActualPartCount int32 `xml:"ActualPartCount,omitempty"`
|
||||
PartNumberRequested int32 `xml:"PartNumberRequested,omitempty"`
|
||||
RangeRequested string `xml:"RangeRequested,omitempty"`
|
||||
ActualObjectSize int64 `xml:"ActualObjectSize,omitempty"`
|
||||
TagKey string `xml:"TagKey,omitempty"`
|
||||
TagValue string `xml:"TagValue,omitempty"`
|
||||
Size int64 `xml:"Size,omitempty"`
|
||||
Header string `xml:"Header,omitempty"`
|
||||
AdditionalMessage s3err.NmpAdditionalMessage `xml:"additionalMessage,omitempty"`
|
||||
Key string `xml:"Key,omitempty"`
|
||||
VersionId string `xml:"VersionId,omitempty"`
|
||||
Condition s3err.Condition `xml:"Condition,omitempty"`
|
||||
RequestTime string `xml:"RequestTime,omitempty"`
|
||||
MaxAllowedSkewMilliseconds int `xml:"MaxAllowedSkewMilliseconds,omitempty"`
|
||||
Region string `xml:"Region,omitempty"`
|
||||
StringToSign string `xml:"StringToSign,omitempty"`
|
||||
SignatureProvided string `xml:"SignatureProvided,omitempty"`
|
||||
StringToSignBytes string `xml:"StringToSignBytes,omitempty"`
|
||||
CanonicalRequest string `xml:"CanonicalRequest,omitempty"`
|
||||
CanonicalRequestBytes string `xml:"CanonicalRequestBytes,omitempty"`
|
||||
RequestID string `xml:"RequestId,omitempty"`
|
||||
HostID string `xml:"HostId,omitempty"`
|
||||
}
|
||||
|
||||
func checkHTTPResponseApiErr(resp *http.Response, expected s3err.S3Error) error {
|
||||
apiErr := expected.BaseError()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -380,7 +439,7 @@ func checkHTTPResponseApiErr(resp *http.Response, apiErr s3err.APIError) error {
|
||||
|
||||
resp.Body.Close()
|
||||
|
||||
var errResp s3err.APIErrorResponse
|
||||
var errResp APIErrorResponse
|
||||
err = xml.Unmarshal(body, &errResp)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -389,25 +448,181 @@ func checkHTTPResponseApiErr(resp *http.Response, apiErr s3err.APIError) error {
|
||||
if resp.StatusCode != apiErr.HTTPStatusCode {
|
||||
return fmt.Errorf("expected response status code to be %v, instead got %v", apiErr.HTTPStatusCode, resp.StatusCode)
|
||||
}
|
||||
return compareS3ApiError(apiErr, &errResp)
|
||||
return compareS3ApiError(expected, &errResp)
|
||||
}
|
||||
|
||||
func compareS3ApiError(expected s3err.APIError, received *s3err.APIErrorResponse) error {
|
||||
func compareS3ApiError(expected s3err.S3Error, received *APIErrorResponse) error {
|
||||
apiErr := expected.BaseError()
|
||||
if received == nil {
|
||||
return fmt.Errorf("expected %w, received nil", expected)
|
||||
return fmt.Errorf("expected %w, received nil", apiErr)
|
||||
}
|
||||
|
||||
if received.Code != expected.Code {
|
||||
return fmt.Errorf("expected error code to be %v, instead got %v", expected.Code, received.Code)
|
||||
if received.Code != apiErr.Code {
|
||||
return fmt.Errorf("expected error code to be %v, instead got %v", apiErr.Code, received.Code)
|
||||
}
|
||||
if received.Message != expected.Description {
|
||||
return fmt.Errorf("expected error message to be %v, instead got %v", expected.Description, received.Message)
|
||||
if received.Message != apiErr.Description {
|
||||
return fmt.Errorf("expected error message to be %v, instead got %v", apiErr.Description, received.Message)
|
||||
}
|
||||
|
||||
return compareS3ApiErr(expected, received)
|
||||
}
|
||||
|
||||
func compareS3ApiErr(expected s3err.S3Error, received *APIErrorResponse) error {
|
||||
switch err := expected.(type) {
|
||||
case s3err.APIError:
|
||||
return nil
|
||||
case s3err.AccessForbiddenError:
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("Method", err.Method, received.Method),
|
||||
compareErrField("ResourceType", err.ResourceType, received.ResourceType),
|
||||
)
|
||||
case s3err.BadDigestError:
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("CalculatedDigest", err.CalculatedDigest, received.CalculatedDigest),
|
||||
compareErrField("ExpectedDigest", err.ExpectedDigest, received.ExpectedDigest),
|
||||
)
|
||||
case s3err.BucketError:
|
||||
return compareErrField("BucketName", err.BucketName, received.BucketName)
|
||||
case s3err.ContentSHA256MismatchError:
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("ClientComputedContentSHA256", err.ClientComputedContentSHA256, received.ClientComputedContentSHA256),
|
||||
compareErrField("S3ComputedContentSHA256", err.S3ComputedContentSHA256, received.S3ComputedContentSHA256),
|
||||
)
|
||||
case s3err.EntityTooLargeError:
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("ProposedSize", err.ProposedSize, received.ProposedSize),
|
||||
compareErrField("MaxSizeAllowed", err.MaxSizeAllowed, received.MaxSizeAllowed),
|
||||
)
|
||||
case s3err.EntityTooSmallError:
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("ProposedSize", err.ProposedSize, received.ProposedSize),
|
||||
compareErrField("MinSizeAllowed", err.MinSizeAllowed, received.MinSizeAllowed),
|
||||
)
|
||||
case s3err.ExpiredPresignedURLError:
|
||||
return compareS3ApiErrFields(
|
||||
checkErrFieldEmptiness("ServerTime", received.ServerTime, true),
|
||||
compareErrField("X-Amz-Expires", err.XAmzExpires, received.XAmzExpires),
|
||||
checkErrFieldEmptiness("Expires", received.Expires, true),
|
||||
)
|
||||
case s3err.InvalidAccessKeyIdError:
|
||||
return compareErrField("AWSAccessKeyId", err.AWSAccessKeyId, received.AWSAccessKeyId)
|
||||
case s3err.InvalidArgumentError:
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("ArgumentName", err.ArgumentName, received.ArgumentName),
|
||||
compareErrField("ArgumentValue", err.ArgumentValue, received.ArgumentValue),
|
||||
)
|
||||
case s3err.InvalidChunkSizeError:
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("Chunk", err.Chunk, received.Chunk),
|
||||
compareErrField("BadChunkSize", err.BadChunkSize, received.BadChunkSize),
|
||||
)
|
||||
case s3err.InvalidDigestError:
|
||||
return compareErrField("Content-MD5", err.ContentMD5, received.ContentMD5)
|
||||
case s3err.InvalidLocationConstraintError:
|
||||
return compareErrField("LocationConstraint", err.LocationConstraint, received.LocationConstraint)
|
||||
case s3err.InvalidPartError:
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("UploadId", err.UploadId, received.UploadId),
|
||||
compareErrField("PartNumber", err.PartNumber, received.PartNumber),
|
||||
compareErrField("ETag", err.ETag, received.ETag),
|
||||
)
|
||||
case s3err.InvalidPartNumberRangeError:
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("ActualPartCount", err.ActualPartCount, received.ActualPartCount),
|
||||
compareErrField("PartNumberRequested", err.PartNumberRequested, received.PartNumberRequested),
|
||||
)
|
||||
case s3err.InvalidRangeError:
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("RangeRequested", err.RangeRequested, received.RangeRequested),
|
||||
compareErrField("ActualObjectSize", err.ActualObjectSize, received.ActualObjectSize),
|
||||
)
|
||||
case s3err.InvalidTagError:
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("TagKey", err.TagKey, received.TagKey),
|
||||
compareErrField("TagValue", err.TagValue, received.TagValue),
|
||||
)
|
||||
case s3err.KeyTooLongError:
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("Size", err.Size, received.Size),
|
||||
compareErrField("MaxSizeAllowed", err.MaxSizeAllowed, received.MaxSizeAllowed),
|
||||
)
|
||||
case s3err.MetadataTooLargeError:
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("Size", int64(err.Size), received.Size),
|
||||
compareErrField("MaxSizeAllowed", int64(err.MaxSizeAllowed), received.MaxSizeAllowed),
|
||||
)
|
||||
case s3err.MethodNotAllowedError:
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("Method", err.Method, received.Method),
|
||||
compareErrField("ResourceType", err.ResourceType, received.ResourceType),
|
||||
)
|
||||
case s3err.MalformedAuthError:
|
||||
return compareErrField("Region", err.Region, received.Region)
|
||||
case s3err.NoSuchUploadError:
|
||||
return compareErrField("UploadId", err.UploadId, received.UploadId)
|
||||
case s3err.NoSuchVersionError:
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("Key", err.Key, received.Key),
|
||||
compareErrField("VersionId", err.VersionId, received.VersionId),
|
||||
)
|
||||
case s3err.NotImplementedError:
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("Header", err.Header, received.Header),
|
||||
compareErrField("additionalMessage", err.AdditionalMessage, received.AdditionalMessage),
|
||||
)
|
||||
case s3err.PreconditionFailedError:
|
||||
return compareErrField("Condition", err.Condition, received.Condition)
|
||||
case s3err.RequestTimeTooSkewedError:
|
||||
return compareS3ApiErrFields(
|
||||
checkErrFieldEmptiness("RequestTime", received.RequestTime, true),
|
||||
checkErrFieldEmptiness("ServerTime", received.ServerTime, true),
|
||||
compareErrField("MaxAllowedSkewMilliseconds", err.MaxAllowedSkewMilliseconds, received.MaxAllowedSkewMilliseconds),
|
||||
)
|
||||
case s3err.SignatureDoesNotMatchError:
|
||||
return compareS3ApiErrFields(
|
||||
compareErrField("AWSAccessKeyId", err.AWSAccessKeyId, received.AWSAccessKeyId),
|
||||
checkErrFieldEmptiness("StringToSign", err.StringToSign, true),
|
||||
compareErrField("SignatureProvided", err.SignatureProvided, received.SignatureProvided),
|
||||
checkErrFieldEmptiness("StringToSignBytes", err.StringToSignBytes, true),
|
||||
checkErrFieldEmptiness("CanonicalRequest", err.CanonicalRequest, true),
|
||||
checkErrFieldEmptiness("CanonicalRequestBytes", err.CanonicalRequestBytes, true),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkApiErr(err error, apiErr s3err.APIError) error {
|
||||
func compareS3ApiErrFields(errs ...error) error {
|
||||
for _, err := range errs {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func compareErrField[T comparable](field string, expected, received T) error {
|
||||
if received != expected {
|
||||
return fmt.Errorf("expected error %s to be %v, instead got %v", field, expected, received)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkErrFieldEmptiness(field, received string, require bool) error {
|
||||
if require && received == "" {
|
||||
return fmt.Errorf("expected error %s to be non-empty", field)
|
||||
}
|
||||
if !require && received != "" {
|
||||
return fmt.Errorf("expected error %s to be empty, instead got %s", field, received)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkApiErr(err error, expected s3err.S3Error) error {
|
||||
apiErr := expected.BaseError()
|
||||
if err == nil {
|
||||
return fmt.Errorf("expected %v, instead got nil", apiErr.Code)
|
||||
}
|
||||
@@ -2478,7 +2693,7 @@ func putBucketPolicy(client *s3.Client, bucket, policy string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func sendSignedRequest(s *S3Conf, req *http.Request, cancel context.CancelFunc) (map[string]string, *s3err.APIErrorResponse, error) {
|
||||
func sendSignedRequest(s *S3Conf, req *http.Request, cancel context.CancelFunc) (map[string]string, *APIErrorResponse, error) {
|
||||
signer := v4.NewSigner()
|
||||
signErr := signer.SignHTTP(req.Context(), aws.Credentials{AccessKeyID: s.awsID, SecretAccessKey: s.awsSecret}, req, "STREAMING-UNSIGNED-PAYLOAD-TRAILER", "s3", s.awsRegion, time.Now())
|
||||
if signErr != nil {
|
||||
@@ -2499,7 +2714,7 @@ func sendSignedRequest(s *S3Conf, req *http.Request, cancel context.CancelFunc)
|
||||
return nil, nil, fmt.Errorf("failed to read the request body: %w", err)
|
||||
}
|
||||
|
||||
var errResp s3err.APIErrorResponse
|
||||
var errResp APIErrorResponse
|
||||
err = xml.Unmarshal(bodyBytes, &errResp)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to unmarshal response body: %w", err)
|
||||
@@ -2516,7 +2731,7 @@ func sendSignedRequest(s *S3Conf, req *http.Request, cancel context.CancelFunc)
|
||||
return headers, nil, nil
|
||||
}
|
||||
|
||||
func testUnsignedStreamingPayloadTrailerObjectPut(s *S3Conf, bucket, object string, body []byte, reqHeaders map[string]string) (map[string]string, *s3err.APIErrorResponse, error) {
|
||||
func testUnsignedStreamingPayloadTrailerObjectPut(s *S3Conf, bucket, object string, body []byte, reqHeaders map[string]string) (map[string]string, *APIErrorResponse, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, s.endpoint+"/"+bucket+"/"+object, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
@@ -2532,7 +2747,7 @@ func testUnsignedStreamingPayloadTrailerObjectPut(s *S3Conf, bucket, object stri
|
||||
return sendSignedRequest(s, req, cancel)
|
||||
}
|
||||
|
||||
func testUnsignedStreamingPayloadTrailerUploadPart(s *S3Conf, bucket, object string, uploadId *string, body []byte, reqHeaders map[string]string) (map[string]string, *s3err.APIErrorResponse, error) {
|
||||
func testUnsignedStreamingPayloadTrailerUploadPart(s *S3Conf, bucket, object string, uploadId *string, body []byte, reqHeaders map[string]string) (map[string]string, *APIErrorResponse, error) {
|
||||
if uploadId == nil {
|
||||
return nil, nil, fmt.Errorf("empty upload id")
|
||||
}
|
||||
@@ -2613,7 +2828,7 @@ func withTrailingChecksum(checksum string) signedReqOpt {
|
||||
}
|
||||
}
|
||||
|
||||
func testSignedStreamingObjectPut(s *S3Conf, bucket, object string, payload []byte, opts ...signedReqOpt) (map[string]string, *s3err.APIErrorResponse, error) {
|
||||
func testSignedStreamingObjectPut(s *S3Conf, bucket, object string, payload []byte, opts ...signedReqOpt) (map[string]string, *APIErrorResponse, error) {
|
||||
cfg := &signedReqCfg{
|
||||
chunkSize: 8192, // minimal valid chunk size
|
||||
}
|
||||
@@ -2707,7 +2922,7 @@ func testSignedStreamingObjectPut(s *S3Conf, bucket, object string, payload []by
|
||||
return nil, nil, fmt.Errorf("failed to read the response body: %w", err)
|
||||
}
|
||||
|
||||
var errResp s3err.APIErrorResponse
|
||||
var errResp APIErrorResponse
|
||||
err = xml.Unmarshal(bodyBytes, &errResp)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to unmarshal response body: %w", err)
|
||||
@@ -3065,6 +3280,15 @@ type PostRequestConfig struct {
|
||||
// sendPostObject sends a POST multipart/form-data request to /{bucket}.
|
||||
// Returns the raw *http.Response for flexible per-test assertions.
|
||||
func sendPostObject(input PostRequestConfig) (*http.Response, error) {
|
||||
req, _, err := newPostObjectRequest(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return input.s3Conf.httpClient.Do(req)
|
||||
}
|
||||
|
||||
func newPostObjectRequest(input PostRequestConfig) (*http.Request, map[string]string, error) {
|
||||
if input.date.IsZero() {
|
||||
input.date = time.Now().UTC()
|
||||
}
|
||||
@@ -3088,7 +3312,7 @@ func sendPostObject(input PostRequestConfig) (*http.Response, error) {
|
||||
var err error
|
||||
policy, err = encodePostPolicy(input.policyConditions, input.policyExpiration, fields, input.omitPolicyConditions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
} else {
|
||||
policy = *input.rawPolicy
|
||||
@@ -3099,14 +3323,14 @@ func sendPostObject(input PostRequestConfig) (*http.Response, error) {
|
||||
|
||||
body, boundary, err := buildPostObjectBody(fields, input.extraFields, input.fileContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
endpoint := fmt.Sprintf("%s/%s", input.s3Conf.endpoint, input.bucket)
|
||||
if input.s3Conf.hostStyle {
|
||||
u, err := url.Parse(input.s3Conf.endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
u.Host = input.bucket + "." + u.Host
|
||||
@@ -3115,13 +3339,13 @@ func sendPostObject(input PostRequestConfig) (*http.Response, error) {
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", fmt.Sprintf("multipart/form-data; boundary=%s", boundary))
|
||||
req.ContentLength = int64(len(body))
|
||||
|
||||
return input.s3Conf.httpClient.Do(req)
|
||||
return req, fields, nil
|
||||
}
|
||||
|
||||
func getEtagBytes(etag string) ([]byte, error) {
|
||||
@@ -3132,3 +3356,20 @@ func md5String(data []byte) string {
|
||||
sum := md5.Sum(data)
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func base64ToHexString(s string) string {
|
||||
data, err := base64.StdEncoding.DecodeString(s)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return hex.EncodeToString(data)
|
||||
}
|
||||
|
||||
func hexBytes(s string) string {
|
||||
data := []byte(s)
|
||||
parts := make([]string, len(data))
|
||||
for i, b := range data {
|
||||
parts[i] = fmt.Sprintf("%02x", b)
|
||||
}
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ func Versioning_CopyObject_invalid_versionId(s *S3Conf) error {
|
||||
CopySource: getPtr(fmt.Sprintf("%v/%v?versionId=invalid_versionId", bucket, srcObj)),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgVersionId, "invalid_versionId"))
|
||||
}, withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
@@ -714,7 +714,7 @@ func Versioning_GetObject_invalid_versionId(s *S3Conf) error {
|
||||
VersionId: getPtr("invalid_version_id"),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgVersionId, "invalid_version_id"))
|
||||
}, withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
@@ -994,7 +994,7 @@ func Versioning_GetObjectAttributes_invalid_versionId(s *S3Conf) error {
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgVersionId, "invalid_versionId"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1112,7 +1112,7 @@ func Versioning_DeleteObject_invalid_versionId(s *S3Conf) error {
|
||||
VersionId: getPtr("invalid_versionId"),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgVersionId, "invalid_versionId"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1184,7 +1184,7 @@ func Versioning_DeleteObject_non_existing_object(s *S3Conf) error {
|
||||
VersionId: getPtr("non_existing_version_id"),
|
||||
})
|
||||
canel()
|
||||
if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId)); err != nil {
|
||||
if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgVersionId, "non_existing_version_id")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1893,7 +1893,7 @@ func Versioning_UploadPartCopy_invalid_versionId(s *S3Conf) error {
|
||||
bucket, srcObj)),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgVersionId, "invalid_versionId"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2099,7 +2099,7 @@ func Versioning_PutObjectRetention_invalid_versionId(s *S3Conf) error {
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgVersionId, "invalid_version_id"))
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
@@ -2148,7 +2148,7 @@ func Versioning_GetObjectRetention_invalid_versionId(s *S3Conf) error {
|
||||
VersionId: getPtr("invalid_versionId"),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgVersionId, "invalid_versionId"))
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
@@ -2292,7 +2292,7 @@ func Versioning_PutObjectLegalHold_invalid_versionId(s *S3Conf) error {
|
||||
},
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgVersionId, "invalid_version_id"))
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
@@ -2339,7 +2339,7 @@ func Versioning_GetObjectLegalHold_invalid_versionId(s *S3Conf) error {
|
||||
VersionId: getPtr("invalid_version_id"),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgVersionId, "invalid_version_id"))
|
||||
}, withLock())
|
||||
}
|
||||
|
||||
@@ -3666,7 +3666,7 @@ func Versioning_GetObjectTagging_invalid_versionId(s *S3Conf) error {
|
||||
VersionId: getPtr("invalid_versionId"),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgVersionId, "invalid_versionId"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3780,7 +3780,7 @@ func Versioning_PutObjectTagging_invalid_versionId(s *S3Conf) error {
|
||||
VersionId: getPtr("invalid_versionId"),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgVersionId, "invalid_versionId"))
|
||||
}, withVersioning(types.BucketVersioningStatusEnabled))
|
||||
}
|
||||
|
||||
@@ -3826,7 +3826,7 @@ func Versioning_DeleteObjectTagging_invalid_versionId(s *S3Conf) error {
|
||||
VersionId: getPtr("invalid_versionId"),
|
||||
})
|
||||
cancel()
|
||||
return checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidVersionId))
|
||||
return checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgVersionId, "invalid_versionId"))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user