From f611c35b4982af862508f2bb835988643c9b6610 Mon Sep 17 00:00:00 2001 From: Alexeer Date: Mon, 26 May 2025 15:56:41 -0600 Subject: [PATCH] =?UTF-8?q?Par=C3=A1metro=20dos=20factores?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/agentes.php | 4 +- app/controllers/login.php | 143 +++++++++++++++++------- app/controllers/seguridad.php | 124 +++++++++++++++++--- views/seguridad/agregar.php | 30 +++++ views/seguridad/dashboard_seguridad.php | 20 +++- 5 files changed, 261 insertions(+), 60 deletions(-) diff --git a/app/controllers/agentes.php b/app/controllers/agentes.php index a2a9e1c..b63ee69 100644 --- a/app/controllers/agentes.php +++ b/app/controllers/agentes.php @@ -110,8 +110,8 @@ function aprobar_solicitud() $email_encrypt = encrypt($email); // Insertar en usuarios_sistema - $sqlInsert = "INSERT INTO usuarios_sistema (nombre, email, password_hash, tipo_usuario, activo, creado_en) - VALUES (?, ?, ?, ?, 1, GETDATE())"; + $sqlInsert = "INSERT INTO usuarios_sistema (nombre, email, password_hash, tipo_usuario, activo, creado_en, dos factores) + VALUES (?, ?, ?, ?, 1, GETDATE()), 0"; $stmtInsert = sqlsrv_query($conn, $sqlInsert, [ $nombre_encrypt, $email_encrypt, $password_hash, $tipo ]); diff --git a/app/controllers/login.php b/app/controllers/login.php index f9b2ef9..35968f6 100644 --- a/app/controllers/login.php +++ b/app/controllers/login.php @@ -32,8 +32,8 @@ function validar() $emailEncrypted = encrypt($email); - // Obtenemos el usuario - $sql = "SELECT id_usuario, nombre, email, password_hash, tipo_usuario, activo + // Obtenemos el usuario incluyendo el campo dos_factores + $sql = "SELECT id_usuario, nombre, email, password_hash, tipo_usuario, activo, dos_factores FROM usuarios_sistema WHERE email = ?"; $stmt = sqlsrv_query($conn, $sql, [$emailEncrypted]); @@ -48,17 +48,19 @@ function validar() } if (password_verify($password, $row['password_hash'])) { + // Credenciales válidas, establecer sesión básica $_SESSION['usuario_id'] = $row['id_usuario']; $_SESSION['usuario_nombre'] = decrypt($row['nombre']); $_SESSION['usuario_email'] = $email; $_SESSION['tipo_usuario'] = $row['tipo_usuario']; - registrarBitacora($conn, $row['id_usuario'], $email, $ip, 1, 'Login exitoso'); - // Buscar RFC y teléfono desde solicitudes_importadores $sqlSolicitudes = "SELECT rfc, phone FROM solicitudes_importadores WHERE email = ?"; $stmtSolicitudes = sqlsrv_query($conn, $sqlSolicitudes, [$email]); + $rfc = null; + $telefono = null; + if ($stmtSolicitudes && $solRow = sqlsrv_fetch_array($stmtSolicitudes, SQLSRV_FETCH_ASSOC)) { // Desencriptar RFC $rfc = strtoupper(decrypt(trim($solRow['rfc']))); @@ -66,12 +68,13 @@ function validar() // Validar teléfono (ej. 123 4567890) $telefono = trim($solRow['phone']); if (!preg_match('/^\d{3}\s\d{7}$/', $telefono)) { - $telefono = null; // o puedes dejarlo como '' + $telefono = null; } } $nombreUsuario = decrypt($row['nombre']); + // Actualizar/insertar información general $sqlCheck = "SELECT COUNT(*) AS total FROM informacion_general WHERE id_usuario = ?"; $stmtCheck = sqlsrv_query($conn, $sqlCheck, [$row['id_usuario']]); $checkRow = sqlsrv_fetch_array($stmtCheck, SQLSRV_FETCH_ASSOC); @@ -87,29 +90,49 @@ function validar() sqlsrv_query($conn, $sqlUpdate, $paramsUpdate); } - $codigo = rand(100000, 999999); - $expiracion = date('Y-m-d H:i:s', strtotime('+5 minutes')); - $usuarioId = $row['id_usuario']; + // **LÓGICA DE DOS FACTORES** + $dos_factores_activo = (int)$row['dos_factores'] === 1; - $sql = "INSERT INTO verificaciones (id_usuario, codigo, expiracion) VALUES (?, ?, ?)"; - $params = [$usuarioId, $codigo, $expiracion]; - $stmt = sqlsrv_query($conn, $sql, $params); + if ($dos_factores_activo) { + // Dos factores ACTIVADO - Generar código y enviar correo + $codigo = rand(100000, 999999); + $expiracion = date('Y-m-d H:i:s', strtotime('+5 minutes')); + $usuarioId = $row['id_usuario']; - enviarConfirmacion($email, $codigo); + $sqlCodigo = "INSERT INTO verificaciones (id_usuario, codigo, expiracion) VALUES (?, ?, ?)"; + $paramsCodigo = [$usuarioId, $codigo, $expiracion]; + $stmtCodigo = sqlsrv_query($conn, $sqlCodigo, $paramsCodigo); - if (!$stmt) { - die('Error al generar código de verificación.'); - } + if (!$stmtCodigo) { + registrarBitacora($conn, $row['id_usuario'], $email, $ip, 0, 'Error al generar código de verificación'); + $_SESSION['login_error'] = 'Error interno. Inténtalo de nuevo.'; + header('Location: /IMPORTADORES/login'); + exit; + } - // Redirección por rol - if ($row['tipo_usuario'] === 'importador') { + // Enviar código por correo + enviarConfirmacion($email, $codigo); + + // Marcar que está pendiente la confirmación + $_SESSION['pendiente_confirmacion'] = true; + + registrarBitacora($conn, $row['id_usuario'], $email, $ip, 1, 'Login exitoso - Pendiente confirmación 2FA'); + + // Redirigir a confirmación de código header('Location: /IMPORTADORES/login/confirmar'); - } elseif ($row['tipo_usuario'] === 'agente_aduanal') { - header('Location: /IMPORTADORES/AGENTES/dashboard'); + exit; + } else { - header('Location: /IMPORTADORES/login/confirmar'); + // Dos factores DESACTIVADO - Login directo + $_SESSION['pendiente_confirmacion'] = false; + + registrarBitacora($conn, $row['id_usuario'], $email, $ip, 1, 'Login exitoso - Sin 2FA'); + + // Redirección directa por rol + redirectByRole($row['tipo_usuario']); + exit; } - exit; + } else { registrarBitacora($conn, $row['id_usuario'], $email, $ip, 0, 'Contraseña incorrecta'); } @@ -122,6 +145,24 @@ function validar() header('Location: /IMPORTADORES/login'); } +/** + * Función auxiliar para redirección por rol + */ +function redirectByRole($tipoUsuario) +{ + switch ($tipoUsuario) { + case 'importador': + header('Location: /IMPORTADORES/importadores/dashboard'); + break; + case 'agente_aduanal': + header('Location: /IMPORTADORES/AGENTES/dashboard'); + break; + default: + header('Location: /IMPORTADORES/importadores/dashboard'); + break; + } +} + function enviarConfirmacion($email, $codigo) { $mail = new PHPMailer(true); @@ -139,21 +180,22 @@ function enviarConfirmacion($email, $codigo) $mail->addAddress($email); $mail->CharSet = 'UTF-8'; $mail->isHTML(true); - $mail->Subject = 'Código de verificación'; + $mail->Subject = 'Código de verificación - Autenticación de dos factores'; $mail->Body = "
-

¿Solicitaste acceso a la plataforma?

+

🔐 Verificación de dos factores

-

Se ha intentado iniciar sesión con tu cuenta.

+

Se ha solicitado acceso a tu cuenta con autenticación de dos factores activada.

Tu código de verificación es:

-

$codigo

+

$codigo

-

Este código expirará en 10 minutos.

+

⏰ Este código expirará en 5 minutos.

+

Si no solicitaste este acceso, ignora este correo y tu cuenta permanecerá segura.

© " . date('Y') . " SIIH · Desarrollado por AduanaSoft @@ -163,13 +205,13 @@ function enviarConfirmacion($email, $codigo) $mail->send(); } catch (Exception $e) { - error_log("Error al enviar confirmación: {$mail->ErrorInfo}"); + error_log("Error al enviar confirmación 2FA: {$mail->ErrorInfo}"); } } function confirmarAcceso() { - $conn = getConnection(); // Asegúrate que esta función retorne una conexión válida + $conn = getConnection(); if (!$conn) { http_response_code(500); exit('Error de conexión'); @@ -177,13 +219,17 @@ function confirmarAcceso() $codigoIngresado = trim($_POST['codigo'] ?? ''); $usuarioId = $_SESSION['usuario_id'] ?? null; - $email = $_SESSION['usuario_email'] ?? ''; // Asegúrate de tenerlo en sesión - $ip = $_SERVER['REMOTE_ADDR']; + $email = $_SESSION['usuario_email'] ?? ''; + $ip = $_SERVER['REMOTE_ADDR'] ?? 'N/A'; - if (!$usuarioId || !$codigoIngresado) { - registrarBitacora($conn, $usuarioId, $email, $ip, 0, 'Código no ingresado o datos incompletos'); - $_SESSION['codigo_error'] = '❌ Datos incompletos.'; - header('Location: /IMPORTADORES/login/confirmar'); + // Verificar que el usuario tenga una sesión válida y pendiente de confirmación + if (!$usuarioId || !$codigoIngresado || !($_SESSION['pendiente_confirmacion'] ?? false)) { + registrarBitacora($conn, $usuarioId, $email, $ip, 0, 'Confirmación 2FA - Datos incompletos o sesión inválida'); + $_SESSION['codigo_error'] = '❌ Datos incompletos o sesión inválida.'; + + // Limpiar sesión por seguridad + session_destroy(); + header('Location: /IMPORTADORES/login'); exit; } @@ -193,7 +239,7 @@ function confirmarAcceso() $stmt = sqlsrv_query($conn, $sql, [$usuarioId, $codigoIngresado]); if (!$stmt || !($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC))) { - registrarBitacora($conn, $usuarioId, $email, $ip, 0, 'Código incorrecto'); + registrarBitacora($conn, $usuarioId, $email, $ip, 0, 'Confirmación 2FA - Código incorrecto'); $_SESSION['codigo_error'] = '❌ Código incorrecto.'; header('Location: /IMPORTADORES/login/confirmar'); exit; @@ -202,22 +248,37 @@ function confirmarAcceso() // Validar expiración $expiracion = $row['expiracion']; if ($expiracion instanceof DateTime && $expiracion->getTimestamp() < time()) { - registrarBitacora($conn, $usuarioId, $email, $ip, 0, 'Código vencido'); + registrarBitacora($conn, $usuarioId, $email, $ip, 0, 'Confirmación 2FA - Código vencido'); $_SESSION['codigo_error'] = '❌ El código ha expirado.'; - header('Location: /IMPORTADORES/login/index'); + + // Limpiar sesión y redirigir al login + session_destroy(); + header('Location: /IMPORTADORES/login'); exit; } - // Código válido - registrarBitacora($conn, $usuarioId, $email, $ip, 1, 'Login exitoso'); + // Código válido - Completar el login + registrarBitacora($conn, $usuarioId, $email, $ip, 1, 'Confirmación 2FA exitosa - Login completado'); + $_SESSION['pendiente_confirmacion'] = false; - $_SESSION['codigo_exito'] = '✅ Código verificado correctamente.'; - header('Location: /IMPORTADORES/importadores/dashboard'); + $_SESSION['codigo_exito'] = '✅ Verificación de dos factores completada correctamente.'; + + // Obtener el tipo de usuario para redirección + $tipoUsuario = $_SESSION['tipo_usuario'] ?? 'importador'; + + // Redirigir según el rol + redirectByRole($tipoUsuario); exit; } function confirmar() { + // Verificar que hay una sesión pendiente de confirmación + if (!($_SESSION['pendiente_confirmacion'] ?? false) || !($_SESSION['usuario_id'] ?? null)) { + header('Location: /IMPORTADORES/login'); + exit; + } + include __DIR__ . '/../../views/login/confirmacion.php'; } diff --git a/app/controllers/seguridad.php b/app/controllers/seguridad.php index 0088cf1..937b24e 100644 --- a/app/controllers/seguridad.php +++ b/app/controllers/seguridad.php @@ -6,14 +6,14 @@ require_once __DIR__ . '/../helpers/crypto.php'; function index() { // Asegúrate de tener conexión - $conn = getConnection(); // Suponiendo que tienes una función getConnection() + $conn = getConnection(); - // Verifica si la sesión tiene el id_usuario - if (!isset($_SESSION['usuario_id']) || ($_SESSION['pendiente_confirmacion'] ?? true)) { + // CORRIGIDO: Cambiar id_usuario por usuario_id (consistente con login.php) + if (!isset($_SESSION['usuario_id']) || ($_SESSION['pendiente_confirmacion'] ?? false)) { die("Usuario no autenticado."); } - $id_usuario = $_SESSION['usuario_id']; + $id_usuario = $_SESSION['usuario_id']; // CORRIGIDO // Obtener correos $correos = obtenerCorreos($conn, $id_usuario); @@ -27,14 +27,94 @@ function opciones() include __DIR__ . '/../../views/seguridad/agregar.php'; } -function correoExtra() +// Función para obtener el estado actual (para mostrar en la vista) +function obtenerEstadoDosFactores() { - if (!isset($_SESSION['usuario_id']) || ($_SESSION['pendiente_confirmacion'] ?? true)) { - echo "Error: no se ha iniciado sesión o falta el ID de usuario."; + $conn = getConnection(); + + // CORRIGIDO: Cambiar id_usuario por usuario_id + $id_usuario = $_SESSION['usuario_id'] ?? null; + if (!$id_usuario) { + return 0; // valor por defecto si no hay sesión + } + + $sql_dos_factores = "SELECT dos_factores FROM usuarios_sistema WHERE id_usuario = ?"; + $params = [$id_usuario]; + $stmt_dos_factores = sqlsrv_prepare($conn, $sql_dos_factores, $params); + + $dos_factores_estado = 0; // valor por defecto + + if ($stmt_dos_factores && sqlsrv_execute($stmt_dos_factores)) { + if ($row = sqlsrv_fetch_array($stmt_dos_factores, SQLSRV_FETCH_ASSOC)) { + $dos_factores_estado = (int)$row['dos_factores']; + } + } + + sqlsrv_free_stmt($stmt_dos_factores); + sqlsrv_close($conn); + + return $dos_factores_estado; +} + +// Función para actualizar el estado (cuando se envía el formulario) +function autenticacionDosFactores() +{ + // Solo ejecutar si es una petición POST + if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + header('Location: /IMPORTADORES/seguridad/index'); exit; } - $id_usuario = $_SESSION['usuario_id']; + $conn = getConnection(); + + // CORRIGIDO: Cambiar id_usuario por usuario_id + $id_usuario = $_SESSION['usuario_id'] ?? null; + if (!$id_usuario) { + die("No autorizado - Sesión inválida"); + } + + // CORRIGIDO: Verificar también que no esté pendiente de confirmación + if ($_SESSION['pendiente_confirmacion'] ?? false) { + die("No autorizado - Confirmación pendiente"); + } + + $dos_factores = isset($_POST['dos_factores']) ? 1 : 0; + + $sql = "UPDATE usuarios_sistema SET dos_factores = ? WHERE id_usuario = ?"; + $params = [$dos_factores, $id_usuario]; + $stmt = sqlsrv_prepare($conn, $sql, $params); + + if (!$stmt) { + die("Error en la preparación: " . print_r(sqlsrv_errors(), true)); + } + + $result = sqlsrv_execute($stmt); + + if ($result === false) { + die("Error al actualizar: " . print_r(sqlsrv_errors(), true)); + } + + sqlsrv_free_stmt($stmt); + sqlsrv_close($conn); + + // MEJORADO: Agregar mensaje de éxito + $_SESSION['config_success'] = $dos_factores ? + 'Autenticación de dos factores activada correctamente.' : + 'Autenticación de dos factores desactivada correctamente.'; + + header('Location: /IMPORTADORES/seguridad/index'); + exit; +} + +function correoExtra() +{ + // CORRIGIDO: Cambiar id_usuario por usuario_id y verificar confirmación + if (!isset($_SESSION['usuario_id']) || ($_SESSION['pendiente_confirmacion'] ?? false)) { + echo "Error: no se ha iniciado sesión o sesión incompleta."; + exit; + } + + $id_usuario = $_SESSION['usuario_id']; // CORRIGIDO $conn = getConnection(); $sql_check = "SELECT COUNT(*) AS total FROM correo_extra WHERE id_usuario = ?"; @@ -57,6 +137,7 @@ function correoExtra() $stmt = sqlsrv_prepare($conn, $query, [$id_usuario, $correo]); if ($stmt && sqlsrv_execute($stmt)) { + $_SESSION['config_success'] = 'Correo adicional registrado correctamente.'; header('Location: /IMPORTADORES/seguridad/index'); exit; } else { @@ -66,12 +147,13 @@ function correoExtra() function correoRespaldo() { - if (!isset($_SESSION['usuario_id']) || ($_SESSION['pendiente_confirmacion'] ?? true)) { - echo "Error: no se ha iniciado sesión o falta el ID de usuario."; + // CORRIGIDO: Cambiar id_usuario por usuario_id y verificar confirmación + if (!isset($_SESSION['usuario_id']) || ($_SESSION['pendiente_confirmacion'] ?? false)) { + echo "Error: no se ha iniciado sesión o sesión incompleta."; exit; } - $id_usuario = $_SESSION['usuario_id']; + $id_usuario = $_SESSION['usuario_id']; // CORRIGIDO $conn = getConnection(); $sql_check = "SELECT COUNT(*) AS total FROM correo_respaldo WHERE id_usuario = ?"; @@ -79,7 +161,7 @@ function correoRespaldo() $row = sqlsrv_fetch_array($stmt_check, SQLSRV_FETCH_ASSOC); if ($row['total'] > 0) { - echo "Ya tienes un correo adicional registrado."; + echo "Ya tienes un correo de respaldo registrado."; return; } @@ -94,23 +176,35 @@ function correoRespaldo() $stmt = sqlsrv_prepare($conn, $query, [$id_usuario, $correo]); if ($stmt && sqlsrv_execute($stmt)) { + $_SESSION['config_success'] = 'Correo de respaldo registrado correctamente.'; header('Location: /IMPORTADORES/seguridad/index'); exit; } else { - echo "Error al registrar el correo extra."; + echo "Error al registrar el correo de respaldo."; } } function obtenerCorreos($conn, $id_usuario) { $correos = [ + 'dos_factores' => 0, 'correo_extra' => '', 'correo_respaldo' => '' ]; + // Obtener estado dos_factores + $sql_dos_factores = "SELECT dos_factores FROM usuarios_sistema WHERE id_usuario = ?"; + $params = [$id_usuario]; + $stmt_dos_factores = sqlsrv_prepare($conn, $sql_dos_factores, $params); + if ($stmt_dos_factores && sqlsrv_execute($stmt_dos_factores)) { + if ($row = sqlsrv_fetch_array($stmt_dos_factores, SQLSRV_FETCH_ASSOC)) { + $correos['dos_factores'] = (int)$row['dos_factores']; // MEJORADO: Cast a int + } + } + // Obtener correo_extra $sql_extra = "SELECT correo FROM correo_extra WHERE id_usuario = ?"; - $stmt_extra = sqlsrv_prepare($conn, $sql_extra, [$id_usuario]); + $stmt_extra = sqlsrv_prepare($conn, $sql_extra, $params); if ($stmt_extra && sqlsrv_execute($stmt_extra)) { if ($row = sqlsrv_fetch_array($stmt_extra, SQLSRV_FETCH_ASSOC)) { $correos['correo_extra'] = $row['correo']; @@ -119,7 +213,7 @@ function obtenerCorreos($conn, $id_usuario) // Obtener correo_respaldo $sql_respaldo = "SELECT correo FROM correo_respaldo WHERE id_usuario = ?"; - $stmt_respaldo = sqlsrv_prepare($conn, $sql_respaldo, [$id_usuario]); + $stmt_respaldo = sqlsrv_prepare($conn, $sql_respaldo, $params); if ($stmt_respaldo && sqlsrv_execute($stmt_respaldo)) { if ($row = sqlsrv_fetch_array($stmt_respaldo, SQLSRV_FETCH_ASSOC)) { $correos['correo_respaldo'] = $row['correo']; diff --git a/views/seguridad/agregar.php b/views/seguridad/agregar.php index 00a4379..d24c131 100644 --- a/views/seguridad/agregar.php +++ b/views/seguridad/agregar.php @@ -1,5 +1,7 @@ @@ -83,6 +85,23 @@ include __DIR__ . '/../partials/sidebar_configuracion.php';
+ +
+
+

Autenticación de dos factores

+
+
+ > + +
+ +
+
+
+
@@ -116,8 +135,19 @@ include __DIR__ . '/../partials/sidebar_configuracion.php';
+
+ + + \ No newline at end of file diff --git a/views/seguridad/dashboard_seguridad.php b/views/seguridad/dashboard_seguridad.php index b9bd3d6..84c76b8 100644 --- a/views/seguridad/dashboard_seguridad.php +++ b/views/seguridad/dashboard_seguridad.php @@ -83,9 +83,25 @@ include __DIR__ . '/../partials/sidebar_configuracion.php';
+
+
+

Autenticación de dos factores

+
+
+ > + +
+
+
+
+
-

Recibe Notificaciones

+

Recibe notificaciones

@@ -97,7 +113,7 @@ include __DIR__ . '/../partials/sidebar_configuracion.php';
-

Correo de Respaldo

+

Correo de respaldo