Bloqueo de cuenta y config. resumen

This commit is contained in:
2025-05-29 15:47:42 -06:00
parent e08a813802
commit e17577dbd8
4 changed files with 181 additions and 41 deletions

View File

@@ -544,49 +544,50 @@ function verificarCodigo()
sqlsrv_free_stmt($stmt); sqlsrv_free_stmt($stmt);
if (!$row) { if (!$row) {
echo json_encode(['success' => false, 'message' => '❌ Código no encontrado o ya usado.']);
exit;
}
// Manejo de intentos fallidos en sesión
if (!isset($_SESSION['intentos_codigo'])) {
$_SESSION['intentos_codigo'] = 0;
}
$_SESSION['intentos_codigo']++;
// Verificar si se han excedido los intentos
if ($_SESSION['intentos_codigo'] >= 5) {
// Bloquear el código en la base de datos
$sqlUpdate = "UPDATE recuperacion_password SET estatus = 1 WHERE id = ?";
$stmtUpdate = sqlsrv_query($conn, $sqlUpdate, [$row['id']]);
if ($stmtUpdate) {
sqlsrv_free_stmt($stmtUpdate);
}
// 🚨 ENVIAR NOTIFICACIÓN DE SEGURIDAD
try {
enviarNotificacionIntentosExcedidos($email);
} catch (Exception $e) {
error_log("❌ Error al enviar notificación de intentos excedidos: " . $e->getMessage());
}
// Limpiar sesión
unset($_SESSION['intentos_codigo']);
unset($_SESSION['email_recuperacion']);
echo json_encode([ echo json_encode([
'success' => false, 'success' => false,
'message' => '🚫 Has excedido el número de intentos permitidos. Por seguridad, se ha bloqueado el código. Solicita uno nuevo.', 'message' => '🚫 Has excedido el número de intentos permitidos. Tu cuenta ha sido bloqueada por seguridad. Contacta a tu agente aduanal.',
'blocked' => true 'blocked' => true,
'redirect' => '/IMPORTADORES/login'
]); ]);
exit; exit;
} }
// Manejo de intentos fallidos en sesión
if (!isset($_SESSION['intentos_codigo']) || !is_numeric($_SESSION['intentos_codigo'])) {
$_SESSION['intentos_codigo'] = 0;
}
// Compara códigos - asegurando que no haya problemas con mayúsculas o espacios // Compara códigos - asegurando que no haya problemas con mayúsculas o espacios
if (strcasecmp($codigo, $row['codigo']) !== 0) { if (strcasecmp($codigo, $row['codigo']) !== 0) {
$intentosRestantes = 5 - $_SESSION['intentos_codigo']; $_SESSION['intentos_codigo']++; // ⬅️ Incrementa solo si es incorrecto
$intentosRestantes = max(0, 5 - $_SESSION['intentos_codigo']);
// Verificar si se han excedido los intentos
if ($_SESSION['intentos_codigo'] >= 5) {
// Bloquear el código
$sqlUpdate = "UPDATE recuperacion_password SET estatus = 1 WHERE id = ?";
$stmtUpdate = sqlsrv_query($conn, $sqlUpdate, [$row['id']]);
if ($stmtUpdate) sqlsrv_free_stmt($stmtUpdate);
// Bloquear la cuenta del usuario
$sqlBloquearUsuario = "UPDATE usuarios_sistema SET activo = 0 WHERE email = ?";
$stmtBloqueo = sqlsrv_query($conn, $sqlBloquearUsuario, [$emailEncrypted]);
if ($stmtBloqueo) sqlsrv_free_stmt($stmtBloqueo);
// Enviar notificación
try {
enviarNotificacionCuentaBloqueada($email);
enviarNotificacionIntentosExcedidos($email);
} catch (Exception $e) {
error_log("❌ Error al enviar notificación de cuenta bloqueada: " . $e->getMessage());
error_log("❌ Error al enviar notificación de intentos excedidos: " . $e->getMessage());
}
unset($_SESSION['intentos_codigo']);
unset($_SESSION['email_recuperacion']);
}
echo json_encode([ echo json_encode([
'success' => false, 'success' => false,
'message' => "❌ Código incorrecto. Te quedan {$intentosRestantes} intentos.", 'message' => "❌ Código incorrecto. Te quedan {$intentosRestantes} intentos.",
@@ -819,6 +820,87 @@ function enviarNotificacionSeguridadIntentos($emailDestino, $nombreUsuario, $dat
} }
} }
/** Función para enviar el email de notificación de cuenta bloqueado **/
function enviarNotificacionCuentaBloqueada($email, $esCorreoAdicional = false)
{
$conn = getConnection();
if (!$conn) throw new Exception("No se pudo conectar a la base de datos");
$emailEncrypted = encrypt($email);
$sqlNotif = "SELECT nombre, email FROM usuarios_sistema WHERE email = ?";
$stmt = sqlsrv_query($conn, $sqlNotif, [$emailEncrypted]);
if (!$stmt) throw new Exception("Error al consultar información del usuario");
$usuario = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
sqlsrv_free_stmt($stmt);
if (!$usuario) {
error_log("⚠️ Usuario no encontrado para enviar notificación de cuenta bloqueada");
return false;
}
$usuario['email'] = decrypt($usuario['email']);
$usuario['nombre'] = decrypt($usuario['nombre']);
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = 'secure.emailsrvr.com';
$mail->SMTPAuth = true;
$mail->Username = 'noreply@aduanasoft.com.mx';
$mail->Password = $_ENV['SMTP_PASS'];
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
// Configuración de correo
$mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | AduanaSoft');
$mail->addAddress($usuario['email']);
$mail->CharSet = 'UTF-8';
$mail->isHTML(true);
$asunto = "🚫 Cuenta Bloqueada por Seguridad";
$mail->Subject = $asunto;
$tipoCorreo = $esCorreoAdicional ? "(Correo Adicional)" : "";
$mail->Body = "
<html>
<head>
<meta charset='UTF-8'>
<title>Alerta de Seguridad</title>
</head>
<body style='font-family: Arial, sans-serif; line-height: 1.6; color: #333;'>
<div style='max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #ddd; border-radius: 10px;'>
<div style='text-align: center; margin-bottom: 30px;'>
<h1 style='color: #dc3545; margin: 0;'>🚫 Cuenta Bloqueada</h1>
<p style='color: #666; margin: 5px 0;'>$tipoCorreo</p>
</div>
<hr>
<div style='background-color: #f8f9fa; padding: 20px; border-radius: 8px; margin-bottom: 20px;'>
<p>Hola <b>{$usuario['nombre']}</b>,</p>
<p>Tu cuenta ha sido bloqueada automáticamente tras 5 intentos fallidos al verificar tu código de recuperación.</p>
<p>Por seguridad, deberás contactar a tu agente aduanal para reactivar tu cuenta.</p>
<p>Fecha: <b>" . date('Y-m-d H:i:s') . "</b><br>
IP: <b>" . ($_SERVER['REMOTE_ADDR'] ?? 'Desconocida') . "</b></p>
</div>
<hr>
<div style='text-align: center; margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee;'>
<p style='color: #666; font-size: 12px; margin: 0;'>
Esta es una notificación automática de seguridad.<br>
Si tienes preguntas, contacta a nuestro equipo de soporte.
</p>
</div>
</div>
</body>
</html>";
$mail->send();
error_log("✅ Notificación de cuenta bloqueada enviada a: {$usuario['email']}");
return true;
}
function cambiarPasswordVista() function cambiarPasswordVista()
{ {
if (!isset($_SESSION['recuperacion_autorizada'])) { if (!isset($_SESSION['recuperacion_autorizada'])) {

View File

@@ -37,6 +37,18 @@ function notificaciones()
$preferenciasRow = false; $preferenciasRow = false;
} }
// Después de obtener $preferenciasRow, antes de crear el array $preferencias
if ($preferenciasRow && isset($preferenciasRow['resumen_diario_hora'])) {
// Formatear la hora para el input HTML
$hora_obj = $preferenciasRow['resumen_diario_hora'];
if ($hora_obj instanceof DateTime) {
$preferenciasRow['resumen_diario_hora'] = $hora_obj->format('H:i');
} elseif (is_string($hora_obj)) {
// Si viene como string "08:00:00.0000000", extraer solo HH:MM
$preferenciasRow['resumen_diario_hora'] = substr($hora_obj, 0, 5);
}
}
// Si no existe registro, crear valores por defecto // Si no existe registro, crear valores por defecto
if (!$preferenciasRow) { if (!$preferenciasRow) {
$preferencias = [ $preferencias = [
@@ -360,8 +372,8 @@ function guardarPreferenciasAjax()
sqlsrv_free_stmt($stmt); sqlsrv_free_stmt($stmt);
$response_data = [ $response_data = [
'resumen_hora' => $hora, 'resumen_diario_hora' => $hora,
'resumen_dias' => $dias 'resumen_diario_dias' => $dias
]; ];
break; break;

View File

@@ -424,6 +424,13 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
}) })
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.blocked) {
mostrarError(data.message); // <-- función personalizada con un div bonito
setTimeout(() => {
window.location.href = data.redirect;
}, 4000);
}
if (data.success) { if (data.success) {
// Código correcto // Código correcto
mostrarExito(data.message); mostrarExito(data.message);
@@ -454,6 +461,16 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
}); });
} }
// Mostrar error de cuenta bloqueada
function mostrarError(mensaje) {
const mensajeDiv = document.getElementById('mensaje');
mensajeDiv.innerHTML = `
<div class="text-danger fade-in">
<i class="fas fa-exclamation-triangle me-2"></i> ${mensaje}
</div>
`;
}
// Manejar código incorrecto // Manejar código incorrecto
function manejarCodigoIncorrecto(data) { function manejarCodigoIncorrecto(data) {
intentosRealizados++; intentosRealizados++;

View File

@@ -320,7 +320,6 @@ include __DIR__ . '/../partials/sidebar_configuracion.php';
<div id="resumen-diario-config" class="fade-transition" style="display: none; opacity: 0;"> <div id="resumen-diario-config" class="fade-transition" style="display: none; opacity: 0;">
<div class="resumen-config"> <div class="resumen-config">
<h6 class="mb-3"> <h6 class="mb-3">
<i class="fas fa-clock me-2"></i>
⏰ Configuración del Resumen Diario ⏰ Configuración del Resumen Diario
</h6> </h6>
@@ -328,7 +327,7 @@ include __DIR__ . '/../partials/sidebar_configuracion.php';
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label for="resumen_hora" class="form-label fw-bold">Hora de envío</label> <label for="resumen_hora" class="form-label fw-bold">Hora de envío</label>
<input type="time" class="form-control time-input" id="resumen_hora" <input type="time" class="form-control time-input" id="resumen_hora"
name="resumen_hora" value="08:00"> name="resumen_hora" value="<?= $preferencias['resumen_diario_hora'] ?? '08:00' ?>">
<div class="form-text">Hora en que se enviará el resumen diario</div> <div class="form-text">Hora en que se enviará el resumen diario</div>
</div> </div>
@@ -401,7 +400,7 @@ include __DIR__ . '/../partials/sidebar_configuracion.php';
</div> </div>
</div> </div>
<input type="hidden" id="resumen_dias" name="resumen_dias" value="L-V"> <input type="hidden" id="resumen_dias" name="resumen_dias" value="<?= $preferencias['resumen_diario_dias'] ?? 'L-V' ?>">
<div class="form-text">Días en que se enviará el resumen diario</div> <div class="form-text">Días en que se enviará el resumen diario</div>
</div> </div>
</div> </div>
@@ -683,6 +682,36 @@ include __DIR__ . '/../partials/sidebar_configuracion.php';
}); });
// === FUNCIONALIDAD DE RESUMEN DIARIO === // === FUNCIONALIDAD DE RESUMEN DIARIO ===
// Función para mostrar/ocultar configuración del resumen diario
function toggleResumenConfig() {
const resumenCheckbox = document.getElementById('resumen_diario');
const resumenConfig = document.getElementById('resumen-diario-config');
if (resumenCheckbox.checked) {
resumenConfig.style.display = 'block';
setTimeout(() => {
resumenConfig.style.opacity = '1';
}, 10);
} else {
resumenConfig.style.opacity = '0';
setTimeout(() => {
resumenConfig.style.display = 'none';
}, 300);
}
}
// Event listener para el checkbox de resumen diario
document.addEventListener('DOMContentLoaded', function() {
const resumenCheckbox = document.getElementById('resumen_diario');
// Verificar estado inicial al cargar la página
toggleResumenConfig();
// Agregar listener para cambios en el checkbox
resumenCheckbox.addEventListener('change', function() {
toggleResumenConfig();
});
});
// Referencias específicas para resumen diario // Referencias específicas para resumen diario
const resumenDiario = document.getElementById('resumen_diario'); const resumenDiario = document.getElementById('resumen_diario');