feat: implements advanced routing for ListBuckets, HeadBucket and bucket delete operations

This commit is contained in:
niksis02
2025-06-13 18:22:59 +04:00
parent b8456bc5ab
commit a7c3cb5cf8
6 changed files with 362 additions and 489 deletions

View File

@@ -73,45 +73,6 @@ func New(be backend.Backend, iam auth.IAMService, logger s3log.AuditLogger, evs
}
}
func (c S3ApiController) ListBuckets(ctx *fiber.Ctx) error {
cToken := ctx.Query("continuation-token")
prefix := ctx.Query("prefix")
maxBucketsStr := ctx.Query("max-buckets")
acct := utils.ContextKeyAccount.Get(ctx).(auth.Account)
var maxBuckets int32 = 10000
if maxBucketsStr != "" {
maxBucketsParsed, err := strconv.ParseInt(maxBucketsStr, 10, 32)
if err != nil || maxBucketsParsed < 0 || maxBucketsParsed > 10000 {
if c.debug {
debuglogger.Logf("error parsing max-buckets %q: %v", maxBucketsStr, err)
}
return SendXMLResponse(ctx, nil, s3err.GetAPIError(s3err.ErrInvalidMaxBuckets),
&MetaOpts{
Logger: c.logger,
MetricsMng: c.mm,
Action: metrics.ActionListAllMyBuckets,
})
}
maxBuckets = int32(maxBucketsParsed)
}
res, err := c.be.ListBuckets(ctx.Context(),
s3response.ListBucketsInput{
Owner: acct.Access,
IsAdmin: acct.Role == auth.RoleAdmin,
MaxBuckets: int32(maxBuckets),
ContinuationToken: cToken,
Prefix: prefix,
})
return SendXMLResponse(ctx, res, err,
&MetaOpts{
Logger: c.logger,
MetricsMng: c.mm,
Action: metrics.ActionListAllMyBuckets,
})
}
func (c S3ApiController) GetActions(ctx *fiber.Ctx) error {
bucket := ctx.Params("bucket")
key := ctx.Params("key")
@@ -2258,174 +2219,6 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error {
})
}
func (c S3ApiController) DeleteBucket(ctx *fiber.Ctx) error {
bucket := ctx.Params("bucket")
acct := utils.ContextKeyAccount.Get(ctx).(auth.Account)
isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool)
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
IsBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx)
if ctx.Request().URI().QueryArgs().Has("tagging") {
err := auth.VerifyAccess(ctx.Context(), c.be,
auth.AccessOptions{
Readonly: c.readonly,
Acl: parsedAcl,
AclPermission: auth.PermissionWrite,
IsRoot: isRoot,
Acc: acct,
Bucket: bucket,
Action: auth.PutBucketTaggingAction,
IsBucketPublic: IsBucketPublic,
})
if err != nil {
return SendResponse(ctx, err,
&MetaOpts{
Logger: c.logger,
MetricsMng: c.mm,
Action: metrics.ActionDeleteBucketTagging,
BucketOwner: parsedAcl.Owner,
})
}
err = c.be.DeleteBucketTagging(ctx.Context(), bucket)
return SendResponse(ctx, err,
&MetaOpts{
Logger: c.logger,
MetricsMng: c.mm,
Action: metrics.ActionDeleteBucketTagging,
BucketOwner: parsedAcl.Owner,
Status: http.StatusNoContent,
})
}
if ctx.Request().URI().QueryArgs().Has("ownershipControls") {
err := auth.VerifyAccess(ctx.Context(), c.be,
auth.AccessOptions{
Readonly: c.readonly,
Acl: parsedAcl,
AclPermission: auth.PermissionWrite,
IsRoot: isRoot,
Acc: acct,
Bucket: bucket,
Action: auth.PutBucketOwnershipControlsAction,
})
if err != nil {
return SendResponse(ctx, err,
&MetaOpts{
Logger: c.logger,
MetricsMng: c.mm,
Action: metrics.ActionDeleteBucketOwnershipControls,
BucketOwner: parsedAcl.Owner,
})
}
err = c.be.DeleteBucketOwnershipControls(ctx.Context(), bucket)
return SendResponse(ctx, err,
&MetaOpts{
Logger: c.logger,
MetricsMng: c.mm,
Action: metrics.ActionDeleteBucketOwnershipControls,
BucketOwner: parsedAcl.Owner,
Status: http.StatusNoContent,
})
}
if ctx.Request().URI().QueryArgs().Has("policy") {
err := auth.VerifyAccess(ctx.Context(), c.be,
auth.AccessOptions{
Readonly: c.readonly,
Acl: parsedAcl,
AclPermission: auth.PermissionWrite,
IsRoot: isRoot,
Acc: acct,
Bucket: bucket,
Action: auth.DeleteBucketPolicyAction,
})
if err != nil {
return SendResponse(ctx, err,
&MetaOpts{
Logger: c.logger,
MetricsMng: c.mm,
Action: metrics.ActionDeleteBucketPolicy,
BucketOwner: parsedAcl.Owner,
})
}
err = c.be.DeleteBucketPolicy(ctx.Context(), bucket)
return SendResponse(ctx, err,
&MetaOpts{
Logger: c.logger,
MetricsMng: c.mm,
Action: metrics.ActionDeleteBucketPolicy,
BucketOwner: parsedAcl.Owner,
Status: http.StatusNoContent,
})
}
if ctx.Request().URI().QueryArgs().Has("cors") {
err := auth.VerifyAccess(ctx.Context(), c.be,
auth.AccessOptions{
Readonly: c.readonly,
Acl: parsedAcl,
AclPermission: auth.PermissionWrite,
IsRoot: isRoot,
Acc: acct,
Bucket: bucket,
Action: auth.PutBucketCorsAction,
IsBucketPublic: IsBucketPublic,
})
if err != nil {
return SendResponse(ctx, err,
&MetaOpts{
Logger: c.logger,
MetricsMng: c.mm,
Action: metrics.ActionDeleteBucketCors,
BucketOwner: parsedAcl.Owner,
})
}
err = c.be.DeleteBucketCors(ctx.Context(), bucket)
return SendResponse(ctx, err,
&MetaOpts{
Logger: c.logger,
MetricsMng: c.mm,
Action: metrics.ActionDeleteBucketCors,
BucketOwner: parsedAcl.Owner,
})
}
err := auth.VerifyAccess(ctx.Context(), c.be,
auth.AccessOptions{
Readonly: c.readonly,
Acl: parsedAcl,
AclPermission: auth.PermissionWrite,
IsRoot: isRoot,
Acc: acct,
Bucket: bucket,
Action: auth.DeleteBucketAction,
IsBucketPublic: IsBucketPublic,
})
if err != nil {
return SendResponse(ctx, err,
&MetaOpts{
Logger: c.logger,
MetricsMng: c.mm,
Action: metrics.ActionDeleteBucket,
BucketOwner: parsedAcl.Owner,
})
}
err = c.be.DeleteBucket(ctx.Context(), bucket)
return SendResponse(ctx, err,
&MetaOpts{
Logger: c.logger,
MetricsMng: c.mm,
Action: metrics.ActionDeleteBucket,
BucketOwner: parsedAcl.Owner,
Status: http.StatusNoContent,
})
}
func (c S3ApiController) DeleteObjects(ctx *fiber.Ctx) error {
bucket := ctx.Params("bucket")
acct := utils.ContextKeyAccount.Get(ctx).(auth.Account)
@@ -2688,7 +2481,7 @@ func (c S3ApiController) DeleteActions(ctx *fiber.Ctx) error {
})
}
func (c S3ApiController) HeadBucket(ctx *fiber.Ctx) error {
func (c S3ApiController) HeadBuckets(ctx *fiber.Ctx) error {
bucket := ctx.Params("bucket")
acct := utils.ContextKeyAccount.Get(ctx).(auth.Account)
isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool)

View File

@@ -85,83 +85,6 @@ func TestNew(t *testing.T) {
}
}
func TestS3ApiController_ListBuckets(t *testing.T) {
type args struct {
req *http.Request
}
app := fiber.New()
s3ApiController := S3ApiController{
be: &BackendMock{
ListBucketsFunc: func(contextMoqParam context.Context, listBucketsInput s3response.ListBucketsInput) (s3response.ListAllMyBucketsResult, error) {
return s3response.ListAllMyBucketsResult{}, nil
},
},
}
app.Use(func(ctx *fiber.Ctx) error {
utils.ContextKeyAccount.Set(ctx, auth.Account{Access: "valid access", Role: "admin:"})
return ctx.Next()
})
app.Get("/", s3ApiController.ListBuckets)
// Error case
appErr := fiber.New()
s3ApiControllerErr := S3ApiController{
be: &BackendMock{
ListBucketsFunc: func(contextMoqParam context.Context, listBucketsInput s3response.ListBucketsInput) (s3response.ListAllMyBucketsResult, error) {
return s3response.ListAllMyBucketsResult{}, s3err.GetAPIError(s3err.ErrMethodNotAllowed)
},
},
}
appErr.Use(func(ctx *fiber.Ctx) error {
utils.ContextKeyAccount.Set(ctx, auth.Account{Access: "valid access", Role: "admin:"})
return ctx.Next()
})
appErr.Get("/", s3ApiControllerErr.ListBuckets)
tests := []struct {
name string
args args
app *fiber.App
wantErr bool
statusCode int
}{
{
name: "List-bucket-method-not-allowed",
args: args{
req: httptest.NewRequest(http.MethodGet, "/", nil),
},
app: appErr,
wantErr: false,
statusCode: 405,
},
{
name: "list-bucket-success",
args: args{
req: httptest.NewRequest(http.MethodGet, "/", nil),
},
app: app,
wantErr: false,
statusCode: 200,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resp, err := tt.app.Test(tt.args.req)
if (err != nil) != tt.wantErr {
t.Errorf("S3ApiController.ListBuckets() error = %v, wantErr %v", err, tt.wantErr)
}
if resp.StatusCode != tt.statusCode {
t.Errorf("S3ApiController.ListBuckets() statusCode = %v, wantStatusCode = %v", resp.StatusCode, tt.statusCode)
}
})
}
}
func getPtr(val string) *string {
return &val
}
@@ -1044,94 +967,6 @@ func TestS3ApiController_PutActions(t *testing.T) {
}
}
func TestS3ApiController_DeleteBucket(t *testing.T) {
type args struct {
req *http.Request
}
app := fiber.New()
s3ApiController := S3ApiController{
be: &BackendMock{
DeleteBucketFunc: func(_ context.Context, bucket string) error {
return nil
},
DeleteBucketTaggingFunc: func(contextMoqParam context.Context, bucket string) error {
return nil
},
DeleteBucketPolicyFunc: func(contextMoqParam context.Context, bucket string) error {
return nil
},
DeleteBucketOwnershipControlsFunc: func(contextMoqParam context.Context, bucket string) error {
return nil
},
},
}
app.Use(func(ctx *fiber.Ctx) error {
utils.ContextKeyAccount.Set(ctx, auth.Account{Access: "valid access"})
utils.ContextKeyIsRoot.Set(ctx, true)
utils.ContextKeyParsedAcl.Set(ctx, auth.ACL{})
return ctx.Next()
})
app.Delete("/:bucket", s3ApiController.DeleteBucket)
tests := []struct {
name string
app *fiber.App
args args
wantErr bool
statusCode int
}{
{
name: "Delete-bucket-success",
app: app,
args: args{
req: httptest.NewRequest(http.MethodDelete, "/my-bucket", nil),
},
wantErr: false,
statusCode: 204,
},
{
name: "Delete-bucket-tagging-success",
app: app,
args: args{
req: httptest.NewRequest(http.MethodDelete, "/my-bucket?tagging", nil),
},
wantErr: false,
statusCode: 204,
},
{
name: "Delete-bucket-ownership-controls-success",
app: app,
args: args{
req: httptest.NewRequest(http.MethodDelete, "/my-bucket?ownershipControls", nil),
},
wantErr: false,
statusCode: 204,
}, {
name: "Delete-bucket-policy-success",
app: app,
args: args{
req: httptest.NewRequest(http.MethodDelete, "/my-bucket?policy", nil),
},
wantErr: false,
statusCode: 204,
},
}
for _, tt := range tests {
resp, err := tt.app.Test(tt.args.req)
if (err != nil) != tt.wantErr {
t.Errorf("S3ApiController.DeleteBucket() error = %v, wantErr %v", err, tt.wantErr)
}
if resp.StatusCode != tt.statusCode {
t.Errorf("S3ApiController.DeleteBucket() statusCode = %v, wantStatusCode = %v", resp.StatusCode, tt.statusCode)
}
}
}
func TestS3ApiController_DeleteObjects(t *testing.T) {
type args struct {
req *http.Request
@@ -1319,95 +1154,6 @@ func TestS3ApiController_DeleteActions(t *testing.T) {
}
}
func TestS3ApiController_HeadBucket(t *testing.T) {
type args struct {
req *http.Request
}
app := fiber.New()
s3ApiController := S3ApiController{
be: &BackendMock{
GetBucketAclFunc: func(context.Context, *s3.GetBucketAclInput) ([]byte, error) {
return acldata, nil
},
HeadBucketFunc: func(context.Context, *s3.HeadBucketInput) (*s3.HeadBucketOutput, error) {
return &s3.HeadBucketOutput{}, nil
},
},
}
app.Use(func(ctx *fiber.Ctx) error {
utils.ContextKeyAccount.Set(ctx, auth.Account{Access: "valid access"})
utils.ContextKeyIsRoot.Set(ctx, true)
utils.ContextKeyParsedAcl.Set(ctx, auth.ACL{})
utils.ContextKeyRegion.Set(ctx, "us-east-1")
return ctx.Next()
})
app.Head("/:bucket", s3ApiController.HeadBucket)
// Error case
appErr := fiber.New()
s3ApiControllerErr := S3ApiController{be: &BackendMock{
GetBucketAclFunc: func(context.Context, *s3.GetBucketAclInput) ([]byte, error) {
return acldata, nil
},
HeadBucketFunc: func(context.Context, *s3.HeadBucketInput) (*s3.HeadBucketOutput, error) {
return nil, s3err.GetAPIError(s3err.ErrBucketNotEmpty)
},
},
}
appErr.Use(func(ctx *fiber.Ctx) error {
utils.ContextKeyAccount.Set(ctx, auth.Account{Access: "valid access"})
utils.ContextKeyIsRoot.Set(ctx, true)
utils.ContextKeyParsedAcl.Set(ctx, auth.ACL{})
utils.ContextKeyRegion.Set(ctx, "us-east-1")
return ctx.Next()
})
appErr.Head("/:bucket", s3ApiControllerErr.HeadBucket)
tests := []struct {
name string
app *fiber.App
args args
wantErr bool
statusCode int
}{
{
name: "Head-bucket-success",
app: app,
args: args{
req: httptest.NewRequest(http.MethodHead, "/my-bucket", nil),
},
wantErr: false,
statusCode: 200,
},
{
name: "Head-bucket-error",
app: appErr,
args: args{
req: httptest.NewRequest(http.MethodHead, "/my-bucket", nil),
},
wantErr: false,
statusCode: 409,
},
}
for _, tt := range tests {
resp, err := tt.app.Test(tt.args.req)
if (err != nil) != tt.wantErr {
t.Errorf("S3ApiController.HeadBucket() error = %v, wantErr %v", err, tt.wantErr)
}
if resp.StatusCode != tt.statusCode {
t.Errorf("S3ApiController.HeadBucket() statusCode = %v, wantStatusCode = %v", resp.StatusCode, tt.statusCode)
}
}
}
func TestS3ApiController_HeadObject(t *testing.T) {
type args struct {
req *http.Request

View File

@@ -0,0 +1,204 @@
// 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 controllers
import (
"net/http"
"github.com/gofiber/fiber/v2"
"github.com/versity/versitygw/auth"
"github.com/versity/versitygw/metrics"
"github.com/versity/versitygw/s3api/utils"
)
func (c S3ApiController) DeleteBucketTagging(ctx *fiber.Ctx) (*Response, error) {
bucket := ctx.Params("bucket")
acct := utils.ContextKeyAccount.Get(ctx).(auth.Account)
isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool)
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
IsBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx)
err := auth.VerifyAccess(ctx.Context(), c.be,
auth.AccessOptions{
Readonly: c.readonly,
Acl: parsedAcl,
AclPermission: auth.PermissionWrite,
IsRoot: isRoot,
Acc: acct,
Bucket: bucket,
Action: auth.PutBucketTaggingAction,
IsBucketPublic: IsBucketPublic,
})
if err != nil {
return &Response{
MetaOpts: &MetaOptions{
Action: metrics.ActionDeleteBucketTagging,
BucketOwner: parsedAcl.Owner,
},
}, err
}
err = c.be.DeleteBucketTagging(ctx.Context(), bucket)
return &Response{
MetaOpts: &MetaOptions{
Action: metrics.ActionDeleteBucketTagging,
BucketOwner: parsedAcl.Owner,
Status: http.StatusNoContent,
},
}, err
}
func (c S3ApiController) DeleteBucketOwnershipControls(ctx *fiber.Ctx) (*Response, error) {
bucket := ctx.Params("bucket")
acct := utils.ContextKeyAccount.Get(ctx).(auth.Account)
isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool)
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
err := auth.VerifyAccess(ctx.Context(), c.be,
auth.AccessOptions{
Readonly: c.readonly,
Acl: parsedAcl,
AclPermission: auth.PermissionWrite,
IsRoot: isRoot,
Acc: acct,
Bucket: bucket,
Action: auth.PutBucketOwnershipControlsAction,
})
if err != nil {
return &Response{
MetaOpts: &MetaOptions{
Action: metrics.ActionDeleteBucketOwnershipControls,
BucketOwner: parsedAcl.Owner,
},
}, err
}
err = c.be.DeleteBucketOwnershipControls(ctx.Context(), bucket)
return &Response{
MetaOpts: &MetaOptions{
Action: metrics.ActionDeleteBucketOwnershipControls,
BucketOwner: parsedAcl.Owner,
Status: http.StatusNoContent,
},
}, err
}
func (c S3ApiController) DeleteBucketPolicy(ctx *fiber.Ctx) (*Response, error) {
bucket := ctx.Params("bucket")
acct := utils.ContextKeyAccount.Get(ctx).(auth.Account)
isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool)
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
err := auth.VerifyAccess(ctx.Context(), c.be,
auth.AccessOptions{
Readonly: c.readonly,
Acl: parsedAcl,
AclPermission: auth.PermissionWrite,
IsRoot: isRoot,
Acc: acct,
Bucket: bucket,
Action: auth.DeleteBucketPolicyAction,
})
if err != nil {
return &Response{
MetaOpts: &MetaOptions{
Action: metrics.ActionDeleteBucketPolicy,
BucketOwner: parsedAcl.Owner,
},
}, err
}
err = c.be.DeleteBucketPolicy(ctx.Context(), bucket)
return &Response{
MetaOpts: &MetaOptions{
Action: metrics.ActionDeleteBucketPolicy,
BucketOwner: parsedAcl.Owner,
Status: http.StatusNoContent,
},
}, err
}
func (c S3ApiController) DeleteBucketCors(ctx *fiber.Ctx) (*Response, error) {
bucket := ctx.Params("bucket")
acct := utils.ContextKeyAccount.Get(ctx).(auth.Account)
isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool)
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
IsBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx)
err := auth.VerifyAccess(ctx.Context(), c.be,
auth.AccessOptions{
Readonly: c.readonly,
Acl: parsedAcl,
AclPermission: auth.PermissionWrite,
IsRoot: isRoot,
Acc: acct,
Bucket: bucket,
Action: auth.PutBucketCorsAction,
IsBucketPublic: IsBucketPublic,
})
if err != nil {
return &Response{
MetaOpts: &MetaOptions{
Action: metrics.ActionDeleteBucketCors,
BucketOwner: parsedAcl.Owner,
},
}, err
}
err = c.be.DeleteBucketCors(ctx.Context(), bucket)
return &Response{
MetaOpts: &MetaOptions{
Action: metrics.ActionDeleteBucketCors,
BucketOwner: parsedAcl.Owner,
},
}, err
}
func (c S3ApiController) DeleteBucket(ctx *fiber.Ctx) (*Response, error) {
bucket := ctx.Params("bucket")
acct := utils.ContextKeyAccount.Get(ctx).(auth.Account)
isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool)
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
IsBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx)
err := auth.VerifyAccess(ctx.Context(), c.be,
auth.AccessOptions{
Readonly: c.readonly,
Acl: parsedAcl,
AclPermission: auth.PermissionWrite,
IsRoot: isRoot,
Acc: acct,
Bucket: bucket,
Action: auth.DeleteBucketAction,
IsBucketPublic: IsBucketPublic,
})
if err != nil {
return &Response{
MetaOpts: &MetaOptions{
Action: metrics.ActionDeleteBucket,
BucketOwner: parsedAcl.Owner,
},
}, err
}
err = c.be.DeleteBucket(ctx.Context(), bucket)
return &Response{
MetaOpts: &MetaOptions{
Action: metrics.ActionDeleteBucket,
BucketOwner: parsedAcl.Owner,
Status: http.StatusNoContent,
},
}, err
}

View File

@@ -0,0 +1,77 @@
// 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 controllers
import (
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/gofiber/fiber/v2"
"github.com/versity/versitygw/auth"
"github.com/versity/versitygw/metrics"
"github.com/versity/versitygw/s3api/utils"
)
func (c S3ApiController) HeadBucket(ctx *fiber.Ctx) (*Response, error) {
bucket := ctx.Params("bucket")
acct := utils.ContextKeyAccount.Get(ctx).(auth.Account)
isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool)
region := utils.ContextKeyRegion.Get(ctx).(string)
parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL)
isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx)
err := auth.VerifyAccess(ctx.Context(), c.be,
auth.AccessOptions{
Readonly: c.readonly,
Acl: parsedAcl,
AclPermission: auth.PermissionRead,
IsRoot: isRoot,
Acc: acct,
Bucket: bucket,
Action: auth.ListBucketAction,
IsBucketPublic: isPublicBucket,
})
if err != nil {
return &Response{
MetaOpts: &MetaOptions{
Action: metrics.ActionHeadBucket,
BucketOwner: parsedAcl.Owner,
},
}, err
}
_, err = c.be.HeadBucket(ctx.Context(),
&s3.HeadBucketInput{
Bucket: &bucket,
})
if err != nil {
return &Response{
MetaOpts: &MetaOptions{
Action: metrics.ActionHeadBucket,
BucketOwner: parsedAcl.Owner,
},
}, err
}
return &Response{
Headers: map[string]string{
"X-Amz-Access-Point-Alias": "false",
"X-Amz-Bucket-Region": region,
},
MetaOpts: &MetaOptions{
Action: metrics.ActionHeadBucket,
BucketOwner: parsedAcl.Owner,
},
}, nil
}

View File

@@ -0,0 +1,50 @@
package controllers
import (
"strconv"
"github.com/gofiber/fiber/v2"
"github.com/versity/versitygw/auth"
"github.com/versity/versitygw/metrics"
"github.com/versity/versitygw/s3api/debuglogger"
"github.com/versity/versitygw/s3api/utils"
"github.com/versity/versitygw/s3err"
"github.com/versity/versitygw/s3response"
)
func (c S3ApiController) ListBuckets(ctx *fiber.Ctx) (*Response, error) {
cToken := ctx.Query("continuation-token")
prefix := ctx.Query("prefix")
maxBucketsStr := ctx.Query("max-buckets")
acct := utils.ContextKeyAccount.Get(ctx).(auth.Account)
var maxBuckets int32 = 10000
if maxBucketsStr != "" {
maxBucketsParsed, err := strconv.ParseInt(maxBucketsStr, 10, 32)
if err != nil || maxBucketsParsed < 0 || maxBucketsParsed > 10000 {
debuglogger.Logf("error parsing max-buckets %q: %v", maxBucketsStr, err)
return &Response{
MetaOpts: &MetaOptions{
Action: metrics.ActionListAllMyBuckets,
},
}, s3err.GetAPIError(s3err.ErrInvalidMaxBuckets)
}
maxBuckets = int32(maxBucketsParsed)
}
res, err := c.be.ListBuckets(ctx.Context(),
s3response.ListBucketsInput{
Owner: acct.Access,
IsAdmin: acct.Role == auth.RoleAdmin,
MaxBuckets: int32(maxBuckets),
ContinuationToken: cToken,
Prefix: prefix,
})
return &Response{
Data: res,
MetaOpts: &MetaOptions{
Action: metrics.ActionListAllMyBuckets,
},
}, err
}

View File

@@ -30,7 +30,7 @@ type S3ApiRouter struct {
}
func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMService, logger s3log.AuditLogger, aLogger s3log.AuditLogger, evs s3event.S3EventSender, mm *metrics.Manager, debug bool, readonly bool) {
s3ApiController := controllers.New(be, iam, logger, evs, mm, debug, readonly)
ctrl := controllers.New(be, iam, logger, evs, mm, debug, readonly)
if sa.WithAdmSrv {
adminController := controllers.NewAdminController(iam, be, aLogger)
@@ -54,33 +54,36 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ
app.Patch("/list-buckets", middlewares.IsAdmin(logger), adminController.ListBuckets)
}
// ListBuckets action
app.Get("/", s3ApiController.ListBuckets)
app.Get("/", controllers.ProcessResponse(ctrl.ListBuckets, logger, evs, mm))
// CreateBucket action
// PutBucketAcl action
app.Put("/:bucket", s3ApiController.PutBucketActions)
// DeleteBucket action
app.Delete("/:bucket", s3ApiController.DeleteBucket)
// Put bucket operations
app.Put("/:bucket", ctrl.PutBucketActions)
// HeadBucket
app.Head("/:bucket", s3ApiController.HeadBucket)
app.Head("/:bucket", controllers.ProcessResponse(ctrl.HeadBucket, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgs("tagging"), controllers.ProcessResponse(s3ApiController.GetBucketTagging, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgs("ownershipControls"), controllers.ProcessResponse(s3ApiController.GetBucketOwnershipControls, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgs("versioning"), controllers.ProcessResponse(s3ApiController.GetBucketVersioning, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgs("policy"), controllers.ProcessResponse(s3ApiController.GetBucketPolicy, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgs("cors"), controllers.ProcessResponse(s3ApiController.GetBucketCors, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgs("object-lock"), controllers.ProcessResponse(s3ApiController.GetObjectLockConfiguration, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgs("acl"), controllers.ProcessResponse(s3ApiController.GetBucketAcl, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgs("uploads"), controllers.ProcessResponse(s3ApiController.ListMultipartUploads, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgs("versions"), controllers.ProcessResponse(s3ApiController.ListObjectVersions, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgWithValue("list-type", "2"), controllers.ProcessResponse(s3ApiController.ListObjectsV2, logger, evs, mm))
app.Get("/:bucket", controllers.ProcessResponse(s3ApiController.ListObjects, logger, evs, mm))
// Delete bucket operations
app.Delete("/:bucket", middlewares.MatchQueryArgs("tagging"), controllers.ProcessResponse(ctrl.DeleteBucketTagging, logger, evs, mm))
app.Delete("/:bucket", middlewares.MatchQueryArgs("ownershipControls"), controllers.ProcessResponse(ctrl.DeleteBucketOwnershipControls, logger, evs, mm))
app.Delete("/:bucket", middlewares.MatchQueryArgs("policy"), controllers.ProcessResponse(ctrl.DeleteBucketPolicy, logger, evs, mm))
app.Delete("/:bucket", middlewares.MatchQueryArgs("cors"), controllers.ProcessResponse(ctrl.DeleteBucketCors, logger, evs, mm))
app.Delete("/:bucket", controllers.ProcessResponse(ctrl.DeleteBucket, logger, evs, mm))
// Get bucket operations
app.Get("/:bucket", middlewares.MatchQueryArgs("tagging"), controllers.ProcessResponse(ctrl.GetBucketTagging, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgs("ownershipControls"), controllers.ProcessResponse(ctrl.GetBucketOwnershipControls, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgs("versioning"), controllers.ProcessResponse(ctrl.GetBucketVersioning, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgs("policy"), controllers.ProcessResponse(ctrl.GetBucketPolicy, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgs("cors"), controllers.ProcessResponse(ctrl.GetBucketCors, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgs("object-lock"), controllers.ProcessResponse(ctrl.GetObjectLockConfiguration, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgs("acl"), controllers.ProcessResponse(ctrl.GetBucketAcl, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgs("uploads"), controllers.ProcessResponse(ctrl.ListMultipartUploads, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgs("versions"), controllers.ProcessResponse(ctrl.ListObjectVersions, logger, evs, mm))
app.Get("/:bucket", middlewares.MatchQueryArgWithValue("list-type", "2"), controllers.ProcessResponse(ctrl.ListObjectsV2, logger, evs, mm))
app.Get("/:bucket", controllers.ProcessResponse(ctrl.ListObjects, logger, evs, mm))
// HeadObject action
app.Head("/:bucket/:key/*", s3ApiController.HeadObject)
app.Head("/:bucket/:key/*", ctrl.HeadObject)
// GetObjectAcl action
// GetObject action
@@ -88,21 +91,21 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ
// GetObjectTagging action
// ListParts action
// GetObjectAttributes action
app.Get("/:bucket/:key/*", s3ApiController.GetActions)
app.Get("/:bucket/:key/*", ctrl.GetActions)
// DeleteObject action
// AbortMultipartUpload action
// DeleteObjectTagging action
app.Delete("/:bucket/:key/*", s3ApiController.DeleteActions)
app.Delete("/:bucket/:key/*", ctrl.DeleteActions)
// DeleteObjects action
app.Post("/:bucket", s3ApiController.DeleteObjects)
app.Post("/:bucket", ctrl.DeleteObjects)
// CompleteMultipartUpload action
// CreateMultipartUpload
// RestoreObject action
// SelectObjectContent action
app.Post("/:bucket/:key/*", s3ApiController.CreateActions)
app.Post("/:bucket/:key/*", ctrl.CreateActions)
// CopyObject action
// PutObject action
@@ -110,5 +113,5 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ
// UploadPartCopy action
// PutObjectTagging action
// PutObjectAcl action
app.Put("/:bucket/:key/*", s3ApiController.PutActions)
app.Put("/:bucket/:key/*", ctrl.PutActions)
}