153 lines
3.5 KiB
Go
153 lines
3.5 KiB
Go
package pwreset
|
|
|
|
import (
|
|
"unicode"
|
|
|
|
"github.com/badoux/checkmail"
|
|
uuid2 "github.com/google/uuid"
|
|
"golang.org/x/crypto/bcrypt"
|
|
"src.dualinventive.com/go/authentication-service/internal/domain"
|
|
"src.dualinventive.com/go/authentication-service/internal/storage"
|
|
)
|
|
|
|
//RequestPasswordReset generates a unique password reset code and sends it to the users email address.
|
|
func RequestPasswordReset(
|
|
credentialsRepo storage.CredentialsRepository,
|
|
emailSender EmailSender,
|
|
templateRepository TemplateRepository,
|
|
codeRepo storage.ResetCodeRepository,
|
|
userName string,
|
|
) error {
|
|
user, err := credentialsRepo.GetUserByUserName(userName, "")
|
|
if err != nil {
|
|
return NewErrUserFetchFailed(userName, err)
|
|
}
|
|
if user == nil {
|
|
return NewErrUserNotFound(userName)
|
|
}
|
|
|
|
toEmail := user.Email
|
|
if toEmail == "" {
|
|
return NewErrUserEmailAddressEmpty(userName)
|
|
}
|
|
if !isValidEmail(toEmail) {
|
|
return NewErrUserEmailInvalid(userName, toEmail)
|
|
}
|
|
|
|
uuid := uuid2.New().String()
|
|
err = codeRepo.CreateCode(userName, uuid)
|
|
if err != nil {
|
|
return NewErrFailedToCreateResetCode(userName, uuid, err)
|
|
}
|
|
|
|
err = sendEmail(emailSender, templateRepository, userName, emailSender.From(), toEmail, uuid)
|
|
if err != nil {
|
|
_ = codeRepo.DeleteCode(userName) //nolint:gosec
|
|
return NewErrFailedToSendEmail(userName, toEmail, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func isValidEmail(email string) bool {
|
|
err := checkmail.ValidateFormat(email)
|
|
return err == nil
|
|
}
|
|
|
|
func sendEmail(
|
|
emailSender EmailSender,
|
|
templateRepo TemplateRepository,
|
|
userName, fromEmail, toEmail, uuid string,
|
|
) error {
|
|
body, err := templateRepo.Template(userName, fromEmail, toEmail, uuid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = emailSender.Send([]string{toEmail}, body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
//RedeemPasswordReset will set a password of a user when the given username and password reset code a valid.
|
|
func RedeemPasswordReset(
|
|
credentialsRepo storage.CredentialsRepository,
|
|
codeRepo storage.ResetCodeRepository,
|
|
userName string,
|
|
passwordResetCode string,
|
|
newPassword string,
|
|
newPasswordVerify string,
|
|
) error {
|
|
user, err := credentialsRepo.GetUserByUserName(userName, "")
|
|
if err != nil {
|
|
return NewErrUserFetchFailed(userName, err)
|
|
}
|
|
if user == nil {
|
|
return NewErrUserNotFound(userName)
|
|
}
|
|
|
|
actualResetCode, err := codeRepo.GetCode(userName)
|
|
if err != nil {
|
|
return NewErrFailedToGetResetCode(userName, err)
|
|
}
|
|
if actualResetCode == "" {
|
|
return NewErrResetCodeNotFound(userName)
|
|
}
|
|
if passwordResetCode != actualResetCode {
|
|
return NewErrInvalidResetCode(userName, passwordResetCode)
|
|
}
|
|
if newPassword != newPasswordVerify {
|
|
return ErrVerifyPasswordFailed
|
|
}
|
|
if !validPassword(newPassword) {
|
|
return ErrInvalidPassword
|
|
}
|
|
|
|
err = setPassword(credentialsRepo, user, newPassword)
|
|
if err != nil {
|
|
return NewErrChangePasswordFailed(userName, err)
|
|
}
|
|
|
|
err = codeRepo.DeleteCode(userName)
|
|
if err != nil {
|
|
return NewErrDeleteResetCodeFailed(userName, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func setPassword(credentialsRepo storage.CredentialsRepository, user *domain.User, password string) error {
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = credentialsRepo.SetPassword(user, hash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validPassword(pass string) bool {
|
|
if len(pass) < 3 {
|
|
return false
|
|
}
|
|
|
|
var lower, upper, numeric bool
|
|
for _, c := range pass {
|
|
switch {
|
|
case unicode.IsLower(c):
|
|
lower = true
|
|
case unicode.IsUpper(c):
|
|
upper = true
|
|
case unicode.IsNumber(c):
|
|
numeric = true
|
|
}
|
|
}
|
|
|
|
return lower && upper && numeric
|
|
}
|