fix: adds BadDigest error for incorrect Content-Md5 s

Closes #1525

* Adds validation for the `Content-MD5` header.
  * If the header value is invalid, the gateway now returns an `InvalidDigest` error.
  * If the value is valid but does not match the payload, it returns a `BadDigest` error.
* Adds integration test cases for `PutBucketCors` with `Content-MD5`.
This commit is contained in:
niksis02
2025-09-18 19:51:30 +04:00
parent 51e54874a8
commit ebdda06633
17 changed files with 566 additions and 452 deletions

View File

@@ -35,42 +35,42 @@ func (ar *S3AdminRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMSe
// CreateUser admin api
app.Patch("/create-user",
controllers.ProcessHandlers(ctrl.CreateUser, metrics.ActionAdminCreateUser, services,
middlewares.VerifyV4Signature(root, iam, region),
middlewares.VerifyV4Signature(root, iam, region, false),
middlewares.IsAdmin(metrics.ActionAdminCreateUser),
))
// DeleteUsers admin api
app.Patch("/delete-user",
controllers.ProcessHandlers(ctrl.DeleteUser, metrics.ActionAdminDeleteUser, services,
middlewares.VerifyV4Signature(root, iam, region),
middlewares.VerifyV4Signature(root, iam, region, false),
middlewares.IsAdmin(metrics.ActionAdminDeleteUser),
))
// UpdateUser admin api
app.Patch("/update-user",
controllers.ProcessHandlers(ctrl.UpdateUser, metrics.ActionAdminUpdateUser, services,
middlewares.VerifyV4Signature(root, iam, region),
middlewares.VerifyV4Signature(root, iam, region, false),
middlewares.IsAdmin(metrics.ActionAdminUpdateUser),
))
// ListUsers admin api
app.Patch("/list-users",
controllers.ProcessHandlers(ctrl.ListUsers, metrics.ActionAdminListUsers, services,
middlewares.VerifyV4Signature(root, iam, region),
middlewares.VerifyV4Signature(root, iam, region, false),
middlewares.IsAdmin(metrics.ActionAdminListUsers),
))
// ChangeBucketOwner admin api
app.Patch("/change-bucket-owner",
controllers.ProcessHandlers(ctrl.ChangeBucketOwner, metrics.ActionAdminChangeBucketOwner, services,
middlewares.VerifyV4Signature(root, iam, region),
middlewares.VerifyV4Signature(root, iam, region, false),
middlewares.IsAdmin(metrics.ActionAdminChangeBucketOwner),
))
// ListBucketsAndOwners admin api
app.Patch("/list-buckets",
controllers.ProcessHandlers(ctrl.ListBuckets, metrics.ActionAdminListBuckets, services,
middlewares.VerifyV4Signature(root, iam, region),
middlewares.VerifyV4Signature(root, iam, region, false),
middlewares.IsAdmin(metrics.ActionAdminListBuckets),
))
}

View File

@@ -37,7 +37,7 @@ type RootUserConfig struct {
Secret string
}
func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, region string) fiber.Handler {
func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, region string, streamBody bool) fiber.Handler {
acct := accounts{root: root, iam: iam}
return func(ctx *fiber.Ctx) error {
@@ -112,7 +112,7 @@ func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, region string)
if !utils.IsValidSh256PayloadHeader(hashPayload) {
return s3err.GetAPIError(s3err.ErrInvalidSHA256Paylod)
}
if utils.IsBigDataAction(ctx) {
if streamBody {
// for streaming PUT actions, authorization is deferred
// until end of stream due to need to get length and
// checksum of the stream to validate authorization
@@ -160,7 +160,7 @@ func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, region string)
}
}
err = utils.CheckValidSignature(ctx, authData, account.Secret, hashPayload, tdate, contentLength)
err = utils.CheckValidSignature(ctx, authData, account.Secret, hashPayload, tdate, contentLength, false)
if err != nil {
return err
}

View File

@@ -16,6 +16,7 @@ package middlewares
import (
"crypto/md5"
"encoding/base64"
"io"
"github.com/gofiber/fiber/v2"
@@ -23,14 +24,18 @@ import (
"github.com/versity/versitygw/s3err"
)
func VerifyMD5Body() fiber.Handler {
func VerifyMD5Body(streamBody bool) fiber.Handler {
return func(ctx *fiber.Ctx) error {
incomingSum := ctx.Get("Content-Md5")
if incomingSum == "" {
return nil
}
if utils.IsBigDataAction(ctx) {
if !isValidMD5(incomingSum) {
return s3err.GetAPIError(s3err.ErrInvalidDigest)
}
if streamBody {
var err error
wrapBodyReader(ctx, func(r io.Reader) io.Reader {
r, err = utils.NewHashReader(r, incomingSum, utils.HashTypeMd5)
@@ -46,9 +51,18 @@ func VerifyMD5Body() fiber.Handler {
calculatedSum := utils.Base64SumString(sum[:])
if incomingSum != calculatedSum {
return s3err.GetAPIError(s3err.ErrInvalidDigest)
return s3err.GetAPIError(s3err.ErrBadDigest)
}
return nil
}
}
func isValidMD5(s string) bool {
decoded, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return false
}
return len(decoded) == 16
}

View File

@@ -0,0 +1,41 @@
// Copyright 2023 Versity Software
// This file is licensed under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package middlewares
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_isValidMD5(t *testing.T) {
tests := []struct {
name string
s string
want bool
}{
{"invalid", "hello world", false},
{"valid base64", "aGVsbCBzLGRham5mamFuc2Zhc2RmZHNhZmRzYWY=", false},
{"valid 1", "CY9rzUYh03PK3k6DJie09g==", true},
{"valid 2", "uU0nuZNNPgilLlLX2n2r+s==", true},
{"valid 3", "7Qdih1MuhjZehB6Sv8UNjA==", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := isValidMD5(tt.s)
assert.Equal(t, tt.want, got)
})
}
}

View File

@@ -24,7 +24,7 @@ import (
"github.com/versity/versitygw/s3err"
)
func VerifyPresignedV4Signature(root RootUserConfig, iam auth.IAMService, region string) fiber.Handler {
func VerifyPresignedV4Signature(root RootUserConfig, iam auth.IAMService, region string, streamBody bool) fiber.Handler {
acct := accounts{root: root, iam: iam}
return func(ctx *fiber.Ctx) error {
@@ -71,7 +71,7 @@ func VerifyPresignedV4Signature(root RootUserConfig, iam auth.IAMService, region
}
}
if utils.IsBigDataAction(ctx) {
if streamBody {
// Content-Length has to be set for data uploads: PutObject, UploadPart
if contentLengthStr == "" {
return s3err.GetAPIError(s3err.ErrMissingContentLength)
@@ -88,7 +88,7 @@ func VerifyPresignedV4Signature(root RootUserConfig, iam auth.IAMService, region
return nil
}
err = utils.CheckPresignedSignature(ctx, authData, account.Secret)
err = utils.CheckPresignedSignature(ctx, authData, account.Secret, streamBody)
if err != nil {
return err
}

View File

@@ -28,7 +28,7 @@ import (
// AuthorizePublicBucketAccess checks if the bucket grants public
// access to anonymous requesters
func AuthorizePublicBucketAccess(be backend.Backend, s3action string, policyPermission auth.Action, permission auth.Permission) fiber.Handler {
func AuthorizePublicBucketAccess(be backend.Backend, s3action string, policyPermission auth.Action, permission auth.Permission, streamBody bool) fiber.Handler {
return func(ctx *fiber.Ctx) error {
// skip for authenticated requests
if utils.IsPresignedURLAuth(ctx) || ctx.Get("Authorization") != "" {
@@ -60,7 +60,7 @@ func AuthorizePublicBucketAccess(be backend.Backend, s3action string, policyPerm
return err
}
if utils.IsBigDataAction(ctx) {
if streamBody {
payloadType := ctx.Get("X-Amz-Content-Sha256")
if utils.IsUnsignedStreamingPayload(payloadType) {
checksumType, err := utils.ExtractChecksumType(ctx)

File diff suppressed because it is too large Load Diff

View File

@@ -106,7 +106,7 @@ func (ar *AuthReader) validateSignature() error {
return s3err.GetAPIError(s3err.ErrMissingDateHeader)
}
return CheckValidSignature(ar.ctx, ar.auth, ar.secret, hashPayload, tdate, int64(ar.size))
return CheckValidSignature(ar.ctx, ar.auth, ar.secret, hashPayload, tdate, int64(ar.size), true)
}
const (
@@ -114,11 +114,11 @@ const (
)
// CheckValidSignature validates the ctx v4 auth signature
func CheckValidSignature(ctx *fiber.Ctx, auth AuthData, secret, checksum string, tdate time.Time, contentLen int64) error {
func CheckValidSignature(ctx *fiber.Ctx, auth AuthData, secret, checksum string, tdate time.Time, contentLen int64, streamBody bool) error {
signedHdrs := strings.Split(auth.SignedHeaders, ";")
// Create a new http request instance from fasthttp request
req, err := createHttpRequestFromCtx(ctx, signedHdrs, contentLen)
req, err := createHttpRequestFromCtx(ctx, signedHdrs, contentLen, streamBody)
if err != nil {
return fmt.Errorf("create http request from context: %w", err)
}

View File

@@ -92,7 +92,7 @@ func Test_Client_UserAgent(t *testing.T) {
}
app.Get("/", func(c *fiber.Ctx) error {
req, err := createHttpRequestFromCtx(c, signedHdrs, int64(c.Request().Header.ContentLength()))
req, err := createHttpRequestFromCtx(c, signedHdrs, int64(c.Request().Header.ContentLength()), true)
if err != nil {
t.Fatal(err)
}

View File

@@ -115,7 +115,7 @@ func (hr *HashReader) Read(p []byte) (int, error) {
case HashTypeMd5:
sum := hr.Sum()
if sum != hr.sum {
return n, s3err.GetAPIError(s3err.ErrInvalidDigest)
return n, s3err.GetAPIError(s3err.ErrBadDigest)
}
case HashTypeSha256Hex:
sum := hr.Sum()

View File

@@ -64,7 +64,7 @@ func (pr *PresignedAuthReader) Read(p []byte) (int, error) {
n, err := pr.r.Read(p)
if errors.Is(err, io.EOF) {
cerr := CheckPresignedSignature(pr.ctx, pr.auth, pr.secret)
cerr := CheckPresignedSignature(pr.ctx, pr.auth, pr.secret, true)
if cerr != nil {
return n, cerr
}
@@ -74,7 +74,7 @@ func (pr *PresignedAuthReader) Read(p []byte) (int, error) {
}
// CheckPresignedSignature validates presigned request signature
func CheckPresignedSignature(ctx *fiber.Ctx, auth AuthData, secret string) error {
func CheckPresignedSignature(ctx *fiber.Ctx, auth AuthData, secret string, streamBody bool) error {
signedHdrs := strings.Split(auth.SignedHeaders, ";")
var contentLength int64
@@ -88,7 +88,7 @@ func CheckPresignedSignature(ctx *fiber.Ctx, auth AuthData, secret string) error
}
// Create a new http request instance from fasthttp request
req, err := createPresignedHttpRequestFromCtx(ctx, signedHdrs, contentLength)
req, err := createPresignedHttpRequestFromCtx(ctx, signedHdrs, contentLength, streamBody)
if err != nil {
return fmt.Errorf("create http request from context: %w", err)
}

View File

@@ -57,10 +57,10 @@ func GetUserMetaData(headers *fasthttp.RequestHeader) (metadata map[string]strin
return
}
func createHttpRequestFromCtx(ctx *fiber.Ctx, signedHdrs []string, contentLength int64) (*http.Request, error) {
func createHttpRequestFromCtx(ctx *fiber.Ctx, signedHdrs []string, contentLength int64, streamBody bool) (*http.Request, error) {
req := ctx.Request()
var body io.Reader
if IsBigDataAction(ctx) {
if streamBody {
body = req.BodyStream()
} else {
body = bytes.NewReader(req.Body())
@@ -112,10 +112,10 @@ var (
}
)
func createPresignedHttpRequestFromCtx(ctx *fiber.Ctx, signedHdrs []string, contentLength int64) (*http.Request, error) {
func createPresignedHttpRequestFromCtx(ctx *fiber.Ctx, signedHdrs []string, contentLength int64, streamBody bool) (*http.Request, error) {
req := ctx.Request()
var body io.Reader
if IsBigDataAction(ctx) {
if streamBody {
body = req.BodyStream()
} else {
body = bytes.NewReader(req.Body())
@@ -236,15 +236,6 @@ func includeHeader(hdr string, signedHdrs []string) bool {
return false
}
func IsBigDataAction(ctx *fiber.Ctx) bool {
if ctx.Method() == http.MethodPut && len(strings.Split(ctx.Path(), "/")) >= 3 {
if !ctx.Request().URI().QueryArgs().Has("tagging") && ctx.Get("X-Amz-Copy-Source") == "" && !ctx.Request().URI().QueryArgs().Has("acl") {
return true
}
}
return false
}
// expiration time window
// https://docs.aws.amazon.com/AmazonS3/latest/userguide/RESTAuthentication.html#RESTAuthenticationTimeStamp
const timeExpirationSec = 15 * 60

View File

@@ -81,7 +81,7 @@ func TestCreateHttpRequestFromCtx(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := createHttpRequestFromCtx(tt.args.ctx, tt.hdrs, 0)
got, err := createHttpRequestFromCtx(tt.args.ctx, tt.hdrs, 0, true)
if (err != nil) != tt.wantErr {
t.Errorf("CreateHttpRequestFromCtx() error = %v, wantErr %v", err, tt.wantErr)
return

View File

@@ -75,6 +75,7 @@ const (
ErrNoSuchUpload
ErrInvalidBucketName
ErrInvalidDigest
ErrBadDigest
ErrInvalidMaxKeys
ErrInvalidMaxBuckets
ErrInvalidMaxUploads
@@ -256,6 +257,11 @@ var errorCodeResponse = map[ErrorCode]APIError{
Description: "The Content-Md5 you specified is not valid.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrBadDigest: {
Code: "BadDigest",
Description: "The Content-MD5 you specified did not match what we received.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrInvalidMaxBuckets: {
Code: "InvalidArgument",
Description: "Argument max-buckets must be an integer between 1 and 10000.",

View File

@@ -36,7 +36,7 @@ func TestAuthentication(s *S3Conf) {
Authentication_date_mismatch(s)
Authentication_incorrect_payload_hash(s)
Authentication_invalid_sha256_payload_hash(s)
Authentication_incorrect_md5(s)
Authentication_md5(s)
Authentication_signature_error_incorrect_secret_key(s)
}
@@ -562,8 +562,7 @@ func TestPutBucketCors(s *S3Conf) {
PutBucketCors_empty_cors_rules(s)
PutBucketCors_invalid_method(s)
PutBucketCors_invalid_header(s)
PutBucketCors_invalid_content_md5(s)
PutBucketCors_incorrect_content_md5(s)
PutBucketCors_md5(s)
PutBucketCors_success(s)
}
@@ -1045,7 +1044,7 @@ func GetIntTests() IntTests {
"Authentication_date_mismatch": Authentication_date_mismatch,
"Authentication_incorrect_payload_hash": Authentication_incorrect_payload_hash,
"Authentication_invalid_sha256_payload_hash": Authentication_invalid_sha256_payload_hash,
"Authentication_incorrect_md5": Authentication_incorrect_md5,
"Authentication_md5": Authentication_md5,
"Authentication_signature_error_incorrect_secret_key": Authentication_signature_error_incorrect_secret_key,
"PresignedAuth_security_token_not_supported": PresignedAuth_security_token_not_supported,
"PresignedAuth_unsupported_algorithm": PresignedAuth_unsupported_algorithm,
@@ -1403,8 +1402,7 @@ func GetIntTests() IntTests {
"PutBucketCors_empty_cors_rules": PutBucketCors_empty_cors_rules,
"PutBucketCors_invalid_method": PutBucketCors_invalid_method,
"PutBucketCors_invalid_header": PutBucketCors_invalid_header,
"PutBucketCors_invalid_content_md5": PutBucketCors_invalid_content_md5,
"PutBucketCors_incorrect_content_md5": PutBucketCors_incorrect_content_md5,
"PutBucketCors_md5": PutBucketCors_md5,
"GetBucketCors_non_existing_bucket": GetBucketCors_non_existing_bucket,
"GetBucketCors_no_such_bucket_cors": GetBucketCors_no_such_bucket_cors,
"GetBucketCors_success": GetBucketCors_success,

View File

@@ -67,7 +67,7 @@ func Authentication_invalid_auth_header(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidAuthHeader))
})
}
@@ -89,7 +89,7 @@ func Authentication_unsupported_signature_version(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrUnsupportedAuthorizationType))
})
}
@@ -110,7 +110,7 @@ func Authentication_missing_components(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.MissingComponents())
})
}
@@ -131,7 +131,7 @@ func Authentication_malformed_component(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.MalformedComponent("SignedHeaders-Content-Length"))
})
}
@@ -152,7 +152,7 @@ func Authentication_missing_credentials(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.MissingCredential())
})
}
@@ -173,7 +173,7 @@ func Authentication_missing_signedheaders(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.MissingSignedHeaders())
})
}
@@ -194,7 +194,7 @@ func Authentication_missing_signature(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.MissingSignature())
})
}
@@ -217,7 +217,7 @@ func Authentication_malformed_credential(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.MalformedCredential())
})
}
@@ -240,7 +240,7 @@ func Authentication_credentials_invalid_terminal(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.InvalidTerminal("aws_request"))
})
}
@@ -263,7 +263,7 @@ func Authentication_credentials_incorrect_service(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.IncorrectService("ec2"))
})
}
@@ -287,7 +287,7 @@ func Authentication_credentials_incorrect_region(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.IncorrectRegion(s.awsRegion, cfg.awsRegion))
})
}
@@ -310,7 +310,7 @@ func Authentication_credentials_invalid_date(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.InvalidDateFormat("3223423234"))
})
}
@@ -407,7 +407,7 @@ func Authentication_credentials_non_existing_access_key(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidAccessKeyID))
})
}
@@ -427,7 +427,7 @@ func Authentication_missing_date_header(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMissingDateHeader))
})
}
@@ -447,7 +447,7 @@ func Authentication_invalid_date_header(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMissingDateHeader))
})
}
@@ -475,7 +475,7 @@ func Authentication_date_mismatch(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.DateMismatch())
})
}
@@ -495,7 +495,7 @@ func Authentication_invalid_sha256_payload_hash(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidSHA256Paylod))
})
}
@@ -516,29 +516,55 @@ func Authentication_incorrect_payload_hash(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrContentSHA256Mismatch))
})
}
func Authentication_incorrect_md5(s *S3Conf) error {
testName := "Authentication_incorrect_md5"
func Authentication_md5(s *S3Conf) error {
testName := "Authentication_md5"
bucket := getBucketName()
return authHandler(s, &authConfig{
testName: testName,
method: http.MethodGet,
method: http.MethodPut,
body: nil,
service: "s3",
date: time.Now(),
path: fmt.Sprintf("%s/obj", bucket),
}, func(req *http.Request) error {
req.Header.Set("Content-Md5", "sadfasdf87sad6f87==")
resp, err := s.httpClient.Do(req)
err := setup(s, bucket)
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidDigest))
for i, test := range []struct {
md5 string
err s3err.APIError
}{
{"invalid_md5", s3err.GetAPIError(s3err.ErrInvalidDigest)},
// valid base64, but invalid md5
{"aGVsbCBzLGRham5mamFuc2Y=", s3err.GetAPIError(s3err.ErrInvalidDigest)},
// valid md5, but incorrect
{"XrY7u+Ae7tCTyyK7j1rNww==", s3err.GetAPIError(s3err.ErrBadDigest)},
} {
req.Header.Set("Content-Md5", test.md5)
resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
if err := checkHTTPResponseApiErr(resp, test.err); err != nil {
return fmt.Errorf("test %v failed: %v", i+1, err)
}
}
err = teardown(s, bucket)
if err != nil {
return err
}
return nil
})
}
@@ -558,7 +584,7 @@ func Authentication_signature_error_incorrect_secret_key(s *S3Conf) error {
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureDoesNotMatch))
})
}
@@ -15212,17 +15238,53 @@ func PutBucketCors_invalid_header(s *S3Conf) error {
})
}
// TODO: report a bug for this case
func PutBucketCors_invalid_content_md5(s *S3Conf) error {
testName := "PutBucketCors_invalid_content_md5"
func PutBucketCors_md5(s *S3Conf) error {
testName := "PutBucketCors_md5"
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
return nil
})
}
cfg := &types.CORSConfiguration{
CORSRules: []types.CORSRule{
{
AllowedOrigins: []string{"http://origin.com", "something.net"},
AllowedMethods: []string{http.MethodPost, http.MethodPut, http.MethodHead},
AllowedHeaders: []string{"X-Amz-Date", "X-Amz-Meta-Something", "Content-Type"},
ExposeHeaders: []string{"Authorization", "Content-Disposition"},
MaxAgeSeconds: getPtr(int32(125)),
ID: getPtr("my-id"),
},
},
}
for i, test := range []struct {
md5 string
err error
}{
// invalid content-md5
{"invalid", s3err.GetAPIError(s3err.ErrInvalidDigest)},
// incorrect content-md5
{"uU0nuZNNPgilLlLX2n2r+s==", s3err.GetAPIError(s3err.ErrBadDigest)},
// correct content-md5
{"liZChnDYdpG46exsGGaBhg==", nil},
} {
err := putBucketCors(s3client, &s3.PutBucketCorsInput{
Bucket: &bucket,
CORSConfiguration: cfg,
ContentMD5: &test.md5,
})
if test.err == nil && err != nil {
return fmt.Errorf("test %v failed: expected no error but got %v", i+1, err)
}
if test.err != nil {
apiErr, ok := test.err.(s3err.APIError)
if !ok {
return fmt.Errorf("test %v failed: expected s3err.APIError", i+1)
}
if err := checkApiErr(err, apiErr); err != nil {
return fmt.Errorf("test %v failed: %v", i+1, err)
}
}
}
func PutBucketCors_incorrect_content_md5(s *S3Conf) error {
testName := "PutBucketCors_incorrect_content_md5"
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
return nil
})
}

View File

@@ -371,6 +371,8 @@ func checkHTTPResponseApiErr(resp *http.Response, apiErr s3err.APIError) error {
return err
}
resp.Body.Close()
var errResp s3err.APIErrorResponse
err = xml.Unmarshal(body, &errResp)
if err != nil {