From e17577dbd8cdec0fbdac401646bc9a23ffda2b7a Mon Sep 17 00:00:00 2001 From: Alexeer Date: Thu, 29 May 2025 15:47:42 -0600 Subject: [PATCH] Bloqueo de cuenta y config. resumen --- app/controllers/login.php | 154 ++++++++++++++++++++------ app/controllers/preferencias.php | 16 ++- views/login/verificar_codigo.php | 17 +++ views/preferencias/notificaciones.php | 35 +++++- 4 files changed, 181 insertions(+), 41 deletions(-) diff --git a/app/controllers/login.php b/app/controllers/login.php index 8402cd7..9cf1196 100644 --- a/app/controllers/login.php +++ b/app/controllers/login.php @@ -544,49 +544,50 @@ function verificarCodigo() sqlsrv_free_stmt($stmt); 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([ - 'success' => false, - 'message' => '🚫 Has excedido el número de intentos permitidos. Por seguridad, se ha bloqueado el código. Solicita uno nuevo.', - 'blocked' => true + 'success' => false, + 'message' => '🚫 Has excedido el número de intentos permitidos. Tu cuenta ha sido bloqueada por seguridad. Contacta a tu agente aduanal.', + 'blocked' => true, + 'redirect' => '/IMPORTADORES/login' ]); 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 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([ 'success' => false, '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 = " + + + + Alerta de Seguridad + + +
+
+

🚫 Cuenta Bloqueada

+

$tipoCorreo

+
+
+ +
+

Hola {$usuario['nombre']},

+

Tu cuenta ha sido bloqueada automáticamente tras 5 intentos fallidos al verificar tu código de recuperación.

+

Por seguridad, deberás contactar a tu agente aduanal para reactivar tu cuenta.

+

Fecha: " . date('Y-m-d H:i:s') . "
+ IP: " . ($_SERVER['REMOTE_ADDR'] ?? 'Desconocida') . "

+
+ +
+
+

+ Esta es una notificación automática de seguridad.
+ Si tienes preguntas, contacta a nuestro equipo de soporte. +

+
+
+ + "; + + $mail->send(); + error_log("✅ Notificación de cuenta bloqueada enviada a: {$usuario['email']}"); + return true; +} + function cambiarPasswordVista() { if (!isset($_SESSION['recuperacion_autorizada'])) { diff --git a/app/controllers/preferencias.php b/app/controllers/preferencias.php index fb75d90..3461d87 100644 --- a/app/controllers/preferencias.php +++ b/app/controllers/preferencias.php @@ -36,6 +36,18 @@ function notificaciones() } else { $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 if (!$preferenciasRow) { @@ -360,8 +372,8 @@ function guardarPreferenciasAjax() sqlsrv_free_stmt($stmt); $response_data = [ - 'resumen_hora' => $hora, - 'resumen_dias' => $dias + 'resumen_diario_hora' => $hora, + 'resumen_diario_dias' => $dias ]; break; diff --git a/views/login/verificar_codigo.php b/views/login/verificar_codigo.php index 16f25fe..0b40c97 100644 --- a/views/login/verificar_codigo.php +++ b/views/login/verificar_codigo.php @@ -424,6 +424,13 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue }) .then(response => response.json()) .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) { // Código correcto 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 = ` +
+ ${mensaje} +
+ `; + } + // Manejar código incorrecto function manejarCodigoIncorrecto(data) { intentosRealizados++; diff --git a/views/preferencias/notificaciones.php b/views/preferencias/notificaciones.php index 3644531..0d50b0c 100644 --- a/views/preferencias/notificaciones.php +++ b/views/preferencias/notificaciones.php @@ -320,7 +320,6 @@ include __DIR__ . '/../partials/sidebar_configuracion.php'; - +
Días en que se enviará el resumen diario
@@ -683,6 +682,36 @@ include __DIR__ . '/../partials/sidebar_configuracion.php'; }); // === 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 const resumenDiario = document.getElementById('resumen_diario');