From dc16c0448fa8716df6253c4de6f1896ccd37e6aa Mon Sep 17 00:00:00 2001 From: niksis02 Date: Fri, 18 Jul 2025 01:21:50 +0400 Subject: [PATCH] feat: implements integration tests for the new advanced router --- backend/common.go | 11 +- go.mod | 3 + s3api/controllers/admin.go | 23 +- s3api/controllers/admin_test.go | 2 +- s3api/controllers/base.go | 6 +- s3api/controllers/base_test.go | 15 +- s3api/controllers/object-put.go | 5 +- .../bucket-object-name-validator.go | 26 +-- s3api/middlewares/public-bucket.go | 20 +- s3api/router.go | 64 +++++- s3api/server.go | 14 +- s3err/s3err.go | 6 + tests/integration/group-tests.go | 14 ++ tests/integration/tests.go | 200 +++++++++++++++--- tests/integration/utils.go | 2 +- 15 files changed, 289 insertions(+), 122 deletions(-) diff --git a/backend/common.go b/backend/common.go index 951d484..7aaa80b 100644 --- a/backend/common.go +++ b/backend/common.go @@ -22,6 +22,7 @@ import ( "hash" "io" "io/fs" + "math" "net/url" "os" "regexp" @@ -113,7 +114,10 @@ func ParseObjectRange(size int64, acceptRange string) (int64, int64, bool, error return 0, size, false, nil } - startOffset, err := strconv.ParseInt(bRange[0], 10, 64) + startOffset, err := strconv.ParseInt(bRange[0], 10, strconv.IntSize) + if startOffset > int64(math.MaxInt) || startOffset < int64(math.MinInt) { + return 0, size, false, errInvalidRange + } if err != nil && bRange[0] != "" { return 0, size, false, nil } @@ -128,7 +132,10 @@ func ParseObjectRange(size int64, acceptRange string) (int64, int64, bool, error return startOffset, size - startOffset, true, nil } - endOffset, err := strconv.ParseInt(bRange[1], 10, 64) + endOffset, err := strconv.ParseInt(bRange[1], 10, strconv.IntSize) + if endOffset > int64(math.MaxInt) { + return 0, size, false, errInvalidRange + } if err != nil { return 0, size, false, nil } diff --git a/go.mod b/go.mod index 6dc6870..bb54d8f 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/pkg/xattr v0.4.12 github.com/segmentio/kafka-go v0.4.48 github.com/smira/go-statsd v1.3.4 + github.com/stretchr/testify v1.10.0 github.com/urfave/cli/v2 v2.27.7 github.com/valyala/fasthttp v1.64.0 github.com/versity/scoutfs-go v0.0.0-20240325223134-38eb2f5f7d44 @@ -39,6 +40,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.25.6 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.4 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.34.1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect github.com/golang-jwt/jwt/v5 v5.2.3 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -57,6 +59,7 @@ require ( golang.org/x/net v0.42.0 // indirect golang.org/x/text v0.27.0 // indirect golang.org/x/time v0.12.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( diff --git a/s3api/controllers/admin.go b/s3api/controllers/admin.go index c4f5f18..a85caf0 100644 --- a/s3api/controllers/admin.go +++ b/s3api/controllers/admin.go @@ -15,13 +15,10 @@ package controllers import ( - "encoding/json" "encoding/xml" - "fmt" "net/http" "strings" - "github.com/aws/aws-sdk-go-v2/service/s3/types" "github.com/gofiber/fiber/v2" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/backend" @@ -149,25 +146,7 @@ func (c AdminController) ChangeBucketOwner(ctx *fiber.Ctx) (*Response, error) { }, s3err.GetAPIError(s3err.ErrAdminUserNotFound) } - acl := auth.ACL{ - Owner: owner, - Grantees: []auth.Grantee{ - { - Permission: auth.PermissionFullControl, - Access: owner, - Type: types.TypeCanonicalUser, - }, - }, - } - - aclParsed, err := json.Marshal(acl) - if err != nil { - return &Response{ - MetaOpts: &MetaOptions{}, - }, fmt.Errorf("failed to marshal the bucket acl: %w", err) - } - - err = c.be.ChangeBucketOwner(ctx.Context(), bucket, aclParsed) + err = c.be.ChangeBucketOwner(ctx.Context(), bucket, owner) return &Response{ MetaOpts: &MetaOptions{}, }, err diff --git a/s3api/controllers/admin_test.go b/s3api/controllers/admin_test.go index e38b79f..73ef184 100644 --- a/s3api/controllers/admin_test.go +++ b/s3api/controllers/admin_test.go @@ -490,7 +490,7 @@ func TestAdminController_ChangeBucketOwner(t *testing.T) { }, } be := &BackendMock{ - ChangeBucketOwnerFunc: func(contextMoqParam context.Context, bucket string, acl []byte) error { + ChangeBucketOwnerFunc: func(contextMoqParam context.Context, bucket, owner string) error { return tt.input.beErr }, } diff --git a/s3api/controllers/base.go b/s3api/controllers/base.go index f788026..cc46677 100644 --- a/s3api/controllers/base.go +++ b/s3api/controllers/base.go @@ -69,8 +69,10 @@ func New(be backend.Backend, iam auth.IAMService, logger s3log.AuditLogger, evs } // Returns MethodNotAllowed for unmatched routes -func (c S3ApiController) HandleUnmatch(ctx *fiber.Ctx) (*Response, error) { - return &Response{}, s3err.GetAPIError(s3err.ErrMethodNotAllowed) +func (c S3ApiController) HandleErrorRoute(err error) Controller { + return func(ctx *fiber.Ctx) (*Response, error) { + return &Response{}, err + } } // MetaOptions holds the metadata for metrics, audit logs and s3 events diff --git a/s3api/controllers/base_test.go b/s3api/controllers/base_test.go index ed62aaf..186726a 100644 --- a/s3api/controllers/base_test.go +++ b/s3api/controllers/base_test.go @@ -201,27 +201,30 @@ func TestNew(t *testing.T) { } } -func TestS3ApiController_HandleUnmatch(t *testing.T) { +func TestS3ApiController_HandleErrorRoute(t *testing.T) { tests := []struct { name string input testInput output testOutput }{ { - name: "return method not allowed", + name: "should return the passed error", + input: testInput{ + extraMockErr: s3err.GetAPIError(s3err.ErrAnonymousCreateMp), + }, output: testOutput{ response: &Response{}, - err: s3err.GetAPIError(s3err.ErrMethodNotAllowed), + err: s3err.GetAPIError(s3err.ErrAnonymousCreateMp), }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctrl := S3ApiController{} - + s3Ctrl := S3ApiController{} + ctrl := s3Ctrl.HandleErrorRoute(tt.input.extraMockErr) testController( t, - ctrl.HandleUnmatch, + ctrl, tt.output.response, tt.output.err, ctxInputs{}) diff --git a/s3api/controllers/object-put.go b/s3api/controllers/object-put.go index 124db9a..2844487 100644 --- a/s3api/controllers/object-put.go +++ b/s3api/controllers/object-put.go @@ -15,6 +15,7 @@ package controllers import ( + "bytes" "encoding/xml" "fmt" "io" @@ -266,7 +267,7 @@ func (c S3ApiController) UploadPart(ctx *fiber.Ctx) (*Response, error) { if bodyi != nil { body = bodyi.(io.Reader) } else { - body = ctx.Request().BodyStream() + body = bytes.NewReader([]byte{}) } res, err := c.be.UploadPart(ctx.Context(), @@ -690,7 +691,7 @@ func (c S3ApiController) PutObject(ctx *fiber.Ctx) (*Response, error) { if bodyi != nil { body = bodyi.(io.Reader) } else { - body = ctx.Request().BodyStream() + body = bytes.NewReader([]byte{}) } res, err := c.be.PutObject(ctx.Context(), diff --git a/s3api/middlewares/bucket-object-name-validator.go b/s3api/middlewares/bucket-object-name-validator.go index 1abd8a0..f099afd 100644 --- a/s3api/middlewares/bucket-object-name-validator.go +++ b/s3api/middlewares/bucket-object-name-validator.go @@ -15,44 +15,28 @@ package middlewares import ( - "net/http" - "github.com/gofiber/fiber/v2" - "github.com/versity/versitygw/metrics" "github.com/versity/versitygw/s3api/utils" "github.com/versity/versitygw/s3err" - "github.com/versity/versitygw/s3log" ) // BucketObjectNameValidator extracts and validates // the bucket and object names from the request URI. -func BucketObjectNameValidator(l s3log.AuditLogger, mm *metrics.Manager) fiber.Handler { +func BucketObjectNameValidator() fiber.Handler { return func(ctx *fiber.Ctx) error { - // skip the check for admin apis - if ctx.Method() == http.MethodPatch { - return ctx.Next() - } - - path := ctx.Path() - // skip the check if the operation isn't bucket/object scoped - // e.g ListBuckets - if path == "/" { - return ctx.Next() - } - - bucket, object := parsePath(path) + bucket, object := parsePath(ctx.Path()) // check if the provided bucket name is valid if !utils.IsValidBucketName(bucket) { - return sendResponse(ctx, s3err.GetAPIError(s3err.ErrInvalidBucketName), l, mm) + return s3err.GetAPIError(s3err.ErrInvalidBucketName) } // check if the provided object name is valid // skip for empty objects: e.g bucket operations: HeadBucket... if object != "" && !utils.IsObjectNameValid(object) { - return sendResponse(ctx, s3err.GetAPIError(s3err.ErrBadRequest), l, mm) + return s3err.GetAPIError(s3err.ErrBadRequest) } - return ctx.Next() + return nil } } diff --git a/s3api/middlewares/public-bucket.go b/s3api/middlewares/public-bucket.go index 30c7aae..5238308 100644 --- a/s3api/middlewares/public-bucket.go +++ b/s3api/middlewares/public-bucket.go @@ -76,26 +76,10 @@ func AuthorizePublicBucketAccess(be backend.Backend, s3action string, policyPerm if err != nil { return err } + } else { + utils.ContextKeyBodyReader.Set(ctx, ctx.Request().BodyStream()) } - } - if utils.IsBigDataAction(ctx) { - payloadType := ctx.Get("X-Amz-Content-Sha256") - if utils.IsUnsignedStreamingPayload(payloadType) { - checksumType, err := utils.ExtractChecksumType(ctx) - if err != nil { - return err - } - - wrapBodyReader(ctx, func(r io.Reader) io.Reader { - var cr io.Reader - cr, err = utils.NewUnsignedChunkReader(r, checksumType) - return cr - }) - if err != nil { - return err - } - } } utils.ContextKeyPublicBucket.Set(ctx, true) diff --git a/s3api/router.go b/s3api/router.go index 2435642..c0694b3 100644 --- a/s3api/router.go +++ b/s3api/router.go @@ -21,6 +21,7 @@ import ( "github.com/versity/versitygw/metrics" "github.com/versity/versitygw/s3api/controllers" "github.com/versity/versitygw/s3api/middlewares" + "github.com/versity/versitygw/s3err" "github.com/versity/versitygw/s3event" "github.com/versity/versitygw/s3log" ) @@ -109,6 +110,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.PutBucketTagging, metrics.ActionPutBucketTagging, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionPutBucketTagging, auth.PutBucketTaggingAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -121,6 +123,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.PutBucketOwnershipControls, metrics.ActionPutBucketOwnershipControls, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionPutBucketOwnershipControls, auth.PutBucketOwnershipControlsAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -133,6 +136,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.PutBucketVersioning, metrics.ActionPutBucketVersioning, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionPutBucketVersioning, auth.PutBucketVersioningAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -145,6 +149,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.PutObjectLockConfiguration, metrics.ActionPutObjectLockConfiguration, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionPutObjectLockConfiguration, auth.PutBucketObjectLockConfigurationAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -157,6 +162,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.PutBucketCors, metrics.ActionPutBucketCors, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionPutBucketCors, auth.PutBucketCorsAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -169,6 +175,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.PutBucketPolicy, metrics.ActionPutBucketPolicy, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionPutBucketPolicy, auth.PutBucketPolicyAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -181,6 +188,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.PutBucketAcl, metrics.ActionPutBucketAcl, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionPutBucketAcl, auth.PutBucketAclAction, auth.PermissionWriteAcp), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -192,6 +200,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.CreateBucket, metrics.ActionCreateBucket, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionCreateBucket, auth.CreateBucketAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -204,6 +213,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.HeadBucket, metrics.ActionHeadBucket, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionHeadBucket, auth.ListBucketAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -218,6 +228,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.DeleteBucketTagging, metrics.ActionDeleteBucketTagging, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionDeleteBucketTagging, auth.PutBucketTaggingAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -230,6 +241,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.DeleteBucketOwnershipControls, metrics.ActionDeleteBucketOwnershipControls, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionDeleteBucketOwnershipControls, auth.PutBucketOwnershipControlsAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -242,6 +254,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.DeleteBucketPolicy, metrics.ActionDeleteBucketPolicy, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionDeleteBucketPolicy, auth.PutBucketPolicyAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -254,6 +267,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.DeleteBucketCors, metrics.ActionDeleteBucketCors, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionDeleteBucketCors, auth.PutBucketCorsAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -265,6 +279,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.DeleteBucket, metrics.ActionDeleteBucket, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionDeleteBucket, auth.DeleteBucketAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -279,6 +294,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.GetBucketTagging, metrics.ActionGetBucketTagging, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionGetBucketTagging, auth.GetBucketTaggingAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -291,6 +307,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.GetBucketOwnershipControls, metrics.ActionGetBucketOwnershipControls, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionGetBucketOwnershipControls, auth.GetBucketOwnershipControlsAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -303,6 +320,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.GetBucketVersioning, metrics.ActionGetBucketVersioning, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionGetBucketVersioning, auth.GetBucketVersioningAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -315,6 +333,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.GetBucketPolicy, metrics.ActionGetBucketPolicy, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionGetBucketPolicy, auth.GetBucketPolicyAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -327,6 +346,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.GetBucketCors, metrics.ActionGetBucketCors, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionGetBucketCors, auth.GetBucketCorsAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -339,6 +359,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.GetObjectLockConfiguration, metrics.ActionGetObjectLockConfiguration, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionGetObjectLockConfiguration, auth.GetBucketObjectLockConfigurationAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -351,6 +372,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.GetBucketAcl, metrics.ActionGetBucketAcl, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionGetBucketAcl, auth.GetBucketAclAction, auth.PermissionReadAcp), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -363,6 +385,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.ListMultipartUploads, metrics.ActionListMultipartUploads, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionListMultipartUploads, auth.ListBucketMultipartUploadsAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -375,6 +398,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.ListObjectVersions, metrics.ActionListObjectVersions, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionListObjectVersions, auth.ListBucketVersionsAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -387,6 +411,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.ListObjectsV2, metrics.ActionListObjectsV2, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionListObjectsV2, auth.ListBucketAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -398,6 +423,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.ListObjects, metrics.ActionListObjects, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionListObjects, auth.ListBucketAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -412,6 +438,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.DeleteObjects, metrics.ActionDeleteObjects, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionDeleteObjects, auth.DeleteObjectAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -425,6 +452,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.HeadObject, metrics.ActionHeadObject, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionHeadObject, auth.GetObjectAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -439,6 +467,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.GetObjectTagging, metrics.ActionGetObjectTagging, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionGetObjectTagging, auth.GetObjectTaggingAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -451,6 +480,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.GetObjectRetention, metrics.ActionGetObjectRetention, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionGetObjectRetention, auth.GetObjectRetentionAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -463,6 +493,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.GetObjectLegalHold, metrics.ActionGetObjectLegalHold, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionGetObjectLegalHold, auth.GetObjectLegalHoldAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -475,6 +506,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.GetObjectAcl, metrics.ActionGetObjectAcl, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionGetObjectAcl, auth.GetObjectAclAction, auth.PermissionReadAcp), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -487,6 +519,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.GetObjectAttributes, metrics.ActionGetObjectAttributes, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionGetObjectAttributes, auth.GetObjectAttributesAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -499,6 +532,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.ListParts, metrics.ActionListParts, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionListParts, auth.ListMultipartUploadPartsAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -510,6 +544,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.GetObject, metrics.ActionGetObject, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionGetObject, auth.GetObjectAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -524,6 +559,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.DeleteObjectTagging, metrics.ActionDeleteObjectTagging, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionDeleteObjectTagging, auth.DeleteObjectTaggingAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -536,6 +572,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.AbortMultipartUpload, metrics.ActionAbortMultipartUpload, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionAbortMultipartUpload, auth.AbortMultipartUploadAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -547,6 +584,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.DeleteObject, metrics.ActionDeleteObject, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionDeleteObject, auth.DeleteObjectAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -560,6 +598,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.RestoreObject, metrics.ActionRestoreObject, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionRestoreObject, auth.RestoreObjectAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -573,6 +612,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.SelectObjectContent, metrics.ActionSelectObjectContent, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionSelectObjectContent, auth.GetObjectAction, auth.PermissionRead), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -585,6 +625,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.CompleteMultipartUpload, metrics.ActionCompleteMultipartUpload, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionCompleteMultipartUpload, auth.PutObjectAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -597,6 +638,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.CreateMultipartUpload, metrics.ActionCreateMultipartUpload, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionCreateMultipartUpload, auth.PutObjectAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -611,6 +653,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.PutObjectTagging, metrics.ActionPutObjectTagging, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionPutObjectTagging, auth.PutObjectTaggingAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -623,6 +666,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.PutObjectRetention, metrics.ActionPutObjectRetention, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionPutObjectRetention, auth.PutObjectRetentionAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -635,6 +679,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.PutObjectLegalHold, metrics.ActionPutObjectLegalHold, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionPutObjectLegalHold, auth.PutObjectLegalHoldAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -647,6 +692,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.PutObjectAcl, metrics.ActionPutObjectAcl, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionPutObjectAcl, auth.PutObjectAclAction, auth.PermissionWriteAcp), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -660,6 +706,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.UploadPartCopy, metrics.ActionUploadPartCopy, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionUploadPartCopy, auth.PutObjectAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -672,18 +719,32 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.UploadPart, metrics.ActionUploadPart, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionUploadPart, auth.PutObjectAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), middlewares.VerifyMD5Body(), middlewares.ParseAcl(be), )) + + // return error if partNumber is used without uploadId + objectRouter.Put("", + middlewares.MatchQueryArgs("partNumber"), + controllers.ProcessHandlers(ctrl.HandleErrorRoute(s3err.GetAPIError(s3err.ErrMissingUploadId)), metrics.ActionUndetected, services)) + + // return 'MethodNotAllowed' if uploadId is provided without partNumber + // before the router reaches to 'PutObject' + objectRouter.Put("", + middlewares.MatchQueryArgs("uploadId"), + controllers.ProcessHandlers(ctrl.HandleErrorRoute(s3err.GetAPIError(s3err.ErrMethodNotAllowed)), metrics.ActionUndetected, services)) + objectRouter.Put("", middlewares.MatchHeader("X-Amz-Copy-Source"), controllers.ProcessHandlers( ctrl.CopyObject, metrics.ActionCopyObject, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionCopyObject, auth.PutObjectAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -695,6 +756,7 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ ctrl.PutObject, metrics.ActionPutObject, services, + middlewares.BucketObjectNameValidator(), middlewares.AuthorizePublicBucketAccess(be, metrics.ActionPutObject, auth.PutObjectAction, auth.PermissionWrite), middlewares.VerifyPresignedV4Signature(root, iam, region, debug), middlewares.VerifyV4Signature(root, iam, region, debug), @@ -703,5 +765,5 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ )) // Return MethodNotAllowed for all the unmatched routes - app.All("*", controllers.ProcessHandlers(ctrl.HandleUnmatch, metrics.ActionUndetected, services)) + app.All("*", controllers.ProcessHandlers(ctrl.HandleErrorRoute(s3err.GetAPIError(s3err.ErrMethodNotAllowed)), metrics.ActionUndetected, services)) } diff --git a/s3api/server.go b/s3api/server.go index c25d265..8030b47 100644 --- a/s3api/server.go +++ b/s3api/server.go @@ -95,19 +95,7 @@ func New( app.Use(middlewares.DebugLogger()) } - // initialize the bucket/object name validator - app.Use(middlewares.BucketObjectNameValidator(l, mm)) - - // Public buckets access checker - app.Use(middlewares.AuthorizePublicBucketAccess(be, l, mm)) - - // Authentication middlewares - app.Use(middlewares.VerifyPresignedV4Signature(root, iam, l, mm, region, server.debug)) - app.Use(middlewares.VerifyV4Signature(root, iam, l, mm, region, server.debug)) - app.Use(middlewares.VerifyMD5Body(l)) - app.Use(middlewares.AclParser(be, l, server.readonly)) - - server.router.Init(app, be, iam, l, adminLogger, evs, mm, server.debug, server.readonly) + server.router.Init(app, be, iam, l, adminLogger, evs, mm, server.debug, server.readonly, region, root) return server, nil } diff --git a/s3err/s3err.go b/s3err/s3err.go index 59aca42..4dd1a37 100644 --- a/s3err/s3err.go +++ b/s3err/s3err.go @@ -168,6 +168,7 @@ const ( ErrInvalidChecksumHeader ErrTrailerHeaderNotSupported ErrBadRequest + ErrMissingUploadId // Non-AWS errors ErrExistingObjectIsDirectory @@ -732,6 +733,11 @@ var errorCodeResponse = map[ErrorCode]APIError{ Description: "Bad Request", HTTPStatusCode: http.StatusBadRequest, }, + ErrMissingUploadId: { + Code: "InvalidArgument", + Description: "This operation does not accept partNumber without uploadId", + HTTPStatusCode: http.StatusBadRequest, + }, // non aws errors ErrExistingObjectIsDirectory: { diff --git a/tests/integration/group-tests.go b/tests/integration/group-tests.go index a3a15f5..e2d7ac7 100644 --- a/tests/integration/group-tests.go +++ b/tests/integration/group-tests.go @@ -637,6 +637,7 @@ func TestFullFlow(s *S3Conf) { TestGetObjectLegalHold(s) TestWORMProtection(s) TestAccessControl(s) + TestRouter(s) // FIXME: The tests should pass for azure as well // but this issue should be fixed with https://github.com/versity/versitygw/issues/1336 if !s.azureTests { @@ -652,6 +653,7 @@ func TestPosix(s *S3Conf) { PutObject_overwrite_file_obj(s) PutObject_overwrite_file_obj_with_nested_obj(s) PutObject_dir_obj_with_data(s) + PutObject_with_slashes(s) CreateMultipartUpload_dir_obj(s) PutObject_name_too_long(s) HeadObject_name_too_long(s) @@ -749,6 +751,7 @@ func TestScoutfs(s *S3Conf) { PutObject_overwrite_file_obj(s) PutObject_overwrite_file_obj_with_nested_obj(s) PutObject_dir_obj_with_data(s) + PutObject_with_slashes(s) CreateMultipartUpload_dir_obj(s) PutObject_name_too_long(s) HeadObject_name_too_long(s) @@ -872,6 +875,12 @@ func TestVersioningDisabled(s *S3Conf) { VersioningDisabled_PutBucketVersioning_not_configured(s) } +func TestRouter(s *S3Conf) { + RouterPutPartNumberWithoutUploadId(s) + RouterPostRoot(s) + RouterPostObjectWithoutQuery(s) +} + type IntTests map[string]func(s *S3Conf) error func GetIntTests() IntTests { @@ -1274,6 +1283,7 @@ func GetIntTests() IntTests { "PutObject_overwrite_file_obj": PutObject_overwrite_file_obj, "PutObject_overwrite_file_obj_with_nested_obj": PutObject_overwrite_file_obj_with_nested_obj, "PutObject_dir_obj_with_data": PutObject_dir_obj_with_data, + "PutObject_with_slashes": PutObject_with_slashes, "CreateMultipartUpload_dir_obj": CreateMultipartUpload_dir_obj, "IAM_user_access_denied": IAM_user_access_denied, "IAM_userplus_access_denied": IAM_userplus_access_denied, @@ -1355,5 +1365,9 @@ func GetIntTests() IntTests { "Versioning_WORM_obj_version_locked_with_governance_retention": Versioning_WORM_obj_version_locked_with_governance_retention, "Versioning_WORM_obj_version_locked_with_compliance_retention": Versioning_WORM_obj_version_locked_with_compliance_retention, "Versioning_concurrent_upload_object": Versioning_concurrent_upload_object, + "RouterPutPartNumberWithoutUploadId": RouterPutPartNumberWithoutUploadId, + "RouterPostRoot": RouterPostRoot, + "RouterPostObjectWithoutQuery": RouterPostObjectWithoutQuery, + "RouterPUTObjectOnlyUploadId": RouterPUTObjectOnlyUploadId, } } diff --git a/tests/integration/tests.go b/tests/integration/tests.go index 7872b5d..65ac11f 100644 --- a/tests/integration/tests.go +++ b/tests/integration/tests.go @@ -63,7 +63,7 @@ func Authentication_invalid_auth_header(s *S3Conf) error { return err } defer resp.Body.Close() - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrMissingFields)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMissingFields)); err != nil { return err } @@ -89,7 +89,7 @@ func Authentication_unsupported_signature_version(s *S3Conf) error { return err } defer resp.Body.Close() - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrSignatureVersionNotSupported)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureVersionNotSupported)); err != nil { return err } @@ -116,7 +116,7 @@ func Authentication_malformed_credentials(s *S3Conf) error { return err } defer resp.Body.Close() - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrCredMalformed)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrCredMalformed)); err != nil { return err } @@ -143,7 +143,7 @@ func Authentication_malformed_credentials_invalid_parts(s *S3Conf) error { return err } defer resp.Body.Close() - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrCredMalformed)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrCredMalformed)); err != nil { return err } @@ -170,7 +170,7 @@ func Authentication_credentials_terminated_string(s *S3Conf) error { return err } defer resp.Body.Close() - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrSignatureTerminationStr)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureTerminationStr)); err != nil { return err } @@ -193,7 +193,7 @@ func Authentication_credentials_incorrect_service(s *S3Conf) error { return err } defer resp.Body.Close() - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrSignatureIncorrService)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureIncorrService)); err != nil { return err } @@ -227,7 +227,7 @@ func Authentication_credentials_incorrect_region(s *S3Conf) error { return err } defer resp.Body.Close() - if err := checkAuthErr(resp, apiErr); err != nil { + if err := checkHTTPResponseApiErr(resp, apiErr); err != nil { return err } @@ -254,7 +254,7 @@ func Authentication_credentials_invalid_date(s *S3Conf) error { return err } defer resp.Body.Close() - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrSignatureDateDoesNotMatch)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureDateDoesNotMatch)); err != nil { return err } @@ -355,7 +355,7 @@ func Authentication_credentials_non_existing_access_key(s *S3Conf) error { return err } defer resp.Body.Close() - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrInvalidAccessKeyID)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidAccessKeyID)); err != nil { return err } @@ -382,7 +382,7 @@ func Authentication_invalid_signed_headers(s *S3Conf) error { return err } defer resp.Body.Close() - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrInvalidQueryParams)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidQueryParams)); err != nil { return err } @@ -406,7 +406,7 @@ func Authentication_missing_date_header(s *S3Conf) error { return err } defer resp.Body.Close() - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrMissingDateHeader)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMissingDateHeader)); err != nil { return err } @@ -430,7 +430,7 @@ func Authentication_invalid_date_header(s *S3Conf) error { return err } defer resp.Body.Close() - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrMalformedDate)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMalformedDate)); err != nil { return err } @@ -454,7 +454,7 @@ func Authentication_date_mismatch(s *S3Conf) error { return err } defer resp.Body.Close() - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrSignatureDateDoesNotMatch)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureDateDoesNotMatch)); err != nil { return err } @@ -478,7 +478,7 @@ func Authentication_incorrect_payload_hash(s *S3Conf) error { return err } defer resp.Body.Close() - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrContentSHA256Mismatch)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrContentSHA256Mismatch)); err != nil { return err } @@ -503,7 +503,7 @@ func Authentication_incorrect_md5(s *S3Conf) error { return err } defer resp.Body.Close() - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrInvalidDigest)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidDigest)); err != nil { return err } @@ -528,7 +528,7 @@ func Authentication_signature_error_incorrect_secret_key(s *S3Conf) error { return err } defer resp.Body.Close() - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrSignatureDoesNotMatch)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureDoesNotMatch)); err != nil { return err } @@ -558,7 +558,7 @@ func PresignedAuth_unsupported_algorithm(s *S3Conf) error { return err } - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrInvalidQuerySignatureAlgo)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidQuerySignatureAlgo)); err != nil { return err } @@ -595,7 +595,7 @@ func PresignedAuth_missing_credentials_query_param(s *S3Conf) error { return err } - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrInvalidQueryParams)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidQueryParams)); err != nil { return err } @@ -632,7 +632,7 @@ func PresignedAuth_malformed_creds_invalid_parts(s *S3Conf) error { return err } - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrCredMalformed)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrCredMalformed)); err != nil { return err } @@ -665,7 +665,7 @@ func PresignedAuth_creds_invalid_terminator(s *S3Conf) error { return err } - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrSignatureTerminationStr)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureTerminationStr)); err != nil { return err } @@ -698,7 +698,7 @@ func PresignedAuth_creds_incorrect_service(s *S3Conf) error { return err } - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrSignatureIncorrService)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureIncorrService)); err != nil { return err } @@ -736,7 +736,7 @@ func PresignedAuth_creds_incorrect_region(s *S3Conf) error { return err } - if err := checkAuthErr(resp, s3err.APIError{ + if err := checkHTTPResponseApiErr(resp, s3err.APIError{ Code: "SignatureDoesNotMatch", Description: fmt.Sprintf("Credential should be scoped to a valid Region, not %v", cfg.awsRegion), HTTPStatusCode: http.StatusForbidden, @@ -773,7 +773,7 @@ func PresignedAuth_creds_invalid_date(s *S3Conf) error { return err } - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrSignatureDateDoesNotMatch)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureDateDoesNotMatch)); err != nil { return err } @@ -806,7 +806,7 @@ func PresignedAuth_non_existing_access_key_id(s *S3Conf) error { return err } - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrInvalidAccessKeyID)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidAccessKeyID)); err != nil { return err } @@ -843,7 +843,7 @@ func PresignedAuth_missing_date_query(s *S3Conf) error { return err } - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrInvalidQueryParams)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidQueryParams)); err != nil { return err } @@ -876,7 +876,7 @@ func PresignedAuth_dates_mismatch(s *S3Conf) error { return err } - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrSignatureDateDoesNotMatch)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureDateDoesNotMatch)); err != nil { return err } @@ -913,7 +913,7 @@ func PresignedAuth_missing_signed_headers_query_param(s *S3Conf) error { return err } - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrInvalidQueryParams)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidQueryParams)); err != nil { return err } @@ -950,7 +950,7 @@ func PresignedAuth_missing_expiration_query_param(s *S3Conf) error { return err } - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrInvalidQueryParams)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidQueryParams)); err != nil { return err } @@ -987,7 +987,7 @@ func PresignedAuth_invalid_expiration_query_param(s *S3Conf) error { return err } - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrMalformedExpires)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMalformedExpires)); err != nil { return err } @@ -1024,7 +1024,7 @@ func PresignedAuth_negative_expiration_query_param(s *S3Conf) error { return err } - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrNegativeExpires)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrNegativeExpires)); err != nil { return err } @@ -1061,7 +1061,7 @@ func PresignedAuth_exceeding_expiration_query_param(s *S3Conf) error { return err } - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrMaximumExpires)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMaximumExpires)); err != nil { return err } @@ -1105,7 +1105,7 @@ func PresignedAuth_expired_request(s *S3Conf) error { return err } - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrExpiredPresignRequest)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrExpiredPresignRequest)); err != nil { return err } @@ -1137,7 +1137,7 @@ func PresignedAuth_incorrect_secret_key(s *S3Conf) error { return err } - if err := checkAuthErr(resp, s3err.GetAPIError(s3err.ErrSignatureDoesNotMatch)); err != nil { + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureDoesNotMatch)); err != nil { return err } @@ -17944,6 +17944,47 @@ func PutObject_dir_obj_with_data(s *S3Conf) error { }) } +func PutObject_with_slashes(s *S3Conf) error { + testName := "PutObject_with_slashes" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + objs, err := putObjects(s3client, []string{ + "/obj", "foo//bar", "/foo/baz/bar", "////////bar", "foo//////quxx", + }, bucket) + if err != nil { + return err + } + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + res, err := s3client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{ + Bucket: &bucket, + }) + cancel() + if err != nil { + return err + } + + // it's en expected bahvior in posix to normalize the object pahts, + // by removing multiple slashes + normalizedObjs := []string{ + "bar", + "foo/bar", + "foo/baz/bar", + "foo/quxx", + "obj", + } + + for i := range objs { + objs[i].Key = &normalizedObjs[i] + } + + if !compareObjects(objs, res.Contents) { + return fmt.Errorf("expected the objects to be %vß, instead got %v", + objStrings(objs), objStrings(res.Contents)) + } + + return nil + }) +} + func CreateMultipartUpload_dir_obj(s *S3Conf) error { testName := "CreateMultipartUpload_dir_obj" return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { @@ -20827,3 +20868,96 @@ func Versioning_concurrent_upload_object(s *S3Conf) error { return nil }, withVersioning(types.BucketVersioningStatusEnabled)) } + +// router tests +func RouterPutPartNumberWithoutUploadId(s *S3Conf) error { + testName := "RouterPutPartNumberWithoutUploadId" + return actionHandlerNoSetup(s, testName, func(s3client *s3.Client, bucket string) error { + req, err := http.NewRequest(http.MethodPut, s.endpoint+"/bucket/object", nil) + if err != nil { + return err + } + + query := req.URL.Query() + query.Add("partNumber", "1") + req.URL.RawQuery = query.Encode() + + resp, err := s.httpClient.Do(req) + if err != nil { + return err + } + + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMissingUploadId)); err != nil { + return err + } + + return nil + }) +} + +func RouterPostRoot(s *S3Conf) error { + testName := "RouterPostRoot" + return actionHandlerNoSetup(s, testName, func(s3client *s3.Client, bucket string) error { + req, err := http.NewRequest(http.MethodPost, s.endpoint+"/", nil) + if err != nil { + return err + } + + resp, err := s.httpClient.Do(req) + if err != nil { + return err + } + + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMethodNotAllowed)); err != nil { + return err + } + + return nil + }) +} + +func RouterPostObjectWithoutQuery(s *S3Conf) error { + testName := "RouterPostObjectWithoutQuery" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + req, err := http.NewRequest(http.MethodPost, s.endpoint+"/bucket/object", nil) + if err != nil { + return err + } + + resp, err := s.httpClient.Do(req) + if err != nil { + return err + } + + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMethodNotAllowed)); err != nil { + return err + } + + return nil + }) +} + +func RouterPUTObjectOnlyUploadId(s *S3Conf) error { + testName := "RouterPUTObjectOnlyUploadId" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + req, err := http.NewRequest(http.MethodPut, s.endpoint+"/bucket/object", nil) + if err != nil { + return err + } + + query := req.URL.Query() + query.Add("uploadId", "my-upload-id") + req.URL.RawQuery = query.Encode() + + resp, err := s.httpClient.Do(req) + if err != nil { + return err + } + + if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMethodNotAllowed)); err != nil { + return err + } + + return nil + }) +} diff --git a/tests/integration/utils.go b/tests/integration/utils.go index 8edcb7b..1ff2a1a 100644 --- a/tests/integration/utils.go +++ b/tests/integration/utils.go @@ -334,7 +334,7 @@ func createSignedReq(method, endpoint, path, access, secret, service, region str return req, nil } -func checkAuthErr(resp *http.Response, apiErr s3err.APIError) error { +func checkHTTPResponseApiErr(resp *http.Response, apiErr s3err.APIError) error { body, err := io.ReadAll(resp.Body) if err != nil { return err