Skip to content

Commit 998878f

Browse files
committed
feat: implement password reset functionality with expiration handling and logging
1 parent 757ecc7 commit 998878f

5 files changed

Lines changed: 62 additions & 38 deletions

File tree

backend/src/main/java/com/park/utmstack/domain/application_events/enums/ApplicationEventType.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,7 @@ public enum ApplicationEventType {
4646
MODULE_ACTIVATION_SUCCESS,
4747
API_KEY_ACCESS_SUCCESS,
4848
API_KEY_ACCESS_FAILURE,
49+
RESET_USER_PASSWORD_ATTEMPT,
50+
RESET_USER_PASSWORD_SUCCESS,
4951
UNDEFINED
5052
}

backend/src/main/java/com/park/utmstack/service/UserService.java

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import com.park.utmstack.util.exceptions.CurrentUserLoginNotFoundException;
1515
import com.park.utmstack.web.rest.errors.BadRequestAlertException;
1616
import com.park.utmstack.web.rest.errors.InvalidPasswordException;
17+
import com.park.utmstack.web.rest.errors.ResetKeyExpiredException;
1718
import org.slf4j.Logger;
1819
import org.slf4j.LoggerFactory;
1920
import org.springframework.boot.context.event.ApplicationReadyEvent;
@@ -79,15 +80,32 @@ public void init() {
7980
}
8081
}
8182

82-
public Optional<User> completePasswordReset(String newPassword, String key) {
83-
log.debug("Reset user password for reset key {}", key);
84-
return userRepository.findOneByResetKey(key).filter(
85-
user -> user.getResetDate().isAfter(Instant.now().minusSeconds(86400))).map(user -> {
86-
user.setPassword(passwordEncoder.encode(newPassword));
87-
user.setResetKey(null);
88-
user.setResetDate(null);
89-
return user;
90-
});
83+
public User completePasswordReset(String newPassword, String key) {
84+
final String ctx = CLASS_NAME + ".completePasswordReset";
85+
log.debug("{}: Processing password reset with key: {}", ctx, key);
86+
87+
Optional<User> userOptional = userRepository.findOneByResetKey(key);
88+
89+
if (userOptional.isEmpty()) {
90+
log.info("{}: No user found with reset key", ctx);
91+
throw new CurrentUserLoginNotFoundException("No user found with reset key");
92+
}
93+
94+
User user = userOptional.get();
95+
Instant resetDeadline = Instant.now().minusSeconds(86400);
96+
97+
if (!user.getResetDate().isAfter(resetDeadline)) {
98+
log.error("{}: Reset key expired for user: {}", ctx, user.getLogin());
99+
throw new ResetKeyExpiredException(String.format("Reset key expired for user: %s", user.getLogin()));
100+
}
101+
102+
user.setPassword(passwordEncoder.encode(newPassword));
103+
user.setResetKey(null);
104+
user.setResetDate(null);
105+
user.setActivated(true);
106+
107+
log.info("{}: Password reset completed successfully for user: {}", ctx, user.getLogin());
108+
return user;
91109
}
92110

93111
public Optional<User> requestPasswordReset(String mail) {
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.park.utmstack.util.exceptions;
22

3-
public class CurrentUserLoginNotFoundException extends RuntimeException {
3+
import org.springframework.http.HttpStatus;
4+
5+
public class CurrentUserLoginNotFoundException extends ApiException {
46
public CurrentUserLoginNotFoundException(String message) {
5-
super(message);
7+
super(message, HttpStatus.NOT_FOUND);
68
}
79
}

backend/src/main/java/com/park/utmstack/web/rest/AccountResource.java

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.park.utmstack.web.rest;
22

33

4+
import com.park.utmstack.aop.logging.AuditEvent;
5+
import com.park.utmstack.aop.logging.Loggable;
46
import com.park.utmstack.domain.User;
57
import com.park.utmstack.domain.application_events.enums.ApplicationEventType;
68
import com.park.utmstack.repository.UserRepository;
@@ -70,7 +72,7 @@ public ResponseEntity<String> isAuthenticated(HttpServletRequest request) {
7072
log.error(msg);
7173
applicationEventService.createEvent(msg, ApplicationEventType.ERROR);
7274
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).headers(
73-
HeaderUtil.createFailureAlert("", "", msg)).body(null);
75+
HeaderUtil.createFailureAlert("", "", msg)).body(null);
7476
}
7577
}
7678

@@ -85,8 +87,8 @@ public UserDTO getAccount() {
8587
final String ctx = CLASSNAME + ".getAccount";
8688
try {
8789
return userService.getUserWithAuthorities()
88-
.map(UserDTO::new)
89-
.orElseThrow(() -> new InternalServerErrorException("User could not be found"));
90+
.map(UserDTO::new)
91+
.orElseThrow(() -> new InternalServerErrorException("User could not be found"));
9092
} catch (InternalServerErrorException e) {
9193
String msg = ctx + ": " + e.getMessage();
9294
log.error(msg);
@@ -116,7 +118,7 @@ public void saveAccount(@Valid @RequestBody UserDTO userDTO) {
116118
throw new InternalServerErrorException("User could not be found");
117119

118120
userService.updateUser(userDTO.getFirstName(), userDTO.getLastName(), userDTO.getEmail(),
119-
userDTO.getLangKey(), userDTO.getImageUrl());
121+
userDTO.getLangKey(), userDTO.getImageUrl());
120122
} catch (Exception e) {
121123
String msg = ctx + ": " + e.getMessage();
122124
log.error(msg);
@@ -156,8 +158,8 @@ public void requestPasswordReset(@RequestBody String mail) {
156158
final String ctx = CLASSNAME + ".requestPasswordReset";
157159
try {
158160
mailService.sendPasswordResetMail(
159-
userService.requestPasswordReset(mail)
160-
.orElseThrow(EmailNotFoundException::new));
161+
userService.requestPasswordReset(mail)
162+
.orElseThrow(EmailNotFoundException::new));
161163
} catch (Exception e) {
162164
String msg = ctx + ": " + e.getMessage();
163165
log.error(msg);
@@ -166,34 +168,23 @@ public void requestPasswordReset(@RequestBody String mail) {
166168
}
167169
}
168170

169-
/**
170-
* POST /account/reset-password/finish : Finish to reset the password of the user
171-
*
172-
* @param keyAndPassword the generated key and the new password
173-
* @throws InvalidPasswordException 400 (Bad Request) if the password is incorrect
174-
* @throws RuntimeException 500 (Internal Server Error) if the password could not be reset
175-
*/
171+
@AuditEvent(
172+
attemptType = ApplicationEventType.RESET_USER_PASSWORD_ATTEMPT,
173+
attemptMessage = "Attempt to reset user password initiated",
174+
successType = ApplicationEventType.RESET_USER_PASSWORD_SUCCESS,
175+
successMessage = "User password reset successfully"
176+
)
176177
@PostMapping(path = "/account/reset-password/finish")
177178
public void finishPasswordReset(@RequestBody KeyAndPasswordVM keyAndPassword) {
178-
final String ctx = CLASSNAME + ".finishPasswordReset";
179-
try {
180-
validatePasswordLength(keyAndPassword.getNewPassword());
181-
Optional<User> user =
182-
userService.completePasswordReset(keyAndPassword.getNewPassword(), keyAndPassword.getKey());
183179

184-
if (user.isEmpty())
185-
throw new InternalServerErrorException("No user was found for this reset key");
186-
} catch (Exception e) {
187-
String msg = ctx + ": " + e.getMessage();
188-
log.error(msg);
189-
applicationEventService.createEvent(msg, ApplicationEventType.ERROR);
190-
throw new RuntimeException(msg);
191-
}
180+
validatePasswordLength(keyAndPassword.getNewPassword());
181+
userService.completePasswordReset(keyAndPassword.getNewPassword(), keyAndPassword.getKey());
182+
192183
}
193184

194185
private void validatePasswordLength(String password) {
195186
if (!StringUtils.hasText(password) || password.length() < ManagedUserVM.PASSWORD_MIN_LENGTH ||
196-
password.length() > ManagedUserVM.PASSWORD_MAX_LENGTH)
187+
password.length() > ManagedUserVM.PASSWORD_MAX_LENGTH)
197188
throw new InvalidPasswordException();
198189
}
199190
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.park.utmstack.web.rest.errors;
2+
3+
import com.park.utmstack.util.exceptions.ApiException;
4+
import org.springframework.http.HttpStatus;
5+
6+
public class ResetKeyExpiredException extends ApiException {
7+
8+
public ResetKeyExpiredException(String message) {
9+
super(message, HttpStatus.BAD_REQUEST);
10+
}
11+
}

0 commit comments

Comments
 (0)