diff --git a/app/src/main/java/it/chalmers/gamma/adapter/primary/web/ForgotPasswordController.java b/app/src/main/java/it/chalmers/gamma/adapter/primary/web/ForgotPasswordController.java
index c0e958a77..ce058fd22 100644
--- a/app/src/main/java/it/chalmers/gamma/adapter/primary/web/ForgotPasswordController.java
+++ b/app/src/main/java/it/chalmers/gamma/adapter/primary/web/ForgotPasswordController.java
@@ -1,9 +1,16 @@
package it.chalmers.gamma.adapter.primary.web;
+import static it.chalmers.gamma.adapter.primary.web.WebValidationHelper.validateObject;
+
+import it.chalmers.gamma.app.common.Email.EmailValidator;
+import it.chalmers.gamma.app.user.domain.Cid.CidValidator;
import it.chalmers.gamma.app.user.passwordreset.UserResetPasswordFacade;
+import it.chalmers.gamma.app.validation.FailedValidation;
+import it.chalmers.gamma.app.validation.SuccessfulValidation;
+import it.chalmers.gamma.app.validation.ValidationResult;
+import it.chalmers.gamma.app.validation.Validator;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
-import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
@@ -18,9 +25,31 @@ public ForgotPasswordController(UserResetPasswordFacade userResetPasswordFacade)
this.userResetPasswordFacade = userResetPasswordFacade;
}
+ public static final class IdentifierValidator implements Validator {
+
+ @Override
+ public ValidationResult validate(String value) {
+ if (new CidValidator().validate(value) instanceof SuccessfulValidation) {
+ return new SuccessfulValidation();
+ } else if (new EmailValidator().validate(value) instanceof SuccessfulValidation) {
+ return new SuccessfulValidation();
+ }
+
+ return new FailedValidation("Neither a valid cid or email");
+ }
+ }
+
+ public record ForgotPassword(@ValidatedWith(IdentifierValidator.class) String cidOrEmail) {}
+
@GetMapping("/forgot-password")
public ModelAndView getForgotPassword(
- @RequestHeader(value = "HX-Request", required = false) boolean htmxRequest) {
+ @RequestHeader(value = "HX-Request", required = false) boolean htmxRequest,
+ ForgotPassword form,
+ BindingResult bindingResult) {
+ if (form == null) {
+ form = new ForgotPassword("");
+ }
+
ModelAndView mv = new ModelAndView();
if (htmxRequest) {
@@ -30,13 +59,15 @@ public ModelAndView getForgotPassword(
mv.addObject("page", "pages/forgot-password");
}
- mv.addObject("form", new ForgotPassword(""));
+ mv.addObject("form", form);
+
+ if (bindingResult.hasErrors()) {
+ mv.addObject(BindingResult.MODEL_KEY_PREFIX + "form", bindingResult);
+ }
return mv;
}
- public record ForgotPassword(String email) {}
-
@PostMapping("/forgot-password")
public ModelAndView sendForgotPassword(
@RequestHeader(value = "HX-Request", required = false) boolean htmxRequest,
@@ -44,23 +75,17 @@ public ModelAndView sendForgotPassword(
final BindingResult bindingResult) {
ModelAndView mv = new ModelAndView();
+ validateObject(form, bindingResult);
+
+ if (bindingResult.hasErrors()) {
+ return getForgotPassword(htmxRequest, form, bindingResult);
+ }
+
try {
- this.userResetPasswordFacade.startResetPasswordProcess(form.email);
+ this.userResetPasswordFacade.startResetPasswordProcess(form.cidOrEmail);
mv.setViewName("redirect:forgot-password/finalize");
} catch (UserResetPasswordFacade.PasswordResetProcessException e) {
mv.setViewName("redirect:forgot-password/finalize");
- } catch (IllegalArgumentException e) {
- if (htmxRequest) {
- mv.setViewName("pages/forgot-password");
- } else {
- mv.setViewName("index");
- mv.addObject("page", "pages/forgot-password");
- }
-
- bindingResult.addError(new FieldError("form", "email", e.getMessage()));
-
- mv.addObject("form", new ForgotPassword(""));
- mv.addObject(BindingResult.MODEL_KEY_PREFIX + "form", bindingResult);
}
return mv;
@@ -99,7 +124,7 @@ public ModelAndView finalizeForgotPassword(
ModelAndView mv = new ModelAndView();
- mv.setViewName("redirect:login");
+ mv.setViewName("redirect:login?password-reset");
return mv;
}
diff --git a/app/src/main/java/it/chalmers/gamma/adapter/primary/web/LoginController.java b/app/src/main/java/it/chalmers/gamma/adapter/primary/web/LoginController.java
index 514ceba46..a41b55b0f 100644
--- a/app/src/main/java/it/chalmers/gamma/adapter/primary/web/LoginController.java
+++ b/app/src/main/java/it/chalmers/gamma/adapter/primary/web/LoginController.java
@@ -19,6 +19,7 @@ public ModelAndView getLogin(
@RequestParam(value = "authorizing", required = false) String authorizing,
@RequestParam(value = "deleted", required = false) String deleted,
@RequestParam(value = "account-created", required = false) String accountCreated,
+ @RequestParam(value = "password-reset", required = false) String passwordReset,
@RequestHeader(value = "HX-Request", required = false) boolean htmxRequest,
@RequestParam(value = "throttle", required = false) String throttle,
HttpServletResponse response) {
@@ -40,6 +41,7 @@ public ModelAndView getLogin(
boolean isThrottled = throttle != null;
boolean isDeleted = deleted != null;
boolean isAccountCreated = accountCreated != null;
+ boolean isPasswordReset = passwordReset != null;
mv.addObject("error", error);
mv.addObject("logout", logout);
@@ -47,6 +49,7 @@ public ModelAndView getLogin(
mv.addObject("deleted", isDeleted);
mv.addObject("throttle", isThrottled);
mv.addObject("accountCreated", isAccountCreated);
+ mv.addObject("passwordReset", isPasswordReset);
response.addHeader("HX-Retarget", "body");
diff --git a/app/src/main/java/it/chalmers/gamma/adapter/primary/web/RegisterAccountController.java b/app/src/main/java/it/chalmers/gamma/adapter/primary/web/RegisterAccountController.java
index 38a1bd8bf..d82c01f7f 100644
--- a/app/src/main/java/it/chalmers/gamma/adapter/primary/web/RegisterAccountController.java
+++ b/app/src/main/java/it/chalmers/gamma/adapter/primary/web/RegisterAccountController.java
@@ -1,5 +1,7 @@
package it.chalmers.gamma.adapter.primary.web;
+import static it.chalmers.gamma.adapter.primary.web.WebValidationHelper.validateObject;
+
import it.chalmers.gamma.app.common.Email.EmailValidator;
import it.chalmers.gamma.app.user.UserCreationFacade;
import it.chalmers.gamma.app.user.activation.domain.UserActivationToken.UserActivationTokenValidator;
@@ -9,6 +11,7 @@
import it.chalmers.gamma.app.user.domain.LastName.LastNameValidator;
import it.chalmers.gamma.app.user.domain.Nick.NickValidator;
import it.chalmers.gamma.app.user.domain.UnencryptedPassword.UnencryptedPasswordValidator;
+import java.time.Year;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
@@ -19,10 +22,6 @@
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.servlet.ModelAndView;
-import java.time.Year;
-
-import static it.chalmers.gamma.adapter.primary.web.WebValidationHelper.validateObject;
-
@Controller
public class RegisterAccountController {
@@ -37,9 +36,11 @@ public RegisterAccountController(UserCreationFacade userCreationFacade) {
@GetMapping("/activate-cid")
public ModelAndView getActivateCid(
- @RequestHeader(value = "HX-Request", required = false) boolean htmxRequest, ActivateCidForm form, BindingResult bindingResult) {
+ @RequestHeader(value = "HX-Request", required = false) boolean htmxRequest,
+ ActivateCidForm form,
+ BindingResult bindingResult) {
- if(form == null) {
+ if (form == null) {
form = new ActivateCidForm("");
}
@@ -51,6 +52,7 @@ public ModelAndView getActivateCid(
mv.addObject("page", "register-account/activate-cid");
}
+ mv.addObject("form", form);
mv.addObject(BindingResult.MODEL_KEY_PREFIX + "form", bindingResult);
return mv;
@@ -123,32 +125,29 @@ public ModelAndView registerAccount(
try {
if (!bindingResult.hasErrors()) {
this.userCreationFacade.createUserWithCode(
- new UserCreationFacade.NewUser(
- form.password,
- form.nick,
- form.firstName,
- form.lastName,
- form.email,
- form.acceptanceYear,
- form.cid,
- form.language),
- form.code,
- form.confirmPassword,
- form.acceptUserAgreement);
+ new UserCreationFacade.NewUser(
+ form.password,
+ form.nick,
+ form.firstName,
+ form.lastName,
+ form.email,
+ form.acceptanceYear,
+ form.cid,
+ form.language),
+ form.code,
+ form.confirmPassword,
+ form.acceptUserAgreement);
}
} catch (UserCreationFacade.SomePropertyNotUniqueRuntimeException e) {
bindingResult.addError(
- new ObjectError(
- "global",
- "Please double check what you have entered. Please send an email to ita@chalmers.it if your issues persist."));
+ new ObjectError(
+ "global",
+ "Please double check what you have entered. Please send an email to ita@chalmers.it if your issues persist."));
LOGGER.info(
- "Some property wasn't unique when a user tried to create an account. More info on debug level...");
+ "Some property wasn't unique when a user tried to create an account. More info on debug level...");
LOGGER.debug(e.getMessage());
} catch (IllegalArgumentException e) {
- bindingResult.addError(
- new ObjectError("global",
- e.getMessage())
- );
+ bindingResult.addError(new ObjectError("global", e.getMessage()));
}
if (bindingResult.hasErrors()) {
diff --git a/app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/password/UserPasswordResetEntity.java b/app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/UserPasswordResetEntity.java
similarity index 94%
rename from app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/password/UserPasswordResetEntity.java
rename to app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/UserPasswordResetEntity.java
index e989b2e0a..e14b45294 100644
--- a/app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/password/UserPasswordResetEntity.java
+++ b/app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/UserPasswordResetEntity.java
@@ -1,4 +1,4 @@
-package it.chalmers.gamma.adapter.secondary.jpa.user.password;
+package it.chalmers.gamma.adapter.secondary.jpa.user;
import it.chalmers.gamma.adapter.secondary.jpa.util.ImmutableEntity;
import it.chalmers.gamma.app.user.domain.UserId;
diff --git a/app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/password/UserPasswordResetJpaRepository.java b/app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/UserPasswordResetJpaRepository.java
similarity index 85%
rename from app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/password/UserPasswordResetJpaRepository.java
rename to app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/UserPasswordResetJpaRepository.java
index 2484c21ac..f91a426b6 100644
--- a/app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/password/UserPasswordResetJpaRepository.java
+++ b/app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/UserPasswordResetJpaRepository.java
@@ -1,4 +1,4 @@
-package it.chalmers.gamma.adapter.secondary.jpa.user.password;
+package it.chalmers.gamma.adapter.secondary.jpa.user;
import java.util.Optional;
import java.util.UUID;
diff --git a/app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/password/UserPasswordResetRepositoryAdapter.java b/app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/UserPasswordResetRepositoryAdapter.java
similarity index 76%
rename from app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/password/UserPasswordResetRepositoryAdapter.java
rename to app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/UserPasswordResetRepositoryAdapter.java
index daa6dcc4f..140c7793c 100644
--- a/app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/password/UserPasswordResetRepositoryAdapter.java
+++ b/app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/UserPasswordResetRepositoryAdapter.java
@@ -1,8 +1,7 @@
-package it.chalmers.gamma.adapter.secondary.jpa.user.password;
+package it.chalmers.gamma.adapter.secondary.jpa.user;
-import it.chalmers.gamma.adapter.secondary.jpa.user.UserEntity;
-import it.chalmers.gamma.adapter.secondary.jpa.user.UserJpaRepository;
import it.chalmers.gamma.app.common.Email;
+import it.chalmers.gamma.app.user.domain.Cid;
import it.chalmers.gamma.app.user.domain.UserId;
import it.chalmers.gamma.app.user.passwordreset.domain.PasswordResetRepository;
import it.chalmers.gamma.app.user.passwordreset.domain.PasswordResetToken;
@@ -24,15 +23,7 @@ public UserPasswordResetRepositoryAdapter(
this.userPasswordResetJpaRepository = userPasswordResetJpaRepository;
}
- @Override
- public PasswordReset createNewToken(Email email) throws UserNotFoundException {
- Optional maybeUserEntity = this.userJpaRepository.findByEmail(email.value());
-
- if (maybeUserEntity.isEmpty()) {
- throw new UserNotFoundException();
- }
-
- UserEntity userEntity = maybeUserEntity.get();
+ private PasswordReset createNewToken(UserEntity userEntity) {
PasswordResetToken token = PasswordResetToken.generate();
UserPasswordResetEntity userPasswordResetEntity =
@@ -45,7 +36,29 @@ public PasswordReset createNewToken(Email email) throws UserNotFoundException {
this.userPasswordResetJpaRepository.save(userPasswordResetEntity);
- return new PasswordReset(token, new UserId(userEntity.getId()));
+ return new PasswordReset(token, new Email(userEntity.email));
+ }
+
+ @Override
+ public PasswordReset createNewToken(Email email) throws UserNotFoundException {
+ Optional maybeUserEntity = this.userJpaRepository.findByEmail(email.value());
+
+ if (maybeUserEntity.isEmpty()) {
+ throw new UserNotFoundException();
+ }
+
+ return this.createNewToken(maybeUserEntity.get());
+ }
+
+ @Override
+ public PasswordReset createNewToken(Cid cid) throws UserNotFoundException {
+ Optional maybeUserEntity = this.userJpaRepository.findByCid(cid.value());
+
+ if (maybeUserEntity.isEmpty()) {
+ throw new UserNotFoundException();
+ }
+
+ return this.createNewToken(maybeUserEntity.get());
}
@Override
diff --git a/app/src/main/java/it/chalmers/gamma/app/user/UserCreationFacade.java b/app/src/main/java/it/chalmers/gamma/app/user/UserCreationFacade.java
index 1dac83cc8..14ab96af0 100644
--- a/app/src/main/java/it/chalmers/gamma/app/user/UserCreationFacade.java
+++ b/app/src/main/java/it/chalmers/gamma/app/user/UserCreationFacade.java
@@ -1,5 +1,8 @@
package it.chalmers.gamma.app.user;
+import static it.chalmers.gamma.app.authentication.AccessGuard.isAdmin;
+import static it.chalmers.gamma.app.authentication.AccessGuard.isNotSignedIn;
+
import it.chalmers.gamma.app.Facade;
import it.chalmers.gamma.app.authentication.AccessGuard;
import it.chalmers.gamma.app.common.Email;
@@ -10,15 +13,11 @@
import it.chalmers.gamma.app.user.allowlist.AllowListRepository;
import it.chalmers.gamma.app.user.domain.*;
import jakarta.transaction.Transactional;
+import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
-import java.util.UUID;
-
-import static it.chalmers.gamma.app.authentication.AccessGuard.isAdmin;
-import static it.chalmers.gamma.app.authentication.AccessGuard.isNotSignedIn;
-
@Service
public class UserCreationFacade extends Facade {
@@ -31,17 +30,18 @@ public class UserCreationFacade extends Facade {
private final AllowListRepository allowListRepository;
public UserCreationFacade(
- AccessGuard accessGuard,
- MailService mailService,
- UserActivationRepository userActivationRepository,
- UserRepository userRepository,
- ThrottlingService throttlingService, AllowListRepository allowListRepository) {
+ AccessGuard accessGuard,
+ MailService mailService,
+ UserActivationRepository userActivationRepository,
+ UserRepository userRepository,
+ ThrottlingService throttlingService,
+ AllowListRepository allowListRepository) {
super(accessGuard);
this.mailService = mailService;
this.userActivationRepository = userActivationRepository;
this.userRepository = userRepository;
this.throttlingService = throttlingService;
- this.allowListRepository = allowListRepository;
+ this.allowListRepository = allowListRepository;
}
public void tryToActivateUser(String cidRaw) {
@@ -56,9 +56,9 @@ public void tryToActivateUser(String cidRaw) {
} else {
LOGGER.info("Throttling an activation and its email...");
}
- LOGGER.info("Cid {} has been activated", cid);
+ LOGGER.info("Cid {} has been activated", cid);
} catch (UserActivationRepository.CidNotAllowedException e) {
- LOGGER.info("Someone tried to activate the cid: {}", cid);
+ LOGGER.info("Someone tried to activate the cid: {}", cid);
}
}
diff --git a/app/src/main/java/it/chalmers/gamma/app/user/passwordreset/UserResetPasswordFacade.java b/app/src/main/java/it/chalmers/gamma/app/user/passwordreset/UserResetPasswordFacade.java
index 14da2e4ce..3817f5c3d 100644
--- a/app/src/main/java/it/chalmers/gamma/app/user/passwordreset/UserResetPasswordFacade.java
+++ b/app/src/main/java/it/chalmers/gamma/app/user/passwordreset/UserResetPasswordFacade.java
@@ -7,11 +7,13 @@
import it.chalmers.gamma.app.common.Email;
import it.chalmers.gamma.app.mail.domain.MailService;
import it.chalmers.gamma.app.throttling.ThrottlingService;
+import it.chalmers.gamma.app.user.domain.Cid;
import it.chalmers.gamma.app.user.domain.GammaUser;
import it.chalmers.gamma.app.user.domain.UnencryptedPassword;
import it.chalmers.gamma.app.user.domain.UserRepository;
import it.chalmers.gamma.app.user.passwordreset.domain.PasswordResetRepository;
import it.chalmers.gamma.app.user.passwordreset.domain.PasswordResetToken;
+import it.chalmers.gamma.app.validation.SuccessfulValidation;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -39,25 +41,30 @@ public UserResetPasswordFacade(
this.throttlingService = throttlingService;
}
- public void startResetPasswordProcess(String emailString) throws PasswordResetProcessException {
+ public void startResetPasswordProcess(String cidOrEmailString)
+ throws PasswordResetProcessException {
this.accessGuard.require(isNotSignedIn());
- Email email = new Email(emailString);
-
try {
- PasswordResetRepository.PasswordReset passwordReset =
- this.passwordResetRepository.createNewToken(email);
+ PasswordResetRepository.PasswordReset passwordReset;
+ if (new Email.EmailValidator().validate(cidOrEmailString) instanceof SuccessfulValidation) {
+ passwordReset = this.passwordResetRepository.createNewToken(new Email(cidOrEmailString));
+ } else if (new Cid.CidValidator().validate(cidOrEmailString)
+ instanceof SuccessfulValidation) {
+ passwordReset = this.passwordResetRepository.createNewToken(new Cid(cidOrEmailString));
+ } else {
+ throw new IllegalArgumentException("Neither an email nor a cid.");
+ }
- if (throttlingService.canProceed(passwordReset.userId().value() + "-password-reset", 3)) {
- sendPasswordResetTokenMail(email, passwordReset.token());
+ if (throttlingService.canProceed(passwordReset.email().value() + "-password-reset", 3)) {
+ sendPasswordResetTokenMail(passwordReset.email(), passwordReset.token());
} else {
LOGGER.info("Throttling password reset process triggered.");
}
} catch (PasswordResetRepository.UserNotFoundException e) {
LOGGER.debug(
- "Someone tried to reset the password for the email "
- + emailString
- + " that doesn't exist");
+ "Someone tried to reset the password for the email {} that doesn't exist",
+ cidOrEmailString);
throw new PasswordResetProcessException();
}
}
diff --git a/app/src/main/java/it/chalmers/gamma/app/user/passwordreset/domain/PasswordResetRepository.java b/app/src/main/java/it/chalmers/gamma/app/user/passwordreset/domain/PasswordResetRepository.java
index c5f5e6bbe..33a06aeb3 100644
--- a/app/src/main/java/it/chalmers/gamma/app/user/passwordreset/domain/PasswordResetRepository.java
+++ b/app/src/main/java/it/chalmers/gamma/app/user/passwordreset/domain/PasswordResetRepository.java
@@ -1,15 +1,18 @@
package it.chalmers.gamma.app.user.passwordreset.domain;
import it.chalmers.gamma.app.common.Email;
+import it.chalmers.gamma.app.user.domain.Cid;
import it.chalmers.gamma.app.user.domain.UserId;
import java.util.Optional;
public interface PasswordResetRepository {
- record PasswordReset(PasswordResetToken token, UserId userId) {}
+ record PasswordReset(PasswordResetToken token, Email email) {}
PasswordReset createNewToken(Email email) throws UserNotFoundException;
+ PasswordReset createNewToken(Cid cid) throws UserNotFoundException;
+
Optional getToken(UserId id);
void removeToken(PasswordResetToken token);
diff --git a/app/src/main/resources/templates/pages/forgot-password.html b/app/src/main/resources/templates/pages/forgot-password.html
index 520017fb8..8e42b658b 100644
--- a/app/src/main/resources/templates/pages/forgot-password.html
+++ b/app/src/main/resources/templates/pages/forgot-password.html
@@ -6,9 +6,9 @@
Reset password
- Please enter your email to begin the reset process.
+ Please enter your cid or email to begin the reset process.
-
+
diff --git a/app/src/main/resources/templates/pages/login.html b/app/src/main/resources/templates/pages/login.html
index 91d9d7986..6ffd82969 100644
--- a/app/src/main/resources/templates/pages/login.html
+++ b/app/src/main/resources/templates/pages/login.html
@@ -39,6 +39,10 @@
Your account has been created.
+
+ Your password was reset.
+
+
Register