Password reset manually tested and fixed
This commit is contained in:
parent
860fae779d
commit
57e9a53cf1
5 changed files with 161 additions and 26 deletions
|
@ -25,6 +25,7 @@ dependencies {
|
||||||
implementation 'org.postgresql:postgresql'
|
implementation 'org.postgresql:postgresql'
|
||||||
compile "io.springfox:springfox-swagger2:2.9.2"
|
compile "io.springfox:springfox-swagger2:2.9.2"
|
||||||
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2'
|
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2'
|
||||||
|
compile "org.springframework.boot:spring-boot-configuration-processor"
|
||||||
|
|
||||||
implementation('org.springframework.boot:spring-boot-starter-web') {
|
implementation('org.springframework.boot:spring-boot-starter-web') {
|
||||||
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-json'
|
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-json'
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to interface with `email.*` properties in application.properties. This properties are used
|
||||||
|
* for generating the email to send on password reset or registration
|
||||||
|
*
|
||||||
|
* @see ch.usi.inf.sa4.sanmarinoes.smarthut.controller.UserAccountController
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@EnableConfigurationProperties
|
||||||
|
@ConfigurationProperties(prefix = "email")
|
||||||
|
public class EmailConfigurationService {
|
||||||
|
|
||||||
|
/** The email subject for a registration email */
|
||||||
|
@NotNull private String registrationSubject;
|
||||||
|
|
||||||
|
/** The text in the email body preceding the confirmation URL for a registration email */
|
||||||
|
@NotNull private String registration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URL to follow for registration email confirmation. Has to end with the start of a query
|
||||||
|
* parameter
|
||||||
|
*/
|
||||||
|
@NotNull private String registrationPath;
|
||||||
|
|
||||||
|
/** The email subject for a reset password email */
|
||||||
|
@NotNull private String resetPasswordSubject;
|
||||||
|
|
||||||
|
/** The text in the email body preceding the confirmation URL for a reset password email */
|
||||||
|
@NotNull private String resetPassword;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URL to follow for password reset email confirmation. Has to end with the start of a query
|
||||||
|
* parameter
|
||||||
|
*/
|
||||||
|
@NotNull private String resetPasswordPath;
|
||||||
|
|
||||||
|
public String getRegistrationSubject() {
|
||||||
|
return registrationSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRegistrationSubject(String registrationSubject) {
|
||||||
|
this.registrationSubject = registrationSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRegistration() {
|
||||||
|
return registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRegistration(String registration) {
|
||||||
|
this.registration = registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRegistrationPath() {
|
||||||
|
return registrationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRegistrationPath(String registrationPath) {
|
||||||
|
this.registrationPath = registrationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResetPasswordSubject() {
|
||||||
|
return resetPasswordSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResetPasswordSubject(String resetPasswordSubject) {
|
||||||
|
this.resetPasswordSubject = resetPasswordSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResetPassword() {
|
||||||
|
return resetPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResetPassword(String resetPassword) {
|
||||||
|
this.resetPassword = resetPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResetPasswordPath() {
|
||||||
|
return resetPasswordPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResetPasswordPath(String resetPasswordPath) {
|
||||||
|
this.resetPasswordPath = resetPasswordPath;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.EmailConfigurationService;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.InitPasswordResetRequest;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.InitPasswordResetRequest;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.PasswordResetRequest;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.PasswordResetRequest;
|
||||||
|
@ -14,56 +15,66 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.EmailSenderService;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.EmailSenderService;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.mail.SimpleMailMessage;
|
import org.springframework.mail.SimpleMailMessage;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/** Unauthenticated set of endpoints to handle registration and password reset */
|
||||||
@RestController
|
@RestController
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@RequestMapping("/register")
|
@RequestMapping("/register")
|
||||||
public class UserAccountController {
|
public class UserAccountController {
|
||||||
|
|
||||||
@Autowired private UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
@Autowired private ConfirmationTokenRepository confirmationTokenRepository;
|
private final ConfirmationTokenRepository confirmationTokenRepository;
|
||||||
|
|
||||||
@Autowired private EmailSenderService emailSenderService;
|
private final EmailSenderService emailSenderService;
|
||||||
|
|
||||||
@Autowired private BCryptPasswordEncoder encoder;
|
private final BCryptPasswordEncoder encoder;
|
||||||
|
|
||||||
@Value("email.registrationsubject")
|
private final EmailConfigurationService emailConfig;
|
||||||
private String emailRegistrationSubject;
|
|
||||||
|
|
||||||
@Value("email.resetpasswordsubject")
|
public UserAccountController(
|
||||||
private String resetPasswordSubject;
|
UserRepository userRepository,
|
||||||
|
ConfirmationTokenRepository confirmationTokenRepository,
|
||||||
@Value("email.registration")
|
EmailSenderService emailSenderService,
|
||||||
private String emailRegistrationText;
|
BCryptPasswordEncoder encoder,
|
||||||
|
EmailConfigurationService emailConfig) {
|
||||||
@Value("email.resetpassword")
|
this.userRepository = userRepository;
|
||||||
private String resetPasswordText;
|
this.confirmationTokenRepository = confirmationTokenRepository;
|
||||||
|
this.emailSenderService = emailSenderService;
|
||||||
@Value("email.serverhost")
|
this.encoder = encoder;
|
||||||
private String serverHost;
|
this.emailConfig = emailConfig;
|
||||||
|
}
|
||||||
|
|
||||||
private void sendEmail(String email, ConfirmationToken token, boolean isRegistration) {
|
private void sendEmail(String email, ConfirmationToken token, boolean isRegistration) {
|
||||||
SimpleMailMessage mailMessage = new SimpleMailMessage();
|
SimpleMailMessage mailMessage = new SimpleMailMessage();
|
||||||
mailMessage.setTo(email);
|
mailMessage.setTo(email);
|
||||||
mailMessage.setSubject(isRegistration ? emailRegistrationSubject : resetPasswordSubject);
|
mailMessage.setSubject(
|
||||||
|
isRegistration
|
||||||
|
? emailConfig.getRegistrationSubject()
|
||||||
|
: emailConfig.getResetPasswordSubject());
|
||||||
mailMessage.setFrom("smarthut.sm@gmail.com");
|
mailMessage.setFrom("smarthut.sm@gmail.com");
|
||||||
mailMessage.setText(
|
mailMessage.setText(
|
||||||
(isRegistration ? emailRegistrationText : resetPasswordText)
|
(isRegistration ? emailConfig.getRegistration() : emailConfig.getResetPassword())
|
||||||
+ " "
|
+ " "
|
||||||
+ serverHost
|
+ (isRegistration
|
||||||
+ "/register/confirm-account?token="
|
? emailConfig.getRegistrationPath()
|
||||||
|
: emailConfig.getResetPasswordPath())
|
||||||
+ token.getConfirmationToken());
|
+ token.getConfirmationToken());
|
||||||
|
|
||||||
emailSenderService.sendEmail(mailMessage);
|
emailSenderService.sendEmail(mailMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unauthenticated endpoint to call to send a password reset email
|
||||||
|
*
|
||||||
|
* @param registrationData registration data of the new user
|
||||||
|
* @return success
|
||||||
|
* @throws DuplicateRegistrationException if a user exists with same email or username
|
||||||
|
*/
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public OkResponse registerUser(@Valid @RequestBody UserRegistrationRequest registrationData)
|
public OkResponse registerUser(@Valid @RequestBody UserRegistrationRequest registrationData)
|
||||||
throws DuplicateRegistrationException {
|
throws DuplicateRegistrationException {
|
||||||
|
@ -104,6 +115,13 @@ public class UserAccountController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unauthenticated endpoint to call to send a password reset email
|
||||||
|
*
|
||||||
|
* @param resetRequest a JSON object containing the email of the user to reset
|
||||||
|
* @return success
|
||||||
|
* @throws UserNotFoundException if given email does not belong to any user
|
||||||
|
*/
|
||||||
@PostMapping("/init-reset-password")
|
@PostMapping("/init-reset-password")
|
||||||
public OkResponse initResetPassword(@Valid @RequestBody InitPasswordResetRequest resetRequest)
|
public OkResponse initResetPassword(@Valid @RequestBody InitPasswordResetRequest resetRequest)
|
||||||
throws UserNotFoundException {
|
throws UserNotFoundException {
|
||||||
|
@ -121,6 +139,10 @@ public class UserAccountController {
|
||||||
} while (confirmationTokenRepository.findByConfirmationToken(token.getConfirmationToken())
|
} while (confirmationTokenRepository.findByConfirmationToken(token.getConfirmationToken())
|
||||||
!= null);
|
!= null);
|
||||||
|
|
||||||
|
// Delete existing email password reset tokens
|
||||||
|
confirmationTokenRepository.deleteByUserAndResetPassword(toReset, true);
|
||||||
|
|
||||||
|
// Save new token
|
||||||
confirmationTokenRepository.save(token);
|
confirmationTokenRepository.save(token);
|
||||||
|
|
||||||
sendEmail(toReset.getEmail(), token, false);
|
sendEmail(toReset.getEmail(), token, false);
|
||||||
|
@ -128,6 +150,13 @@ public class UserAccountController {
|
||||||
return new OkResponse();
|
return new OkResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unauthenticated endpoint to call with token sent by email to reset password
|
||||||
|
*
|
||||||
|
* @param resetRequest the token given via email and the new password
|
||||||
|
* @return success
|
||||||
|
* @throws EmailTokenNotFoundException if given token is not a valid token for password reset
|
||||||
|
*/
|
||||||
@PutMapping("/reset-password")
|
@PutMapping("/reset-password")
|
||||||
public OkResponse resetPassword(@Valid @RequestBody PasswordResetRequest resetRequest)
|
public OkResponse resetPassword(@Valid @RequestBody PasswordResetRequest resetRequest)
|
||||||
throws EmailTokenNotFoundException {
|
throws EmailTokenNotFoundException {
|
||||||
|
@ -135,7 +164,7 @@ public class UserAccountController {
|
||||||
confirmationTokenRepository.findByConfirmationToken(
|
confirmationTokenRepository.findByConfirmationToken(
|
||||||
resetRequest.getConfirmationToken());
|
resetRequest.getConfirmationToken());
|
||||||
|
|
||||||
if (token == null || token.getResetPassword()) {
|
if (token == null || !token.getResetPassword()) {
|
||||||
throw new EmailTokenNotFoundException();
|
throw new EmailTokenNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,9 +172,20 @@ public class UserAccountController {
|
||||||
user.setPassword(encoder.encode(resetRequest.getPassword()));
|
user.setPassword(encoder.encode(resetRequest.getPassword()));
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
|
|
||||||
|
// Delete token to prevent further password changes
|
||||||
|
confirmationTokenRepository.delete(token);
|
||||||
|
|
||||||
return new OkResponse();
|
return new OkResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unauthenticated endpoint to call with token sent by email to enable user
|
||||||
|
*
|
||||||
|
* @param confirmationToken the token given via email
|
||||||
|
* @return success
|
||||||
|
* @throws EmailTokenNotFoundException if given token is not a valid token for email
|
||||||
|
* confirmation
|
||||||
|
*/
|
||||||
@GetMapping(value = "/confirm-account")
|
@GetMapping(value = "/confirm-account")
|
||||||
public OkResponse confirmUserAccount(@RequestParam("token") @NotNull String confirmationToken)
|
public OkResponse confirmUserAccount(@RequestParam("token") @NotNull String confirmationToken)
|
||||||
throws EmailTokenNotFoundException {
|
throws EmailTokenNotFoundException {
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import javax.transaction.Transactional;
|
||||||
import org.springframework.data.repository.CrudRepository;
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
|
||||||
public interface ConfirmationTokenRepository extends CrudRepository<ConfirmationToken, String> {
|
public interface ConfirmationTokenRepository extends CrudRepository<ConfirmationToken, String> {
|
||||||
ConfirmationToken findByConfirmationToken(String confirmationToken);
|
ConfirmationToken findByConfirmationToken(String confirmationToken);
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
void deleteByUserAndResetPassword(User user, boolean resetPassword);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,8 @@ spring.mail.properties.mail.smtp.writetimeout=5000
|
||||||
|
|
||||||
email.registrationsubject=Complete your SmartHut.sm registration
|
email.registrationsubject=Complete your SmartHut.sm registration
|
||||||
email.registration=To confirm your registration, please click here:
|
email.registration=To confirm your registration, please click here:
|
||||||
|
email.registraionpath=http://localhost:8080/register/confirm-account?token=
|
||||||
|
|
||||||
email.resetpasswordsubject=SmartHut.sm password reset
|
email.resetpasswordsubject=SmartHut.sm password reset
|
||||||
email.resetpassword=To reset your password, please click here:
|
email.resetpassword=To reset your password, please click here:
|
||||||
|
email.resetpasswordpath=http://localhost:3000/password-reset?token=
|
||||||
email.serverhost=http://localhost:8080/
|
|
Loading…
Reference in a new issue