It("Should resend confirmation email", func() { if skipUser { Skip("Previous error") } js := `{"data": {}}` status, _, err := client.PostJson("/api/method/users.send-confirmation-email", js) Expect(err).ToNot(HaveOccurred()) Expect(status).To(Equal(200)) // Check that confirmation email was sent. logEntry := logMessages[len(logMessages)-2] Expect(logEntry.Data["action"]).To(Equal("users.email_confirmation_mail_sent")) Expect(logEntry.Data["user_id"]).To(Equal(currentUser.GetId())) Expect(logEntry.Data["email"]).To(Equal(currentUser.GetEmail())) currentToken = logEntry.Data["token"].(string) }) It("Should confirm email", func() { if skipUser || currentToken == "" { Skip("Previous error") } js := fmt.Sprintf(`{"data": {"token": "%v"}}`, currentToken) status, _, err := client.PostJson("/api/method/users.confirm-email", js) Expect(err).ToNot(HaveOccurred()) Expect(status).To(Equal(200)) logEntry := logMessages[len(logMessages)-2]
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) CreateUser(user kit.User, adaptorName string, authData map[string]interface{}) apperror.Error { adaptor := s.AuthAdaptor(adaptorName) if adaptor == nil { return &apperror.Err{ Code: "unknown_auth_adaptor", Message: fmt.Sprintf("Auth adaptor %v was not registered with user service", adaptorName), Public: true, } } authItem, err := adaptor.RegisterUser(user, authData) if err != nil { return apperror.Wrap(err, "adaptor_error", "") } if user.GetUsername() == "" { user.SetUsername(user.GetEmail()) } // Check if user with same username or email exists. oldUser, err2 := s.Users.Q(). Filter("email", user.GetEmail()).Or("username", user.GetUsername()).First() if err2 != nil { return err2 } else if oldUser != nil { return &apperror.Err{ Code: "user_exists", Message: "A user with the username or email already exists", Public: true, } } user.SetIsActive(true) profile := user.GetProfile() // If a profile is configured, and the user does not have a profile yet, // create a new one. if s.Profiles != nil && profile == nil { profile = s.Profiles.CreateModel().(kit.UserProfile) user.SetProfile(profile) } if err := s.Users.Create(user, nil); err != nil { return err } // Create profile if one exists. if profile != nil { profile.SetUser(user) if err := s.Profiles.Create(profile, user); err != nil { s.Users.Backend().Delete(user) return apperror.Wrap(err, "user_profile_create_error", "Could not create the user profile") } } // Persist auth item. if authItemUserId, ok := authItem.(kit.UserModel); ok { authItemUserId.SetUserId(user.GetId()) } if err := s.Users.Backend().Create(authItem); err != nil { s.Users.Backend().Delete(user) if profile != nil { s.Profiles.Backend().Delete(profile) } return apperror.Wrap(err, "auth_item_create_error", "") } if err := s.SendConfirmationEmail(user); err != nil { s.registry.Logger().Errorf("Could not send confirmation email: %v", err) } 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 }
func (a *AuthAdaptorOauth) RegisterUser(user kit.User, data map[string]interface{}) (kit.AuthItem, apperror.Error) { serviceName, _ := GetStringFromMap(data, "service") if serviceName == "" { return nil, apperror.New("invalid_data_missing_service") } service := a.services[serviceName] if service == nil { return nil, &apperror.Err{ Code: "unconfigured_service", Message: fmt.Sprintf("The oauth service '%v' was not configured in oauth auth adaptor", serviceName), } } accessToken, _ := GetStringFromMap(data, "access_token") if accessToken == "" { return nil, apperror.New("invalid_data_missing_access_token") } // Exchange access token for long lived token. // This also verifies that the supplied token is valid. appToken, err := service.Exchange(accessToken) if err != nil { return nil, apperror.Wrap(err, "oauth_exchange_token_error", "") } userData, err := service.GetUserData(appToken) if err != nil { return nil, apperror.Wrap(err, "fetch_user_data_failed", "") } if userData.Id == "" { return nil, &apperror.Err{ Code: "fetched_userdata_missing_user_id", Message: "The userData fetched from the service does not contain a userId", } } item := &AuthItemOauth{ Service: serviceName, UserId: user.GetStrId(), ExternalUserId: userData.Id, Token: appToken, } item.Id = serviceName + "_" + userData.Id // Fill in user information. if user.GetEmail() == "" { if userData.Email != "" { user.SetEmail(userData.Email) user.SetIsEmailConfirmed(true) } else { return nil, &apperror.Err{ Code: "oauth_service_insufficient_data_error", Message: fmt.Sprintf("The oauth service %v did not supply the users email, which is required", serviceName), } } } if user.GetUsername() == "" && userData.Username != "" { user.SetUsername(userData.Username) } return item, nil }