recovery del pass hecho en el backend a falta de hacer los formularios

This commit is contained in:
2025-09-28 18:36:44 +02:00
parent 22198b4f25
commit 865b1573b9
15 changed files with 517 additions and 8 deletions

View File

@ -0,0 +1,86 @@
package com.imprimelibros.erp.auth;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import com.imprimelibros.erp.users.UserDao;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.HexFormat;
@Service
public class PasswordResetService {
private final PasswordResetTokenRepository tokenRepo;
private final UserDao userRepo; // tu repo real
private final PasswordEncoder passwordEncoder;
private static final SecureRandom RNG = new SecureRandom();
private static final HexFormat HEX = HexFormat.of();
public PasswordResetService(PasswordResetTokenRepository tokenRepo, UserDao userRepo, PasswordEncoder enc) {
this.tokenRepo = tokenRepo;
this.userRepo = userRepo;
this.passwordEncoder = enc;
}
/** Elimina tokens previos sin usar, genera uno nuevo y lo guarda con auditoría básica. */
public String createTokenForUser(Long userId, int hoursToExpire, String requestIp, String userAgent) {
// Invalidar anteriores
tokenRepo.deleteAllByUserIdAndUsedAtIsNull(userId);
// Generar token
byte[] raw = new byte[32];
RNG.nextBytes(raw);
String token = HEX.formatHex(raw); // token plano (64 hex)
String tokenHash = sha256(token);
PasswordResetToken prt = new PasswordResetToken();
prt.setUserId(userId);
prt.setTokenHash(tokenHash);
prt.setExpiresAt(Instant.now().plus(hoursToExpire, ChronoUnit.HOURS));
prt.setRequestIp(truncate(requestIp, 64));
prt.setUserAgent(truncate(userAgent, 255));
tokenRepo.save(prt);
return token; // Esto se envía por email
}
public Long validateTokenAndGetUserId(String tokenPlain) {
return tokenRepo.findByTokenHashAndUsedAtIsNullAndExpiresAtAfter(sha256(tokenPlain), Instant.now())
.map(PasswordResetToken::getUserId)
.orElse(null);
}
public void consumeTokenAndSetPassword(String tokenPlain, String newPassword) {
var tokenOpt = tokenRepo.findByTokenHashAndUsedAtIsNullAndExpiresAtAfter(sha256(tokenPlain), Instant.now());
var prt = tokenOpt.orElseThrow(() -> new IllegalArgumentException("Token inválido o caducado"));
var user = userRepo.findById(prt.getUserId())
.orElseThrow(() -> new IllegalStateException("Usuario no encontrado"));
user.setPassword(passwordEncoder.encode(newPassword));
userRepo.save(user);
prt.setUsedAt(Instant.now());
tokenRepo.save(prt);
}
private static String sha256(String s) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
return HEX.formatHex(md.digest(s.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static String truncate(String v, int max) {
if (v == null) return null;
return v.length() <= max ? v : v.substring(0, max);
}
}