mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-01-13 00:48:49 +00:00
125 lines
4.5 KiB
Java
125 lines
4.5 KiB
Java
package com.imprimelibros.erp.auth;
|
|
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.security.MessageDigest;
|
|
import java.security.SecureRandom;
|
|
import java.time.LocalDateTime;
|
|
import java.util.Base64;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
|
|
import org.springframework.context.MessageSource;
|
|
import org.springframework.mail.javamail.JavaMailSender;
|
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.thymeleaf.context.Context;
|
|
import org.thymeleaf.spring6.SpringTemplateEngine;
|
|
|
|
import com.imprimelibros.erp.common.email.EmailService;
|
|
import com.imprimelibros.erp.users.User;
|
|
import com.imprimelibros.erp.users.UserDao; // ajusta al path real de tu UserRepository
|
|
|
|
@Service
|
|
public class PasswordResetService {
|
|
|
|
private final PasswordResetTokenRepository tokenRepo;
|
|
private final UserDao userRepo;
|
|
private final PasswordEncoder passwordEncoder;
|
|
private final JavaMailSender mailSender;
|
|
private final SpringTemplateEngine templateEngine;
|
|
private final MessageSource messages;
|
|
private final EmailService emailService;
|
|
|
|
public PasswordResetService(
|
|
PasswordResetTokenRepository tokenRepo,
|
|
UserDao userRepo,
|
|
PasswordEncoder passwordEncoder,
|
|
JavaMailSender mailSender,
|
|
SpringTemplateEngine templateEngine,
|
|
MessageSource messages,
|
|
EmailService emailService
|
|
) {
|
|
this.tokenRepo = tokenRepo;
|
|
this.userRepo = userRepo;
|
|
this.passwordEncoder = passwordEncoder;
|
|
this.mailSender = mailSender;
|
|
this.templateEngine = templateEngine;
|
|
this.messages = messages;
|
|
this.emailService = emailService;
|
|
}
|
|
|
|
// 3.1 Solicitar reset (si el email existe, genera token y envía)
|
|
@Transactional
|
|
public void requestReset(String email, String baseUrl, String ip, String userAgent, int minutes, Locale locale) {
|
|
|
|
User user = userRepo.findByUserNameIgnoreCase(email).orElse(null);
|
|
// Siempre responder OK aunque no exista para evitar enumeración
|
|
if (user == null) return;
|
|
|
|
tokenRepo.invalidateActiveTokens(user.getId(), LocalDateTime.now());
|
|
|
|
String token = generateToken(); // token en claro SOLO para el enlace
|
|
String tokenHash = sha256(token); // guardamos hash en DB
|
|
|
|
PasswordResetToken row = new PasswordResetToken();
|
|
row.setUserId(user.getId());
|
|
row.setCreatedAt(LocalDateTime.now());
|
|
row.setExpiresAt(LocalDateTime.now().plusMinutes(minutes));
|
|
row.setRequestIp(ip);
|
|
row.setUserAgent(userAgent);
|
|
row.setTokenHash(tokenHash);
|
|
tokenRepo.save(row);
|
|
|
|
String resetUrl = baseUrl + "/auth/password/reset?uid=" + user.getId() + "&token=" + token;
|
|
|
|
emailService.sendPasswordResetMail(user.getUserName(), user.getFullName(), resetUrl, locale);
|
|
}
|
|
|
|
// 3.2 Validar token (para mostrar el formulario de nueva contraseña)
|
|
public boolean isValid(Long userId, String token) {
|
|
String hash = sha256(token);
|
|
return tokenRepo.findValidByUserAndHash(userId, hash, LocalDateTime.now()).isPresent();
|
|
}
|
|
|
|
// 3.3 Confirmar reseteo y marcar token como usado
|
|
@Transactional
|
|
public boolean resetPassword(Long userId, String token, String newPassword) {
|
|
String hash = sha256(token);
|
|
var opt = tokenRepo.findValidByUserAndHash(userId, hash, LocalDateTime.now());
|
|
if (opt.isEmpty()) return false;
|
|
|
|
var entry = opt.get();
|
|
var user = userRepo.findById(userId).orElse(null);
|
|
if (user == null) return false;
|
|
|
|
user.setPassword(passwordEncoder.encode(newPassword));
|
|
userRepo.save(user);
|
|
|
|
entry.setUsedAt(LocalDateTime.now());
|
|
tokenRepo.save(entry);
|
|
|
|
// (Opcional) invalidar otros tokens activos del usuario
|
|
tokenRepo.invalidateActiveTokens(userId, LocalDateTime.now());
|
|
return true;
|
|
}
|
|
|
|
// --- helpers ---
|
|
private String generateToken() {
|
|
byte[] buf = new byte[32];
|
|
new SecureRandom().nextBytes(buf);
|
|
return Base64.getUrlEncoder().withoutPadding().encodeToString(buf);
|
|
}
|
|
private String sha256(String s) {
|
|
try {
|
|
var md = MessageDigest.getInstance("SHA-256");
|
|
return Base64.getEncoder().encodeToString(md.digest(s.getBytes(StandardCharsets.UTF_8)));
|
|
} catch (Exception e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|