Proyectos de Subversion Moodle

Rev

Rev 1 | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |

<?php

// Este archivo es parte de Moodle - http://moodle.org/
//
// Moodle es software libre: puedes redistribuirlo y/o modificarlo
// bajo los términos de la Licencia Pública General GNU publicada por
// la Free Software Foundation, ya sea la versión 3 de la Licencia, o
// (a tu elección) cualquier versión posterior.
//
// Moodle se distribuye con la esperanza de que sea útil,
// pero SIN NINGUNA GARANTÍA; sin siquiera la garantía implícita de
// COMERCIABILIDAD o IDONEIDAD PARA UN PROPÓSITO PARTICULAR. Ver la
// Licencia Pública General GNU para más detalles.
//
// Deberías haber recibido una copia de la Licencia Pública General GNU
// junto con Moodle. Si no, ve <http://www.gnu.org/licenses/>.

/**
 * Página principal de inicio de sesión de Moodle.
 * Este archivo maneja todo el proceso de autenticación de usuarios,
 * incluyendo la validación de credenciales, manejo de sesiones,
 * redirecciones y mensajes de error.
 *
 * @package    core
 * @subpackage auth
 * @copyright  1999 onwards Martin Dougiamas  http://dougiamas.com
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

// Incluir archivos de configuración y librerías necesarias
require('../config.php');
require_once('lib.php');

// Verificar si se requiere una actualización mayor del sistema
// Si es así, redirigir al usuario a la página de actualización
redirect_if_major_upgrade_required();

// ============================================================================
// PARÁMETROS DE LA PÁGINA DE INICIO DE SESIÓN
// ============================================================================

// Parámetros opcionales que controlan el comportamiento del inicio de sesión
$testsession = optional_param('testsession', 0, PARAM_INT);     // Verifica el funcionamiento correcto de la sesión
$anchor = optional_param('anchor', '', PARAM_RAW);              // Mantiene la posición del ancla en la URL de destino
$loginredirect = optional_param('loginredirect', 1, PARAM_BOOL); // Controla la redirección a URL de login alternativo
$resendconfirmemail = optional_param('resendconfirmemail', false, PARAM_BOOL); // Permite reenviar email de confirmación

// Verificación especial para sitios Behat (entorno de pruebas)
// Esto podría ser seguro para sitios no-Behat, pero por ahora solo lo permitimos en sitios Behat
if (defined('BEHAT_SITE_RUNNING') && BEHAT_SITE_RUNNING) {
    $wantsurl = optional_param('wantsurl', '', PARAM_LOCALURL);   // Sobrescribe $SESSION->wantsurl si se proporciona
    if ($wantsurl !== '') {
        $SESSION->wantsurl = (new moodle_url($wantsurl))->out(false);
    }
}

// Configuración del contexto y la página
$context = context_system::instance();
$PAGE->set_url("$CFG->wwwroot/login/index.php");
$PAGE->set_context($context);
$PAGE->set_pagelayout('login');

// ============================================================================
// CONFIGURACIÓN INICIAL Y VARIABLES
// ============================================================================

// Inicialización de variables para el manejo de mensajes y errores
$errormsg = '';    // Almacena mensajes de error para mostrar al usuario
$infomsg = '';     // Almacena mensajes informativos para mostrar al usuario
$errorcode = 0;    // Código numérico que identifica el tipo de error

// ============================================================================
// VERIFICACIÓN DE SESIÓN
// ============================================================================

// Prueba de funcionamiento de la sesión
if ($testsession) {
    if ($testsession == $USER->id) {
        // Si la sesión es válida, redirigir a la URL deseada
        if (isset($SESSION->wantsurl)) {
            $urltogo = $SESSION->wantsurl;
        } else {
            $urltogo = $CFG->wwwroot . '/';
        }
        unset($SESSION->wantsurl);
        redirect($urltogo);
    } else {
        // Si la sesión no es válida, mostrar error de cookies
        $errormsg = get_string("cookiesnotenabled");
        $errorcode = 1;
    }
}

// ============================================================================
// MANEJO DE SESIONES EXPIRADAS
// ============================================================================

// Verificar si la sesión actual ha expirado
if (!empty($SESSION->has_timed_out)) {
    $session_has_timed_out = true;
    unset($SESSION->has_timed_out);
} else {
    $session_has_timed_out = false;
}

// Inicialización de variables para el formulario y usuario
$frm = false;
$user = false;

// Obtener la secuencia de plugins de autenticación habilitados
$authsequence = get_enabled_auth_plugins();
foreach ($authsequence as $authname) {
    $authplugin = get_auth_plugin($authname);
    // El hook loginpage_hook() del plugin de autenticación puede establecer $frm y/o $user
    $authplugin->loginpage_hook();
}

// ============================================================================
// CONFIGURACIÓN DEL SITIO Y NAVEGACIÓN
// ============================================================================

// Obtener información del sitio
$site = get_site();

// Ignorar páginas activas en la navegación/configuración
$PAGE->navbar->ignore_active();
$loginsite = get_string("loginsite");
$PAGE->navbar->add($loginsite);

// ============================================================================
// PROCESO DE AUTENTICACIÓN
// ============================================================================

// Verificar si algún plugin de autenticación ya proporcionó el usuario completo
if ($user !== false or $frm !== false or $errormsg !== '') {
    // El plugin de autenticación ya proporcionó el usuario completo, datos del formulario falso
    // o impidió el inicio de sesión con mensaje de error
} else if (!empty($SESSION->wantsurl) && file_exists($CFG->dirroot . '/login/weblinkauth.php')) {
    // Maneja el caso de otro sitio Moodle vinculando a una página en este sitio
    include($CFG->dirroot . '/login/weblinkauth.php');
    if (function_exists('weblink_auth')) {
        $user = weblink_auth($SESSION->wantsurl);
    }
    if ($user) {
        $frm->username = $user->username;
    } else {
        $frm = data_submitted();
    }
} else {
    $frm = data_submitted();
}

// Restaurar el #anchor a la wantsurl original
if ($anchor && isset($SESSION->wantsurl) && strpos($SESSION->wantsurl, '#') === false) {
    $wantsurl = new moodle_url($SESSION->wantsurl);
    $wantsurl->set_anchor(substr($anchor, 1));
    $SESSION->wantsurl = $wantsurl->out();
}

// ============================================================================
// VERIFICACIÓN DE CREDENCIALES
// ============================================================================

// Verificar si el usuario ha enviado datos de inicio de sesión
if ($frm and isset($frm->username)) {
    // Normalizar el nombre de usuario
    $frm->username = trim(core_text::strtolower($frm->username));

    // Verificar la autenticación 'none' si está habilitada
    if (is_enabled_auth('none')) {
        if ($frm->username !== core_user::clean_field($frm->username, 'username')) {
            $errormsg = get_string('username') . ': ' . get_string("invalidusername");
            $errorcode = 2;
            $user = null;
        }
    }

    // Verificar si el usuario es invitado
    if ($user) {
        // El plugin de autenticación ya proporcionó el usuario
    } else if (($frm->username == 'guest') and empty($CFG->guestloginbutton)) {
        $user = false;    // No se puede iniciar sesión como invitado si el botón de invitado está deshabilitado
        $frm = false;
    } else {
        if (empty($errormsg)) {
            // Intentar autenticar al usuario
            $logintoken = isset($frm->logintoken) ? $frm->logintoken : '';
            $loginrecaptcha = login_captcha_enabled() ? $frm->{'g-recaptcha-response'} ?? '' : false;
            $user = authenticate_user_login($frm->username, $frm->password, false, $errorcode, $logintoken, $loginrecaptcha);
        }
    }

    // ============================================================================
    // MANEJO DE USUARIOS RESTAURADOS
    // ============================================================================

    // Procesar usuarios que han sido restaurados desde una copia de seguridad
    if (!$user and $frm and is_restored_user($frm->username)) {
        $PAGE->set_title(get_string('restoredaccount'));
        $PAGE->set_heading($site->fullname);
        echo $OUTPUT->header();
        echo $OUTPUT->heading(get_string('restoredaccount'));
        echo $OUTPUT->box(get_string('restoredaccountinfo'), 'generalbox boxaligncenter');
        require_once('restored_password_form.php');
        $form = new login_forgot_password_form('forgot_password.php', array('username' => $frm->username));
        $form->display();
        echo $OUTPUT->footer();
        die;
    }

    // ============================================================================
    // CONFIRMACIÓN DE CUENTA
    // ============================================================================

    if ($user) {
        // Configuración del idioma
        if (isguestuser($user)) {
            // Sin idioma predefinido para invitados - usar sesión existente o idioma predeterminado del sitio
            unset($user->lang);
        } else if (!empty($user->lang)) {
            // Desactivar idioma de sesión anterior - usar preferencia del usuario
            unset($SESSION->lang);
        }

        // Verificar si la cuenta nunca fue confirmada
        if (empty($user->confirmed)) {
            $PAGE->set_title(get_string("mustconfirm"));
            $PAGE->set_heading($site->fullname);
            echo $OUTPUT->header();
            echo $OUTPUT->heading(get_string("mustconfirm"));
            if ($resendconfirmemail) {
                if (!send_confirmation_email($user)) {
                    echo $OUTPUT->notification(get_string('emailconfirmsentfailure'), \core\output\notification::NOTIFY_ERROR);
                } else {
                    echo $OUTPUT->notification(get_string('emailconfirmsentsuccess'), \core\output\notification::NOTIFY_SUCCESS);
                }
            }
            echo $OUTPUT->box(get_string("emailconfirmsent", "", s($user->email)), "generalbox boxaligncenter");
            $resendconfirmurl = new moodle_url(
                '/login/index.php',
                [
                    'username' => $frm->username,
                    'password' => $frm->password,
                    'resendconfirmemail' => true,
                    'logintoken' => \core\session\manager::get_login_token()
                ]
            );
            echo $OUTPUT->single_button($resendconfirmurl, get_string('emailconfirmationresend'));
            echo $OUTPUT->footer();
            die;
        }

        // ============================================================================
        // COMPLETAR INICIO DE SESIÓN
        // ============================================================================

        // Completar el proceso de inicio de sesión
        complete_user_login($user);

        // Aplicar límite de inicio de sesión concurrente
        \core\session\manager::apply_concurrent_login_limit($user->id, session_id());

        // Configurar cookie de nombre de usuario
        if (!empty($CFG->nolastloggedin)) {
            // No almacenar último usuario conectado en cookie
        } else if (empty($CFG->rememberusername)) {
            // Sin cookies permanentes, eliminar la anterior si existe
            set_moodle_cookie('');
        } else {
            set_moodle_cookie($USER->username);
        }

        // Obtener URL de redirección
        $urltogo = core_login_get_return_url();

        // ============================================================================
        // VERIFICACIÓN DE CONTRASEÑAS EXPIRADAS
        // ============================================================================

        // Verificar si la contraseña del usuario ha expirado (solo para autenticación LDAP)
        $userauth = get_auth_plugin($USER->auth);
        if (!isguestuser() and !empty($userauth->config->expiration) and $userauth->config->expiration == 1) {
            $externalchangepassword = false;
            if ($userauth->can_change_password()) {
                $passwordchangeurl = $userauth->change_password_url();
                if (!$passwordchangeurl) {
                    $passwordchangeurl = $CFG->wwwroot . '/login/change_password.php';
                } else {
                    $externalchangepassword = true;
                }
            } else {
                $passwordchangeurl = $CFG->wwwroot . '/login/change_password.php';
            }
            $days2expire = $userauth->password_expire($USER->username);
            $PAGE->set_title($loginsite);
            $PAGE->set_heading("$site->fullname");
            if (intval($days2expire) > 0 && intval($days2expire) < intval($userauth->config->expiration_warning)) {
                echo $OUTPUT->header();
                echo $OUTPUT->confirm(get_string('auth_passwordwillexpire', 'auth', $days2expire), $passwordchangeurl, $urltogo);
                echo $OUTPUT->footer();
                exit;
            } elseif (intval($days2expire) < 0) {
                if ($externalchangepassword) {
                    // Cerrar sesión si el formulario de cambio de contraseña es externo
                    require_logout();
                } else {
                    // Forzar cambio de contraseña si se usa el formulario estándar
                    set_user_preference('auth_forcepasswordchange', 1, $USER);
                }
                echo $OUTPUT->header();
                echo $OUTPUT->confirm(get_string('auth_passwordisexpired', 'auth'), $passwordchangeurl, $urltogo);
                echo $OUTPUT->footer();
                exit;
            }
        }

        // Limpiar mensajes de error antes de la última redirección
        unset($SESSION->loginerrormsg);
        unset($SESSION->logininfomsg);

        // Descartar loginredirect si estamos redirigiendo
        unset($SESSION->loginredirect);

        // Probar que la sesión funciona redirigiendo a sí mismo
        $SESSION->wantsurl = $urltogo;
        redirect(new moodle_url(get_login_url(), array('testsession' => $USER->id)));
    } else {
        // Manejo de errores de autenticación
        if (empty($errormsg)) {
            if ($errorcode == AUTH_LOGIN_UNAUTHORISED) {
                $errormsg = get_string("unauthorisedlogin", "", $frm->username);
            } else if ($errorcode == AUTH_LOGIN_FAILED_RECAPTCHA) {
                $errormsg = get_string('missingrecaptchachallengefield');
            } else {
                $errormsg = get_string("invalidlogin");
                $errorcode = 3;
            }
        }
    }
}

// ============================================================================
// DETECCIÓN DE PROBLEMAS CON SESIONES
// ============================================================================

// Detectar problemas con sesiones con tiempo de espera agotado
if ($session_has_timed_out and !data_submitted()) {
    $errormsg = get_string('sessionerroruser', 'error');
    $errorcode = 4;
}

// ============================================================================
// MANEJO DE URL DE DESTINO
// ============================================================================

// Recordar dónde estaba intentando llegar el usuario antes de llegar aquí
if (empty($SESSION->wantsurl)) {
    $SESSION->wantsurl = null;
    $referer = get_local_referer(false);
    if (
        $referer &&
        $referer != $CFG->wwwroot &&
        $referer != $CFG->wwwroot . '/' &&
        $referer != $CFG->wwwroot . '/login/' &&
        strpos($referer, $CFG->wwwroot . '/login/?') !== 0 &&
        strpos($referer, $CFG->wwwroot . '/login/index.php') !== 0
    ) {
        $SESSION->wantsurl = $referer;
    }
}

// Verificar si loginredirect está establecido en la SESSION
if ($errorcode && isset($SESSION->loginredirect)) {
    $loginredirect = $SESSION->loginredirect;
}
$SESSION->loginredirect = $loginredirect;

// ============================================================================
// REDIRECCIÓN A URL DE INICIO DE SESIÓN ALTERNATIVA
// ============================================================================

// Redirigir a URL de inicio de sesión alternativa si es necesario
if (!empty($CFG->alternateloginurl) && $loginredirect) {
    $loginurl = new moodle_url($CFG->alternateloginurl);
    $loginurlstr = $loginurl->out(false);

    if ($SESSION->wantsurl != '' && strpos($SESSION->wantsurl, $loginurlstr) === 0) {
        // No queremos volver a la URL alternativa
        $SESSION->wantsurl = null;
    }

    // Si hay código de error, agregarlo a la URL
    if ($errorcode) {
        $loginurl->param('errorcode', $errorcode);
    }

    redirect($loginurl->out(false));
}

// ============================================================================
// GENERACIÓN DE LA PÁGINA DE INICIO DE SESIÓN
// ============================================================================

// Inicializar el objeto del formulario si no existe
if (!isset($frm) or !is_object($frm)) {
    $frm = new stdClass();
}

// Configurar valores predeterminados del formulario
if (empty($frm->username) && $authsequence[0] != 'shibboleth') {
    if (!empty($_GET["username"])) {
        $frm->username = clean_param($_GET["username"], PARAM_RAW);
    } else {
        $frm->username = get_moodle_cookie();
    }
    $frm->password = "";
}

// Manejar mensajes de error e información
if (!empty($SESSION->loginerrormsg) || !empty($SESSION->logininfomsg)) {
    $errormsg = $SESSION->loginerrormsg ?? '';
    $infomsg = $SESSION->logininfomsg ?? '';
    unset($SESSION->loginerrormsg);
    unset($SESSION->logininfomsg);
} else if ($testsession) {
    unset($SESSION->loginerrormsg);
    unset($SESSION->logininfomsg);
} else if ($errormsg or !empty($frm->password)) {
    if ($errormsg) {
        $SESSION->loginerrormsg = $errormsg;
    }
    $loginurl = new moodle_url('/login/index.php');
    $loginurl->param('loginredirect', $SESSION->loginredirect);
    redirect($loginurl->out(false));
}

// ============================================================================
// RENDERIZADO DE LA PÁGINA
// ============================================================================

// Configurar título y encabezado de la página
$PAGE->set_title($loginsite);
$PAGE->set_heading("$site->fullname");

// Mostrar el encabezado de la página
echo $OUTPUT->header();

// Verificar el estado de la sesión del usuario
if (isloggedin() and !isguestuser()) {
    // Si el usuario ya está conectado, mostrar mensaje y opciones
    echo $OUTPUT->box_start();
    $logout = new single_button(new moodle_url('/login/logout.php', array('sesskey' => sesskey(), 'loginpage' => 1)), get_string('logout'), 'post');
    $continue = new single_button(new moodle_url('/'), get_string('cancel'), 'get');
    echo $OUTPUT->confirm(get_string('alreadyloggedin', 'error', fullname($USER)), $logout, $continue);
    echo $OUTPUT->box_end();
} else {
    // Mostrar el formulario de inicio de sesión
    $loginform = new \core_auth\output\login($authsequence, $frm->username);
    $loginform->set_error($errormsg);
    $loginform->set_info($infomsg);
    echo $OUTPUT->render($loginform);
}

// Mostrar el pie de página
echo $OUTPUT->footer();