Proyectos de Subversion Moodle

Rev

Rev 1414 | Mostrar el archivo completo | | | Autoría | Ultima modificación | Ver Log |

Rev 1414 Rev 1415
Línea 1... Línea 1...
1
<?php
1
<?php
2
// Configuración de headers para respuesta JSON y CORS
-
 
3
// header('Content-Type: application/json'); // MOVED: Will be set only when outputting JSON
-
 
4
// header('Access-Control-Allow-Origin: *'); // MOVED: Will be set only when outputting JSON
-
 
Línea -... Línea 2...
-
 
2
 
-
 
3
// NOTA IMPORTANTE:
-
 
4
// Las credenciales y claves RSA NO DEBEN ESTAR HARDCODEADAS EN UN ENTORNO DE PRODUCCIÓN.
-
 
5
// Considere usar variables de entorno, un archivo de configuración externo, o las propias configuraciones de Moodle
5
 
6
// para almacenar LLWS_USERNAME, LLWS_PASSWORD, LLWS_RSA_N, LLWS_RSA_D, LLWS_RSA_E.
-
 
7
// Por simplicidad en este ejemplo, se mantienen aquí para la demostración de los cambios.
6
// Definición de constantes para autenticación y encriptación
8
 
7
define('LLWS_USERNAME', 'leaderdslinked-ws');
9
define('LLWS_USERNAME', 'leaderdslinked-ws');
8
define('LLWS_PASSWORD', 'KFB3lFsLp&*CpB0uc2VNc!zVn@w!EZqZ*jr$J0AlE@sYREUcyP');
10
define('LLWS_PASSWORD', 'KFB3lFsLp&*CpB0uc2VNc!zVn@w!EZqZ*jr$J0AlE@sYREUcyP');
9
define('LLWS_RSA_N', 34589927);  // Clave pública RSA N
11
define('LLWS_RSA_N', 34589927);     // Clave pública RSA N
10
define('LLWS_RSA_D', 4042211);   // Clave privada RSA D
12
define('LLWS_RSA_D', 4042211);      // Clave privada RSA D
11
define('LLWS_RSA_E', 7331);      // Clave pública RSA E
13
define('LLWS_RSA_E', 7331);         // Clave pública RSA E
Línea 12... Línea 14...
12
define('LLWS_CATEGORY_ID', 6);   // ID de categoría para cursos
14
define('LLWS_CATEGORY_ID', 6);      // ID de categoría para cursos
13
 
15
 
14
// Inclusión de archivos necesarios de Moodle
16
// Inclusión de archivos necesarios de Moodle
Línea 15... Línea 17...
15
require_once(__DIR__ . '/../config.php');
17
require_once(__DIR__ . '/../config.php');
16
global $DB, $CFG;
18
global $DB, $CFG;
17
 
19
 
18
// Inclusión de librerías de Moodle
20
// Inclusión de librerías de Moodle
19
require_once($CFG->libdir . '/moodlelib.php');
21
require_once($CFG->libdir . '/moodlelib.php');
20
require_once($CFG->libdir . '/externallib.php');
22
require_once($CFG->libdir . '/externallib.php');
21
require_once($CFG->libdir . '/authlib.php');
-
 
22
require_once($CFG->libdir . '/gdlib.php');
-
 
23
require_once($CFG->dirroot . '/user/lib.php');
-
 
24
require_once(__DIR__ . '/rsa.php');
-
 
25
require_once(__DIR__ . '/lib.php');
-
 
26
 
-
 
27
// Obtención y sanitización de parámetros de la petición
-
 
28
$username   = trim(isset($_REQUEST['username']) ? filter_var($_REQUEST['username'], FILTER_SANITIZE_STRING) : '');
-
 
29
$password   = trim(isset($_REQUEST['password']) ? filter_var($_REQUEST['password'], FILTER_SANITIZE_STRING) : '');
-
 
30
$timestamp  = trim(isset($_REQUEST['timestamp']) ? filter_var($_REQUEST['timestamp'], FILTER_SANITIZE_STRING) : '');
-
 
31
$rand       = intval(isset($_REQUEST['rand']) ? filter_var($_REQUEST['rand'], FILTER_SANITIZE_NUMBER_INT) : 0, 10);
-
 
32
$data       = trim(isset($_REQUEST['data']) ? filter_var($_REQUEST['data'], FILTER_SANITIZE_STRING) : '');
-
 
33
 
-
 
34
/*
-
 
35
$data = '';
-
 
36
$input = fopen("php://input", "r");
-
 
37
 
-
 
38
 
-
 
39
while ($line = fread($input, 1024))
-
 
40
{
-
 
41
    $data .= $line; 
-
 
42
}
-
 
43
fclose($input);*/
-
 
44
 
-
 
45
/*
-
 
46
echo 'username = '. $username . PHP_EOL;
-
 
47
echo 'password = '. $password . PHP_EOL;
-
 
48
echo 'rand = '. $rand . PHP_EOL;
-
 
49
echo 'timestamp = '. $timestamp . PHP_EOL;
-
 
50
echo 'data = '. $data . PHP_EOL;
-
 
51
 
-
 
52
 
-
 
53
/*
-
 
54
list($usec, $sec) = explode(' ', microtime());
-
 
55
$seed = intval($sec + ((float) $usec * 100000));
-
 
56
 
-
 
57
$username   = LLWS_USERNAME;
-
 
58
$timestamp  = date('Y-m-d\TH:i:s');
-
 
59
mt_srand($seed, MT_RAND_MT19937);
-
 
60
$rand =  mt_rand();
23
require_once($CFG->libdir . '/authlib.php');
61
 
-
 
62
$password   = LLWS_PASSWORD;
24
require_once($CFG->libdir . '/gdlib.php'); // Para procesamiento de imágenes
63
$password  = password_hash($username.'-'. $password. '-' . $rand. '-' . $timestamp, PASSWORD_DEFAULT);
-
 
Línea 64... Línea 25...
64
 
25
require_once($CFG->dirroot . '/user/lib.php'); // Para funciones de usuario
-
 
26
require_once(__DIR__ . '/rsa.php'); // Asegúrate de que esta ruta sea correcta para tu clase RSA
-
 
27
require_once(__DIR__ . '/lib.php'); // Asegúrate de que esta ruta sea correcta para tus funciones ll_get_user_by_email, ll_get_username_available, ll_create_user
65
$data       = $rsa->encrypt(json_encode(['email' => 'usuario4@test.com', 'first_name' => 'usuario4', 'last_name' => 'test']));
28
 
66
*/
29
// Helper function to output JSON error and exit
67
 
30
// NOTA: Estos headers solo se configuran cuando hay un error para una respuesta JSON.
68
// Helper function to output JSON error and exit
31
// Para un login exitoso, no se necesitan estos headers.
69
function output_json_error($errorCode)
32
function output_json_error($errorCode)
70
{
33
{
71
    // It's good practice to ensure session is closed before output,
34
    // Es buena práctica asegurar que la sesión se cierre antes de la salida,
72
    // though Moodle's exit handling might cover this.
35
    // aunque el manejo de salida de Moodle podría cubrir esto.
-
 
36
    if (function_exists('session_write_close')) {
-
 
37
        session_write_close();
-
 
38
    }
73
    if (function_exists('session_write_close')) {
39
    header('Content-Type: application/json');
74
        session_write_close();
40
    // Para entornos de producción, considera restringir Access-Control-Allow-Origin
75
    }
41
    // a dominios específicos si esta vista será accedida vía AJAX desde otros orígenes.
76
    header('Content-Type: application/json');
42
    // Si es una redirección directa, CORS es irrelevante para una respuesta JSON.
Línea -... Línea 43...
-
 
43
    header('Access-Control-Allow-Origin: *');
-
 
44
    echo json_encode(['success' => false, 'data' => $errorCode]);
-
 
45
    exit;
-
 
46
}
-
 
47
 
-
 
48
// Obtención y sanitización de parámetros de la petición
-
 
49
$username   = trim(isset($_REQUEST['username']) ? filter_var($_REQUEST['username'], FILTER_SANITIZE_STRING) : '');
-
 
50
$password   = trim(isset($_REQUEST['password']) ? filter_var($_REQUEST['password'], FILTER_SANITIZE_STRING) : '');
-
 
51
$timestamp  = trim(isset($_REQUEST['timestamp']) ? filter_var($_REQUEST['timestamp'], FILTER_SANITIZE_STRING) : '');
-
 
52
// Usamos FILTER_VALIDATE_INT para asegurar que sea un entero y no un string numérico grande.
77
    header('Access-Control-Allow-Origin: *'); // Assuming CORS is needed for errors too
53
$rand       = intval(isset($_REQUEST['rand']) ? filter_var($_REQUEST['rand'], FILTER_VALIDATE_INT) : 0, 10);
78
    echo json_encode(['success' => false, 'data' => $errorCode]);
54
$data       = trim(isset($_REQUEST['data']) ? filter_var($_REQUEST['data'], FILTER_SANITIZE_STRING) : '');
79
    exit;
55
 
80
}
56
// --- Validaciones de Seguridad ---
Línea 81... Línea 57...
81
 
57
 
82
// Validación de parámetros de seguridad
58
// Validación de parámetros de seguridad mínimos
83
if (empty($username) || empty($password) || empty($timestamp) || empty($rand) || !is_integer($rand)) {
59
if (empty($username) || empty($password) || empty($timestamp) || empty($rand) || !is_integer($rand)) {
84
    output_json_error('ERROR_SECURITY1');
60
    output_json_error('ERROR_SECURITY1');
Línea 85... Línea 61...
85
}
61
}
86
 
62
 
87
// Validación del nombre de usuario
63
// Validación del nombre de usuario de la API
88
if ($username != LLWS_USERNAME) {
64
if ($username !== LLWS_USERNAME) { // Usamos !== para comparación estricta
89
    output_json_error('ERROR_SECURITY2');
65
    output_json_error('ERROR_SECURITY2');
Línea 90... Línea 66...
90
}
66
}
91
 
67
 
92
// Validación del formato del timestamp
68
// Validación del formato del timestamp
93
$dt = \DateTime::createFromFormat('Y-m-d\TH:i:s', $timestamp);
69
$dt = \DateTime::createFromFormat('Y-m-d\TH:i:s', $timestamp);
Línea 94... Línea 70...
94
if (!$dt) {
70
if (!$dt) {
95
    output_json_error('ERROR_SECURITY3');
-
 
96
}
71
    output_json_error('ERROR_SECURITY3');
97
 
-
 
98
// Validación del rango de tiempo del timestamp (±5 minutos)
72
}
Línea 99... Línea 73...
99
$t0 = $dt->getTimestamp();
73
 
100
$t1 = strtotime('-5 minutes');
74
// Validación del rango de tiempo del timestamp (±5 minutos) para prevenir Replay Attacks
-
 
75
$t0 = $dt->getTimestamp();
101
$t2 = strtotime('+5 minutes');
76
$t1 = strtotime('-5 minutes');
102
 
77
$t2 = strtotime('+5 minutes');
Línea -... Línea 78...
-
 
78
 
-
 
79
if ($t0 < $t1 || $t0 > $t2) {
103
if ($t0 < $t1 || $t0 > $t2) {
80
    output_json_error('ERROR_SECURITY4'); // Habilitado para prevenir Replay Attacks
104
    //echo json_encode(['success' => false, 'data' => 'ERROR_SECURITY4']) ;
81
}
105
    //output_json_error('ERROR_SECURITY4'); // Kept commented as in original for this line
82
 
106
    //exit;
83
// Validación de la contraseña (hash de la contraseña de la API + rand + timestamp)
Línea 107... Línea 84...
107
}
84
$expectedPasswordString = $username . '-' . LLWS_PASSWORD . '-' . $rand . '-' . $timestamp;
108
 
85
if (!password_verify($expectedPasswordString, $password)) {
109
// Validación de la contraseña
86
    output_json_error('ERROR_SECURITY5');
110
if (!password_verify($username . '-' . LLWS_PASSWORD . '-' . $rand . '-' . $timestamp, $password)) {
87
}
111
    output_json_error('ERROR_SECURITY5');
88
 
Línea 112... Línea 89...
112
}
89
// --- Validación y Desencriptación de Datos de Usuario ---
113
 
90
 
114
// Validación de datos
91
// Validación de datos
115
if (empty($data)) {
92
if (empty($data)) {
116
    output_json_error('ERROR_PARAMETERS1');
93
    output_json_error('ERROR_PARAMETERS1');
117
}
94
}
-
 
95
 
-
 
96
// Decodificación de datos en base64
118
 
97
$data = base64_decode($data);
119
// Decodificación de datos en base64
98
if ($data === false || empty($data)) { // base64_decode puede retornar false
Línea 120... Línea 99...
120
$data = base64_decode($data);
99
    output_json_error('ERROR_PARAMETERS2');
121
if (empty($data)) {
100
}
122
    output_json_error('ERROR_PARAMETERS2');
101
 
123
}
102
// Desencriptación de datos usando RSA
124
 
103
try {
Línea 125... Línea 104...
125
// Desencriptación de datos usando RSA
104
    $rsa = new rsa(); // Asegúrate de que la clase 'rsa' esté correctamente cargada.
126
try {
105
    $rsa->setKeys(LLWS_RSA_N, LLWS_RSA_D, LLWS_RSA_E);
127
    $rsa = new rsa();
106
    $data = $rsa->decrypt($data);
128
    $rsa->setKeys(LLWS_RSA_N, LLWS_RSA_D, LLWS_RSA_E);
107
} catch (Throwable $e) {
Línea -... Línea 108...
-
 
108
    // Registra el error para depuración, pero no lo expongas al cliente.
129
    $data = $rsa->decrypt($data);
109
    error_log("RSA Decryption Error: " . $e->getMessage());
130
} catch (Throwable $e) {
110
    output_json_error('ERROR_PARAMETERS3');
131
    output_json_error('ERROR_PARAMETERS3');
111
}
Línea 132... Línea 112...
132
}
112
 
-
 
113
// Conversión de datos a array
133
 
114
$data = (array) json_decode($data, true); // Añadido 'true' para asegurar un array asociativo
-
 
115
if (empty($data)) {
-
 
116
    output_json_error('ERROR_PARAMETERS4');
134
// Conversión de datos a array
117
}
135
$data = (array) json_decode($data);
118
 
136
if (empty($data)) {
119
// Extracción y validación de datos del usuario
-
 
120
$email      = trim(isset($data['email']) ? filter_var($data['email'], FILTER_SANITIZE_EMAIL) : '');
137
    output_json_error('ERROR_PARAMETERS4');
121
$first_name = trim(isset($data['first_name']) ? filter_var($data['first_name'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) : '');
138
}
122
$last_name  = trim(isset($data['last_name']) ? filter_var($data['last_name'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) : '');
139
 
123
 
Línea 140... Línea 124...
140
// Extracción y validación de datos del usuario
124
// Asegúrate de que los datos esenciales del usuario estén presentes y sean válidos.
141
$email      = trim(isset($data['email']) ? filter_var($data['email'], FILTER_SANITIZE_EMAIL) : '');
125
if (!filter_var($email, FILTER_VALIDATE_EMAIL) || empty($first_name) || empty($last_name)) {
142
$first_name = trim(isset($data['first_name']) ? filter_var($data['first_name'], FILTER_SANITIZE_STRING) : '');
126
    output_json_error('ERROR_PARAMETERS5');
143
$last_name  = trim(isset($data['last_name']) ? filter_var($data['last_name'], FILTER_SANITIZE_STRING) : '');
127
}
Línea 144... Línea 128...
144
 
128
 
-
 
129
// --- Búsqueda o Creación de Usuario en Moodle ---
145
if (!filter_var($email, FILTER_VALIDATE_EMAIL) || empty($first_name) || empty($last_name)) {
130
 
146
    output_json_error('ERROR_PARAMETERS5');
131
$user = ll_get_user_by_email($email); // Asume que esta función busca al usuario por email
-
 
132
$new_user = false;
-
 
133
 
147
}
134
if ($user) {
-
 
135
    // Usuario encontrado
148
 
136
} else {
149
// Búsqueda o creación de usuario
137
    // Usuario no encontrado, proceder a la creación
150
$user = ll_get_user_by_email($email);
138
    $new_user = true;
151
if ($user) {
139
    $username_moodle = ll_get_username_available($first_name, $last_name); // Genera un nombre de usuario disponible para Moodle
-
 
140
    $user = ll_create_user($username_moodle, $email, $first_name, $last_name); // Crea el usuario
152
    $new_user = false;
141
 
153
} else {
142
    // Procesamiento de imagen de perfil si se proporciona y el usuario fue creado
154
    $new_user = true;
143
    if ($user) {
-
 
144
        $filename   = trim(isset($data['image_filename']) ? filter_var($data['image_filename'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) : '');
-
 
145
        $content    = trim(isset($data['image_content']) ? filter_var($data['image_content'], FILTER_SANITIZE_STRING) : '');
155
    $username = ll_get_username_available($first_name, $last_name);
146
 
-
 
147
        // Validación básica de nombre de archivo para evitar path traversal
156
    $user = ll_create_user($username, $email, $first_name, $last_name);
148
        if ($filename && strpos($filename, '..') === false && strpos($filename, DIRECTORY_SEPARATOR) === false) {
157
 
149
            $tempfile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $filename; // Usar directorio temporal del sistema
158
    // Procesamiento de imagen de perfil si se proporciona
150
            try {
159
    if ($user) {
151
                $decoded_content = base64_decode($content);
160
        $filename   = trim(isset($data['image_filename']) ? filter_var($data['image_filename'], FILTER_SANITIZE_EMAIL) : '');
152
                if ($decoded_content !== false && !empty($decoded_content)) {
161
        $content    = trim(isset($data['image_content']) ? filter_var($data['image_content'], FILTER_SANITIZE_STRING) : '');
153
                    file_put_contents($tempfile, $decoded_content);
162
 
154
 
Línea 163... Línea 155...
163
        if ($filename && $content) {
155
                    if (file_exists($tempfile)) {
164
            $tempfile = __DIR__ . DIRECTORY_SEPARATOR . $filename;
156
                        $usericonid = process_new_icon(context_user::instance($user->id, MUST_EXIST), 'user', 'icon', 0, $tempfile);
165
            try {
157
                        if ($usericonid) {
166
                file_put_contents($filename, base64_decode($content));
158
                            $DB->set_field('user', 'picture', $usericonid, array('id' => $user->id));
Línea 167... Línea 159...
167
                if (file_exists($tempfile)) {
159
                        }
-
 
160
                    }
168
                    $usericonid = process_new_icon(context_user::instance($user->id, MUST_EXIST), 'user', 'icon', 0, $tempfile);
161
                }
169
                    if ($usericonid) {
162
            } catch (\Throwable $e) {
170
                        $DB->set_field('user', 'picture', $usericonid, array('id' => $user->id));
163
                // IMPORTANT: Registra el error de la imagen, pero no abortes el login del usuario si la imagen falla.
Línea 171... Línea 164...
171
                    }
164
                error_log("Error processing user image for user ID {$user->id}: " . $e->getMessage());
172
                }
165
            } finally {
-
 
166
                // Asegúrate de eliminar el archivo temporal
173
            } catch (\Throwable $e) {
167
                if (file_exists($tempfile)) {
174
            } finally {
168
                    unlink($tempfile);
175
                if (file_exists($tempfile)) {
169
                }
176
                    unlink($tempfile);
170
            }
177
                }
171
        }
178
            }
-
 
179
        }
-
 
180
    }
172
    }
181
}
-
 
182
 
173
}
183
// Verificación de creación de usuario
174
 
184
if (!$user) {
175
// Verificación de creación/recuperación de usuario
185
    output_json_error('ERROR_MOODLE1');
-
 
186
}
-
 
187
 
176
if (!$user) {
-
 
177
    output_json_error('ERROR_MOODLE1');
188
// Inscripción en cursos para usuarios nuevos
178
}
189
if ($new_user) {
179
 
190
    $role = $DB->get_record('role', array('archetype' => 'student'));
180
// --- Inscripción en Cursos para Usuarios Nuevos ---
-
 
181
 
-
 
182
if ($new_user) {
191
    $enrolmethod = 'manual';
183
    $role = $DB->get_record('role', array('archetype' => 'student'));
192
 
-
 
193
    $courses = get_courses();
184
    $enrolmethod = 'manual';
-
 
185
 
-
 
186
    $courses = get_courses(); // Obtiene todos los cursos
194
    foreach ($courses as $course) {
187
    foreach ($courses as $course) {
195
        if ($course->categoy_id == LLWS_CATEGORY_ID) {
188
        // CORRECCIÓN: Error tipográfico 'categoy_id' a 'category_id'
196
            $context = context_course::instance($course->id);
189
        if ($course->category_id == LLWS_CATEGORY_ID) {
-
 
190
            $context = context_course::instance($course->id);
197
            if (!is_enrolled($context, $user)) {
191
            if (!is_enrolled($context, $user)) { // Verifica si el usuario ya está inscrito
198
                $enrol = enrol_get_plugin($enrolmethod);
192
                $enrol = enrol_get_plugin($enrolmethod);
199
                if ($enrol === null) {
193
                if ($enrol === null) {
200
                    return false;
194
                    // Si el método de matriculación manual no existe, loguea un error y continúa.
201
                }
195
                    // No debería abortar el login del usuario por esto.
Línea 202... Línea 196...
202
                $instances = enrol_get_instances($course->id, true);
196
                    error_log("Enrolment method '{$enrolmethod}' not found for course ID {$course->id}.");
-
 
197
                    continue; // Pasa al siguiente curso
-
 
198
                }
203
                $manualinstance = null;
199
 
-
 
200
                // Obtener o crear una instancia de matriculación manual para el curso
204
                foreach ($instances as $instance) {
201
                $instance = $enrol->add_default_instance($course); // Esto intenta encontrar o crear una instancia por defecto
205
                    if ($instance->name == $enrolmethod) {
202
                if ($instance === null) {
206
                        $manualinstance = $instance;
203
                    // Si add_default_instance falla (ej. no hay permisos), intenta crear una nueva instancia.
207
                        break;
204
                    // Esto es menos común si add_default_instance no funciona, pero es una alternativa.
208
                    }
205
                    $instanceid = $enrol->add_instance($course);
Línea 237... Línea 234...
237
        if (intval($days2expire) < 0) {
234
        if (intval($days2expire) < 0) {
238
            output_json_error('PASSWORD_EXPIRED');
235
            output_json_error('PASSWORD_EXPIRED');
239
        }
236
        }
240
    }
237
    }
Línea -... Línea 238...
-
 
238
 
-
 
239
    // CORRECCIÓN: Para un flujo de login web, Moodle maneja la sesión existente.
-
 
240
    // complete_user_login() es suficiente para establecer la sesión del usuario.
241
 
241
    // Eliminar el cierre agresivo de todas las sesiones a menos que sea un requisito muy específico.
-
 
242
    // Si la sesión actual ya está autenticada, complete_user_login() la actualizará.
242
    // Si hay una sesión existente, cerrarla primero
243
    // Si necesitas forzar un re-login de la sesión actual, puedes hacerlo de forma más granular:
243
    if (isloggedin()) {
244
    /*
244
        \core\session\manager::kill_all_sessions();
245
    if (isloggedin() && get_userid() !== $user->id) { // Si un usuario diferente ya está logueado en esta sesión
245
        \core\session\manager::terminate_current();
246
        \core\session\manager::terminate_current();
246
        session_destroy();
-
 
247
        // Delete MoodleSession cookie
247
        session_destroy();
248
        setcookie('MoodleSession', '', time() - 3600, '/');
248
        setcookie('MoodleSession', '', time() - 3600, '/'); // Eliminar la cookie de la sesión anterior
-
 
249
    }
Línea 249... Línea 250...
249
    }
250
    */
-
 
251
 
250
 
252
    // Completar el proceso de inicio de sesión de Moodle.
Línea 251... Línea 253...
251
    // Completar el proceso de inicio de sesión
253
    // Esto establecerá la sesión del usuario $user.
252
    complete_user_login($user);
254
    complete_user_login($user);
Línea 253... Línea 255...
253
 
255
 
254
    // Aplicar límite de inicio de sesión concurrente
256
    // Aplicar límite de inicio de sesión concurrente (descomentado si es necesario)
255
    //\core\session\manager::apply_concurrent_login_limit($user->id, session_id());
257
    // \core\session\manager::apply_concurrent_login_limit($user->id, session_id());
256
 
258
 
257
    // Configurar cookie de nombre de usuario
259
    // Configurar cookie de nombre de usuario (Moodle lo maneja internamente)
258
    if (!empty($CFG->nolastloggedin)) {
260
    if (!empty($CFG->nolastloggedin)) {
259
        // No almacenar último usuario conectado en cookie
261
        // No almacenar último usuario conectado en cookie
260
    } else if (empty($CFG->rememberusername)) {
262
    } else if (empty($CFG->rememberusername)) {
261
        // Sin cookies permanentes, eliminar la anterior si existe
263
        // Sin cookies permanentes, eliminar la anterior si existe
Línea 262... Línea 264...
262
        set_moodle_cookie('');
264
        set_moodle_cookie('');
263
    } else {
265
    } else {
264
        set_moodle_cookie($user->username);
266
        set_moodle_cookie($user->username);
265
    }
-
 
266
 
267
    }
267
    // Limpiar mensajes de error antes de la última redirección
-
 
Línea 268... Línea 268...
268
    unset($SESSION->loginerrormsg);
268
 
269
    unset($SESSION->logininfomsg);
269
    // Limpiar mensajes de error y redirección de sesión antes de la redirección final
270
 
-
 
Línea 271... Línea 270...
271
    // Descartar loginredirect si estamos redirigiendo
270
    unset($SESSION->loginerrormsg);
272
    unset($SESSION->loginredirect);
271
    unset($SESSION->logininfomsg);
273
 
272
    unset($SESSION->loginredirect); // Descartar loginredirect si estamos redirigiendo
274
    // Configurar la URL de destino
-
 
275
    $urltogo = $CFG->wwwroot . '/my';
-
 
276
    $SESSION->wantsurl = $urltogo;
-
 
277
 
-
 
278
    // Verificar que la sesión se haya iniciado correctamente
-
 
279
    if (isloggedin() && !isguestuser()) {
-
 
280
        // Enviar respuesta exitosa con datos del usuario
-
 
281
        // Instead of echoing JSON here and then redirecting (which is problematic),
-
 
282
        // we will just redirect. The browser will follow the redirect, and the
-
 
283
        // new session (with its cookie) will be active for the target page.
-
 
284
        /*
-
 
285
        echo json_encode([
-
 
286
            'success' => true,
-
 
287
            'data' => [
-
 
288
                'userid' => $user->id,
-
 
289
                'username' => $user->username,
-
 
290
                'fullname' => fullname($user),
-
 
291
                'email' => $user->email,
-
 
292
                'redirect' => $urltogo
273
 
293
            ]
274
    // Configurar la URL de destino
-
 
275
    $urltogo = $CFG->wwwroot . '/my'; // Página "Mi Moodle"
294
        ]);
276
 
295
        */
277
    // Verificar que la sesión se haya iniciado correctamente antes de redirigir
296
 
278
    if (isloggedin() && !isguestuser()) {
-
 
279
        // Redirección HTTP para una vista web exitosa
-
 
280
        redirect($urltogo);
297
        // Redirigir al usuario
281
    } else {
298
        // redirect() will handle session_write_close(), send Location header, and exit.
282
        // Falló la autenticación final en Moodle, a pesar de las validaciones previas.
-
 
283
        output_json_error('LOGIN_FAILED_MOODLE');
299
        redirect($urltogo);
284
    }
300
    } else {
285
} else {
-
 
286
    // Esto debería ser capturado por output_json_error('ERROR_MOODLE1') anteriormente,