package passwordresetroute import ( "crypto/sha256" "fmt" "math/rand" "sync" "time" "bitbucket.org/nemt/nemt-portal-api/application/applicationservice" "bitbucket.org/nemt/nemt-portal-api/application/viewmodel" "bitbucket.org/nemt/nemt-portal-api/infra/config" "bitbucket.org/nemt/nemt-portal-api/server/router/routeutils" "github.com/labstack/echo" ) const ( tokenExpirationTime = 90 // in minutes randomStringLength = 15 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" ) var ( instance *controller once sync.Once ) type controller struct { svc *applicationservice.Service cfg *config.Config } func controllerInstance(svc *applicationservice.Service, cfg *config.Config) *controller { once.Do(func() { instance = &controller{ svc: svc, cfg: cfg, } rand.Seed(time.Now().UTC().UnixNano()) }) return instance } 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) } //create and store reset token timeNow := time.Now() expirationTime := timeNow.Add(time.Hour * tokenExpirationTime) randomArray := make([]byte, randomStringLength) rand.Read(randomArray) h := sha256.New() h.Write(randomArray) token := string(h.Sum(nil)) passwordResetEntry := viewmodel.PasswordReset{ User: user, Token: token, Expires: expirationTime, Opened: false, Used: false, } _, err = c.svc.PasswordReset.CreatePasswordResetEntry(passwordResetEntry) if err != nil { return routeutils.HandleAPIError(ctx, err) } //Send email with reset link notification := viewmodel.Notification{ Type: applicationservice.NotificationTypeEmail, From: c.cfg.Email.Sender, To: *user.Email, Subject: passwordResetEmailSubject, Message: passwordResetEmailMainBody + token + passwordResetEmailFooter, } notification, err = c.svc.Notification.SendNotificationWithoutWritingToDatabase(notification) if err != nil { return routeutils.HandleAPIError(ctx, err) } return routeutils.ResponseAPIOK(ctx, nil) } func (c *controller) handleResetComplete(ctx echo.Context) error { /* userEmail, err := routeutils.GetAndValidateStringParam(ctx, "email", "mandatory field") if err != nil { return routeutils.HandleAPIError(ctx, err) } //find if user with email exists user, err := c.svc.Users.GetByEmail(userEmail) if err != nil { return routeutils.HandleAPIError(ctx, err) } //create and store reset token //send email with reset link */ return routeutils.ResponseAPIOK(ctx, nil) }