fix(change-password): return updated user via record outcome w/ flush
CI / Build Native (push) Successful in 7m49s
CI / Build Native (push) Successful in 7m49s
User.getEntityManager().refresh() requiere una tx activa, y el caller (AuthResource.changePassword) no es @Transactional -> ese approach tiraba TransactionRequiredException. En lugar de refrescar en el caller, cambio AuthService.changePassword para devolver la User ya actualizada en un record ChangePasswordOutcome, despues de un flush() explicito para que los cambios lleguen a DB antes de cerrar la tx. Asi el response del endpoint refleja el estado real de la fila.
This commit is contained in:
@@ -91,16 +91,14 @@ public class AuthResource {
|
|||||||
Optional<User> user = authService.getEntityFromToken(jwt.get());
|
Optional<User> user = authService.getEntityFromToken(jwt.get());
|
||||||
if (user.isEmpty()) return Response.status(401).build();
|
if (user.isEmpty()) return Response.status(401).build();
|
||||||
User current = user.get();
|
User current = user.get();
|
||||||
AuthService.ChangePasswordResult result = authService.changePassword(
|
AuthService.ChangePasswordOutcome outcome = authService.changePassword(
|
||||||
current.id, body.currentPassword(), body.newPassword());
|
current.id, body.currentPassword(), body.newPassword());
|
||||||
switch (result) {
|
switch (outcome.result()) {
|
||||||
case OK:
|
case OK:
|
||||||
// Refetch fresh desde DB porque la version cacheada en el
|
// outcome.updatedUser() viene de la propia tx con flush() hecho,
|
||||||
// persistence context tenia mustChangePassword=true (estado
|
// asi que mustChangePassword=false y todas las columnas reflejan
|
||||||
// previo al cambio). Mejor un read fresco.
|
// el estado actual de la DB.
|
||||||
User refreshed = User.findById(current.id);
|
User refreshed = outcome.updatedUser();
|
||||||
if (refreshed == null) return Response.status(401).build();
|
|
||||||
User.getEntityManager().refresh(refreshed);
|
|
||||||
String newToken = authService.buildToken(refreshed.id);
|
String newToken = authService.buildToken(refreshed.id);
|
||||||
return Response.ok(AuthService.toAuthMe(refreshed))
|
return Response.ok(AuthService.toAuthMe(refreshed))
|
||||||
.cookie(buildAuthCookie(newToken))
|
.cookie(buildAuthCookie(newToken))
|
||||||
|
|||||||
@@ -95,19 +95,22 @@ public class AuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ChangePasswordResult changePassword(UUID userId, String currentPassword, String newPassword) {
|
public ChangePasswordOutcome changePassword(UUID userId, String currentPassword, String newPassword) {
|
||||||
User user = User.findById(userId);
|
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)) {
|
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) {
|
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.passwordHash = BcryptUtil.bcryptHash(newPassword);
|
||||||
user.mustChangePassword = false;
|
user.mustChangePassword = false;
|
||||||
user.persist();
|
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
|
@Transactional
|
||||||
@@ -141,4 +144,7 @@ public class AuthService {
|
|||||||
public static AuthMeResponse toAuthMe(User user) {
|
public static AuthMeResponse toAuthMe(User user) {
|
||||||
return new AuthMeResponse(user.id, user.username, user.createdAt, user.mustChangePassword, user.isAdmin);
|
return new AuthMeResponse(user.id, user.username, user.createdAt, user.mustChangePassword, user.isAdmin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record ChangePasswordOutcome(ChangePasswordResult result, User updatedUser) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user