Esempio n. 1
0
func (h *Service) AuthenticateUser(userIdentifier string, authAdaptorName string, data map[string]interface{}) (kit.User, apperror.Error) {
	authAdaptor := h.AuthAdaptor(authAdaptorName)
	if authAdaptor == nil {
		return nil, &apperror.Err{
			Public:  true,
			Code:    "unknown_auth_adaptor",
			Message: "Unknown auth adaptor: " + authAdaptorName}
	}

	var user kit.User
	var err apperror.Error

	if userIdentifier != "" {
		user, err = h.FindUser(userIdentifier)

		if err != nil {
			return nil, err
		} else if user == nil {
			return nil, apperror.New("user_not_found", "Username/Email does not exist ", true)
		}
	}

	userId := ""
	if user != nil {
		userId = user.GetStrId()
	}

	userId, err = authAdaptor.Authenticate(userId, data)
	if err != nil {
		if err.IsPublic() {
			return nil, err
		} else {
			return nil, apperror.Wrap(err, "adaptor_error", true)
		}
	}

	if user == nil {
		// Query user to get a full user with permissions and profile.
		user, err = h.FindUser(userId)
		if err != nil {
			return nil, err
		} else if user == nil {
			return nil, &apperror.Err{
				Code:    "user_not_found",
				Message: fmt.Sprintf("User with id %v could not be found", userId),
				Public:  true,
			}
		}
	}

	if !user.IsActive() {
		return nil, apperror.New("user_inactive", true)
	}

	return user, nil
}
Esempio n. 2
0
func (s *Service) ChangePassword(user kit.User, newPassword string) apperror.Error {
	adaptor := s.AuthAdaptor("password")
	if adaptor == nil {
		return &apperror.Err{
			Code:    "no_password_adaptor",
			Message: "The UserService does not have the password auth adaptor",
		}
	}

	passwordAdaptor := adaptor.(*password.AuthAdaptorPassword)

	if err := passwordAdaptor.ChangePassword(user.GetStrId(), newPassword); err != nil {
		if err.IsPublic() {
			return err
		} else {
			return apperror.Wrap(err, "adapter_error")
		}
		return err
	}

	return nil
}
Esempio n. 3
0
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
}
Esempio n. 4
0
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
}
Esempio n. 5
0
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
}