Added support for password reset (still needs testing)
This commit is contained in:
parent
6d2f7a25fb
commit
860fae779d
6 changed files with 171 additions and 25 deletions
|
@ -52,10 +52,11 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
.authorizeRequests()
|
.authorizeRequests()
|
||||||
.antMatchers(
|
.antMatchers(
|
||||||
"/auth/login",
|
"/auth/login",
|
||||||
"/auth/register",
|
|
||||||
"/swagger-ui.html",
|
"/swagger-ui.html",
|
||||||
"/register",
|
"/register",
|
||||||
"/register/confirm-account",
|
"/register/confirm-account",
|
||||||
|
"/register/init-reset-password",
|
||||||
|
"/register/reset-password",
|
||||||
"/v2/api-docs",
|
"/v2/api-docs",
|
||||||
"/webjars/**",
|
"/webjars/**",
|
||||||
"/swagger-resources/**",
|
"/swagger-resources/**",
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||||
|
|
||||||
|
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.UserRegistrationRequest;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateRegistrationException;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateRegistrationException;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.EmailTokenNotFoundException;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.EmailTokenNotFoundException;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UserNotFoundException;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationToken;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationToken;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
|
||||||
|
@ -12,15 +15,11 @@ 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.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.GetMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
|
@ -35,6 +34,36 @@ public class UserAccountController {
|
||||||
|
|
||||||
@Autowired private BCryptPasswordEncoder encoder;
|
@Autowired private BCryptPasswordEncoder encoder;
|
||||||
|
|
||||||
|
@Value("email.registrationsubject")
|
||||||
|
private String emailRegistrationSubject;
|
||||||
|
|
||||||
|
@Value("email.resetpasswordsubject")
|
||||||
|
private String resetPasswordSubject;
|
||||||
|
|
||||||
|
@Value("email.registration")
|
||||||
|
private String emailRegistrationText;
|
||||||
|
|
||||||
|
@Value("email.resetpassword")
|
||||||
|
private String resetPasswordText;
|
||||||
|
|
||||||
|
@Value("email.serverhost")
|
||||||
|
private String serverHost;
|
||||||
|
|
||||||
|
private void sendEmail(String email, ConfirmationToken token, boolean isRegistration) {
|
||||||
|
SimpleMailMessage mailMessage = new SimpleMailMessage();
|
||||||
|
mailMessage.setTo(email);
|
||||||
|
mailMessage.setSubject(isRegistration ? emailRegistrationSubject : resetPasswordSubject);
|
||||||
|
mailMessage.setFrom("smarthut.sm@gmail.com");
|
||||||
|
mailMessage.setText(
|
||||||
|
(isRegistration ? emailRegistrationText : resetPasswordText)
|
||||||
|
+ " "
|
||||||
|
+ serverHost
|
||||||
|
+ "/register/confirm-account?token="
|
||||||
|
+ token.getConfirmationToken());
|
||||||
|
|
||||||
|
emailSenderService.sendEmail(mailMessage);
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public OkResponse registerUser(@Valid @RequestBody UserRegistrationRequest registrationData)
|
public OkResponse registerUser(@Valid @RequestBody UserRegistrationRequest registrationData)
|
||||||
throws DuplicateRegistrationException {
|
throws DuplicateRegistrationException {
|
||||||
|
@ -60,35 +89,72 @@ public class UserAccountController {
|
||||||
toSave.setEmail(registrationData.getEmail());
|
toSave.setEmail(registrationData.getEmail());
|
||||||
userRepository.save(toSave);
|
userRepository.save(toSave);
|
||||||
|
|
||||||
ConfirmationToken confirmationToken = new ConfirmationToken(toSave);
|
ConfirmationToken token;
|
||||||
|
do {
|
||||||
|
token = new ConfirmationToken(toSave);
|
||||||
|
} while (confirmationTokenRepository.findByConfirmationToken(
|
||||||
|
token.getConfirmationToken())
|
||||||
|
!= null);
|
||||||
|
|
||||||
confirmationTokenRepository.save(confirmationToken);
|
confirmationTokenRepository.save(token);
|
||||||
|
|
||||||
SimpleMailMessage mailMessage = new SimpleMailMessage();
|
sendEmail(toSave.getEmail(), token, true);
|
||||||
mailMessage.setTo(registrationData.getEmail());
|
|
||||||
mailMessage.setSubject("Complete Registration!");
|
|
||||||
mailMessage.setFrom("smarthut.sm@gmail.com");
|
|
||||||
mailMessage.setText(
|
|
||||||
"To confirm your account, please click here : "
|
|
||||||
+ "http://localhost:8080/register/confirm-account?token="
|
|
||||||
+ confirmationToken.getConfirmationToken());
|
|
||||||
|
|
||||||
emailSenderService.sendEmail(mailMessage);
|
|
||||||
|
|
||||||
return new OkResponse();
|
return new OkResponse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/init-reset-password")
|
||||||
|
public OkResponse initResetPassword(@Valid @RequestBody InitPasswordResetRequest resetRequest)
|
||||||
|
throws UserNotFoundException {
|
||||||
|
final User toReset = userRepository.findByEmailIgnoreCase(resetRequest.getEmail());
|
||||||
|
|
||||||
|
// Check if an User with the same email already exists
|
||||||
|
if (toReset == null) {
|
||||||
|
throw new UserNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfirmationToken token;
|
||||||
|
do {
|
||||||
|
token = new ConfirmationToken(toReset);
|
||||||
|
token.setResetPassword(true);
|
||||||
|
} while (confirmationTokenRepository.findByConfirmationToken(token.getConfirmationToken())
|
||||||
|
!= null);
|
||||||
|
|
||||||
|
confirmationTokenRepository.save(token);
|
||||||
|
|
||||||
|
sendEmail(toReset.getEmail(), token, false);
|
||||||
|
|
||||||
|
return new OkResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/reset-password")
|
||||||
|
public OkResponse resetPassword(@Valid @RequestBody PasswordResetRequest resetRequest)
|
||||||
|
throws EmailTokenNotFoundException {
|
||||||
|
final ConfirmationToken token =
|
||||||
|
confirmationTokenRepository.findByConfirmationToken(
|
||||||
|
resetRequest.getConfirmationToken());
|
||||||
|
|
||||||
|
if (token == null || token.getResetPassword()) {
|
||||||
|
throw new EmailTokenNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
final User user = token.getUser();
|
||||||
|
user.setPassword(encoder.encode(resetRequest.getPassword()));
|
||||||
|
userRepository.save(user);
|
||||||
|
|
||||||
|
return new OkResponse();
|
||||||
|
}
|
||||||
|
|
||||||
@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 {
|
||||||
final ConfirmationToken token =
|
final ConfirmationToken token =
|
||||||
confirmationTokenRepository.findByConfirmationToken(confirmationToken);
|
confirmationTokenRepository.findByConfirmationToken(confirmationToken);
|
||||||
|
|
||||||
if (token != null) {
|
if (token != null && !token.getResetPassword()) {
|
||||||
final User user = userRepository.findByEmailIgnoreCase(token.getUser().getEmail());
|
token.getUser().setEnabled(true);
|
||||||
user.setEnabled(true);
|
userRepository.save(token.getUser());
|
||||||
userRepository.save(user);
|
|
||||||
// TODO: redirect to frontend
|
// TODO: redirect to frontend
|
||||||
return new OkResponse();
|
return new OkResponse();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Email;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
|
|
||||||
|
/** DTO for password reset request */
|
||||||
|
public class InitPasswordResetRequest {
|
||||||
|
/**
|
||||||
|
* The user's email (validated according to criteria used in <code>>input type="email"<>
|
||||||
|
* </code>, technically not RFC 5322 compliant
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "Please provide an email")
|
||||||
|
@Email(message = "Please provide a valid email address")
|
||||||
|
@Pattern(regexp = ".+@.+\\..+", message = "Please provide a valid email address")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.*;
|
||||||
|
|
||||||
|
/** DTO for password reset request */
|
||||||
|
public class PasswordResetRequest {
|
||||||
|
|
||||||
|
@NotNull private String confirmationToken;
|
||||||
|
|
||||||
|
/** A properly salted way to store the password */
|
||||||
|
@NotNull
|
||||||
|
@NotEmpty(message = "Please provide a password")
|
||||||
|
@Size(
|
||||||
|
min = 6,
|
||||||
|
max = 255,
|
||||||
|
message = "Your password should be at least 6 characters long and up to 255 chars long")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
public String getConfirmationToken() {
|
||||||
|
return confirmationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConfirmationToken(String confirmationToken) {
|
||||||
|
this.confirmationToken = confirmationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ public class ConfirmationToken {
|
||||||
@Column(name = "id", updatable = false, nullable = false)
|
@Column(name = "id", updatable = false, nullable = false)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@Column(name = "confirmation_token")
|
@Column(name = "confirmation_token", unique = true)
|
||||||
private String confirmationToken;
|
private String confirmationToken;
|
||||||
|
|
||||||
@Temporal(TemporalType.TIMESTAMP)
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
@ -31,10 +31,14 @@ public class ConfirmationToken {
|
||||||
@JoinColumn(nullable = false, name = "user_id")
|
@JoinColumn(nullable = false, name = "user_id")
|
||||||
private User user;
|
private User user;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
private Boolean resetPassword;
|
||||||
|
|
||||||
public ConfirmationToken(User user) {
|
public ConfirmationToken(User user) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
createdDate = new Date();
|
createdDate = new Date();
|
||||||
confirmationToken = UUID.randomUUID().toString();
|
confirmationToken = UUID.randomUUID().toString();
|
||||||
|
resetPassword = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Constructor for hibernate reflective stuff things whatever */
|
/** Constructor for hibernate reflective stuff things whatever */
|
||||||
|
@ -71,4 +75,12 @@ public class ConfirmationToken {
|
||||||
public void setUser(User user) {
|
public void setUser(User user) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getResetPassword() {
|
||||||
|
return resetPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResetPassword(Boolean resetPassword) {
|
||||||
|
this.resetPassword = resetPassword;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,4 +22,12 @@ spring.mail.properties.mail.smtp.starttls.required=true
|
||||||
spring.mail.properties.mail.smtp.auth=true
|
spring.mail.properties.mail.smtp.auth=true
|
||||||
spring.mail.properties.mail.smtp.connectiontimeout=5000
|
spring.mail.properties.mail.smtp.connectiontimeout=5000
|
||||||
spring.mail.properties.mail.smtp.timeout=5000
|
spring.mail.properties.mail.smtp.timeout=5000
|
||||||
spring.mail.properties.mail.smtp.writetimeout=5000
|
spring.mail.properties.mail.smtp.writetimeout=5000
|
||||||
|
|
||||||
|
email.registrationsubject=Complete your SmartHut.sm registration
|
||||||
|
email.registration=To confirm your registration, please click here:
|
||||||
|
|
||||||
|
email.resetpasswordsubject=SmartHut.sm password reset
|
||||||
|
email.resetpassword=To reset your password, please click here:
|
||||||
|
|
||||||
|
email.serverhost=http://localhost:8080/
|
Loading…
Reference in a new issue