func (s *Service) SendPasswordResetEmail(user kit.User) apperror.Error { // Check that an email service is configured. mailService := s.registry.EmailService() if mailService == nil { return apperror.New("no_email_service") } hoursValid := 48 // Generate a token. expiresAt := time.Now().Add(time.Hour * time.Duration(hoursValid)) tokenItem, err := s.BuildToken("password_reset", user.GetStrId(), expiresAt) if err != nil { return err } token := tokenItem.GetToken() conf := s.registry.Config() // Build the confirmation url. url := conf.UString("url") if url == "" { return &apperror.Err{ Code: "no_url_set", Message: "Config must specify url", } } resetPath := conf.UString("users.passwordResetPath") if resetPath == "" { return &apperror.Err{ Code: "no_password_reset_path", Message: "Config must specify users.passwordResetPath", } } if !strings.Contains(resetPath, "{token}") { return &apperror.Err{ Code: "invalid_password_reset_path", Message: "users.passwordResetPath does not contain {token} placeholder", } } resetUrl := url + "/" + strings.Replace(resetPath, "{token}", token, -1) // Render email. subject := conf.UString("users.passwordResetSubject", "Password reset") var txtContent, htmlContent []byte txtTpl := conf.UString("users.passwordResetTextTpl") htmlTpl := conf.UString("users.passwordResetHtmlTpl") if txtTpl != "" && htmlTpl != "" { // Check that a template engine is configured. engine := s.registry.TemplateEngine() if engine == nil { return apperror.New("no_template_engine") } data := map[string]interface{}{ "user": user, "token": token, "hours_valid": hoursValid, } var err apperror.Error txtContent, err = s.registry.TemplateEngine().BuildFileAndRender(txtTpl, data) if err != nil { return apperror.Wrap(err, "password_reset_tpl_error", "Could not render password reset tpl") } htmlContent, err = s.registry.TemplateEngine().BuildFileAndRender(htmlTpl, data) if err != nil { return apperror.Wrap(err, "password_reset_tpl_error", "Could not render password reset tpl") } } else { tpl := `Password reset To reset your password, please visit %v. The link will be valid for %v hours. ` htmlTpl := `Password Reset<br><br> To reset your password, please visit <a href="%v">this link</a>.<br> The link will be valid for %v hours. ` txtContent = []byte(fmt.Sprintf(tpl, resetUrl, hoursValid)) htmlContent = []byte(fmt.Sprintf(htmlTpl, resetUrl, hoursValid)) } // Now build the email and send it. email := email.NewMail() email.SetSubject(subject) email.AddBody("text/plain", txtContent) email.AddBody("text/html", htmlContent) email.AddTo(user.GetEmail(), "") if err := mailService.Send(email); err != nil { return err } s.registry.Logger().WithFields(logrus.Fields{ "action": "users.password_reset_requested", "email": user.GetEmail(), "user_id": user.GetId(), "token": token, }).Debugf("Password reset email sent to %v for user %v", user.GetEmail(), user.GetId()) return nil }
func (s *Service) SendConfirmationEmail(user kit.User) apperror.Error { // Check that an email service is configured. mailService := s.registry.EmailService() if mailService == nil { return apperror.New("no_email_service") } conf := s.registry.Config() // Check that sending is enabled. if !conf.UBool("users.sendEmailConfirmationEmail", true) { return nil } // Generate a token. tokenItem, err := s.BuildToken("email_confirmation", user.GetStrId(), time.Time{}) if err != nil { return err } token := tokenItem.GetToken() // Build the confirmation url. confirmationPath := conf.UString("users.emailConfirmationPath") if confirmationPath == "" { return &apperror.Err{ Code: "no_email_confirmation_path", Message: "Config must specify users.emailConfirmationPath", } } if !strings.Contains(confirmationPath, "{token}") { return &apperror.Err{ Code: "invalid_email_confirmation_path", Message: "users.emailConfirmationPath does not contain {token} placeholder", } } confirmationUrl := conf.UString("url") + "/" + strings.Replace(confirmationPath, "{token}", token, -1) // Render email. subject := conf.UString("users.emailConfirmationSubject", "Confirm your Email") var txtContent, htmlContent []byte txtTpl := conf.UString("users.emailConfirmationEmailTextTpl") htmlTpl := conf.UString("users.emailConfirmationEmailHtmlTpl") if txtTpl != "" && htmlTpl != "" { // Check that a template engine is configured. engine := s.registry.TemplateEngine() if engine == nil { return apperror.New("no_template_engine") } data := map[string]interface{}{ "user": user, "token": token, } var err apperror.Error txtContent, err = s.registry.TemplateEngine().BuildFileAndRender(txtTpl, data) if err != nil { return apperror.Wrap(err, "email_confirmation_tpl_error", "Could not render email confirmation tpl") } htmlContent, err = s.registry.TemplateEngine().BuildFileAndRender(htmlTpl, data) if err != nil { return apperror.Wrap(err, "email_confirmation_tpl_error", "Could not render email confirmation tpl") } } else { tpl := `Welcome to Appkit To confirm your email address, please visit %v. ` htmlTpl := `Welcome to Appkit<br><br> To confirm your email address, please visit <a href="%v">this link</a>. ` txtContent = []byte(fmt.Sprintf(tpl, confirmationUrl)) htmlContent = []byte(fmt.Sprintf(htmlTpl, confirmationUrl)) } // Now build the email and send it. email := email.NewMail() email.SetSubject(subject) email.AddBody("text/plain", txtContent) email.AddBody("text/html", htmlContent) email.AddTo(user.GetEmail(), "") if err := mailService.Send(email); err != nil { return err } s.registry.Logger().WithFields(logrus.Fields{ "action": "users.email_confirmation_mail_sent", "email": user.GetEmail(), "user_id": user.GetId(), "token": token, }).Debugf("Password reset email sent to %v for user %v", user.GetEmail(), user.GetId()) return nil }