From abdf342ef7307383205d5b46422f282fde37bf2b Mon Sep 17 00:00:00 2001 From: niksis02 Date: Fri, 20 Jun 2025 23:23:53 +0400 Subject: [PATCH] feat: implements advanced routing for the admin apis. Adds the debug logging and quite mode for the separate admin server. Adjusts the admin apis to the new advanced routing changes. Enables debug logging for the separate admin server(when a separate server is run for the admin apis). Adds the quiet mode for the separate admin server. --- cmd/versitygw/main.go | 65 +++-- s3api/admin-router.go | 14 +- s3api/admin-server.go | 19 +- s3api/controllers/admin.go | 164 +++++++----- s3api/controllers/admin_test.go | 454 -------------------------------- s3api/controllers/base.go | 90 ------- s3api/middlewares/acl-parser.go | 4 +- s3api/router.go | 12 +- 8 files changed, 162 insertions(+), 660 deletions(-) delete mode 100644 s3api/controllers/admin_test.go diff --git a/cmd/versitygw/main.go b/cmd/versitygw/main.go index 8ff4e62..f5b84ef 100644 --- a/cmd/versitygw/main.go +++ b/cmd/versitygw/main.go @@ -621,30 +621,6 @@ func runGateway(ctx context.Context, be backend.Backend) error { opts = append(opts, s3api.WithHostStyle(virtualDomain)) } - admApp := fiber.New(fiber.Config{ - AppName: "versitygw", - ServerHeader: "VERSITYGW", - Network: fiber.NetworkTCP, - DisableStartupMessage: true, - }) - - var admOpts []s3api.AdminOpt - - if admCertFile != "" || admKeyFile != "" { - if admCertFile == "" { - return fmt.Errorf("TLS key specified without cert file") - } - if admKeyFile == "" { - return fmt.Errorf("TLS cert specified without key file") - } - - cert, err := tls.LoadX509KeyPair(admCertFile, admKeyFile) - if err != nil { - return fmt.Errorf("tls: load certs: %v", err) - } - admOpts = append(admOpts, s3api.WithAdminSrvTLS(cert)) - } - iam, err := auth.New(&auth.Opts{ RootAccount: auth.Account{ Access: rootUserAccess, @@ -732,7 +708,41 @@ func runGateway(ctx context.Context, be backend.Backend) error { return fmt.Errorf("init gateway: %v", err) } - admSrv := s3api.NewAdminServer(admApp, be, middlewares.RootUserConfig{Access: rootUserAccess, Secret: rootUserSecret}, admPort, region, iam, loggers.AdminLogger, admOpts...) + var admSrv *s3api.S3AdminServer + + if admPort != "" { + admApp := fiber.New(fiber.Config{ + AppName: "versitygw", + ServerHeader: "VERSITYGW", + Network: fiber.NetworkTCP, + DisableStartupMessage: true, + }) + + var opts []s3api.AdminOpt + + if admCertFile != "" || admKeyFile != "" { + if admCertFile == "" { + return fmt.Errorf("TLS key specified without cert file") + } + if admKeyFile == "" { + return fmt.Errorf("TLS cert specified without key file") + } + + cert, err := tls.LoadX509KeyPair(admCertFile, admKeyFile) + if err != nil { + return fmt.Errorf("tls: load certs: %v", err) + } + opts = append(opts, s3api.WithAdminSrvTLS(cert)) + } + if quiet { + opts = append(opts, s3api.WithAdminQuiet()) + } + if debug { + opts = append(opts, s3api.WithAdminDebug()) + } + + admSrv = s3api.NewAdminServer(admApp, be, middlewares.RootUserConfig{Access: rootUserAccess, Secret: rootUserSecret}, admPort, region, iam, loggers.AdminLogger, opts...) + } if !quiet { printBanner(port, admPort, certFile != "", admCertFile != "") @@ -977,10 +987,7 @@ func getMatchingIPs(spec string) ([]string, error) { const columnWidth = 70 func centerText(text string) string { - padding := (columnWidth - 2 - len(text)) / 2 - if padding < 0 { - padding = 0 - } + padding := max((columnWidth-2-len(text))/2, 0) return strings.Repeat(" ", padding) + text } diff --git a/s3api/admin-router.go b/s3api/admin-router.go index 65ec084..16c2df5 100644 --- a/s3api/admin-router.go +++ b/s3api/admin-router.go @@ -25,23 +25,23 @@ import ( type S3AdminRouter struct{} func (ar *S3AdminRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMService, logger s3log.AuditLogger) { - controller := controllers.NewAdminController(iam, be, logger) + ctrl := controllers.NewAdminController(iam, be, logger) // CreateUser admin api - app.Patch("/create-user", controller.CreateUser) + app.Patch("/create-user", controllers.ProcessResponse(ctrl.CreateUser, logger, nil, nil)) // DeleteUsers admin api - app.Patch("/delete-user", controller.DeleteUser) + app.Patch("/delete-user", controllers.ProcessResponse(ctrl.DeleteUser, logger, nil, nil)) // UpdateUser admin api - app.Patch("/update-user", controller.UpdateUser) + app.Patch("/update-user", controllers.ProcessResponse(ctrl.UpdateUser, logger, nil, nil)) // ListUsers admin api - app.Patch("/list-users", controller.ListUsers) + app.Patch("/list-users", controllers.ProcessResponse(ctrl.ListUsers, logger, nil, nil)) // ChangeBucketOwner admin api - app.Patch("/change-bucket-owner", controller.ChangeBucketOwner) + app.Patch("/change-bucket-owner", controllers.ProcessResponse(ctrl.ChangeBucketOwner, logger, nil, nil)) // ListBucketsAndOwners admin api - app.Patch("/list-buckets", controller.ListBuckets) + app.Patch("/list-buckets", controllers.ProcessResponse(ctrl.ListBuckets, logger, nil, nil)) } diff --git a/s3api/admin-server.go b/s3api/admin-server.go index ee132d7..23b6601 100644 --- a/s3api/admin-server.go +++ b/s3api/admin-server.go @@ -31,6 +31,8 @@ type S3AdminServer struct { router *S3AdminRouter port string cert *tls.Certificate + quiet bool + debug bool } func NewAdminServer(app *fiber.App, be backend.Backend, root middlewares.RootUserConfig, port, region string, iam auth.IAMService, l s3log.AuditLogger, opts ...AdminOpt) *S3AdminServer { @@ -46,8 +48,13 @@ func NewAdminServer(app *fiber.App, be backend.Backend, root middlewares.RootUse } // Logging middlewares - app.Use(logger.New()) + if !server.quiet { + app.Use(logger.New(logger.Config{ + Format: "${time} | ${status} | ${latency} | ${ip} | ${method} | ${path} | ${error} | ${queryParams}\n", + })) + } app.Use(middlewares.DecodeURL(l, nil)) + app.Use(middlewares.DebugLogger()) // Authentication middlewares app.Use(middlewares.VerifyV4Signature(root, iam, l, nil, region, false)) @@ -67,6 +74,16 @@ func WithAdminSrvTLS(cert tls.Certificate) AdminOpt { return func(s *S3AdminServer) { s.cert = &cert } } +// WithQuiet silences default logging output +func WithAdminQuiet() AdminOpt { + return func(s *S3AdminServer) { s.quiet = true } +} + +// WithAdminDebug enables the debug logging +func WithAdminDebug() AdminOpt { + return func(s *S3AdminServer) { s.debug = true } +} + func (sa *S3AdminServer) Serve() (err error) { if sa.cert != nil { return sa.app.ListenTLSWithCertificate(sa.port, *sa.cert) diff --git a/s3api/controllers/admin.go b/s3api/controllers/admin.go index d10dae1..30cb671 100644 --- a/s3api/controllers/admin.go +++ b/s3api/controllers/admin.go @@ -15,10 +15,13 @@ 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" @@ -38,23 +41,23 @@ func NewAdminController(iam auth.IAMService, be backend.Backend, l s3log.AuditLo return AdminController{iam: iam, be: be, l: l} } -func (c AdminController) CreateUser(ctx *fiber.Ctx) error { +func (c AdminController) CreateUser(ctx *fiber.Ctx) (*Response, error) { var usr auth.Account err := xml.Unmarshal(ctx.Body(), &usr) if err != nil { - return SendResponse(ctx, s3err.GetAPIError(s3err.ErrMalformedXML), - &MetaOpts{ - Logger: c.l, + return &Response{ + MetaOpts: &MetaOptions{ Action: metrics.ActionAdminCreateUser, - }) + }, + }, s3err.GetAPIError(s3err.ErrMalformedXML) } if !usr.Role.IsValid() { - return SendResponse(ctx, s3err.GetAPIError(s3err.ErrAdminInvalidUserRole), - &MetaOpts{ - Logger: c.l, + return &Response{ + MetaOpts: &MetaOptions{ Action: metrics.ActionAdminCreateUser, - }) + }, + }, s3err.GetAPIError(s3err.ErrAdminInvalidUserRole) } err = c.iam.CreateAccount(usr) @@ -63,47 +66,47 @@ func (c AdminController) CreateUser(ctx *fiber.Ctx) error { err = s3err.GetAPIError(s3err.ErrAdminUserExists) } - return SendResponse(ctx, err, - &MetaOpts{ - Logger: c.l, + return &Response{ + MetaOpts: &MetaOptions{ Action: metrics.ActionAdminCreateUser, - }) + }, + }, err } - return SendResponse(ctx, nil, - &MetaOpts{ - Logger: c.l, + return &Response{ + MetaOpts: &MetaOptions{ Action: metrics.ActionAdminCreateUser, Status: http.StatusCreated, - }) + }, + }, nil } -func (c AdminController) UpdateUser(ctx *fiber.Ctx) error { +func (c AdminController) UpdateUser(ctx *fiber.Ctx) (*Response, error) { access := ctx.Query("access") if access == "" { - return SendResponse(ctx, s3err.GetAPIError(s3err.ErrAdminMissingUserAcess), - &MetaOpts{ - Logger: c.l, + return &Response{ + MetaOpts: &MetaOptions{ Action: metrics.ActionAdminUpdateUser, - }) + }, + }, s3err.GetAPIError(s3err.ErrAdminMissingUserAcess) } var props auth.MutableProps if err := xml.Unmarshal(ctx.Body(), &props); err != nil { - return SendResponse(ctx, s3err.GetAPIError(s3err.ErrMalformedXML), - &MetaOpts{ - Logger: c.l, + return &Response{ + MetaOpts: &MetaOptions{ Action: metrics.ActionAdminUpdateUser, - }) + }, + }, s3err.GetAPIError(s3err.ErrMalformedXML) } err := props.Validate() if err != nil { - return SendResponse(ctx, s3err.GetAPIError(s3err.ErrAdminInvalidUserRole), - &MetaOpts{ - Logger: c.l, + return &Response{ + MetaOpts: &MetaOptions{ Action: metrics.ActionAdminUpdateUser, - }) + }, + }, s3err.GetAPIError(s3err.ErrAdminInvalidUserRole) } err = c.iam.UpdateUserAccount(access, props) @@ -112,78 +115,97 @@ func (c AdminController) UpdateUser(ctx *fiber.Ctx) error { err = s3err.GetAPIError(s3err.ErrAdminUserNotFound) } - return SendResponse(ctx, err, - &MetaOpts{ - Logger: c.l, + return &Response{ + MetaOpts: &MetaOptions{ Action: metrics.ActionAdminUpdateUser, - }) + }, + }, err } - return SendResponse(ctx, nil, - &MetaOpts{ - Logger: c.l, + return &Response{ + MetaOpts: &MetaOptions{ Action: metrics.ActionAdminUpdateUser, - }) + }, + }, nil } -func (c AdminController) DeleteUser(ctx *fiber.Ctx) error { +func (c AdminController) DeleteUser(ctx *fiber.Ctx) (*Response, error) { access := ctx.Query("access") err := c.iam.DeleteUserAccount(access) - return SendResponse(ctx, err, - &MetaOpts{ - Logger: c.l, + return &Response{ + MetaOpts: &MetaOptions{ Action: metrics.ActionAdminDeleteUser, - }) + }, + }, err } -func (c AdminController) ListUsers(ctx *fiber.Ctx) error { +func (c AdminController) ListUsers(ctx *fiber.Ctx) (*Response, error) { accs, err := c.iam.ListUserAccounts() - return SendXMLResponse(ctx, - auth.ListUserAccountsResult{ - Accounts: accs, - }, err, - &MetaOpts{ - Logger: c.l, + return &Response{ + Data: auth.ListUserAccountsResult{Accounts: accs}, + MetaOpts: &MetaOptions{ Action: metrics.ActionAdminListUsers, - }) + }, + }, err } -func (c AdminController) ChangeBucketOwner(ctx *fiber.Ctx) error { +func (c AdminController) ChangeBucketOwner(ctx *fiber.Ctx) (*Response, error) { owner := ctx.Query("owner") bucket := ctx.Query("bucket") accs, err := auth.CheckIfAccountsExist([]string{owner}, c.iam) if err != nil { - return SendResponse(ctx, err, - &MetaOpts{ - Logger: c.l, + return &Response{ + MetaOpts: &MetaOptions{ Action: metrics.ActionAdminChangeBucketOwner, - }) + }, + }, err } if len(accs) > 0 { - return SendResponse(ctx, s3err.GetAPIError(s3err.ErrAdminUserNotFound), - &MetaOpts{ - Logger: c.l, + return &Response{ + MetaOpts: &MetaOptions{ Action: metrics.ActionAdminChangeBucketOwner, - }) + }, + }, s3err.GetAPIError(s3err.ErrAdminUserNotFound) } - err = c.be.ChangeBucketOwner(ctx.Context(), bucket, owner) - return SendResponse(ctx, err, - &MetaOpts{ - Logger: c.l, + 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{ + Action: metrics.ActionAdminChangeBucketOwner, + }, + }, fmt.Errorf("failed to marshal the bucket acl: %w", err) + } + + err = c.be.ChangeBucketOwner(ctx.Context(), bucket, aclParsed) + return &Response{ + MetaOpts: &MetaOptions{ Action: metrics.ActionAdminChangeBucketOwner, - }) + }, + }, err } -func (c AdminController) ListBuckets(ctx *fiber.Ctx) error { +func (c AdminController) ListBuckets(ctx *fiber.Ctx) (*Response, error) { buckets, err := c.be.ListBucketsAndOwners(ctx.Context()) - return SendXMLResponse(ctx, - s3response.ListBucketsResult{ + return &Response{ + Data: s3response.ListBucketsResult{ Buckets: buckets, - }, err, &MetaOpts{ - Logger: c.l, + }, + MetaOpts: &MetaOptions{ Action: metrics.ActionAdminListBuckets, - }) + }, + }, err } diff --git a/s3api/controllers/admin_test.go b/s3api/controllers/admin_test.go deleted file mode 100644 index aaa82a0..0000000 --- a/s3api/controllers/admin_test.go +++ /dev/null @@ -1,454 +0,0 @@ -// 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 ( - "context" - "fmt" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/gofiber/fiber/v2" - "github.com/versity/versitygw/auth" - "github.com/versity/versitygw/s3response" -) - -func TestAdminController_CreateUser(t *testing.T) { - type args struct { - req *http.Request - } - - adminController := AdminController{ - iam: &IAMServiceMock{ - CreateAccountFunc: func(account auth.Account) error { - return nil - }, - }, - } - - app := fiber.New() - - app.Patch("/create-user", adminController.CreateUser) - - succUser := ` - - access - secret - admin - 0 - 0 - - ` - invuser := ` - - access - secret - invalid_role - 0 - 0 - - ` - - tests := []struct { - name string - app *fiber.App - args args - wantErr bool - statusCode int - }{ - { - name: "Admin-create-user-malformed-body", - app: app, - args: args{ - req: httptest.NewRequest(http.MethodPatch, "/create-user", nil), - }, - wantErr: false, - statusCode: 400, - }, - { - name: "Admin-create-user-invalid-requester-role", - app: app, - args: args{ - req: httptest.NewRequest(http.MethodPatch, "/create-user", strings.NewReader(invuser)), - }, - wantErr: false, - statusCode: 400, - }, - { - name: "Admin-create-user-success", - app: app, - args: args{ - req: httptest.NewRequest(http.MethodPatch, "/create-user", strings.NewReader(succUser)), - }, - wantErr: false, - statusCode: 201, - }, - } - for _, tt := range tests { - resp, err := tt.app.Test(tt.args.req) - - if (err != nil) != tt.wantErr { - t.Errorf("AdminController.CreateUser() error = %v, wantErr %v", err, tt.wantErr) - } - - if resp.StatusCode != tt.statusCode { - t.Errorf("AdminController.CreateUser() statusCode = %v, wantStatusCode = %v", resp.StatusCode, tt.statusCode) - } - } -} - -func TestAdminController_UpdateUser(t *testing.T) { - type args struct { - req *http.Request - } - - adminController := AdminController{ - iam: &IAMServiceMock{ - UpdateUserAccountFunc: func(access string, props auth.MutableProps) error { - return nil - }, - }, - } - - app := fiber.New() - - app.Patch("/update-user", adminController.UpdateUser) - - adminControllerErr := AdminController{ - iam: &IAMServiceMock{ - UpdateUserAccountFunc: func(access string, props auth.MutableProps) error { - return auth.ErrNoSuchUser - }, - }, - } - - appNotFound := fiber.New() - - appNotFound.Patch("/update-user", adminControllerErr.UpdateUser) - - succUser := ` - - secret - 0 - 0 - - ` - - tests := []struct { - name string - app *fiber.App - args args - wantErr bool - statusCode int - }{ - { - name: "Admin-update-user-success", - app: app, - args: args{ - req: httptest.NewRequest(http.MethodPatch, "/update-user?access=access", strings.NewReader(succUser)), - }, - wantErr: false, - statusCode: 200, - }, - { - name: "Admin-update-user-missing-access", - app: app, - args: args{ - req: httptest.NewRequest(http.MethodPatch, "/update-user", strings.NewReader(succUser)), - }, - wantErr: false, - statusCode: 404, - }, - { - name: "Admin-update-user-invalid-request-body", - app: app, - args: args{ - req: httptest.NewRequest(http.MethodPatch, "/update-user?access=access", nil), - }, - wantErr: false, - statusCode: 400, - }, - { - name: "Admin-update-user-not-found", - app: appNotFound, - args: args{ - req: httptest.NewRequest(http.MethodPatch, "/update-user?access=access", strings.NewReader(succUser)), - }, - wantErr: false, - statusCode: 404, - }, - } - for _, tt := range tests { - resp, err := tt.app.Test(tt.args.req) - - if (err != nil) != tt.wantErr { - t.Errorf("AdminController.UpdateUser() error = %v, wantErr %v", err, tt.wantErr) - } - - if resp.StatusCode != tt.statusCode { - t.Errorf("AdminController.UpdateUser() statusCode = %v, wantStatusCode = %v", resp.StatusCode, tt.statusCode) - } - } -} - -func TestAdminController_DeleteUser(t *testing.T) { - type args struct { - req *http.Request - } - - adminController := AdminController{ - iam: &IAMServiceMock{ - DeleteUserAccountFunc: func(access string) error { - return nil - }, - }, - } - - app := fiber.New() - - app.Patch("/delete-user", adminController.DeleteUser) - - tests := []struct { - name string - app *fiber.App - args args - wantErr bool - statusCode int - }{ - { - name: "Admin-delete-user-success", - app: app, - args: args{ - req: httptest.NewRequest(http.MethodPatch, "/delete-user?access=test", nil), - }, - wantErr: false, - statusCode: 200, - }, - } - for _, tt := range tests { - resp, err := tt.app.Test(tt.args.req) - - if (err != nil) != tt.wantErr { - t.Errorf("AdminController.DeleteUser() error = %v, wantErr %v", err, tt.wantErr) - } - - if resp.StatusCode != tt.statusCode { - t.Errorf("AdminController.DeleteUser() statusCode = %v, wantStatusCode = %v", resp.StatusCode, tt.statusCode) - } - } -} - -func TestAdminController_ListUsers(t *testing.T) { - type args struct { - req *http.Request - } - - adminController := AdminController{ - iam: &IAMServiceMock{ - ListUserAccountsFunc: func() ([]auth.Account, error) { - return []auth.Account{}, nil - }, - }, - } - - adminControllerErr := AdminController{ - iam: &IAMServiceMock{ - ListUserAccountsFunc: func() ([]auth.Account, error) { - return []auth.Account{}, fmt.Errorf("server error") - }, - }, - } - - appErr := fiber.New() - appErr.Patch("/list-users", adminControllerErr.ListUsers) - - appSucc := fiber.New() - appSucc.Patch("/list-users", adminController.ListUsers) - - tests := []struct { - name string - app *fiber.App - args args - wantErr bool - statusCode int - }{ - { - name: "Admin-list-users-iam-error", - app: appErr, - args: args{ - req: httptest.NewRequest(http.MethodPatch, "/list-users", nil), - }, - wantErr: false, - statusCode: 500, - }, - { - name: "Admin-list-users-success", - app: appSucc, - args: args{ - req: httptest.NewRequest(http.MethodPatch, "/list-users", nil), - }, - wantErr: false, - statusCode: 200, - }, - } - for _, tt := range tests { - resp, err := tt.app.Test(tt.args.req) - - if (err != nil) != tt.wantErr { - t.Errorf("AdminController.ListUsers() error = %v, wantErr %v", err, tt.wantErr) - } - - if resp.StatusCode != tt.statusCode { - t.Errorf("AdminController.ListUsers() statusCode = %v, wantStatusCode = %v", resp.StatusCode, tt.statusCode) - } - } -} - -func TestAdminController_ChangeBucketOwner(t *testing.T) { - type args struct { - req *http.Request - } - adminController := AdminController{ - be: &BackendMock{ - ChangeBucketOwnerFunc: func(contextMoqParam context.Context, bucket, owner string) error { - return nil - }, - }, - iam: &IAMServiceMock{ - GetUserAccountFunc: func(access string) (auth.Account, error) { - return auth.Account{}, nil - }, - }, - } - - adminControllerIamErr := AdminController{ - iam: &IAMServiceMock{ - GetUserAccountFunc: func(access string) (auth.Account, error) { - return auth.Account{}, fmt.Errorf("unknown server error") - }, - }, - } - - adminControllerIamAccDoesNotExist := AdminController{ - iam: &IAMServiceMock{ - GetUserAccountFunc: func(access string) (auth.Account, error) { - return auth.Account{}, auth.ErrNoSuchUser - }, - }, - } - - app := fiber.New() - app.Patch("/change-bucket-owner", adminController.ChangeBucketOwner) - - appIamErr := fiber.New() - appIamErr.Patch("/change-bucket-owner", adminControllerIamErr.ChangeBucketOwner) - - appIamNoSuchUser := fiber.New() - appIamNoSuchUser.Patch("/change-bucket-owner", adminControllerIamAccDoesNotExist.ChangeBucketOwner) - - tests := []struct { - name string - app *fiber.App - args args - wantErr bool - statusCode int - }{ - { - name: "Change-bucket-owner-check-account-server-error", - app: appIamErr, - args: args{ - req: httptest.NewRequest(http.MethodPatch, "/change-bucket-owner", nil), - }, - wantErr: false, - statusCode: 500, - }, - { - name: "Change-bucket-owner-acc-does-not-exist", - app: appIamNoSuchUser, - args: args{ - req: httptest.NewRequest(http.MethodPatch, "/change-bucket-owner", nil), - }, - wantErr: false, - statusCode: 404, - }, - { - name: "Change-bucket-owner-success", - app: app, - args: args{ - req: httptest.NewRequest(http.MethodPatch, "/change-bucket-owner?bucket=bucket&owner=owner", nil), - }, - wantErr: false, - statusCode: 200, - }, - } - for _, tt := range tests { - resp, err := tt.app.Test(tt.args.req) - - if (err != nil) != tt.wantErr { - t.Errorf("AdminController.ChangeBucketOwner() error = %v, wantErr %v", err, tt.wantErr) - } - - if resp.StatusCode != tt.statusCode { - t.Errorf("AdminController.ChangeBucketOwner() statusCode = %v, wantStatusCode = %v", resp.StatusCode, tt.statusCode) - } - } -} - -func TestAdminController_ListBuckets(t *testing.T) { - type args struct { - req *http.Request - } - adminController := AdminController{ - be: &BackendMock{ - ListBucketsAndOwnersFunc: func(contextMoqParam context.Context) ([]s3response.Bucket, error) { - return []s3response.Bucket{}, nil - }, - }, - } - - app := fiber.New() - app.Patch("/list-buckets", adminController.ListBuckets) - - tests := []struct { - name string - app *fiber.App - args args - wantErr bool - statusCode int - }{ - { - name: "List-buckets-success", - app: app, - args: args{ - req: httptest.NewRequest(http.MethodPatch, "/list-buckets", nil), - }, - wantErr: false, - statusCode: 200, - }, - } - for _, tt := range tests { - resp, err := tt.app.Test(tt.args.req) - - if (err != nil) != tt.wantErr { - t.Errorf("AdminController.ListBuckets() error = %v, wantErr %v", err, tt.wantErr) - } - - if resp.StatusCode != tt.statusCode { - t.Errorf("AdminController.ListBuckets() statusCode = %v, wantStatusCode = %v", resp.StatusCode, tt.statusCode) - } - } -} diff --git a/s3api/controllers/base.go b/s3api/controllers/base.go index 34c36b4..00c79a1 100644 --- a/s3api/controllers/base.go +++ b/s3api/controllers/base.go @@ -1991,93 +1991,3 @@ func SendResponse(ctx *fiber.Ctx, err error, l *MetaOpts) error { ctx.Status(l.Status) return nil } - -func SendXMLResponse(ctx *fiber.Ctx, resp any, err error, l *MetaOpts) error { - if l.MetricsMng != nil { - if l.ObjectCount > 0 { - l.MetricsMng.Send(ctx, err, l.Action, l.ObjectCount, l.Status) - } else { - l.MetricsMng.Send(ctx, err, l.Action, l.ContentLength, l.Status) - } - } - if err != nil { - if l.Logger != nil { - l.Logger.Log(ctx, err, nil, s3log.LogMeta{ - Action: l.Action, - BucketOwner: l.BucketOwner, - ObjectSize: l.ObjectSize, - }) - } - serr, ok := err.(s3err.APIError) - if ok { - ctx.Status(serr.HTTPStatusCode) - return ctx.Send(s3err.GetAPIErrorResponse(serr, "", "", "")) - } - - fmt.Fprintf(os.Stderr, "Internal Error, %v\n", err) - ctx.Status(http.StatusInternalServerError) - - return ctx.Send(s3err.GetAPIErrorResponse( - s3err.GetAPIError(s3err.ErrInternalError), "", "", "")) - } - - var b []byte - - // Handle already encoded responses(text, json...) - encodedResp, ok := resp.([]byte) - if ok { - b = encodedResp - } - - if resp != nil && !ok { - if b, err = xml.Marshal(resp); err != nil { - return err - } - - if len(b) > 0 { - ctx.Response().Header.Set("Content-Length", fmt.Sprint(len(b))) - ctx.Response().Header.SetContentType(fiber.MIMEApplicationXML) - } - } - - if l.Logger != nil { - l.Logger.Log(ctx, nil, b, s3log.LogMeta{ - Action: l.Action, - BucketOwner: l.BucketOwner, - ObjectSize: l.ObjectSize, - }) - } - - if l.EvSender != nil { - l.EvSender.SendEvent(ctx, s3event.EventMeta{ - BucketOwner: l.BucketOwner, - ObjectSize: l.ObjectSize, - ObjectETag: l.ObjectETag, - VersionId: l.VersionId, - EventName: l.EventName, - }) - } - - if ok { - if len(b) > 0 { - ctx.Response().Header.Set("Content-Length", fmt.Sprint(len(b))) - } - - return ctx.Send(b) - } - - msglen := len(xmlhdr) + len(b) - if msglen > maxXMLBodyLen { - debuglogger.Logf("XML encoded body len %v exceeds max len %v", - msglen, maxXMLBodyLen) - ctx.Status(http.StatusInternalServerError) - - return ctx.Send(s3err.GetAPIErrorResponse( - s3err.GetAPIError(s3err.ErrInternalError), "", "", "")) - } - res := make([]byte, 0, msglen) - res = append(res, xmlhdr...) - res = append(res, b...) - - return ctx.Send(res) -} diff --git a/s3api/middlewares/acl-parser.go b/s3api/middlewares/acl-parser.go index 20a4a56..8a8dcee 100644 --- a/s3api/middlewares/acl-parser.go +++ b/s3api/middlewares/acl-parser.go @@ -55,10 +55,10 @@ func AclParser(be backend.Backend, logger s3log.AuditLogger, readonly bool) fibe !ctx.Request().URI().QueryArgs().Has("cors") { isRoot, acct := utils.ContextKeyIsRoot.Get(ctx).(bool), utils.ContextKeyAccount.Get(ctx).(auth.Account) if err := auth.MayCreateBucket(acct, isRoot); err != nil { - return controllers.SendXMLResponse(ctx, nil, err, &controllers.MetaOpts{Logger: logger, Action: "CreateBucket"}) + return controllers.SendResponse(ctx, err, &controllers.MetaOpts{Logger: logger, Action: "CreateBucket"}) } if readonly { - return controllers.SendXMLResponse(ctx, nil, s3err.GetAPIError(s3err.ErrAccessDenied), + return controllers.SendResponse(ctx, s3err.GetAPIError(s3err.ErrAccessDenied), &controllers.MetaOpts{ Logger: logger, Action: "CreateBucket", diff --git a/s3api/router.go b/s3api/router.go index 31aa0ce..047d35a 100644 --- a/s3api/router.go +++ b/s3api/router.go @@ -36,22 +36,22 @@ func (sa *S3ApiRouter) Init(app *fiber.App, be backend.Backend, iam auth.IAMServ adminController := controllers.NewAdminController(iam, be, aLogger) // CreateUser admin api - app.Patch("/create-user", middlewares.IsAdmin(logger), adminController.CreateUser) + app.Patch("/create-user", middlewares.IsAdmin(logger), controllers.ProcessResponse(adminController.CreateUser, aLogger, nil, nil)) // DeleteUsers admin api - app.Patch("/delete-user", middlewares.IsAdmin(logger), adminController.DeleteUser) + app.Patch("/delete-user", middlewares.IsAdmin(logger), controllers.ProcessResponse(adminController.DeleteUser, aLogger, nil, nil)) // UpdateUser admin api - app.Patch("/update-user", middlewares.IsAdmin(logger), adminController.UpdateUser) + app.Patch("/update-user", middlewares.IsAdmin(logger), controllers.ProcessResponse(adminController.UpdateUser, aLogger, nil, nil)) // ListUsers admin api - app.Patch("/list-users", middlewares.IsAdmin(logger), adminController.ListUsers) + app.Patch("/list-users", middlewares.IsAdmin(logger), controllers.ProcessResponse(adminController.ListUsers, aLogger, nil, nil)) // ChangeBucketOwner admin api - app.Patch("/change-bucket-owner", middlewares.IsAdmin(logger), adminController.ChangeBucketOwner) + app.Patch("/change-bucket-owner", middlewares.IsAdmin(logger), controllers.ProcessResponse(adminController.ChangeBucketOwner, aLogger, nil, nil)) // ListBucketsAndOwners admin api - app.Patch("/list-buckets", middlewares.IsAdmin(logger), adminController.ListBuckets) + app.Patch("/list-buckets", middlewares.IsAdmin(logger), controllers.ProcessResponse(adminController.ListBuckets, aLogger, nil, nil)) } // ListBuckets action