From d51b853169aaab18db8e1633554fc962c88b22b3 Mon Sep 17 00:00:00 2001 From: Agit Rubar Demir <61833677+agitrubard@users.noreply.github.com> Date: Thu, 1 Aug 2024 09:48:50 +0300 Subject: [PATCH] AYS-363 | User Password Service Has Been Refactored with New Password Flows (#352) --- src/main/java/org/ays/auth/model/AysUser.java | 1 + .../ays/auth/model/entity/AysUserEntity.java | 3 + .../auth/service/AysUserPasswordService.java | 19 ++++- .../impl/AysUserPasswordServiceImpl.java | 78 +++++++++++-------- .../db/changelog/changes/1-ays-ddl.xml | 1 + .../auth/controller/AysAuthEndToEndTest.java | 16 +++- .../org/ays/auth/model/AysUserBuilder.java | 4 +- .../impl/AysUserPasswordServiceImplTest.java | 31 +++++--- 8 files changed, 106 insertions(+), 47 deletions(-) diff --git a/src/main/java/org/ays/auth/model/AysUser.java b/src/main/java/org/ays/auth/model/AysUser.java index 822c97296..94f823091 100644 --- a/src/main/java/org/ays/auth/model/AysUser.java +++ b/src/main/java/org/ays/auth/model/AysUser.java @@ -111,6 +111,7 @@ public static class Password extends BaseDomainModel { private String id; private String value; + private LocalDateTime forgotAt; } diff --git a/src/main/java/org/ays/auth/model/entity/AysUserEntity.java b/src/main/java/org/ays/auth/model/entity/AysUserEntity.java index c2e079d85..01184dac0 100644 --- a/src/main/java/org/ays/auth/model/entity/AysUserEntity.java +++ b/src/main/java/org/ays/auth/model/entity/AysUserEntity.java @@ -123,6 +123,9 @@ public static class PasswordEntity extends BaseEntity { @JoinColumn(name = "USER_ID", referencedColumnName = "ID") private AysUserEntity user; + @Column(name = "FORGOT_AT") + private LocalDateTime forgotAt; + } diff --git a/src/main/java/org/ays/auth/service/AysUserPasswordService.java b/src/main/java/org/ays/auth/service/AysUserPasswordService.java index 7d7a60921..a67fe726c 100644 --- a/src/main/java/org/ays/auth/service/AysUserPasswordService.java +++ b/src/main/java/org/ays/auth/service/AysUserPasswordService.java @@ -1,6 +1,9 @@ package org.ays.auth.service; import org.ays.auth.model.request.AysForgotPasswordRequest; +import org.ays.auth.util.exception.AysEmailAddressNotValidException; +import org.ays.auth.util.exception.AysUserPasswordCannotChangedException; +import org.ays.auth.util.exception.AysUserPasswordDoesNotExistException; /** * Service interface for handling user password operations. @@ -11,15 +14,27 @@ public interface AysUserPasswordService { /** * Handles the forgot password request by sending an email to the user * with instructions to create a new password. + *
+ * This method checks if a user exists with the provided email address. + * If the user exists and has no password set, a new temp password is generated. + * If the user already has a password, the forgot password timestamp is updated. + * In both cases, an email is sent to the user with instructions to create a new password. * * @param forgotPasswordRequest the request containing the user's email address. + * @throws AysEmailAddressNotValidException if no user is found with the provided email address. */ void forgotPassword(AysForgotPasswordRequest forgotPasswordRequest); /** - * Checks the existence and validity of a password reset token by its ID. + * Checks the validity of changing the user's password. + *
+ * This method verifies if the password change request is valid by checking the + * existence and expiry of the password change request associated with the given password ID. + * It throws an exception if the password cannot be changed due to invalid conditions. * - * @param passwordId the ID of the password reset token to be checked. + * @param passwordId The ID of the password to check for change validity. + * @throws AysUserPasswordDoesNotExistException if no password is found for the given ID. + * @throws AysUserPasswordCannotChangedException if the password cannot be changed due to invalid conditions. */ void checkPasswordChangingValidity(String passwordId); diff --git a/src/main/java/org/ays/auth/service/impl/AysUserPasswordServiceImpl.java b/src/main/java/org/ays/auth/service/impl/AysUserPasswordServiceImpl.java index 2314cdcd1..c1bfc6ac2 100644 --- a/src/main/java/org/ays/auth/service/impl/AysUserPasswordServiceImpl.java +++ b/src/main/java/org/ays/auth/service/impl/AysUserPasswordServiceImpl.java @@ -11,7 +11,6 @@ import org.ays.auth.util.exception.AysUserPasswordCannotChangedException; import org.ays.auth.util.exception.AysUserPasswordDoesNotExistException; import org.ays.common.util.AysRandomUtil; -import org.ays.common.util.AysUUID; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -37,15 +36,16 @@ class AysUserPasswordServiceImpl implements AysUserPasswordService { /** - * Handles the forgot password process for a user. + * Handles the forgot password request by sending an email to the user + * with instructions to create a new password. *
- * This method is triggered when a user requests to reset their password. It checks if the user's email - * address is valid and then sends a password reset email. If the user does not have an existing password, - * a new temporary password is generated and saved. - *
+ * This method checks if a user exists with the provided email address. + * If the user exists and has no password set, a new temp password is generated. + * If the user already has a password, the forgot password timestamp is updated. + * In both cases, an email is sent to the user with instructions to create a new password. * - * @param forgotPasswordRequest The request object containing the user's email address. - * @throws AysEmailAddressNotValidException if the email address provided does not exist in the system. + * @param forgotPasswordRequest The request containing the user's email address. + * @throws AysEmailAddressNotValidException if no user is found with the provided email address. */ @Override public void forgotPassword(final AysForgotPasswordRequest forgotPasswordRequest) { @@ -54,29 +54,31 @@ public void forgotPassword(final AysForgotPasswordRequest forgotPasswordRequest) final AysUser user = userReadPort.findByEmailAddress(emailAddress) .orElseThrow(() -> new AysEmailAddressNotValidException(emailAddress)); - if (user.getPassword() != null) { - userMailService.sendPasswordCreateEmail(user); - return; + if (user.getPassword() == null) { + final AysUser.Password password = AysUser.Password.builder() + .value(AysRandomUtil.generateUUID()) + .forgotAt(LocalDateTime.now()) + .build(); + user.setPassword(password); + } else { + user.getPassword().setForgotAt(LocalDateTime.now()); } - final AysUser.Password password = AysUser.Password.builder() - .value(AysRandomUtil.generateUUID()) - .build(); - user.setPassword(password); - AysUser savedUser = userSavePort.save(user); - + final AysUser savedUser = userSavePort.save(user); userMailService.sendPasswordCreateEmail(savedUser); } /** - * Validates the provided password ID to ensure it is valid for password reset. - * This method checks if the password ID exists, if the password value is in UUID format, - * and if the password was updated within the last 2 hours. + * Checks the validity of changing the user's password. + *+ * This method verifies if the password change request is valid by checking the + * existence and expiry of the password change request associated with the given password ID. + * It throws an exception if the password cannot be changed due to invalid conditions. * - * @param passwordId the ID of the password to be checked. - * @throws AysUserPasswordDoesNotExistException if the password ID does not exist. - * @throws AysUserPasswordCannotChangedException if the password value is not in UUID format or if the password update time exceeds the allowed limit. + * @param passwordId The ID of the password to check for change validity. + * @throws AysUserPasswordDoesNotExistException if no password is found for the given ID. + * @throws AysUserPasswordCannotChangedException if the password cannot be changed due to invalid conditions. */ @Override public void checkPasswordChangingValidity(final String passwordId) { @@ -85,17 +87,31 @@ public void checkPasswordChangingValidity(final String passwordId) { .orElseThrow(() -> new AysUserPasswordDoesNotExistException(passwordId)) .getPassword(); - boolean isUUID = AysUUID.isValid(password.getValue()); - if (!isUUID) { - throw new AysUserPasswordCannotChangedException(passwordId); + this.checkChangingValidity(password); + } + + + /** + * Checks the validity of changing the password. + *
+ * This method verifies if the password change request is valid by checking if the password change request
+ * was initiated within the allowable time frame. It throws an exception if the password cannot be changed.
+ *
+ * @param password The AysUser.Password object representing the user's password.
+ * @throws AysUserPasswordCannotChangedException if the password cannot be changed due to invalid conditions.
+ */
+ private void checkChangingValidity(final AysUser.Password password) {
+
+ Optional