package bo.gotthardt.user; import bo.gotthardt.email.EmailService; import bo.gotthardt.model.EmailVerification; import bo.gotthardt.model.HashedValue; import bo.gotthardt.model.User; import com.avaje.ebean.EbeanServer; import lombok.extern.slf4j.Slf4j; import org.joda.time.DateTime; import javax.inject.Inject; import java.time.Duration; import java.time.LocalDateTime; /** * Service for requesting and executing a password reset via email. * * @author Bo Gotthardt */ @Slf4j public class PasswordResetService { private static final Duration TOKEN_LIFETIME = Duration.ofDays(2); private final EbeanServer db; private final EmailService email; @Inject public PasswordResetService(EbeanServer db, EmailService email) { this.db = db; this.email = email; } /** * Request a password reset link be mail to the user with the specified username or email address. * * @param usernameOrEmail The username or email. */ public void requestPasswordReset(String usernameOrEmail) { User user = db.find(User.class).where().disjunction() .eq("username", usernameOrEmail) .eq("email", usernameOrEmail).findUnique(); if (user != null) { EmailVerification verify = db.find(EmailVerification.class).where() .eq("user", user) .eq("type", EmailVerification.Type.PASSWORD_RESET) .lt("expirationDate", DateTime.now()) .findUnique(); if (verify != null) { verify.setExpirationDate(LocalDateTime.now().plus(TOKEN_LIFETIME)); db.save(verify); } else { verify = new EmailVerification(user, TOKEN_LIFETIME, EmailVerification.Type.PASSWORD_RESET); db.save(verify); } log.info("Emailing link to '{}' to {}", verify.getUrl(), user.getEmail()); email.send(user.getEmail(), "Password reset", "Password reset: " + verify.getUrl()); } } /** * Use the specified password reset token ID to reset the associated password. * * @param token The token. * @param newPassword The new password. */ public void doPasswordReset(String token, String newPassword) { EmailVerification verify = db.find(EmailVerification.class, token); if (verify != null) { User user = verify.getUser(); if (verify.isValid() && verify.getType() == EmailVerification.Type.PASSWORD_RESET) { user.setPassword(new HashedValue(newPassword)); db.save(user); verify.setExpirationDate(LocalDateTime.now()); db.save(verify); log.info("Changed password for user {} from email token.", user); } else { log.info("Tried to change password for {} with invalid token", user); } } else { log.info("Tried to change password with non-existent token [}", token); } } }