Files
ByteCenter/app/controllers/logInController.php
T
2026-08-04 19:20:18 -03:00

646 lines
20 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// Requiere el modelo de logIn
require_once __DIR__ . '/../models/logInModel.php';
$loginmodel = new logInModel();
$opt = $_GET['opt'] ?? 'login_operador';
// Manejo de opt enviado por URL
switch ($opt) {
// LOGIN
case 'login':
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
if (isset($_SESSION['reset_autorizado'])) {
unset($_SESSION['reset_autorizado']);
}
ob_start();
require __DIR__ . '/../views/login/logInView.php';
return ob_get_clean();
}
$usuario = trim($_POST['usuario'] ?? ''); // Se almacenan los datos enviados por $_POST desde el form en las variables
$password = trim($_POST['password'] ?? '');
$errores = [];
// Validaciones para iniciar sesión
if ($usuario === '') {
$errores[] = "El usuario no puede estar vacío.";
} elseif (!preg_match('/^(?=.*[a-zA-Z])(?!.*[._]{2})[a-zA-Z0-9._]{5,30}$/', $usuario)) {
$errores[] = "El usuario debe tener 530 caracteres, incluir al menos una letra y solo usar letras, números, puntos o guiónes bajos no consecutivos.";
}
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.";
}
// Control de intentos de inicio de sesión
$maxIntentos = 3;
$tiempoBloqueo = 60; // segundos
if (!isset($_SESSION['login_intentos'])) {
$_SESSION['login_intentos'] = 0;
$_SESSION['login_ultimo_intento'] = time();
}
// Si pasó el tiempo, resetear
if (time() - $_SESSION['login_ultimo_intento'] > $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('<br>', $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('<br>', $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();
}