Skip to content

Commit

Permalink
fix: Repository udpate operation checks
Browse files Browse the repository at this point in the history
Signed-off-by: jay-dee7 <[email protected]>
  • Loading branch information
jay-dee7 committed Dec 10, 2023
1 parent ddf3030 commit e58ab04
Show file tree
Hide file tree
Showing 17 changed files with 257 additions and 232 deletions.
67 changes: 17 additions & 50 deletions auth/reset_password.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package auth

import (
"encoding/json"
"errors"
"fmt"
"net/http"
"time"

"github.com/containerish/OpenRegistry/services/email"
v2_types "github.com/containerish/OpenRegistry/store/v1/types"
"github.com/containerish/OpenRegistry/types"
"github.com/containerish/OpenRegistry/store/v1/types"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"github.com/jackc/pgx/v4"
Expand Down Expand Up @@ -41,8 +39,8 @@ func (a *auth) ResetForgottenPassword(ctx echo.Context) error {
return echoErr
}

var pwd *types.Password
if err := json.NewDecoder(ctx.Request().Body).Decode(&pwd); err != nil {
var body types.PasswordResetRequest
if err := ctx.Bind(&body); err != nil {
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
"message": "request body could not be decoded",
Expand Down Expand Up @@ -72,7 +70,7 @@ func (a *auth) ResetForgottenPassword(ctx echo.Context) error {
return echoErr
}

if err = v2_types.ValidatePassword(pwd.NewPassword); err != nil {
if err = types.ValidatePassword(body.NewPassword); err != nil {
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
"message": `password must be alphanumeric, at least 8 chars long, must have at least one special character
Expand All @@ -82,7 +80,7 @@ and an uppercase letter`,
return echoErr
}

if a.verifyPassword(user.Password, pwd.NewPassword) {
if a.verifyPassword(user.Password, body.NewPassword) {
err = fmt.Errorf("new password can not be same as old password")
// error is already user friendly
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
Expand All @@ -93,7 +91,7 @@ and an uppercase letter`,
return echoErr
}

hashPassword, err := a.hashPassword(pwd.NewPassword)
hashPassword, err := a.hashPassword(body.NewPassword)
if err != nil {
echoErr := ctx.JSON(http.StatusInternalServerError, echo.Map{
"error": err.Error(),
Expand Down Expand Up @@ -122,30 +120,19 @@ and an uppercase letter`,
func (a *auth) ResetPassword(ctx echo.Context) error {
ctx.Set(types.HandlerStartTime, time.Now())

token, ok := ctx.Get("user").(*jwt.Token)
user, ok := ctx.Get(string(types.UserContextKey)).(*types.User)
if !ok {
err := fmt.Errorf("ERR_EMPTY_TOKEN")
echoErr := ctx.JSON(http.StatusUnauthorized, echo.Map{
"error": err.Error(),
"message": "JWT token can not be empty",
})
a.logger.Log(ctx, err).Send()
return echoErr
}

c, ok := token.Claims.(*Claims)
if !ok {
err := fmt.Errorf("ERR_INVALID_CLAIMS")
errMsg := fmt.Errorf("ERR_MISSING_AUTH_CREDENTIALS")
echoErr := ctx.JSON(http.StatusInternalServerError, echo.Map{
"error": err.Error(),
"message": "invalid claims in JWT",
"error": errMsg,
"message": "error updating new password",
})
a.logger.Log(ctx, err).Send()
a.logger.Log(ctx, errMsg).Send()
return echoErr
}

var pwd *types.Password
err := json.NewDecoder(ctx.Request().Body).Decode(&pwd)
var body types.PasswordResetRequest
err := ctx.Bind(&body)
if err != nil {
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
Expand All @@ -156,28 +143,8 @@ func (a *auth) ResetPassword(ctx echo.Context) error {
}
defer ctx.Request().Body.Close()

userId, err := uuid.Parse(c.ID)
if err != nil {
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
"message": "invalid user id format",
})
a.logger.Log(ctx, err).Send()
return echoErr
}

user, err := a.userStore.GetUserByID(ctx.Request().Context(), userId)
if err != nil {
echoErr := ctx.JSON(http.StatusNotFound, echo.Map{
"error": err.Error(),
"message": "error getting user by ID from DB",
})
a.logger.Log(ctx, err).Send()
return echoErr
}

// compare the current password with password hash from DB
if !a.verifyPassword(user.Password, pwd.OldPassword) {
if !a.verifyPassword(user.Password, body.OldPassword) {
err = fmt.Errorf("ERR_WRONG_PASSWORD")
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
Expand All @@ -187,7 +154,7 @@ func (a *auth) ResetPassword(ctx echo.Context) error {
return echoErr
}

hashPassword, err := a.hashPassword(pwd.NewPassword)
hashPassword, err := a.hashPassword(body.NewPassword)
if err != nil {
echoErr := ctx.JSON(http.StatusInternalServerError, echo.Map{
"error": err.Error(),
Expand All @@ -208,7 +175,7 @@ func (a *auth) ResetPassword(ctx echo.Context) error {
return echoErr
}

if err = v2_types.ValidatePassword(pwd.NewPassword); err != nil {
if err = types.ValidatePassword(body.NewPassword); err != nil {
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
"message": `password must be alphanumeric, at least 8 chars long, must have at least one special character
Expand All @@ -218,7 +185,7 @@ and an uppercase letter`,
return echoErr
}

if err = a.userStore.UpdateUserPWD(ctx.Request().Context(), userId, hashPassword); err != nil {
if err = a.userStore.UpdateUserPWD(ctx.Request().Context(), user.ID, hashPassword); err != nil {
echoErr := ctx.JSON(http.StatusInternalServerError, echo.Map{
"error": err.Error(),
"message": "error updating new password",
Expand Down
2 changes: 1 addition & 1 deletion auth/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"strings"
"time"

"github.com/containerish/OpenRegistry/types"
"github.com/containerish/OpenRegistry/store/v1/types"
"github.com/labstack/echo/v4"
)

Expand Down
18 changes: 17 additions & 1 deletion registry/v2/extensions/catalog_detail.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package extensions

import (
"context"
"fmt"
"net/http"
"strconv"
Expand Down Expand Up @@ -151,7 +152,22 @@ func (ext *extension) RepositoryDetail(ctx echo.Context) error {
offset = o
}

repository, err := ext.store.GetRepoDetail(ctx.Request().Context(), namespace, pageSize, offset)
if namespace == "" {
errMsg := fmt.Errorf("invalid request input, missing required field: ns")
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": errMsg.Error(),
})

ext.logger.Log(ctx, errMsg).Send()
return echoErr
}

requestContext := ctx.Request().Context()
if user, ok := ctx.Get(string(types.UserContextKey)).(*types.User); ok {
requestContext = context.WithValue(requestContext, types.UserContextKey, user)
}

repository, err := ext.store.GetRepoDetail(requestContext, namespace, pageSize, offset)
if err != nil {
echoErr := ctx.JSON(http.StatusInternalServerError, echo.Map{
"error": err.Error(),
Expand Down
34 changes: 27 additions & 7 deletions registry/v2/extensions/private_images.go
Original file line number Diff line number Diff line change
@@ -1,34 +1,54 @@
package extensions

import (
"encoding/json"
"net/http"
"time"

store_v1 "github.com/containerish/OpenRegistry/store/v1"
"github.com/containerish/OpenRegistry/store/v1/types"
"github.com/labstack/echo/v4"
)

func (ext *extension) ChangeContainerImageVisibility(ctx echo.Context) error {
ctx.Set(types.HandlerStartTime, time.Now())

var body types.ContainerImageVisibilityChangeRequest
user, ok := ctx.Get(string(types.UserContextKey)).(*types.User)
if !ok {
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": store_v1.ErrMissingUserInContext.Error(),
})

if err := json.NewDecoder(ctx.Request().Body).Decode(&body); err != nil {
return ctx.JSON(http.StatusBadRequest, echo.Map{
ext.logger.Log(ctx, store_v1.ErrMissingUserInContext).Send()
return echoErr
}

var body types.ContainerImageVisibilityChangeRequest
if err := ctx.Bind(&body); err != nil {
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": "invalid request body",
})
ext.logger.Log(ctx, err).Send()
return echoErr
}
defer ctx.Request().Body.Close()

err := ext.store.SetContainerImageVisibility(ctx.Request().Context(), body.ImageManifestUUID, body.Visibility)
err := ext.store.SetContainerImageVisibility(
ctx.Request().Context(),
body.RepositoryID,
user.ID,
body.Visibility,
)
if err != nil {
return ctx.JSON(http.StatusBadRequest, echo.Map{
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
})
ext.logger.Log(ctx, err).Send()
return echoErr
}

return ctx.JSON(http.StatusOK, echo.Map{
echoErr := ctx.JSON(http.StatusOK, echo.Map{
"message": "container image visibility mode changed successfully",
})
ext.logger.Log(ctx, nil).Send()
return echoErr
}
1 change: 0 additions & 1 deletion registry/v2/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -970,7 +970,6 @@ func (r *registry) DeleteTagOrManifest(ctx echo.Context) error {

func (r *registry) DeleteLayer(ctx echo.Context) error {
ctx.Set(types.HandlerStartTime, time.Now())

digest := ctx.Param("digest")

txnOp, _ := r.store.NewTxn(context.Background())
Expand Down
25 changes: 0 additions & 25 deletions router/helpers.go

This file was deleted.

17 changes: 7 additions & 10 deletions router/middlewares.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package router
import (
"fmt"
"net/http"
"regexp"

"github.com/containerish/OpenRegistry/common"
"github.com/containerish/OpenRegistry/config"
"github.com/containerish/OpenRegistry/registry/v2"
registry_store "github.com/containerish/OpenRegistry/store/v1/registry"
"github.com/containerish/OpenRegistry/store/v1/types"
Expand All @@ -14,9 +14,6 @@ import (
)

func registryNamespaceValidator(logger telemetry.Logger) echo.MiddlewareFunc {
// Reference: https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pulling-manifests
nsRegex := regexp.MustCompile(`[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*(/[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*)*`)

return func(handler echo.HandlerFunc) echo.HandlerFunc {
return func(ctx echo.Context) error {
// we skip the /v2/ path since it isn't a namespaced path
Expand All @@ -25,7 +22,7 @@ func registryNamespaceValidator(logger telemetry.Logger) echo.MiddlewareFunc {
}

namespace := ctx.Param("username") + "/" + ctx.Param("imagename")
if namespace != "/" && !nsRegex.MatchString(namespace) {
if namespace != "/" && !config.RegistryNSRegex.MatchString(namespace) {
registryErr := common.RegistryErrorResponse(
registry.RegistryErrorCodeNameInvalid,
"invalid user namespace",
Expand All @@ -45,18 +42,18 @@ func registryNamespaceValidator(logger telemetry.Logger) echo.MiddlewareFunc {
}

func registryReferenceOrTagValidator(logger telemetry.Logger) echo.MiddlewareFunc {
// Reference: https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pulling-manifests
refRegex := regexp.MustCompile(`[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}`)

return func(handler echo.HandlerFunc) echo.HandlerFunc {
return func(ctx echo.Context) error {
ref := ctx.Param("reference")
if ref == "" || !refRegex.MatchString(ref) {
if ref == "" || !config.RegistryManifestRefRegex.MatchString(ref) {
registryErr := common.RegistryErrorResponse(
registry.RegistryErrorCodeTagInvalid,
"reference/tag does not match the required format",
echo.Map{
"error": fmt.Sprintf("reference/tag must match the following regex: %s", refRegex.String()),
"error": fmt.Sprintf(
"reference/tag must match the following regex: %s",
config.RegistryManifestRefRegex.String(),
),
},
)

Expand Down
10 changes: 5 additions & 5 deletions router/orgmode.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import (
"github.com/labstack/echo/v4"
)

func RegisterOrgModeRoutes(router *echo.Group, svc orgmode.OrgMode) {
router.POST("/migrate", svc.MigrateToOrg)
router.POST("/users", svc.AddUserToOrg)
router.PATCH("/permissions/users", svc.UpdateUserPermissions)
router.DELETE("/permissions/users/:orgId/:userId", svc.RemoveUserFromOrg)
func RegisterOrgModeRoutes(router *echo.Group, svc orgmode.OrgMode, mws ...echo.MiddlewareFunc) {
router.POST("/migrate", svc.MigrateToOrg, mws...)
router.POST("/users", svc.AddUserToOrg, mws...)
router.PATCH("/permissions/users", svc.UpdateUserPermissions, mws...)
router.DELETE("/permissions/users/:orgId/:userId", svc.RemoveUserFromOrg, mws...)
}
8 changes: 5 additions & 3 deletions router/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,19 @@ func RegisterExtensionsRoutes(
ext extensions.Extenion,
middlewares ...echo.MiddlewareFunc,
) {

// GET /v2/_catalog
// GET
group.Add(http.MethodGet, Catalog, reg.Catalog)
group.Add(http.MethodGet, PublicCatalog, ext.PublicCatalog)
// Auto-complete image search
group.Add(http.MethodGet, Search, reg.GetImageNamespace)
group.Add(http.MethodGet, CatalogDetail, ext.CatalogDetail, middlewares...)
group.Add(http.MethodGet, RepositoryDetail, ext.RepositoryDetail, middlewares...)
group.Add(http.MethodGet, UserCatalog, ext.GetUserCatalog, middlewares...)

// POST
group.Add(http.MethodPost, ChangeRepositoryVisibility, ext.ChangeContainerImageVisibility, middlewares...)
group.Add(http.MethodPost, CreateRepository, reg.CreateRepository, middlewares...)
group.Add(http.MethodPost, RepositoryFavorites, ext.AddRepositoryToFavorites, middlewares...)

// DELETE
group.Add(http.MethodDelete, RepositoryFavorites, ext.RemoveRepositoryFromFavorites, middlewares...)
}
4 changes: 2 additions & 2 deletions router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func Register(

baseAPIRouter := e.Group("/api")
githubRouter := e.Group("/github")
authRouter := e.Group(Auth)
authRouter := e.Group(Auth, authApi.JWTRest())
webauthnRouter := e.Group(Webauthn)
orgModeRouter := e.Group("/org", authApi.JWTRest(), orgModeApi.AllowOrgAdmin())
ociRouter := e.Group(V2, registryNamespaceValidator(logger), authApi.BasicAuth(), authApi.JWT())
Expand All @@ -58,7 +58,7 @@ func Register(
RegisterUserRoutes(userApiRouter, usersApi)
RegisterNSRoutes(nsRouter, registryApi, registryStore, logger)
RegisterAuthRoutes(authRouter, authApi)
RegisterExtensionsRoutes(ociRouter, registryApi, extensionsApi)
RegisterExtensionsRoutes(ociRouter, registryApi, extensionsApi, authApi.JWTRest())
RegisterWebauthnRoutes(webauthnRouter, webauthnApi)
RegisterOrgModeRoutes(orgModeRouter, orgModeApi)

Expand Down
Loading

0 comments on commit e58ab04

Please sign in to comment.