Skip to content

Commit

Permalink
Fix: Reset Forgotten password
Browse files Browse the repository at this point in the history
Signed-off-by: jay-dee7 <[email protected]>
  • Loading branch information
jay-dee7 committed Apr 7, 2022
1 parent aa4f100 commit aa29484
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 59 deletions.
1 change: 1 addition & 0 deletions auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type Authentication interface {
RenewAccessToken(ctx echo.Context) error
VerifyEmail(ctx echo.Context) error
ResetPassword(ctx echo.Context) error
ResetForgottenPassword(ctx echo.Context) error
ForgotPassword(ctx echo.Context) error
Invites(ctx echo.Context) error
}
Expand Down
6 changes: 1 addition & 5 deletions auth/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,7 @@ func (a *auth) GithubLoginCallbackHandler(ctx echo.Context) error {

sessionId := uuid.NewString()
if err = a.pgStore.AddSession(ctx.Request().Context(), sessionId, refreshToken, oauthUser.Username); err != nil {
ctx.Redirect(http.StatusTemporaryRedirect, a.c.WebAppErrorRedirectURL)
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
"message": "ERR_CREATING_SESSION",
})
echoErr := ctx.Redirect(http.StatusTemporaryRedirect, a.c.WebAppErrorRedirectPath)
a.logger.Log(ctx, err)
return echoErr
}
Expand Down
2 changes: 0 additions & 2 deletions auth/jwt_middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"time"

"github.com/containerish/OpenRegistry/types"
"github.com/fatih/color"
"github.com/golang-jwt/jwt"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
Expand Down Expand Up @@ -45,7 +44,6 @@ func (a *auth) JWT() echo.MiddlewareFunc {
ErrorHandlerWithContext: func(err error, ctx echo.Context) error {
// ErrorHandlerWithContext only logs the failing requtest
ctx.Set(types.HandlerStartTime, time.Now())
color.Red(ctx.QueryParam("token"))
a.logger.Log(ctx, err)
return ctx.JSON(http.StatusUnauthorized, echo.Map{
"error": err.Error(),
Expand Down
162 changes: 114 additions & 48 deletions auth/reset_password.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/labstack/echo/v4"
)

func (a *auth) ResetPassword(ctx echo.Context) error {
func (a *auth) ResetForgottenPassword(ctx echo.Context) error {
token, ok := ctx.Get("user").(*jwt.Token)
if !ok {
err := fmt.Errorf("ERR_EMPTY_TOKEN")
Expand All @@ -36,6 +36,18 @@ func (a *auth) ResetPassword(ctx echo.Context) error {
return echoErr
}

var pwd *types.Password
err := json.NewDecoder(ctx.Request().Body).Decode(&pwd)
if err != nil {
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
"message": "request body could not be decoded",
})
a.logger.Log(ctx, err)
return echoErr
}
_ = ctx.Request().Body.Close()

userId := c.Id
user, err := a.pgStore.GetUserById(ctx.Request().Context(), userId, true)
if err != nil {
Expand All @@ -47,79 +59,112 @@ func (a *auth) ResetPassword(ctx echo.Context) error {
return echoErr
}

var pwd *types.Password
kind := ctx.QueryParam("kind")
if err = validatePassword(pwd.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
and an uppercase letter`,
})
a.logger.Log(ctx, err)
return echoErr
}

err = json.NewDecoder(ctx.Request().Body).Decode(&pwd)
if err != nil {
if a.verifyPassword(user.Password, pwd.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{
"error": err.Error(),
"message": "request body could not be decoded",
"message": err.Error(),
})
a.logger.Log(ctx, err)
return echoErr
}
_ = ctx.Request().Body.Close()

if err = verifyPassword(pwd.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
and an uppercase letter`,
hashPassword, err := a.hashPassword(pwd.NewPassword)
if err != nil {
echoErr := ctx.JSON(http.StatusInternalServerError, echo.Map{
"error": err.Error(),
"message": "ERR_HASH_NEW_PASSWORD",
})
a.logger.Log(ctx, err)
return echoErr
}

if kind == "forgot_password_callback" {
hashPassword, err := a.hashPassword(pwd.NewPassword)
if err != nil {
echoErr := ctx.JSON(http.StatusInternalServerError, echo.Map{
"error": err.Error(),
"message": "ERR_HASH_NEW_PASSWORD",
})
a.logger.Log(ctx, err)
return echoErr
}
if err = a.pgStore.UpdateUserPWD(ctx.Request().Context(), userId, hashPassword); err != nil {
echoErr := ctx.JSON(http.StatusInternalServerError, echo.Map{
"error": err.Error(),
"message": "error updating new password",
})
a.logger.Log(ctx, err)
return echoErr
}

if user.Password == hashPassword {
err = fmt.Errorf("new password can not be same as old password")
// error is already user friendly
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
"message": err.Error(),
})
a.logger.Log(ctx, err)
return echoErr
}
err = ctx.JSON(http.StatusAccepted, echo.Map{
"message": "password changed successfully",
})
a.logger.Log(ctx, err)
return err
}

if err = a.pgStore.UpdateUserPWD(ctx.Request().Context(), userId, hashPassword); err != nil {
echoErr := ctx.JSON(http.StatusInternalServerError, echo.Map{
"error": err.Error(),
"message": "error updating new password",
})
a.logger.Log(ctx, err)
return echoErr
}
func (a *auth) ResetPassword(ctx echo.Context) error {
token, ok := ctx.Get("user").(*jwt.Token)
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)
return echoErr
}

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

if pwd.OldPassword == pwd.NewPassword {
err = fmt.Errorf("OLD_NEW_PWD_SAME")
var pwd *types.Password
err := json.NewDecoder(ctx.Request().Body).Decode(&pwd)
if err != nil {
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
"message": "new password can not be same as old password",
"message": "request body could not be decoded",
})
a.logger.Log(ctx, err)
return echoErr
}
_ = ctx.Request().Body.Close()

newHashedPwd, err := a.hashPassword(pwd.NewPassword)
userId := c.Id
user, err := a.pgStore.GetUserById(ctx.Request().Context(), userId, true)
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)
return echoErr
}

// compare the current password with password hash from DB
if !a.verifyPassword(user.Password, pwd.OldPassword) {
err = fmt.Errorf("ERR_WRONG_PASSWORD")
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
"message": "password is wrong",
})
a.logger.Log(ctx, err)
return echoErr
}

hashPassword, err := a.hashPassword(pwd.NewPassword)
if err != nil {
echoErr := ctx.JSON(http.StatusInternalServerError, echo.Map{
"error": err.Error(),
Expand All @@ -129,7 +174,28 @@ and an uppercase letter`,
return echoErr
}

if err = a.pgStore.UpdateUserPWD(ctx.Request().Context(), userId, newHashedPwd); err != nil {
if user.Password == hashPassword {
err = fmt.Errorf("new password can not be same as old password")
// error is already user friendly
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
"message": err.Error(),
})
a.logger.Log(ctx, err)
return echoErr
}

if err = validatePassword(pwd.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
and an uppercase letter`,
})
a.logger.Log(ctx, err)
return echoErr
}

if err = a.pgStore.UpdateUserPWD(ctx.Request().Context(), userId, hashPassword); err != nil {
echoErr := ctx.JSON(http.StatusInternalServerError, echo.Map{
"error": err.Error(),
"message": "error updating new password",
Expand Down
4 changes: 2 additions & 2 deletions auth/signup.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func (a *auth) SignUp(ctx echo.Context) error {
return echoErr
}

if err := verifyPassword(u.Password); err != nil {
if err := validatePassword(u.Password); err != nil {
// err.Error() is already user friendly
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
Expand Down Expand Up @@ -155,7 +155,7 @@ func verifyEmail(email string) error {
return nil
}

func verifyPassword(password string) error {
func validatePassword(password string) error {
var uppercasePresent bool
var lowercasePresent bool
var numberPresent bool
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ require (
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
)

require github.com/sendgrid/rest v2.6.9+incompatible // indirect

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/sendgrid/rest v2.6.9+incompatible // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/go-playground/locales v0.14.0 // indirect
Expand Down
1 change: 1 addition & 0 deletions router/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ func RegisterAuthRoutes(authRouter *echo.Group, authSvc auth.Authentication) {
authRouter.Add(http.MethodDelete, "/sessions", authSvc.ExpireSessions)
authRouter.Add(http.MethodGet, "/renew", authSvc.RenewAccessToken)
authRouter.Add(http.MethodPost, "/reset-password", authSvc.ResetPassword, authSvc.JWT())
authRouter.Add(http.MethodPost, "/reset-forgotten-password", authSvc.ResetForgottenPassword, authSvc.JWT())
authRouter.Add(http.MethodGet, "/forgot-password", authSvc.ForgotPassword)
}
1 change: 0 additions & 1 deletion router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,5 +131,4 @@ func Extensions(group *echo.Group, reg registry.Registry, ext extensions.Extenio
group.Add(http.MethodGet, Search, reg.GetImageNamespace)
group.Add(http.MethodGet, CatalogDetail, ext.CatalogDetail, middlewares...)
group.Add(http.MethodGet, RepositoryDetail, ext.RepositoryDetail, middlewares...)

}

0 comments on commit aa29484

Please sign in to comment.