diff --git a/src/main/java/com/l2/shots/auth/AuthResource.java b/src/main/java/com/l2/shots/auth/AuthResource.java index efe5ad4..cd6c435 100644 --- a/src/main/java/com/l2/shots/auth/AuthResource.java +++ b/src/main/java/com/l2/shots/auth/AuthResource.java @@ -91,16 +91,14 @@ public class AuthResource { Optional user = authService.getEntityFromToken(jwt.get()); if (user.isEmpty()) return Response.status(401).build(); User current = user.get(); - AuthService.ChangePasswordResult result = authService.changePassword( + AuthService.ChangePasswordOutcome outcome = authService.changePassword( current.id, body.currentPassword(), body.newPassword()); - switch (result) { + switch (outcome.result()) { case OK: - // Refetch fresh desde DB porque la version cacheada en el - // persistence context tenia mustChangePassword=true (estado - // previo al cambio). Mejor un read fresco. - User refreshed = User.findById(current.id); - if (refreshed == null) return Response.status(401).build(); - User.getEntityManager().refresh(refreshed); + // outcome.updatedUser() viene de la propia tx con flush() hecho, + // asi que mustChangePassword=false y todas las columnas reflejan + // el estado actual de la DB. + User refreshed = outcome.updatedUser(); String newToken = authService.buildToken(refreshed.id); return Response.ok(AuthService.toAuthMe(refreshed)) .cookie(buildAuthCookie(newToken)) diff --git a/src/main/java/com/l2/shots/auth/AuthService.java b/src/main/java/com/l2/shots/auth/AuthService.java index 395d94e..3b40e94 100644 --- a/src/main/java/com/l2/shots/auth/AuthService.java +++ b/src/main/java/com/l2/shots/auth/AuthService.java @@ -95,19 +95,22 @@ public class AuthService { } @Transactional - public ChangePasswordResult changePassword(UUID userId, String currentPassword, String newPassword) { + public ChangePasswordOutcome changePassword(UUID userId, String currentPassword, String newPassword) { User user = User.findById(userId); - if (user == null) return ChangePasswordResult.NOT_FOUND; + if (user == null) return new ChangePasswordOutcome(ChangePasswordResult.NOT_FOUND, null); if (currentPassword == null || !BcryptUtil.matches(currentPassword, user.passwordHash)) { - return ChangePasswordResult.WRONG_CURRENT_PASSWORD; + return new ChangePasswordOutcome(ChangePasswordResult.WRONG_CURRENT_PASSWORD, null); } if (newPassword == null || newPassword.length() < MIN_PASSWORD_LENGTH) { - return ChangePasswordResult.WEAK_NEW_PASSWORD; + return new ChangePasswordOutcome(ChangePasswordResult.WEAK_NEW_PASSWORD, null); } user.passwordHash = BcryptUtil.bcryptHash(newPassword); user.mustChangePassword = false; user.persist(); - return ChangePasswordResult.OK; + // Forzar flush para que los cambios lleguen a DB antes de salir de la tx. + // Asi el caller (que NO esta en una tx) puede leer el estado actualizado. + User.getEntityManager().flush(); + return new ChangePasswordOutcome(ChangePasswordResult.OK, user); } @Transactional @@ -141,4 +144,7 @@ public class AuthService { public static AuthMeResponse toAuthMe(User user) { return new AuthMeResponse(user.id, user.username, user.createdAt, user.mustChangePassword, user.isAdmin); } + + public record ChangePasswordOutcome(ChangePasswordResult result, User updatedUser) { + } }