2018-05-30 16:52:57 +02:00
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/"
2018-05-30 18:24:53 +02:00
passwordResetEmailFooter = "\nThis link expires in 90 minutes"
2018-05-30 16:52:57 +02:00
)
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 {
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 )
}
2018-05-30 18:24:53 +02:00
if user . Email == nil || ( * user . Email != userEmail ) {
return routeutils . ResponseAPIOK ( ctx , nil ) //more secure, don't inform user (attacker) that email doesn't exists
}
2018-05-30 16:52:57 +02:00
//create and store reset token
timeNow := time . Now ( )
2018-05-30 18:24:53 +02:00
expirationTime := timeNow . Add ( time . Minute * tokenExpirationTime )
2018-05-30 16:52:57 +02:00
randomArray := make ( [ ] byte , randomStringLength )
rand . Read ( randomArray )
2018-05-30 18:24:53 +02:00
token := fmt . Sprintf ( "%x" , sha256 . Sum256 ( randomArray ) )
2018-05-30 16:52:57 +02:00
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 )
}