From a59e53a5c57f0f0b40e148ec867aa761c8fec85c Mon Sep 17 00:00:00 2001 From: Tosone Date: Tue, 30 Jan 2024 00:05:08 +0800 Subject: [PATCH] :white_check_mark: Add namespace update ut --- pkg/auth/artifact.go | 14 +- pkg/auth/auth.go | 8 +- pkg/auth/mocks/service.go | 20 +- pkg/auth/namespace.go | 33 +- pkg/auth/repository.go | 14 +- pkg/auth/tag.go | 14 +- pkg/handlers/distribution/base/tags_list.go | 17 +- pkg/handlers/distribution/blob/blob_delete.go | 14 +- pkg/handlers/distribution/blob/blob_get.go | 13 +- pkg/handlers/distribution/blob/blob_head.go | 14 +- .../distribution/manifest/manifest_delete.go | 14 +- .../distribution/manifest/manifest_get.go | 14 +- .../distribution/manifest/manifest_head.go | 14 +- .../distribution/manifest/manifest_put.go | 13 +- .../distribution/upload/upload_patch.go | 16 +- .../distribution/upload/upload_post.go | 15 +- .../distribution/upload/upload_put.go | 13 +- pkg/handlers/namespaces/namespaces_delete.go | 11 +- .../namespaces/namespaces_delete_test.go | 4 +- pkg/handlers/namespaces/namespaces_get.go | 11 +- .../namespaces/namespaces_get_test.go | 241 +++++----- .../namespaces/namespaces_members_add.go | 11 +- pkg/handlers/namespaces/namespaces_update.go | 11 +- .../namespaces/namespaces_update_test.go | 429 ++++++++++-------- .../repositories/repositories_create.go | 13 +- .../repositories/repositories_delete.go | 15 +- pkg/handlers/repositories/repositories_get.go | 16 +- .../repositories/repositories_update.go | 15 +- pkg/handlers/tags/tags_delete.go | 11 +- pkg/handlers/tags/tags_get.go | 13 +- pkg/handlers/tags/tags_list.go | 11 +- pkg/utils/utils.go | 14 + pkg/utils/utils_test.go | 34 ++ pkg/xerrors/distribution.go | 11 + 34 files changed, 734 insertions(+), 397 deletions(-) diff --git a/pkg/auth/artifact.go b/pkg/auth/artifact.go index 5cc0171a..c8074400 100644 --- a/pkg/auth/artifact.go +++ b/pkg/auth/artifact.go @@ -15,21 +15,29 @@ package auth import ( + "errors" + "fmt" + "github.com/labstack/echo/v4" "github.com/rs/zerolog/log" + "gorm.io/gorm" "github.com/go-sigma/sigma/pkg/types/enums" ) // Tag ... -func (s service) Artifact(c echo.Context, artifactID int64, auth enums.Auth) bool { +func (s service) Artifact(c echo.Context, artifactID int64, auth enums.Auth) (bool, error) { ctx := log.Logger.WithContext(c.Request().Context()) artifactService := s.artifactServiceFactory.New() artifactObj, err := artifactService.Get(ctx, artifactID) if err != nil { - log.Error().Err(err).Msg("Get artifact by id failed") - return false + if !errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(err).Int64("artifactID", artifactID).Msg("Get artifact by id failed") + return false, errors.Join(err, fmt.Errorf("Get artifact by id(%d) failed", artifactID)) + } + log.Error().Err(err).Int64("artifactID", artifactID).Msg("Get artifact by id not found") + return false, errors.Join(err, fmt.Errorf("Get artifact by id(%d) not found", artifactID)) } return s.Repository(c, artifactObj.RepositoryID, auth) } diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 5f3c721c..8e220355 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -27,13 +27,13 @@ import ( // Service is the interface for the auth service type Service interface { // Namespace ... - Namespace(c echo.Context, namespaceID int64, auth enums.Auth) bool + Namespace(c echo.Context, namespaceID int64, auth enums.Auth) (bool, error) // Repository ... - Repository(c echo.Context, repositoryID int64, auth enums.Auth) bool + Repository(c echo.Context, repositoryID int64, auth enums.Auth) (bool, error) // Tag ... - Tag(c echo.Context, tagID int64, auth enums.Auth) bool + Tag(c echo.Context, tagID int64, auth enums.Auth) (bool, error) // Artifact ... - Artifact(c echo.Context, artifactID int64, auth enums.Auth) bool + Artifact(c echo.Context, artifactID int64, auth enums.Auth) (bool, error) } // ServiceFactory is the interface that provides the artifact service factory methods. diff --git a/pkg/auth/mocks/service.go b/pkg/auth/mocks/service.go index 261075f1..393bd4ce 100644 --- a/pkg/auth/mocks/service.go +++ b/pkg/auth/mocks/service.go @@ -41,11 +41,12 @@ func (m *MockService) EXPECT() *MockServiceMockRecorder { } // Artifact mocks base method. -func (m *MockService) Artifact(arg0 echo.Context, arg1 int64, arg2 enums.Auth) bool { +func (m *MockService) Artifact(arg0 echo.Context, arg1 int64, arg2 enums.Auth) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Artifact", arg0, arg1, arg2) ret0, _ := ret[0].(bool) - return ret0 + ret1, _ := ret[1].(error) + return ret0, ret1 } // Artifact indicates an expected call of Artifact. @@ -55,11 +56,12 @@ func (mr *MockServiceMockRecorder) Artifact(arg0, arg1, arg2 any) *gomock.Call { } // Namespace mocks base method. -func (m *MockService) Namespace(arg0 echo.Context, arg1 int64, arg2 enums.Auth) bool { +func (m *MockService) Namespace(arg0 echo.Context, arg1 int64, arg2 enums.Auth) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Namespace", arg0, arg1, arg2) ret0, _ := ret[0].(bool) - return ret0 + ret1, _ := ret[1].(error) + return ret0, ret1 } // Namespace indicates an expected call of Namespace. @@ -69,11 +71,12 @@ func (mr *MockServiceMockRecorder) Namespace(arg0, arg1, arg2 any) *gomock.Call } // Repository mocks base method. -func (m *MockService) Repository(arg0 echo.Context, arg1 int64, arg2 enums.Auth) bool { +func (m *MockService) Repository(arg0 echo.Context, arg1 int64, arg2 enums.Auth) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Repository", arg0, arg1, arg2) ret0, _ := ret[0].(bool) - return ret0 + ret1, _ := ret[1].(error) + return ret0, ret1 } // Repository indicates an expected call of Repository. @@ -83,11 +86,12 @@ func (mr *MockServiceMockRecorder) Repository(arg0, arg1, arg2 any) *gomock.Call } // Tag mocks base method. -func (m *MockService) Tag(arg0 echo.Context, arg1 int64, arg2 enums.Auth) bool { +func (m *MockService) Tag(arg0 echo.Context, arg1 int64, arg2 enums.Auth) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Tag", arg0, arg1, arg2) ret0, _ := ret[0].(bool) - return ret0 + ret1, _ := ret[1].(error) + return ret0, ret1 } // Tag indicates an expected call of Tag. diff --git a/pkg/auth/namespace.go b/pkg/auth/namespace.go index 5201ff9f..a156c532 100644 --- a/pkg/auth/namespace.go +++ b/pkg/auth/namespace.go @@ -16,6 +16,7 @@ package auth import ( "errors" + "fmt" "github.com/labstack/echo/v4" "github.com/rs/zerolog/log" @@ -27,53 +28,57 @@ import ( ) // Namespace ... -func (s service) Namespace(c echo.Context, namespaceID int64, auth enums.Auth) bool { +func (s service) Namespace(c echo.Context, namespaceID int64, auth enums.Auth) (bool, error) { ctx := log.Logger.WithContext(c.Request().Context()) iuser := c.Get(consts.ContextUser) if iuser == nil { log.Error().Msg("Get user from header failed") - return false + return false, nil } user, ok := iuser.(*models.User) if !ok { log.Error().Msg("Convert user from header failed") - return false + return false, nil } // 1. check user is admin or not if user.Role == enums.UserRoleAdmin || user.Role == enums.UserRoleRoot { - return true + return true, nil } // 2. check namespace visibility namespaceService := s.namespaceServiceFactory.New() namespaceObj, err := namespaceService.Get(ctx, namespaceID) if err != nil { - log.Error().Err(err).Msg("Get namespace by id failed") - return false + if !errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(err).Msg("Get namespace by id failed") + return false, errors.Join(err, fmt.Errorf("Get namespace by id(%d) failed", namespaceID)) + } + log.Error().Err(err).Msg("Get namespace by id not found") + return false, errors.Join(err, fmt.Errorf("Get namespace by id(%d) not found", namespaceID)) } if namespaceObj.Visibility == enums.VisibilityPublic && auth == enums.AuthRead { - return true + return true, nil } // 3. check user is member of the namespace roleService := s.roleServiceFactory.New() namespaceMemberObj, err := roleService.GetNamespaceMember(ctx, namespaceID, user.ID) if err != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { - log.Error().Err(err).Msg("Get namespace member failed") + if !errors.Is(err, gorm.ErrRecordNotFound) { // check user's role in this namespace + log.Error().Err(err).Msg("Get namespace member by namespace id and user id failed") } - return false + return false, err } if namespaceMemberObj.Role == enums.NamespaceRoleReader && auth == enums.AuthRead { - return true + return true, nil } if namespaceMemberObj.Role == enums.NamespaceRoleManager && (auth == enums.AuthManage || auth == enums.AuthRead) { - return true + return true, nil } if namespaceMemberObj.Role == enums.NamespaceRoleAdmin && (auth == enums.AuthAdmin || auth == enums.AuthManage || auth == enums.AuthRead) { - return true + return true, nil } - return false + return false, nil } diff --git a/pkg/auth/repository.go b/pkg/auth/repository.go index 70210dbd..3e9ebad1 100644 --- a/pkg/auth/repository.go +++ b/pkg/auth/repository.go @@ -15,20 +15,28 @@ package auth import ( + "errors" + "fmt" + "github.com/labstack/echo/v4" "github.com/rs/zerolog/log" + "gorm.io/gorm" "github.com/go-sigma/sigma/pkg/types/enums" ) // Repository ... -func (s service) Repository(c echo.Context, repositoryID int64, auth enums.Auth) bool { +func (s service) Repository(c echo.Context, repositoryID int64, auth enums.Auth) (bool, error) { ctx := log.Logger.WithContext(c.Request().Context()) repositoryService := s.repositoryServiceFactory.New() repositoryObj, err := repositoryService.Get(ctx, repositoryID) if err != nil { - log.Error().Err(err).Msg("Get repository by id failed") - return false + if !errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(err).Int64("repositoryID", repositoryID).Msg("Get repository by id failed") + return false, errors.Join(err, fmt.Errorf("Get repository by id(%d) failed", repositoryID)) + } + log.Error().Err(err).Int64("repositoryID", repositoryID).Msg("Get repository by id not found") + return false, errors.Join(err, fmt.Errorf("Get repository by id(%d) not found", repositoryID)) } return s.Namespace(c, repositoryObj.NamespaceID, auth) } diff --git a/pkg/auth/tag.go b/pkg/auth/tag.go index cceaf8ca..9c660a80 100644 --- a/pkg/auth/tag.go +++ b/pkg/auth/tag.go @@ -15,21 +15,29 @@ package auth import ( + "errors" + "fmt" + "github.com/labstack/echo/v4" "github.com/rs/zerolog/log" + "gorm.io/gorm" "github.com/go-sigma/sigma/pkg/types/enums" ) // Tag ... -func (s service) Tag(c echo.Context, tagID int64, auth enums.Auth) bool { +func (s service) Tag(c echo.Context, tagID int64, auth enums.Auth) (bool, error) { ctx := log.Logger.WithContext(c.Request().Context()) tagService := s.tagServiceFactory.New() tagObj, err := tagService.GetByID(ctx, tagID) if err != nil { - log.Error().Err(err).Msg("Get tag by id failed") - return false + if !errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(err).Int64("tagID", tagID).Msg("Get tag by id failed") + return false, errors.Join(err, fmt.Errorf("Get tag by id(%d) failed", tagID)) + } + log.Error().Err(err).Int64("tagID", tagID).Msg("Get tag by id not found") + return false, errors.Join(err, fmt.Errorf("Get tag by id(%d) not found", tagID)) } return s.Repository(c, tagObj.RepositoryID, auth) } diff --git a/pkg/handlers/distribution/base/tags_list.go b/pkg/handlers/distribution/base/tags_list.go index ec2c6e42..301ddd44 100644 --- a/pkg/handlers/distribution/base/tags_list.go +++ b/pkg/handlers/distribution/base/tags_list.go @@ -32,6 +32,7 @@ import ( "github.com/go-sigma/sigma/pkg/consts" "github.com/go-sigma/sigma/pkg/dal/models" "github.com/go-sigma/sigma/pkg/types/enums" + "github.com/go-sigma/sigma/pkg/utils" "github.com/go-sigma/sigma/pkg/xerrors" ) @@ -79,12 +80,20 @@ func (h *handler) ListTags(c echo.Context) error { return xerrors.NewDSError(c, xerrors.DSErrCodeUnknown) } - if !h.authServiceFactory.New().Repository(c, repositoryObj.ID, enums.AuthRead) { - log.Error().Int64("UserID", user.ID).Int64("NamespaceID", repositoryObj.ID).Msg("Auth check failed") - return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") + authChecked, err := h.authServiceFactory.New().Repository(c, repositoryObj.ID, enums.AuthRead) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Msg("Resource not found") + return xerrors.GenDSErrCodeResourceNotFound(err) + } + return xerrors.NewDSError(c, xerrors.DSErrCodeUnknown) + } + if !authChecked { + log.Error().Int64("UserID", user.ID).Int64("RepositoryID", repositoryObj.ID).Msg("Auth check failed") + return xerrors.NewDSError(c, xerrors.DSErrCodeUnauthorized) } - lastFound := false + var lastFound bool var lastID int64 = 0 tagService := h.tagServiceFactory.New() diff --git a/pkg/handlers/distribution/blob/blob_delete.go b/pkg/handlers/distribution/blob/blob_delete.go index f063543c..38cdcf37 100644 --- a/pkg/handlers/distribution/blob/blob_delete.go +++ b/pkg/handlers/distribution/blob/blob_delete.go @@ -27,6 +27,7 @@ import ( "github.com/go-sigma/sigma/pkg/consts" "github.com/go-sigma/sigma/pkg/dal/models" "github.com/go-sigma/sigma/pkg/types/enums" + "github.com/go-sigma/sigma/pkg/utils" "github.com/go-sigma/sigma/pkg/utils/imagerefs" "github.com/go-sigma/sigma/pkg/validators" "github.com/go-sigma/sigma/pkg/xerrors" @@ -65,9 +66,18 @@ func (h *handler) DeleteBlob(c echo.Context) error { log.Error().Err(err).Str("Name", repository).Msg("Get repository by name failed") return xerrors.NewDSError(c, xerrors.DSErrCodeBlobUnknown) } - if !h.authServiceFactory.New().Repository(c, namespaceObj.ID, enums.AuthManage) { + + authChecked, err := h.authServiceFactory.New().Repository(c, namespaceObj.ID, enums.AuthManage) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Msg("Resource not found") + return xerrors.GenDSErrCodeResourceNotFound(err) + } + return xerrors.NewDSError(c, xerrors.DSErrCodeUnknown) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("NamespaceID", namespaceObj.ID).Msg("Auth check failed") - return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") + return xerrors.NewDSError(c, xerrors.DSErrCodeUnauthorized) } dgest, err := digest.Parse(strings.TrimPrefix(uri[strings.LastIndex(uri, "/"):], "/")) diff --git a/pkg/handlers/distribution/blob/blob_get.go b/pkg/handlers/distribution/blob/blob_get.go index 172a8dd3..ea0ef785 100644 --- a/pkg/handlers/distribution/blob/blob_get.go +++ b/pkg/handlers/distribution/blob/blob_get.go @@ -75,9 +75,18 @@ func (h *handler) GetBlob(c echo.Context) error { log.Error().Err(err).Str("Name", repository).Msg("Get repository by name failed") return xerrors.NewDSError(c, xerrors.DSErrCodeBlobUnknown) } - if !h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthRead) { + + authChecked, err := h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthRead) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Msg("Resource not found") + return xerrors.GenDSErrCodeResourceNotFound(err) + } + return xerrors.NewDSError(c, xerrors.DSErrCodeUnknown) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("NamespaceID", namespaceObj.ID).Msg("Auth check failed") - return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") + return xerrors.NewDSError(c, xerrors.DSErrCodeUnauthorized) } dgest, err := digest.Parse(strings.TrimPrefix(uri[strings.LastIndex(uri, "/"):], "/")) diff --git a/pkg/handlers/distribution/blob/blob_head.go b/pkg/handlers/distribution/blob/blob_head.go index 79fd3e89..d8a35d4e 100644 --- a/pkg/handlers/distribution/blob/blob_head.go +++ b/pkg/handlers/distribution/blob/blob_head.go @@ -31,6 +31,7 @@ import ( "github.com/go-sigma/sigma/pkg/handlers/distribution/clients" "github.com/go-sigma/sigma/pkg/modules/cacher" "github.com/go-sigma/sigma/pkg/types/enums" + "github.com/go-sigma/sigma/pkg/utils" "github.com/go-sigma/sigma/pkg/utils/imagerefs" "github.com/go-sigma/sigma/pkg/utils/ptr" "github.com/go-sigma/sigma/pkg/validators" @@ -69,9 +70,18 @@ func (h *handler) HeadBlob(c echo.Context) error { log.Error().Err(err).Str("Name", repository).Msg("Get repository by name failed") return xerrors.NewDSError(c, xerrors.DSErrCodeBlobUnknown) } - if !h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthRead) { + + authChecked, err := h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthRead) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Msg("Resource not found") + return xerrors.GenDSErrCodeResourceNotFound(err) + } + return xerrors.NewDSError(c, xerrors.DSErrCodeUnknown) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("NamespaceID", namespaceObj.ID).Msg("Auth check failed") - return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") + return xerrors.NewDSError(c, xerrors.DSErrCodeUnauthorized) } dgest, err := digest.Parse(strings.TrimPrefix(uri[strings.LastIndex(uri, "/"):], "/")) diff --git a/pkg/handlers/distribution/manifest/manifest_delete.go b/pkg/handlers/distribution/manifest/manifest_delete.go index e2022d8a..92294968 100644 --- a/pkg/handlers/distribution/manifest/manifest_delete.go +++ b/pkg/handlers/distribution/manifest/manifest_delete.go @@ -28,6 +28,7 @@ import ( "github.com/go-sigma/sigma/pkg/dal/models" "github.com/go-sigma/sigma/pkg/dal/query" "github.com/go-sigma/sigma/pkg/types/enums" + "github.com/go-sigma/sigma/pkg/utils" "github.com/go-sigma/sigma/pkg/utils/imagerefs" "github.com/go-sigma/sigma/pkg/validators" "github.com/go-sigma/sigma/pkg/xerrors" @@ -67,9 +68,18 @@ func (h *handler) DeleteManifest(c echo.Context) error { log.Error().Err(err).Str("Name", repository).Msg("Get repository by name failed") return xerrors.NewDSError(c, xerrors.DSErrCodeBlobUnknown) } - if !h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthManage) { + + authChecked, err := h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthManage) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Msg("Resource not found") + return xerrors.GenDSErrCodeResourceNotFound(err) + } + return xerrors.NewDSError(c, xerrors.DSErrCodeUnknown) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("NamespaceID", namespaceObj.ID).Msg("Auth check failed") - return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") + return xerrors.NewDSError(c, xerrors.DSErrCodeUnauthorized) } ref := strings.TrimPrefix(uri[strings.LastIndex(uri, "/"):], "/") diff --git a/pkg/handlers/distribution/manifest/manifest_get.go b/pkg/handlers/distribution/manifest/manifest_get.go index 55e70efe..93c92e65 100644 --- a/pkg/handlers/distribution/manifest/manifest_get.go +++ b/pkg/handlers/distribution/manifest/manifest_get.go @@ -27,6 +27,7 @@ import ( "github.com/go-sigma/sigma/pkg/consts" "github.com/go-sigma/sigma/pkg/dal/models" "github.com/go-sigma/sigma/pkg/types/enums" + "github.com/go-sigma/sigma/pkg/utils" "github.com/go-sigma/sigma/pkg/utils/imagerefs" "github.com/go-sigma/sigma/pkg/validators" "github.com/go-sigma/sigma/pkg/xerrors" @@ -64,9 +65,18 @@ func (h *handler) GetManifest(c echo.Context) error { log.Error().Err(err).Str("Name", repository).Msg("Get repository by name failed") return xerrors.NewDSError(c, xerrors.DSErrCodeBlobUnknown) } - if !h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthRead) { + + authChecked, err := h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthRead) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Msg("Resource not found") + return xerrors.GenDSErrCodeResourceNotFound(err) + } + return xerrors.NewDSError(c, xerrors.DSErrCodeUnknown) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("NamespaceID", namespaceObj.ID).Msg("Auth check failed") - return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") + return xerrors.NewDSError(c, xerrors.DSErrCodeUnauthorized) } ref := strings.TrimPrefix(uri[strings.LastIndex(uri, "/"):], "/") diff --git a/pkg/handlers/distribution/manifest/manifest_head.go b/pkg/handlers/distribution/manifest/manifest_head.go index 904d68fc..91ccc310 100644 --- a/pkg/handlers/distribution/manifest/manifest_head.go +++ b/pkg/handlers/distribution/manifest/manifest_head.go @@ -28,6 +28,7 @@ import ( "github.com/go-sigma/sigma/pkg/consts" "github.com/go-sigma/sigma/pkg/dal/models" "github.com/go-sigma/sigma/pkg/types/enums" + "github.com/go-sigma/sigma/pkg/utils" "github.com/go-sigma/sigma/pkg/utils/imagerefs" "github.com/go-sigma/sigma/pkg/validators" "github.com/go-sigma/sigma/pkg/xerrors" @@ -65,9 +66,18 @@ func (h *handler) HeadManifest(c echo.Context) error { log.Error().Err(err).Str("Name", repository).Msg("Get repository by name failed") return xerrors.NewDSError(c, xerrors.DSErrCodeBlobUnknown) } - if !h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthRead) { + + authChecked, err := h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthRead) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Msg("Resource not found") + return xerrors.GenDSErrCodeResourceNotFound(err) + } + return xerrors.NewDSError(c, xerrors.DSErrCodeUnknown) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("NamespaceID", namespaceObj.ID).Msg("Auth check failed") - return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") + return xerrors.NewDSError(c, xerrors.DSErrCodeUnauthorized) } ref := strings.TrimPrefix(uri[strings.LastIndex(uri, "/"):], "/") diff --git a/pkg/handlers/distribution/manifest/manifest_put.go b/pkg/handlers/distribution/manifest/manifest_put.go index 76c04d82..63d40c91 100644 --- a/pkg/handlers/distribution/manifest/manifest_put.go +++ b/pkg/handlers/distribution/manifest/manifest_put.go @@ -82,9 +82,18 @@ func (h *handler) PutManifest(c echo.Context) error { log.Error().Err(err).Str("Name", repository).Msg("Get repository by name failed") return xerrors.NewDSError(c, xerrors.DSErrCodeBlobUnknown) } - if !h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthRead) { + + authChecked, err := h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthRead) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Msg("Resource not found") + return xerrors.GenDSErrCodeResourceNotFound(err) + } + return xerrors.NewDSError(c, xerrors.DSErrCodeUnknown) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("NamespaceID", namespaceObj.ID).Msg("Auth check failed") - return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") + return xerrors.NewDSError(c, xerrors.DSErrCodeUnauthorized) } ref := strings.TrimPrefix(uri[strings.LastIndex(uri, "/"):], "/") diff --git a/pkg/handlers/distribution/upload/upload_patch.go b/pkg/handlers/distribution/upload/upload_patch.go index 73b64d9a..4cffb9d3 100644 --- a/pkg/handlers/distribution/upload/upload_patch.go +++ b/pkg/handlers/distribution/upload/upload_patch.go @@ -15,17 +15,20 @@ package upload import ( + "errors" "fmt" "net/http" "strings" "github.com/labstack/echo/v4" "github.com/rs/zerolog/log" + "gorm.io/gorm" "github.com/go-sigma/sigma/pkg/consts" "github.com/go-sigma/sigma/pkg/dal/models" "github.com/go-sigma/sigma/pkg/storage" "github.com/go-sigma/sigma/pkg/types/enums" + "github.com/go-sigma/sigma/pkg/utils" "github.com/go-sigma/sigma/pkg/utils/counter" "github.com/go-sigma/sigma/pkg/utils/imagerefs" "github.com/go-sigma/sigma/pkg/validators" @@ -70,9 +73,18 @@ func (h *handler) PatchUpload(c echo.Context) error { log.Error().Err(err).Str("Name", repository).Msg("Get repository by name failed") return xerrors.NewDSError(c, xerrors.DSErrCodeBlobUnknown) } - if !h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthManage) { + + authChecked, err := h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthManage) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Msg("Resource not found") + return xerrors.GenDSErrCodeResourceNotFound(err) + } + return xerrors.NewDSError(c, xerrors.DSErrCodeUnknown) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("NamespaceID", namespaceObj.ID).Msg("Auth check failed") - return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") + return xerrors.NewDSError(c, xerrors.DSErrCodeUnauthorized) } blobUploadService := h.blobUploadServiceFactory.New() diff --git a/pkg/handlers/distribution/upload/upload_post.go b/pkg/handlers/distribution/upload/upload_post.go index 47e6caaa..2fd1f14e 100644 --- a/pkg/handlers/distribution/upload/upload_post.go +++ b/pkg/handlers/distribution/upload/upload_post.go @@ -15,6 +15,7 @@ package upload import ( + "errors" "fmt" "net/http" "path" @@ -24,6 +25,7 @@ import ( gonanoid "github.com/matoous/go-nanoid" "github.com/opencontainers/go-digest" "github.com/rs/zerolog/log" + "gorm.io/gorm" "github.com/go-sigma/sigma/pkg/consts" "github.com/go-sigma/sigma/pkg/dal/models" @@ -70,9 +72,18 @@ func (h *handler) PostUpload(c echo.Context) error { log.Error().Err(err).Str("Name", repository).Msg("Get repository by name failed") return xerrors.NewDSError(c, xerrors.DSErrCodeBlobUnknown) } - if !h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthManage) { + + authChecked, err := h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthManage) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Msg("Resource not found") + return xerrors.GenDSErrCodeResourceNotFound(err) + } + return xerrors.NewDSError(c, xerrors.DSErrCodeUnknown) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("NamespaceID", namespaceObj.ID).Msg("Auth check failed") - return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") + return xerrors.NewDSError(c, xerrors.DSErrCodeUnauthorized) } // fileID is the filename that upload to the blob_uploads diff --git a/pkg/handlers/distribution/upload/upload_put.go b/pkg/handlers/distribution/upload/upload_put.go index e2f3eea8..55ddf753 100644 --- a/pkg/handlers/distribution/upload/upload_put.go +++ b/pkg/handlers/distribution/upload/upload_put.go @@ -74,9 +74,18 @@ func (h *handler) PutUpload(c echo.Context) error { log.Error().Err(err).Str("Repository", repository).Msg("Repository must container a valid namespace") return xerrors.NewDSError(c, xerrors.DSErrCodeManifestWithNamespace) } - if !h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthManage) { + + authChecked, err := h.authServiceFactory.New().Namespace(c, namespaceObj.ID, enums.AuthManage) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Msg("Resource not found") + return xerrors.GenDSErrCodeResourceNotFound(err) + } + return xerrors.NewDSError(c, xerrors.DSErrCodeUnknown) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("NamespaceID", namespaceObj.ID).Msg("Auth check failed") - return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") + return xerrors.NewDSError(c, xerrors.DSErrCodeUnauthorized) } dgest, err := digest.Parse(c.QueryParam("digest")) diff --git a/pkg/handlers/namespaces/namespaces_delete.go b/pkg/handlers/namespaces/namespaces_delete.go index 9fb5bc3a..869bb649 100644 --- a/pkg/handlers/namespaces/namespaces_delete.go +++ b/pkg/handlers/namespaces/namespaces_delete.go @@ -66,7 +66,16 @@ func (h *handler) DeleteNamespace(c echo.Context) error { return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeBadRequest, err.Error()) } - if !h.authServiceFactory.New().Namespace(c, req.ID, enums.AuthManage) { + authChecked, err := h.authServiceFactory.New().Namespace(c, req.ID, enums.AuthManage) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Int64("NamespaceID", req.ID).Msg("Resource not found") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeNotFound, utils.UnwrapJoinedErrors(err)) + } + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Int64("NamespaceID", req.ID).Err(err).Msg("Get resource failed") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeInternalError, utils.UnwrapJoinedErrors(err)) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("NamespaceID", req.ID).Msg("Auth check failed") return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") } diff --git a/pkg/handlers/namespaces/namespaces_delete_test.go b/pkg/handlers/namespaces/namespaces_delete_test.go index 883ec227..f52e5a8c 100644 --- a/pkg/handlers/namespaces/namespaces_delete_test.go +++ b/pkg/handlers/namespaces/namespaces_delete_test.go @@ -67,8 +67,8 @@ func TestDeleteNamespace(t *testing.T) { }).Times(2) authService := authmocks.NewMockService(ctrl) - authService.EXPECT().Namespace(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(c echo.Context, namespaceID int64, auth enums.Auth) bool { - return true + authService.EXPECT().Namespace(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(c echo.Context, namespaceID int64, auth enums.Auth) (bool, error) { + return true, nil }).Times(3) authServiceFactory := authmocks.NewMockServiceFactory(ctrl) diff --git a/pkg/handlers/namespaces/namespaces_get.go b/pkg/handlers/namespaces/namespaces_get.go index 816213b2..8ac3300f 100644 --- a/pkg/handlers/namespaces/namespaces_get.go +++ b/pkg/handlers/namespaces/namespaces_get.go @@ -76,7 +76,16 @@ func (h *handler) GetNamespace(c echo.Context) error { return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeInternalError, err.Error()) } - if !h.authServiceFactory.New().Namespace(c, req.ID, enums.AuthRead) { + authChecked, err := h.authServiceFactory.New().Namespace(c, req.ID, enums.AuthRead) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Int64("NamespaceID", req.ID).Msg("Resource not found") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeNotFound, utils.UnwrapJoinedErrors(err)) + } + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Int64("NamespaceID", req.ID).Err(err).Msg("Get resource failed") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeInternalError, utils.UnwrapJoinedErrors(err)) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("NamespaceID", req.ID).Msg("Auth check failed") return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") } diff --git a/pkg/handlers/namespaces/namespaces_get_test.go b/pkg/handlers/namespaces/namespaces_get_test.go index a8b98e2a..24ab0913 100644 --- a/pkg/handlers/namespaces/namespaces_get_test.go +++ b/pkg/handlers/namespaces/namespaces_get_test.go @@ -14,120 +14,127 @@ package namespaces -// import ( -// "bytes" -// "context" -// "fmt" -// "net/http" -// "net/http/httptest" -// "strconv" -// "testing" - -// "github.com/labstack/echo/v4" -// "github.com/stretchr/testify/assert" -// "github.com/tidwall/gjson" -// "go.uber.org/mock/gomock" - -// "github.com/go-sigma/sigma/pkg/consts" -// "github.com/go-sigma/sigma/pkg/dal" -// "github.com/go-sigma/sigma/pkg/dal/dao" -// daomock "github.com/go-sigma/sigma/pkg/dal/dao/mocks" -// "github.com/go-sigma/sigma/pkg/dal/models" -// "github.com/go-sigma/sigma/pkg/dal/query" -// "github.com/go-sigma/sigma/pkg/logger" -// "github.com/go-sigma/sigma/pkg/tests" -// "github.com/go-sigma/sigma/pkg/utils/ptr" -// "github.com/go-sigma/sigma/pkg/validators" -// ) - -// func TestGetNamespace(t *testing.T) { -// logger.SetLevel("debug") -// e := echo.New() -// validators.Initialize(e) -// err := tests.Initialize(t) -// assert.NoError(t, err) -// err = tests.DB.Init() -// assert.NoError(t, err) -// defer func() { -// conn, err := dal.DB.DB() -// assert.NoError(t, err) -// err = conn.Close() -// assert.NoError(t, err) -// err = tests.DB.DeInit() -// assert.NoError(t, err) -// }() - -// namespaceHandler := handlerNew() - -// userServiceFactory := dao.NewUserServiceFactory() -// userService := userServiceFactory.New() - -// ctx := context.Background() -// userObj := &models.User{Username: "list-namespace", Password: ptr.Of("test"), Email: ptr.Of("test@gmail.com")} -// err = userService.Create(ctx, userObj) -// assert.NoError(t, err) - -// req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`{"name":"test","description":""}`)) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec := httptest.NewRecorder() -// c := e.NewContext(req, rec) -// c.Set(consts.ContextUser, userObj) -// err = namespaceHandler.PostNamespace(c) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusCreated, c.Response().Status) -// bytes := rec.Body.Bytes() -// resultID := gjson.GetBytes(bytes, "id").Int() - -// req = httptest.NewRequest(http.MethodDelete, "/", nil) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec = httptest.NewRecorder() -// c = e.NewContext(req, rec) -// c.SetParamNames("id") -// c.SetParamValues(strconv.FormatInt(resultID, 10)) -// err = namespaceHandler.GetNamespace(c) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusOK, c.Response().Status) - -// req = httptest.NewRequest(http.MethodDelete, "/", nil) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec = httptest.NewRecorder() -// c = e.NewContext(req, rec) -// err = namespaceHandler.GetNamespace(c) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusBadRequest, c.Response().Status) - -// req = httptest.NewRequest(http.MethodDelete, "/", nil) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec = httptest.NewRecorder() -// c = e.NewContext(req, rec) -// c.SetParamNames("id") -// c.SetParamValues(strconv.FormatUint(3, 10)) -// err = namespaceHandler.GetNamespace(c) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusNotFound, c.Response().Status) - -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() - -// daoMockNamespaceService := daomock.NewMockNamespaceService(ctrl) -// daoMockNamespaceService.EXPECT().Get(gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, _ int64) (*models.Namespace, error) { -// return nil, fmt.Errorf("test") -// }).Times(1) - -// daoMockNamespaceServiceFactory := daomock.NewMockNamespaceServiceFactory(ctrl) -// daoMockNamespaceServiceFactory.EXPECT().New(gomock.Any()).DoAndReturn(func(txs ...*query.Query) dao.NamespaceService { -// return daoMockNamespaceService -// }).Times(1) - -// namespaceHandler = handlerNew(inject{namespaceServiceFactory: daoMockNamespaceServiceFactory}) - -// req = httptest.NewRequest(http.MethodDelete, "/", nil) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec = httptest.NewRecorder() -// c = e.NewContext(req, rec) -// c.SetParamNames("id") -// c.SetParamValues(strconv.FormatUint(3, 10)) -// err = namespaceHandler.GetNamespace(c) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusInternalServerError, c.Response().Status) -// } +import ( + "bytes" + "context" + "fmt" + "net/http" + "net/http/httptest" + "strconv" + "testing" + + "github.com/labstack/echo/v4" + "github.com/stretchr/testify/assert" + "github.com/tidwall/gjson" + "go.uber.org/mock/gomock" + + "github.com/go-sigma/sigma/pkg/consts" + "github.com/go-sigma/sigma/pkg/dal" + "github.com/go-sigma/sigma/pkg/dal/dao" + daomock "github.com/go-sigma/sigma/pkg/dal/dao/mocks" + "github.com/go-sigma/sigma/pkg/dal/models" + "github.com/go-sigma/sigma/pkg/dal/query" + "github.com/go-sigma/sigma/pkg/logger" + "github.com/go-sigma/sigma/pkg/modules/workq/definition" + workqmocks "github.com/go-sigma/sigma/pkg/modules/workq/definition/mocks" + "github.com/go-sigma/sigma/pkg/tests" + "github.com/go-sigma/sigma/pkg/utils/ptr" + "github.com/go-sigma/sigma/pkg/validators" +) + +func TestGetNamespace(t *testing.T) { + logger.SetLevel("debug") + e := echo.New() + validators.Initialize(e) + assert.NoError(t, tests.Initialize(t)) + assert.NoError(t, tests.DB.Init()) + defer func() { + conn, err := dal.DB.DB() + assert.NoError(t, err) + assert.NoError(t, conn.Close()) + assert.NoError(t, tests.DB.DeInit()) + }() + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + workQueueProducer := workqmocks.NewMockWorkQueueProducer(ctrl) + workQueueProducer.EXPECT().Produce(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, topic string, payload any, option definition.ProducerOption) error { + return nil + }).Times(1) + + namespaceHandler := handlerNew(inject{producerClient: workQueueProducer}) + + userServiceFactory := dao.NewUserServiceFactory() + userService := userServiceFactory.New() + + ctx := context.Background() + userObj := &models.User{Username: "list-namespace", Password: ptr.Of("test"), Email: ptr.Of("test@gmail.com")} + err := userService.Create(ctx, userObj) + assert.NoError(t, err) + + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`{"name":"test","description":""}`)) + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + c.Set(consts.ContextUser, userObj) + err = namespaceHandler.PostNamespace(c) + assert.NoError(t, err) + assert.Equal(t, http.StatusCreated, c.Response().Status) + bytes := rec.Body.Bytes() + resultID := gjson.GetBytes(bytes, "id").Int() + + req = httptest.NewRequest(http.MethodDelete, "/", nil) + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + rec = httptest.NewRecorder() + c = e.NewContext(req, rec) + c.Set(consts.ContextUser, userObj) + c.SetParamNames("id") + c.SetParamValues(strconv.FormatInt(resultID, 10)) + err = namespaceHandler.GetNamespace(c) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, c.Response().Status) + + req = httptest.NewRequest(http.MethodDelete, "/", nil) + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + rec = httptest.NewRecorder() + c = e.NewContext(req, rec) + c.Set(consts.ContextUser, userObj) + err = namespaceHandler.GetNamespace(c) + assert.NoError(t, err) + assert.Equal(t, http.StatusBadRequest, c.Response().Status) + + req = httptest.NewRequest(http.MethodDelete, "/", nil) + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + rec = httptest.NewRecorder() + c = e.NewContext(req, rec) + c.Set(consts.ContextUser, userObj) + c.SetParamNames("id") + c.SetParamValues(strconv.FormatUint(3, 10)) + err = namespaceHandler.GetNamespace(c) + assert.NoError(t, err) + assert.Equal(t, http.StatusNotFound, c.Response().Status) + + daoMockNamespaceService := daomock.NewMockNamespaceService(ctrl) + daoMockNamespaceService.EXPECT().Get(gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, _ int64) (*models.Namespace, error) { + return nil, fmt.Errorf("test") + }).Times(1) + + daoMockNamespaceServiceFactory := daomock.NewMockNamespaceServiceFactory(ctrl) + daoMockNamespaceServiceFactory.EXPECT().New(gomock.Any()).DoAndReturn(func(txs ...*query.Query) dao.NamespaceService { + return daoMockNamespaceService + }).Times(1) + + namespaceHandler = handlerNew(inject{namespaceServiceFactory: daoMockNamespaceServiceFactory}) + + req = httptest.NewRequest(http.MethodDelete, "/", nil) + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + rec = httptest.NewRecorder() + c = e.NewContext(req, rec) + c.Set(consts.ContextUser, userObj) + c.SetParamNames("id") + c.SetParamValues(strconv.FormatUint(3, 10)) + err = namespaceHandler.GetNamespace(c) + assert.NoError(t, err) + assert.Equal(t, http.StatusInternalServerError, c.Response().Status) +} diff --git a/pkg/handlers/namespaces/namespaces_members_add.go b/pkg/handlers/namespaces/namespaces_members_add.go index ef15986c..c978b82a 100644 --- a/pkg/handlers/namespaces/namespaces_members_add.go +++ b/pkg/handlers/namespaces/namespaces_members_add.go @@ -67,7 +67,16 @@ func (h *handler) AddNamespaceMember(c echo.Context) error { return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeBadRequest, err.Error()) } - if !h.authServiceFactory.New().Namespace(c, req.NamespaceID, enums.AuthAdmin) { + authChecked, err := h.authServiceFactory.New().Namespace(c, req.NamespaceID, enums.AuthAdmin) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(err).Int64("id", req.NamespaceID).Msg("Namespace not found") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeNotFound, fmt.Sprintf("Namespace(%d) not found", req.NamespaceID)) + } + log.Error().Err(err).Msg("Get namespace failed") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeInternalError, fmt.Sprintf("Get namespace(%d) failed", req.NamespaceID)) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("NamespaceID", req.NamespaceID).Msg("Auth check failed") return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") } diff --git a/pkg/handlers/namespaces/namespaces_update.go b/pkg/handlers/namespaces/namespaces_update.go index bec29533..f8b492e2 100644 --- a/pkg/handlers/namespaces/namespaces_update.go +++ b/pkg/handlers/namespaces/namespaces_update.go @@ -65,7 +65,16 @@ func (h *handler) PutNamespace(c echo.Context) error { return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeBadRequest, fmt.Sprintf("Bind and validate request body failed: %v", err)) } - if !h.authServiceFactory.New().Namespace(c, req.ID, enums.AuthManage) { + authChecked, err := h.authServiceFactory.New().Namespace(c, req.ID, enums.AuthAdmin) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Int64("NamespaceID", req.ID).Msg("Resource not found") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeNotFound, utils.UnwrapJoinedErrors(err)) + } + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Int64("NamespaceID", req.ID).Err(err).Msg("Get resource failed") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeInternalError, utils.UnwrapJoinedErrors(err)) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("NamespaceID", req.ID).Msg("Auth check failed") return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") } diff --git a/pkg/handlers/namespaces/namespaces_update_test.go b/pkg/handlers/namespaces/namespaces_update_test.go index dd792478..9c15bc06 100644 --- a/pkg/handlers/namespaces/namespaces_update_test.go +++ b/pkg/handlers/namespaces/namespaces_update_test.go @@ -14,204 +14,231 @@ package namespaces -// import ( -// "bytes" -// "context" -// "fmt" -// "net/http" -// "net/http/httptest" -// "strconv" -// "testing" - -// "github.com/labstack/echo/v4" -// "github.com/stretchr/testify/assert" -// "github.com/tidwall/gjson" -// "go.uber.org/mock/gomock" - -// "github.com/go-sigma/sigma/pkg/consts" -// "github.com/go-sigma/sigma/pkg/dal" -// "github.com/go-sigma/sigma/pkg/dal/dao" -// daomock "github.com/go-sigma/sigma/pkg/dal/dao/mocks" -// "github.com/go-sigma/sigma/pkg/dal/models" -// "github.com/go-sigma/sigma/pkg/dal/query" -// "github.com/go-sigma/sigma/pkg/logger" -// "github.com/go-sigma/sigma/pkg/tests" -// "github.com/go-sigma/sigma/pkg/utils/ptr" -// "github.com/go-sigma/sigma/pkg/validators" -// ) - -// func TestPutNamespace(t *testing.T) { -// logger.SetLevel("debug") -// e := echo.New() -// validators.Initialize(e) -// err := tests.Initialize(t) -// assert.NoError(t, err) -// err = tests.DB.Init() -// assert.NoError(t, err) -// defer func() { -// conn, err := dal.DB.DB() -// assert.NoError(t, err) -// err = conn.Close() -// assert.NoError(t, err) -// err = tests.DB.DeInit() -// assert.NoError(t, err) -// }() - -// namespaceHandler := handlerNew() - -// userServiceFactory := dao.NewUserServiceFactory() -// userService := userServiceFactory.New() - -// ctx := context.Background() -// userObj := &models.User{Username: "put-namespace", Password: ptr.Of("test"), Email: ptr.Of("test@gmail.com")} -// err = userService.Create(ctx, userObj) -// assert.NoError(t, err) - -// req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`{"name":"test","size_limit":10}`)) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec := httptest.NewRecorder() -// c := e.NewContext(req, rec) -// c.Set(consts.ContextUser, userObj) -// err = namespaceHandler.PostNamespace(c) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusCreated, c.Response().Status) -// resultID := gjson.GetBytes(rec.Body.Bytes(), "id").Int() - -// req = httptest.NewRequest(http.MethodPut, "/", bytes.NewBufferString(`{"description":"test"}`)) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec = httptest.NewRecorder() -// c = e.NewContext(req, rec) -// c.Set(consts.ContextUser, userObj) -// c.SetParamNames("id") -// c.SetParamValues(strconv.FormatInt(resultID, 10)) -// err = namespaceHandler.PutNamespace(c) -// assert.NoError(t, err) -// fmt.Println(rec.Body.String()) -// assert.Equal(t, http.StatusNoContent, c.Response().Status) - -// req = httptest.NewRequest(http.MethodPut, "/", bytes.NewBufferString(`{"size_limit":101}`)) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec = httptest.NewRecorder() -// c = e.NewContext(req, rec) -// c.Set(consts.ContextUser, userObj) -// c.SetParamNames("id") -// c.SetParamValues(strconv.FormatInt(resultID, 10)) -// err = namespaceHandler.PutNamespace(c) -// assert.NoError(t, err) -// fmt.Println(rec.Body.String()) -// assert.Equal(t, http.StatusNoContent, c.Response().Status) - -// req = httptest.NewRequest(http.MethodPut, "/", bytes.NewBufferString(`{"visibility":"test"}`)) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec = httptest.NewRecorder() -// c = e.NewContext(req, rec) -// c.Set(consts.ContextUser, userObj) -// c.SetParamNames("id") -// c.SetParamValues(strconv.FormatInt(resultID, 10)) -// err = namespaceHandler.PutNamespace(c) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusBadRequest, c.Response().Status) - -// req = httptest.NewRequest(http.MethodPut, "/", bytes.NewBufferString(`{"size_limit":1}`)) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec = httptest.NewRecorder() -// c = e.NewContext(req, rec) -// c.Set(consts.ContextUser, userObj) -// c.SetParamNames("id") -// c.SetParamValues(strconv.FormatInt(resultID, 10)) -// err = namespaceHandler.PutNamespace(c) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusBadRequest, c.Response().Status) - -// req = httptest.NewRequest(http.MethodPut, "/", bytes.NewBufferString(`{"description":"test"}`)) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec = httptest.NewRecorder() -// c = e.NewContext(req, rec) -// c.Set(consts.ContextUser, userObj) -// c.SetParamNames("id") -// c.SetParamValues(strconv.FormatUint(3, 10)) -// err = namespaceHandler.PutNamespace(c) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusNotFound, c.Response().Status) - -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() - -// daoMockNamespaceService := daomock.NewMockNamespaceService(ctrl) -// daoMockNamespaceService.EXPECT().UpdateByID(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, _ int64, _ map[string]any) error { -// return fmt.Errorf("test") -// }).Times(1) -// daoMockNamespaceService.EXPECT().Get(gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, _ int64) (*models.Namespace, error) { -// return &models.Namespace{Name: "test", SizeLimit: 100}, nil -// }).Times(1) - -// daoMockNamespaceServiceFactory := daomock.NewMockNamespaceServiceFactory(ctrl) -// daoMockNamespaceServiceFactory.EXPECT().New(gomock.Any()).DoAndReturn(func(txs ...*query.Query) dao.NamespaceService { -// return daoMockNamespaceService -// }).Times(2) - -// namespaceHandler = handlerNew(inject{namespaceServiceFactory: daoMockNamespaceServiceFactory}) - -// req = httptest.NewRequest(http.MethodPut, "/", bytes.NewBufferString(`{"description":"test"}`)) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec = httptest.NewRecorder() -// c = e.NewContext(req, rec) -// c.Set(consts.ContextUser, userObj) -// c.SetParamNames("id") -// c.SetParamValues(strconv.FormatUint(3, 10)) -// err = namespaceHandler.PutNamespace(c) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusInternalServerError, c.Response().Status) -// } - -// func TestPutNamespaceFailed1(t *testing.T) { -// logger.SetLevel("debug") -// e := echo.New() -// validators.Initialize(e) -// err := tests.Initialize(t) -// assert.NoError(t, err) -// err = tests.DB.Init() -// assert.NoError(t, err) -// defer func() { -// conn, err := dal.DB.DB() -// assert.NoError(t, err) -// err = conn.Close() -// assert.NoError(t, err) -// err = tests.DB.DeInit() -// assert.NoError(t, err) -// }() - -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() - -// userServiceFactory := dao.NewUserServiceFactory() -// userService := userServiceFactory.New() - -// ctx := context.Background() -// userObj := &models.User{Username: "put-namespace", Password: ptr.Of("test"), Email: ptr.Of("test@gmail.com")} -// err = userService.Create(ctx, userObj) -// assert.NoError(t, err) - -// daoMockNamespaceService := daomock.NewMockNamespaceService(ctrl) -// daoMockNamespaceService.EXPECT().Get(gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, _ int64) (*models.Namespace, error) { -// return nil, fmt.Errorf("test") -// }).Times(1) - -// daoMockNamespaceServiceFactory := daomock.NewMockNamespaceServiceFactory(ctrl) -// daoMockNamespaceServiceFactory.EXPECT().New(gomock.Any()).DoAndReturn(func(txs ...*query.Query) dao.NamespaceService { -// return daoMockNamespaceService -// }).Times(1) - -// namespaceHandler := handlerNew(inject{namespaceServiceFactory: daoMockNamespaceServiceFactory}) - -// req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`{"limit":10}`)) -// req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) -// rec := httptest.NewRecorder() -// c := e.NewContext(req, rec) -// c.Set(consts.ContextUser, userObj) -// c.SetParamNames("id") -// c.SetParamValues(strconv.FormatUint(3, 10)) -// err = namespaceHandler.PutNamespace(c) -// assert.NoError(t, err) -// assert.Equal(t, http.StatusInternalServerError, c.Response().Status) -// } +import ( + "bytes" + "context" + "fmt" + "net/http" + "net/http/httptest" + "strconv" + "testing" + + "github.com/labstack/echo/v4" + "github.com/stretchr/testify/assert" + "github.com/tidwall/gjson" + "go.uber.org/mock/gomock" + + "github.com/go-sigma/sigma/pkg/auth" + authmocks "github.com/go-sigma/sigma/pkg/auth/mocks" + "github.com/go-sigma/sigma/pkg/consts" + "github.com/go-sigma/sigma/pkg/dal" + "github.com/go-sigma/sigma/pkg/dal/dao" + daomock "github.com/go-sigma/sigma/pkg/dal/dao/mocks" + "github.com/go-sigma/sigma/pkg/dal/models" + "github.com/go-sigma/sigma/pkg/dal/query" + "github.com/go-sigma/sigma/pkg/logger" + "github.com/go-sigma/sigma/pkg/modules/workq/definition" + workqmocks "github.com/go-sigma/sigma/pkg/modules/workq/definition/mocks" + "github.com/go-sigma/sigma/pkg/tests" + "github.com/go-sigma/sigma/pkg/types/enums" + "github.com/go-sigma/sigma/pkg/utils/ptr" + "github.com/go-sigma/sigma/pkg/validators" +) + +func TestPutNamespace(t *testing.T) { + logger.SetLevel("debug") + e := echo.New() + validators.Initialize(e) + assert.NoError(t, tests.Initialize(t)) + assert.NoError(t, tests.DB.Init()) + defer func() { + conn, err := dal.DB.DB() + assert.NoError(t, err) + assert.NoError(t, conn.Close()) + assert.NoError(t, tests.DB.DeInit()) + }() + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + workQueueProducer := workqmocks.NewMockWorkQueueProducer(ctrl) + workQueueProducer.EXPECT().Produce(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, topic string, payload any, option definition.ProducerOption) error { + return nil + }).Times(1) + + namespaceHandler := handlerNew(inject{producerClient: workQueueProducer}) + + userServiceFactory := dao.NewUserServiceFactory() + userService := userServiceFactory.New() + + ctx := context.Background() + userObj := &models.User{Username: "put-namespace", Password: ptr.Of("test"), Email: ptr.Of("test@gmail.com")} + err := userService.Create(ctx, userObj) + assert.NoError(t, err) + + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`{"name":"test","size_limit":10}`)) + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + c.Set(consts.ContextUser, userObj) + err = namespaceHandler.PostNamespace(c) + assert.NoError(t, err) + assert.Equal(t, http.StatusCreated, c.Response().Status) + resultID := gjson.GetBytes(rec.Body.Bytes(), "id").Int() + + req = httptest.NewRequest(http.MethodPut, "/", bytes.NewBufferString(`{"description":"test"}`)) + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + rec = httptest.NewRecorder() + c = e.NewContext(req, rec) + c.Set(consts.ContextUser, userObj) + c.SetParamNames("id") + c.SetParamValues(strconv.FormatInt(resultID, 10)) + err = namespaceHandler.PutNamespace(c) + assert.NoError(t, err) + fmt.Println(rec.Body.String()) + assert.Equal(t, http.StatusNoContent, c.Response().Status) + + req = httptest.NewRequest(http.MethodPut, "/", bytes.NewBufferString(`{"size_limit":101}`)) + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + rec = httptest.NewRecorder() + c = e.NewContext(req, rec) + c.Set(consts.ContextUser, userObj) + c.SetParamNames("id") + c.SetParamValues(strconv.FormatInt(resultID, 10)) + err = namespaceHandler.PutNamespace(c) + assert.NoError(t, err) + fmt.Println(rec.Body.String()) + assert.Equal(t, http.StatusNoContent, c.Response().Status) + + req = httptest.NewRequest(http.MethodPut, "/", bytes.NewBufferString(`{"visibility":"test"}`)) + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + rec = httptest.NewRecorder() + c = e.NewContext(req, rec) + c.Set(consts.ContextUser, userObj) + c.SetParamNames("id") + c.SetParamValues(strconv.FormatInt(resultID, 10)) + err = namespaceHandler.PutNamespace(c) + assert.NoError(t, err) + assert.Equal(t, http.StatusBadRequest, c.Response().Status) + + req = httptest.NewRequest(http.MethodPut, "/", bytes.NewBufferString(`{"size_limit":1}`)) + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + rec = httptest.NewRecorder() + c = e.NewContext(req, rec) + c.Set(consts.ContextUser, userObj) + c.SetParamNames("id") + c.SetParamValues(strconv.FormatInt(resultID, 10)) + err = namespaceHandler.PutNamespace(c) + assert.NoError(t, err) + assert.Equal(t, http.StatusBadRequest, c.Response().Status) + + req = httptest.NewRequest(http.MethodPut, "/", bytes.NewBufferString(`{"description":"test"}`)) + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + rec = httptest.NewRecorder() + c = e.NewContext(req, rec) + c.Set(consts.ContextUser, userObj) + c.SetParamNames("id") + c.SetParamValues(strconv.FormatUint(3, 10)) + err = namespaceHandler.PutNamespace(c) + assert.NoError(t, err) + assert.Equal(t, http.StatusNotFound, c.Response().Status) + + daoMockNamespaceService := daomock.NewMockNamespaceService(ctrl) + daoMockNamespaceService.EXPECT().UpdateByID(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, _ int64, _ map[string]any) error { + return fmt.Errorf("test") + }).Times(1) + daoMockNamespaceService.EXPECT().Get(gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, _ int64) (*models.Namespace, error) { + return &models.Namespace{Name: "test", SizeLimit: 100}, nil + }).Times(1) + + daoMockNamespaceServiceFactory := daomock.NewMockNamespaceServiceFactory(ctrl) + daoMockNamespaceServiceFactory.EXPECT().New(gomock.Any()).DoAndReturn(func(txs ...*query.Query) dao.NamespaceService { + return daoMockNamespaceService + }).Times(2) + + authService := authmocks.NewMockService(ctrl) + authService.EXPECT().Namespace(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(c echo.Context, namespaceID int64, auth enums.Auth) (bool, error) { + return true, nil + }).Times(1) + + authServiceFactory := authmocks.NewMockServiceFactory(ctrl) + authServiceFactory.EXPECT().New().DoAndReturn(func() auth.Service { + return authService + }).Times(1) + + namespaceHandler = handlerNew(inject{namespaceServiceFactory: daoMockNamespaceServiceFactory, authServiceFactory: authServiceFactory}) + + req = httptest.NewRequest(http.MethodPut, "/", bytes.NewBufferString(`{"description":"test"}`)) + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + rec = httptest.NewRecorder() + c = e.NewContext(req, rec) + c.Set(consts.ContextUser, userObj) + c.SetParamNames("id") + c.SetParamValues(strconv.FormatUint(3, 10)) + err = namespaceHandler.PutNamespace(c) + assert.NoError(t, err) + assert.Equal(t, http.StatusInternalServerError, c.Response().Status) +} + +func TestPutNamespaceFailed1(t *testing.T) { + logger.SetLevel("debug") + e := echo.New() + validators.Initialize(e) + assert.NoError(t, tests.Initialize(t)) + assert.NoError(t, tests.DB.Init()) + defer func() { + conn, err := dal.DB.DB() + assert.NoError(t, err) + assert.NoError(t, conn.Close()) + assert.NoError(t, tests.DB.DeInit()) + }() + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // workQueueProducer := workqmocks.NewMockWorkQueueProducer(ctrl) + // workQueueProducer.EXPECT().Produce(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, topic string, payload any, option definition.ProducerOption) error { + // return nil + // }).Times(1) + + userServiceFactory := dao.NewUserServiceFactory() + userService := userServiceFactory.New() + + ctx := context.Background() + userObj := &models.User{Username: "put-namespace", Password: ptr.Of("test"), Email: ptr.Of("test@gmail.com")} + err := userService.Create(ctx, userObj) + assert.NoError(t, err) + + daoMockNamespaceService := daomock.NewMockNamespaceService(ctrl) + daoMockNamespaceService.EXPECT().Get(gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, _ int64) (*models.Namespace, error) { + return nil, fmt.Errorf("test") + }).Times(1) + + daoMockNamespaceServiceFactory := daomock.NewMockNamespaceServiceFactory(ctrl) + daoMockNamespaceServiceFactory.EXPECT().New(gomock.Any()).DoAndReturn(func(txs ...*query.Query) dao.NamespaceService { + return daoMockNamespaceService + }).Times(1) + + authService := authmocks.NewMockService(ctrl) + authService.EXPECT().Namespace(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(c echo.Context, namespaceID int64, auth enums.Auth) (bool, error) { + return true, nil + }).Times(1) + + authServiceFactory := authmocks.NewMockServiceFactory(ctrl) + authServiceFactory.EXPECT().New().DoAndReturn(func() auth.Service { + return authService + }).Times(1) + + namespaceHandler := handlerNew(inject{namespaceServiceFactory: daoMockNamespaceServiceFactory, authServiceFactory: authServiceFactory}) + + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`{"limit":10}`)) + req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + c.Set(consts.ContextUser, userObj) + c.SetParamNames("id") + c.SetParamValues(strconv.FormatUint(3, 10)) + err = namespaceHandler.PutNamespace(c) + assert.NoError(t, err) + assert.Equal(t, http.StatusInternalServerError, c.Response().Status) +} diff --git a/pkg/handlers/repositories/repositories_create.go b/pkg/handlers/repositories/repositories_create.go index 24a01323..05933ca1 100644 --- a/pkg/handlers/repositories/repositories_create.go +++ b/pkg/handlers/repositories/repositories_create.go @@ -69,9 +69,18 @@ func (h *handler) CreateRepository(c echo.Context) error { return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeBadRequest, err.Error()) } - if !h.authServiceFactory.New().Namespace(c, req.NamespaceID, enums.AuthManage) { + authChecked, err := h.authServiceFactory.New().Namespace(c, req.NamespaceID, enums.AuthManage) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Int64("NamespaceID", req.NamespaceID).Msg("Resource not found") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeNotFound, utils.UnwrapJoinedErrors(err)) + } + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Int64("NamespaceID", req.NamespaceID).Msg("Get resource failed") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeInternalError, utils.UnwrapJoinedErrors(err)) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("NamespaceID", req.NamespaceID).Msg("Auth check failed") - return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api or resource") } namespaceService := h.namespaceServiceFactory.New() diff --git a/pkg/handlers/repositories/repositories_delete.go b/pkg/handlers/repositories/repositories_delete.go index 8ffac119..59cf5689 100644 --- a/pkg/handlers/repositories/repositories_delete.go +++ b/pkg/handlers/repositories/repositories_delete.go @@ -66,9 +66,18 @@ func (h *handler) DeleteRepository(c echo.Context) error { return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeBadRequest, err.Error()) } - if !h.authServiceFactory.New().Repository(c, req.ID, enums.AuthManage) { - log.Error().Int64("UserID", user.ID).Int64("NamespaceID", req.ID).Msg("Auth check failed") - return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") + authChecked, err := h.authServiceFactory.New().Repository(c, req.ID, enums.AuthManage) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Int64("NamespaceID", req.NamespaceID).Int64("RepositoryID", req.ID).Msg("Resource not found") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeNotFound, utils.UnwrapJoinedErrors(err)) + } + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Int64("NamespaceID", req.NamespaceID).Int64("RepositoryID", req.ID).Msg("Get resource failed") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeInternalError, utils.UnwrapJoinedErrors(err)) + } + if !authChecked { + log.Error().Int64("UserID", user.ID).Int64("NamespaceID", req.NamespaceID).Int64("RepositoryID", req.ID).Msg("Auth check failed") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api or resource") } namespaceService := h.namespaceServiceFactory.New() diff --git a/pkg/handlers/repositories/repositories_get.go b/pkg/handlers/repositories/repositories_get.go index 0f49a52d..7b08be11 100644 --- a/pkg/handlers/repositories/repositories_get.go +++ b/pkg/handlers/repositories/repositories_get.go @@ -15,6 +15,7 @@ package repositories import ( + "errors" "fmt" "net/http" "strings" @@ -67,9 +68,18 @@ func (h *handler) GetRepository(c echo.Context) error { return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeBadRequest, fmt.Sprintf("Bind and validate request body failed: %v", err)) } - if !h.authServiceFactory.New().Repository(c, req.ID, enums.AuthRead) { - log.Error().Int64("UserID", user.ID).Int64("NamespaceID", req.ID).Msg("Auth check failed") - return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") + authChecked, err := h.authServiceFactory.New().Repository(c, req.ID, enums.AuthRead) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Int64("NamespaceID", req.NamespaceID).Int64("RepositoryID", req.ID).Msg("Resource not found") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeNotFound, utils.UnwrapJoinedErrors(err)) + } + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Int64("NamespaceID", req.NamespaceID).Int64("RepositoryID", req.ID).Msg("Get resource failed") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeInternalError, utils.UnwrapJoinedErrors(err)) + } + if !authChecked { + log.Error().Int64("UserID", user.ID).Int64("NamespaceID", req.NamespaceID).Int64("RepositoryID", req.ID).Msg("Auth check failed") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api or resource") } repositoryService := h.repositoryServiceFactory.New() diff --git a/pkg/handlers/repositories/repositories_update.go b/pkg/handlers/repositories/repositories_update.go index ce9219e1..e9894c7c 100644 --- a/pkg/handlers/repositories/repositories_update.go +++ b/pkg/handlers/repositories/repositories_update.go @@ -68,9 +68,18 @@ func (h *handler) UpdateRepository(c echo.Context) error { return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeBadRequest, err.Error()) } - if !h.authServiceFactory.New().Repository(c, req.ID, enums.AuthManage) { - log.Error().Int64("UserID", user.ID).Int64("NamespaceID", req.NamespaceID).Msg("Auth check failed") - return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") + authChecked, err := h.authServiceFactory.New().Repository(c, req.ID, enums.AuthManage) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Int64("NamespaceID", req.NamespaceID).Int64("RepositoryID", req.ID).Msg("Resource not found") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeNotFound, utils.UnwrapJoinedErrors(err)) + } + log.Error().Err(errors.New(utils.UnwrapJoinedErrors(err))).Int64("NamespaceID", req.NamespaceID).Int64("RepositoryID", req.ID).Msg("Get resource failed") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeInternalError, utils.UnwrapJoinedErrors(err)) + } + if !authChecked { + log.Error().Int64("UserID", user.ID).Int64("NamespaceID", req.NamespaceID).Int64("RepositoryID", req.ID).Msg("Auth check failed") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api or resource") } namespaceService := h.namespaceServiceFactory.New() diff --git a/pkg/handlers/tags/tags_delete.go b/pkg/handlers/tags/tags_delete.go index 1ac1a0ad..2b4a310e 100644 --- a/pkg/handlers/tags/tags_delete.go +++ b/pkg/handlers/tags/tags_delete.go @@ -66,7 +66,16 @@ func (h *handler) DeleteTag(c echo.Context) error { return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeBadRequest, err.Error()) } - if !h.authServiceFactory.New().Repository(c, req.ID, enums.AuthRead) { + authChecked, err := h.authServiceFactory.New().Repository(c, req.ID, enums.AuthRead) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(err).Int64("NamespaceID", req.NamespaceID).Msg("Namespace not found") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeNotFound, fmt.Sprintf("Namespace(%d) not found: %v", req.NamespaceID, err)) + } + log.Error().Err(err).Int64("NamespaceID", req.NamespaceID).Msg("Namespace find failed") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeInternalError, fmt.Sprintf("Namespace(%d) find failed: %v", req.NamespaceID, err)) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("RepositoryID", req.ID).Msg("Auth check failed") return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") } diff --git a/pkg/handlers/tags/tags_get.go b/pkg/handlers/tags/tags_get.go index b1e3acef..a3aa077b 100644 --- a/pkg/handlers/tags/tags_get.go +++ b/pkg/handlers/tags/tags_get.go @@ -15,6 +15,8 @@ package tag import ( + "errors" + "fmt" "time" "github.com/labstack/echo/v4" @@ -64,7 +66,16 @@ func (h *handler) GetTag(c echo.Context) error { return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeBadRequest, err.Error()) } - if !h.authServiceFactory.New().Repository(c, req.ID, enums.AuthRead) { + authChecked, err := h.authServiceFactory.New().Repository(c, req.ID, enums.AuthRead) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(err).Int64("NamespaceID", req.NamespaceID).Msg("Namespace not found") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeNotFound, fmt.Sprintf("Namespace(%d) not found: %v", req.NamespaceID, err)) + } + log.Error().Err(err).Int64("NamespaceID", req.NamespaceID).Msg("Namespace find failed") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeInternalError, fmt.Sprintf("Namespace(%d) find failed: %v", req.NamespaceID, err)) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("RepositoryID", req.ID).Msg("Auth check failed") return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") } diff --git a/pkg/handlers/tags/tags_list.go b/pkg/handlers/tags/tags_list.go index 0e60a2c0..42ccfe22 100644 --- a/pkg/handlers/tags/tags_list.go +++ b/pkg/handlers/tags/tags_list.go @@ -73,7 +73,16 @@ func (h *handler) ListTag(c echo.Context) error { return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeBadRequest, err.Error()) } - if !h.authServiceFactory.New().Repository(c, req.RepositoryID, enums.AuthRead) { + authChecked, err := h.authServiceFactory.New().Repository(c, req.RepositoryID, enums.AuthRead) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Error().Err(err).Int64("NamespaceID", req.NamespaceID).Msg("Namespace not found") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeNotFound, fmt.Sprintf("Namespace(%d) not found: %v", req.NamespaceID, err)) + } + log.Error().Err(err).Int64("NamespaceID", req.NamespaceID).Msg("Namespace find failed") + return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeInternalError, fmt.Sprintf("Namespace(%d) find failed: %v", req.NamespaceID, err)) + } + if !authChecked { log.Error().Int64("UserID", user.ID).Int64("RepositoryID", req.RepositoryID).Msg("Auth check failed") return xerrors.NewHTTPError(c, xerrors.HTTPErrCodeUnauthorized, "No permission with this api") } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 565e04d8..55d614fa 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -177,3 +177,17 @@ func StringsJoin[T stringsJoin](strs []T, sep string) string { } return strings.Join(b, sep) } + +// UnwrapJoinedErrors ... +func UnwrapJoinedErrors(err error) string { + e, ok := err.(interface{ Unwrap() []error }) + if !ok { + return err.Error() + } + es := e.Unwrap() + var ss = make([]string, len(es)) + for index, e := range es { + ss[index] = e.Error() + } + return strings.Join(ss, ": ") +} diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index 102b8bed..8e46d637 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -16,6 +16,7 @@ package utils import ( "bytes" + "errors" "fmt" "net/http" "net/http/httptest" @@ -366,3 +367,36 @@ func TestStringsJoin(t *testing.T) { }) } } + +func TestUnwrapJoinedErrors(t *testing.T) { + type args struct { + err error + } + tests := []struct { + name string + args args + want string + }{ + { + name: "normal", + args: args{ + err: fmt.Errorf("normal error"), + }, + want: "normal error", + }, + { + name: "joined", + args: args{ + err: errors.Join(fmt.Errorf("normal error"), fmt.Errorf("normal error2")), + }, + want: "normal error: normal error2", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := UnwrapJoinedErrors(tt.args.err); got != tt.want { + t.Errorf("UnwrapJoinedErrors() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/xerrors/distribution.go b/pkg/xerrors/distribution.go index 2cdddc6d..a2c91587 100644 --- a/pkg/xerrors/distribution.go +++ b/pkg/xerrors/distribution.go @@ -319,3 +319,14 @@ func GenDSErrCodeResourceCountQuotaExceedNamespaceTag(name string, limit int64) } return c } + +// GenDSErrCodeResourceNotFound ... +func GenDSErrCodeResourceNotFound(err error) ErrCode { + c := ErrCode{ + Code: "RESOURCE_NOT_FOUND", + Title: fmt.Sprintf("%v", err), + Description: `The request resource is not found.`, + HTTPStatusCode: http.StatusNotFound, + } + return c +}