$tiempoBloqueo) { $_SESSION['login_intentos'] = 0; } // Verificar bloqueo if ($_SESSION['login_intentos'] >= $maxIntentos) { $_SESSION['flash_error'] = "Demasiados intentos. Espere 1 minuto antes de volver a intentar."; header("Location: index.php?opt=login_operador"); exit; } // Si existen errores de validación, se informan definiendo flash_error en el array asociativo $_SESSION if (!empty($errores)) { // Se suma el intento erroneo de inicio de sesión y se guarda el tiempo $_SESSION['login_intentos']++; $_SESSION['login_ultimo_intento'] = time(); $_SESSION['flash_error'] = implode('
', $errores); // Agrega un salto de linea entre cada error encontrado y los guarda en $_SESSION header("Location: index.php?opt=login_operador"); exit; } // Si no existen errores, buscamos el usuario mediante logInModel $user = $loginmodel->buscarPorUsuario($usuario); if (!$user || !password_verify($password, $user['password'])) { $_SESSION['login_intentos']++; $_SESSION['login_ultimo_intento'] = time(); $_SESSION['flash_error'] = 'Usuario o contraseña incorrectos'; header("Location: index.php?opt=login_operador"); exit; } // Verificamos el rol del usuario para cargar la vista correspondiente if ($user['rol'] === 'operador') { // Se resetean los intentos $_SESSION['login_intentos'] = 0; $_SESSION['login_ultimo_intento'] = time(); $_SESSION['user'] = $user; // Se define $user dentro del array asociativo $_SESSION para tenerlo disponible en todo el sistema header("Location: index.php?opt=inicio_operador"); exit; } else if ($user['rol'] === 'admin') { // Se resetean los intentos $_SESSION['login_intentos'] = 0; $_SESSION['login_ultimo_intento'] = time(); $_SESSION['user'] = $user; // Se define $user dentro del array asociativo $_SESSION para tenerlo disponible en todo el sistema header("Location: index.php?opt=inicio_admin"); exit; } // HOME OPERADOR case 'home_operador': // Validar sesión if (!isset($_SESSION['user'])) { header("Location: index.php?opt=login_operador"); exit; } // Si el rol es admin, se redirigue al home_admin (en caso de ingresar por URL) if ($_SESSION['user']['rol'] !== 'operador') { header("Location: index.php?opt=home_admin"); exit; } // Se carga en caché el home operador y se envía el HTML ob_start(); require __DIR__ . '/../views/login/homeOperadorView.php'; return ob_get_clean(); // HOME ADMIN case 'home_admin': // Validar sesión if (!isset($_SESSION['user'])) { header("Location: index.php?opt=login_admin"); exit; } // Si el rol es operador, se redirigue al home_operador (en caso de ingresar por URL) if ($_SESSION['user']['rol'] !== 'admin') { header("Location: index.php?opt=home_operador"); exit; } // Se carga en caché el home admin y se envía el HTML ob_start(); require __DIR__ . '/../views/login/homeAdminView.php'; return ob_get_clean(); // INICIO OPERADOR case 'inicio_operador': // Validar sesión if (!isset($_SESSION['user'])) { header("Location: index.php?opt=login_operador"); exit; } // Si el rol es admin, se redirigue al inicio_admin (en caso de ingresar por URL) if ($_SESSION['user']['rol'] !== 'operador') { header("Location: index.php?opt=inicio_admin"); exit; } // Se carga en caché el inicio operador y se envía el HTML ob_start(); require __DIR__ . '/../views/login/inicioOperadorView.php'; return ob_get_clean(); // INICIO ADMIN case 'inicio_admin': // Validar sesión if (!isset($_SESSION['user'])) { header("Location: index.php?opt=login_admin"); exit; } // Si el rol es operador, se redirigue al inicio_operador (en caso de ingresar por URL) if ($_SESSION['user']['rol'] !== 'admin') { header("Location: index.php?opt=inicio_operador"); exit; } // Calcular si mostrar alerta de backup (solo a admin) if ($_SESSION['user']['rol'] === 'admin') { require __DIR__ . '/../models/configuracionModel.php'; $configuracionmodel = new configuracionModel(); $fechaUltimoBackup = $configuracionmodel->obtenerFechaUltimoBackup(); if ($fechaUltimoBackup) { $dias = (new DateTime($fechaUltimoBackup))->diff(new DateTime())->days; if ($dias >= 7) { header("Location: index.php?opt=generar_backup&from=inicio_admin&auto=1"); exit; } } else { $ahora = time(); // Si nunca se mostró o ya pasó 1 hora if (!isset($_SESSION['ultimo_aviso_backup']) || ($ahora - $_SESSION['ultimo_aviso_backup']) >= 3600) { $_SESSION['flash_error'] = "No se encontraron copias de seguridad. Por favor, verifique."; // Guardamos cuándo mostramos el mensaje $_SESSION['ultimo_aviso_backup'] = $ahora; } } } // Se carga en caché el inicio admin y se envía el HTML ob_start(); require __DIR__ . '/../views/login/inicioAdminView.php'; return ob_get_clean(); // LOGOUT case 'logout': session_unset(); // Libera las variables almacenadas en $_SESSION session_destroy(); // Destruye la sesión del servidor setcookie(session_name(), '', time() - 3600, '/'); // Borra la cookie en el navegaodor header("Location: index.php?opt=login"); exit; // CARGAR VISTA README (AYUDA) case 'readme': if ($_SERVER['REQUEST_METHOD'] === 'GET') { ob_start(); require __DIR__ . '/../views/ayudas/readMe.php'; return ob_get_clean(); } // RECUPERACIÓN DE CUENTA case 'recuperar_cuenta': if ($_SERVER['REQUEST_METHOD'] === 'GET') { ob_start(); require __DIR__ . '/../views/login/recuperarCuentaView.php'; return ob_get_clean(); } if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) { // Recorre todos los caracteres (siempre) y retorna true or false $_SESSION['flash_error'] = 'Acción inválida.'; header('Location: index.php?opt=login'); exit; } // Requiero modelos necesarios require_once __DIR__ . '/../models/usuarioModel.php'; $usuariomodel = new usuarioModel(); require_once __DIR__ . '/../models/logInModel.php'; $loginmodel = new logInModel(); require_once __DIR__ . '/../helpers/email_recuperar_cuenta.php'; $email = trim($_POST['email'] ?? $_SESSION['reset_email'] ?? ''); // Cooltime de envío de mails if (isset($_SESSION['ultimo_envio_codigo'])) { $segundosRestantes = 60 - (time() - $_SESSION['ultimo_envio_codigo']); if ($segundosRestantes > 0) { $_SESSION['flash_success'] = "Ya se envió un código recientemente. Espere {$segundosRestantes} segundos."; header('Location: index.php?opt=verificar_codigo'); exit; } } // Validación if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) { $_SESSION['flash_error'] = 'Email inválido'; header('Location: index.php?opt=recuperar_cuenta'); exit; } // Buscar usuario $usuario = $usuariomodel->obtenerPorEmail($email); // Siempre respuesta genérica (seguridad) if (!$usuario || $usuario['rol'] !== 'admin') { $_SESSION['flash_error'] = 'Si el correo ingresado es válido, recibirás un código de verificación.'; header('Location: index.php?opt=recuperar_cuenta'); exit; } // Generar código $codigo = random_int(100000, 999999); $codigoHash = password_hash($codigo, PASSWORD_DEFAULT); $expira = date('Y-m-d H:i:s', strtotime('+10 minutes')); // Invalidar códigos anteriores if (!$loginmodel->invalidarPorUsuario($usuario['id'])) { $_SESSION['flash_error'] = 'Ocurrió un error inesperado.'; header('Location: index.php?opt=recuperar_cuenta'); exit; } // Guardar nuevo código if (!$loginmodel->crear([ 'id_usuario' => $usuario['id'], 'codigo' => $codigoHash, 'expira_a' => $expira ])) { $_SESSION['flash_error'] = 'Ocurrió un error inesperado.'; header('Location: index.php?opt=recuperar_cuenta'); exit; } // Guardar email en sesión (UX) $_SESSION['reset_email'] = $email; // Enviar mail if (!enviarCodigoRecuperacion($email, $codigo)) { $_SESSION['flash_error'] = 'Ocurrió un error inesperado al enviar el email.'; header('Location: index.php?opt=recuperar_cuenta'); exit; } $_SESSION['ultimo_envio_codigo'] = time(); // Redirigir a verificación $_SESSION['flash_success'] = 'Si el correo ingresado es válido, recibirás un código de verificación.'; header('Location: index.php?opt=verificar_codigo'); exit; } case 'verificar_codigo': if ($_SERVER['REQUEST_METHOD'] === 'GET') { if (empty($_SESSION['reset_email'])) { header('Location: index.php?opt=recuperar_cuenta'); exit; } ob_start(); require __DIR__ . '/../views/login/verificarCodigoView.php'; return ob_get_clean(); } if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) { $_SESSION['flash_error'] = 'Acción inválida.'; header('Location: index.php?opt=login'); exit; } // Inicializamos contador if (!isset($_SESSION['codigo_intentos'])) { $_SESSION['codigo_intentos'] = 0; } // VALIDAMOS BLOQUEO ACTIVO // SI EL BLOQUEO YA EXPIRÓ, RESETEAMOS if (isset($_SESSION['codigo_bloqueado_hasta']) && time() >= $_SESSION['codigo_bloqueado_hasta']) { unset($_SESSION['codigo_bloqueado_hasta']); $_SESSION['codigo_intentos'] = 0; } // SI SIGUE BLOQUEADO if (isset($_SESSION['codigo_bloqueado_hasta']) && time() < $_SESSION['codigo_bloqueado_hasta']) { $segundos = $_SESSION['codigo_bloqueado_hasta'] - time(); $minutos = ceil($segundos / 60); $_SESSION['flash_error'] = "Demasiados intentos fallidos. Espere {$minutos} minuto(s)."; header('Location: index.php?opt=verificar_codigo'); exit; } require_once __DIR__ . '/../models/usuarioModel.php'; $usuariomodel = new usuarioModel(); require_once __DIR__ . '/../models/logInModel.php'; $loginmodel = new logInModel(); $codigo = trim($_POST['codigo'] ?? ''); $email = $_SESSION['reset_email'] ?? null; if (empty($codigo) || empty($email)) { $_SESSION['flash_error'] = 'Debe ingresar el código.'; header('Location: index.php?opt=verificar_codigo'); exit; } $usuario = $usuariomodel->obtenerPorEmail($email); if (!$usuario) { $_SESSION['flash_error'] = 'Ocurrió un error.'; header('Location: index.php?opt=login'); exit; } $registro = $loginmodel->obtenerCodigoValido($usuario['id']); if (!$registro) { $_SESSION['codigo_intentos']++; // BLOQUEO AUTOMÁTICO if ($_SESSION['codigo_intentos'] >= 5) { $_SESSION['codigo_bloqueado_hasta'] = time() + (15 * 60); $_SESSION['flash_error'] = 'Demasiados intentos fallidos. Intente nuevamente en 15 minutos.'; } else { $_SESSION['flash_error'] = 'El código no es válido.'; } header('Location: index.php?opt=verificar_codigo'); exit; } // EXPIRACIÓN if (strtotime($registro['expira_a']) < time()) { $loginmodel->marcarComoUsado($registro['id']); $_SESSION['flash_error'] = 'El código ha expirado.'; header('Location: index.php?opt=verificar_codigo'); exit; } // VALIDACIÓN HASH if (!password_verify($codigo, $registro['codigo'])) { $_SESSION['codigo_intentos']++; // BLOQUEAR SI LLEGA AL LÍMITE if ($_SESSION['codigo_intentos'] >= 5) { $_SESSION['codigo_bloqueado_hasta'] = time() + (15 * 60); $_SESSION['flash_error'] = 'Demasiados intentos fallidos. Intente nuevamente en 15 minutos.'; } else { $_SESSION['flash_error'] = 'Código incorrecto.'; } header('Location: index.php?opt=verificar_codigo'); exit; } // ÉXITO $loginmodel->marcarComoUsado($registro['id']); $_SESSION['reset_autorizado'] = $usuario['id']; // Limpiar protección unset($_SESSION['codigo_intentos']); unset($_SESSION['codigo_bloqueado_hasta']); header('Location: index.php?opt=restablecer_contrasenia'); exit; } // RESTABLECER CONTRASENIA case 'restablecer_contrasenia': if (!isset($_SESSION['reset_autorizado'])) { header('Location: index.php?opt=login'); exit; } // MOSTRAR VISTA if ($_SERVER['REQUEST_METHOD'] === 'GET') { ob_start(); require __DIR__ . '/../views/login/restablecerContraseniaView.php'; return ob_get_clean(); } // GUARDAR NUEVA CONTRASENIA if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) { $_SESSION['flash_error'] = 'Acción inválida.'; header('Location: index.php?opt=login'); exit; } require_once __DIR__ . '/../models/usuarioModel.php'; $usuariomodel = new usuarioModel(); $password = trim($_POST['password'] ?? ''); $confirmar = trim($_POST['confirmar_password'] ?? ''); $errores = []; // VALIDACIONES if (empty($password) || empty($confirmar)) { $_SESSION['flash_error'] = 'Complete todos los campos.'; header('Location: index.php?opt=restablecer_contrasenia'); exit; } // VALIDACIONES DE CONTRASENIA if ($password === '') { $errores[] = "La contraseña no puede estar vacía."; } elseif (strpos($password, ' ') !== false) { $errores[] = "La contraseña no puede contener espacios."; } elseif (!preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{6,}$/', $password)) { $errores[] = "La contraseña debe tener al menos una mayúscula, una minúscula y un número, y mínimo 6 caracteres."; } if ($password !== $confirmar) { $_SESSION['flash_error'] = 'Las contraseñas no coinciden.'; header('Location: index.php?opt=restablecer_contrasenia'); exit; } if (!empty($errores)) { $_SESSION['flash_error'] = implode('
', $errores); header("Location: index.php?opt=restablecer_contrasenia"); exit; } // HASH $passwordHash = password_hash($password, PASSWORD_DEFAULT); // ACTUALIZAR if (!$usuariomodel->actualizarContrasenia ( $_SESSION['reset_autorizado'], $passwordHash )) { $_SESSION['flash_error'] = 'Ocurrió un error inesperado.'; header('Location: index.php?opt=restablecer_contrasenia'); exit; } // INVALIDAMOS TODOS LOS CODIGOS (PARA PREVENIR) $loginmodel->invalidarPorUsuario($_SESSION['reset_autorizado']); // Limpiar sesión temporal unset($_SESSION['reset_autorizado']); unset($_SESSION['reset_email']); // REGENERACION DE SESSION ID session_regenerate_id(true); $_SESSION['flash_success'] = 'Contraseña actualizada correctamente.'; header('Location: index.php?opt=login'); exit; } // DEFAULT (404) default: ob_start(); require __DIR__ . '/../views/404View.php'; return ob_get_clean(); }