From 4731cfe7c23c5d8dada8e3a9c3baa9e242908d02 Mon Sep 17 00:00:00 2001 From: GotPPay Date: Wed, 30 May 2018 18:24:53 +0200 Subject: [PATCH] add custom error for password reset --- data/datamysql/passwordreset.go | 23 ++++++++++++------- .../router/passwordresetroute/controller.go | 15 ++++++------ server/router/routeutils/response.go | 5 ++++ 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/data/datamysql/passwordreset.go b/data/datamysql/passwordreset.go index 36297f8..619a8c2 100644 --- a/data/datamysql/passwordreset.go +++ b/data/datamysql/passwordreset.go @@ -2,6 +2,7 @@ package datamysql import ( "database/sql" + "fmt" "bitbucket.org/nemt/nemt-portal-api/domain/entity" "bitbucket.org/nemt/nemt-portal-api/infra/errors" @@ -29,7 +30,7 @@ func (c *passwordResetRepo) getQuery() string { a.token, a.create_date, a.expire_date, - (IFNULL(a.used, b'0') = b'1') used + (IFNULL(a.used, b'0') = b'1') used, (IFNULL(a.opened, b'0') = b'1') opened FROM tab_password_reset a @@ -68,20 +69,26 @@ func (c *passwordResetRepo) GetAll() ([]entity.PasswordReset, error) { return c.parseSet(c.conn.Query(c.getQuery())) } -/* -func (c *zipcodeRepo) GetByParticipatingZipcode(zipcode string) (entity.Zipcode, error) { - return c.parseEntity(c.conn.QueryRow(c.getQuery()+"WHERE a.participating = 1 AND a.zipcode = ?", zipcode)) -}*/ - func (c *passwordResetRepo) CreatePasswordResetEntry(passwordResetEntry entity.PasswordReset) (entity.PasswordReset, error) { const ( - query = `INSERT INTO tab_password_reset(password_reset_uuid, user_id, token, expire_date, used, opened) VALUES(?, ?, ?, ?, ?, 0, 0);` + insertQuery = `INSERT INTO tab_password_reset(password_reset_uuid, user_id, token, expire_date, used, opened) VALUES(?, ?, ?, ?, 0, 0);` ) retVal := passwordResetEntry guid, _ := uuid.NewV4() - results, err := c.conn.Exec(query, guid, passwordResetEntry.User.ID, passwordResetEntry.Token, passwordResetEntry.Expires) + userRepo := newUserRepo(c.conn) + + fullUser, err := userRepo.GetByEmail(passwordResetEntry.User.Email) + if err != nil { + return retVal, err + } + + if fullUser.Email != passwordResetEntry.User.Email { + return retVal, fmt.Errorf("User not found") + } + + results, err := c.conn.Exec(insertQuery, guid, fullUser.ID, passwordResetEntry.Token, passwordResetEntry.Expires) if err != nil { return retVal, err } diff --git a/server/router/passwordresetroute/controller.go b/server/router/passwordresetroute/controller.go index 9d1205c..0520cdc 100644 --- a/server/router/passwordresetroute/controller.go +++ b/server/router/passwordresetroute/controller.go @@ -20,7 +20,7 @@ const ( baseURL = "http://localhost:5000" passwordResetEmailSubject = "Reset Your Password" passwordResetEmailMainBody = "To reset your password click here or copy the following link and paste it into your browser: \n\n " + baseURL + "/#/reset-password/" - passwordResetEmailFooter = "\nThis link expires in " + string(tokenExpirationTime) + " minutes" + passwordResetEmailFooter = "\nThis link expires in 90 minutes" ) var ( @@ -46,30 +46,29 @@ func controllerInstance(svc *applicationservice.Service, cfg *config.Config) *co } func (c *controller) handleResetRequest(ctx echo.Context) error { - fmt.Println("\n\nRequest...") userEmail, err := routeutils.GetAndValidateStringParam(ctx, "email", "mandatory field") if err != nil { return routeutils.HandleAPIError(ctx, err) } - fmt.Println("\nEmail : ", userEmail) - //find if user with email exists user, err := c.svc.Users.GetByEmail(userEmail) if err != nil { return routeutils.HandleAPIError(ctx, err) } + if user.Email == nil || (*user.Email != userEmail) { + return routeutils.ResponseAPIOK(ctx, nil) //more secure, don't inform user (attacker) that email doesn't exists + } + //create and store reset token timeNow := time.Now() - expirationTime := timeNow.Add(time.Hour * tokenExpirationTime) + expirationTime := timeNow.Add(time.Minute * tokenExpirationTime) randomArray := make([]byte, randomStringLength) rand.Read(randomArray) - h := sha256.New() - h.Write(randomArray) - token := string(h.Sum(nil)) + token := fmt.Sprintf("%x", sha256.Sum256(randomArray)) passwordResetEntry := viewmodel.PasswordReset{ User: user, diff --git a/server/router/routeutils/response.go b/server/router/routeutils/response.go index 2aab6da..d14fe53 100644 --- a/server/router/routeutils/response.go +++ b/server/router/routeutils/response.go @@ -102,6 +102,11 @@ func ResponseAPINotEligibleWithMessageError(c echo.Context, message string) erro return ResponseAPIError(c, http.StatusForbidden, message, false) } +//ResponseAPIPasswordResetFailed returns a standard API error when password reset fails +func ResponseAPIPasswordResetFailed(c echo.Context, message string) error { + return ResponseAPIError(c, http.StatusForbidden, message, false) +} + func ignoreDefaultWrappedErrors(c echo.Context, errorToHandle *errors.WrappedError, handler func(echo.Context, error) error) error { err := errorToHandle.GetOriginalError()