Proyectos de Subversion Moodle

Rev

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

Rev 1441 Rev 1469
Línea 31... Línea 31...
31
 *
31
 *
32
 * @package    core
32
 * @package    core
33
 * @copyright  2013 Petr Skoda {@link http://skodak.org}
33
 * @copyright  2013 Petr Skoda {@link http://skodak.org}
34
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35
 */
35
 */
36
class manager {
36
class manager
-
 
37
{
37
    /** @var int A hard cutoff of maximum stored history */
38
    /** @var int A hard cutoff of maximum stored history */
38
    const MAXIMUM_STORED_SESSION_HISTORY = 50;
39
    const MAXIMUM_STORED_SESSION_HISTORY = 50;
Línea 39... Línea 40...
39
 
40
 
40
    /** @var int The recent session locks array is reset if there is a time gap more than this value in seconds */
41
    /** @var int The recent session locks array is reset if there is a time gap more than this value in seconds */
Línea 76... Línea 77...
76
     *
77
     *
77
     * @param bool $readonlysession Used by debugging logic to determine if whatever
78
     * @param bool $readonlysession Used by debugging logic to determine if whatever
78
     *                              triggered the restart (e.g., a webservice) declared
79
     *                              triggered the restart (e.g., a webservice) declared
79
     *                              itself as read only.
80
     *                              itself as read only.
80
     */
81
     */
81
    public static function restart_with_write_lock(bool $readonlysession) {
82
    public static function restart_with_write_lock(bool $readonlysession)
-
 
83
    {
82
        global $CFG;
84
        global $CFG;
Línea 83... Línea 85...
83
 
85
 
84
        if (!empty($CFG->enable_read_only_sessions) || !empty($CFG->enable_read_only_sessions_debug)) {
86
        if (!empty($CFG->enable_read_only_sessions) || !empty($CFG->enable_read_only_sessions_debug)) {
85
            self::$requireslockdebug = !$readonlysession;
87
            self::$requireslockdebug = !$readonlysession;
Línea 95... Línea 97...
95
    /**
97
    /**
96
     * Start user session.
98
     * Start user session.
97
     *
99
     *
98
     * Note: This is intended to be called only from lib/setup.php!
100
     * Note: This is intended to be called only from lib/setup.php!
99
     */
101
     */
100
    public static function start() {
102
    public static function start()
-
 
103
    {
101
        global $CFG, $DB, $PERF;
104
        global $CFG, $DB, $PERF;
Línea 102... Línea 105...
102
 
105
 
103
        if (isset(self::$sessionactive)) {
106
        if (isset(self::$sessionactive)) {
104
            debugging('Session was already started!', DEBUG_DEVELOPER);
107
            debugging('Session was already started!', DEBUG_DEVELOPER);
Línea 140... Línea 143...
140
     * Handles starting a session.
143
     * Handles starting a session.
141
     *
144
     *
142
     * @param bool $requireslock If this is false then no write lock will be acquired,
145
     * @param bool $requireslock If this is false then no write lock will be acquired,
143
     *                           and the session will be read-only.
146
     *                           and the session will be read-only.
144
     */
147
     */
145
    private static function start_session(bool $requireslock) {
148
    private static function start_session(bool $requireslock)
-
 
149
    {
146
        global $PERF, $CFG;
150
        global $PERF, $CFG;
Línea 147... Línea 151...
147
 
151
 
148
        try {
152
        try {
149
            self::$handler->init();
153
            self::$handler->init();
Línea 181... Línea 185...
181
            // this is tricky because PHP does not allow references to references
185
            // this is tricky because PHP does not allow references to references
182
            // and global keyword uses internally once reference to the $GLOBALS array.
186
            // and global keyword uses internally once reference to the $GLOBALS array.
183
            // The solution is to use the $GLOBALS['USER'] and $GLOBALS['$SESSION']
187
            // The solution is to use the $GLOBALS['USER'] and $GLOBALS['$SESSION']
184
            // as the main storage of data and put references to $_SESSION.
188
            // as the main storage of data and put references to $_SESSION.
185
            $GLOBALS['USER'] = $_SESSION['USER'];
189
            $GLOBALS['USER'] = $_SESSION['USER'];
186
            $_SESSION['USER'] =& $GLOBALS['USER'];
190
            $_SESSION['USER'] = &$GLOBALS['USER'];
187
            $GLOBALS['SESSION'] = $_SESSION['SESSION'];
191
            $GLOBALS['SESSION'] = $_SESSION['SESSION'];
188
            $_SESSION['SESSION'] =& $GLOBALS['SESSION'];
192
            $_SESSION['SESSION'] = &$GLOBALS['SESSION'];
189
 
-
 
190
        } catch (\Exception $ex) {
193
        } catch (\Exception $ex) {
191
            self::init_empty_session();
194
            self::init_empty_session();
192
            self::$sessionactive = false;
195
            self::$sessionactive = false;
193
            throw $ex;
196
            throw $ex;
194
        }
197
        }
Línea 197... Línea 200...
197
    /**
200
    /**
198
     * Returns current page performance info.
201
     * Returns current page performance info.
199
     *
202
     *
200
     * @return array perf info
203
     * @return array perf info
201
     */
204
     */
202
    public static function get_performance_info() {
205
    public static function get_performance_info()
-
 
206
    {
203
        global $CFG, $PERF;
207
        global $CFG, $PERF;
Línea 204... Línea 208...
204
 
208
 
205
        if (!session_id()) {
209
        if (!session_id()) {
206
            return array();
210
            return array();
Línea 217... Línea 221...
217
 
221
 
218
        if (!empty($CFG->debugsessionlock)) {
222
        if (!empty($CFG->debugsessionlock)) {
219
            $sessionlock = self::get_session_lock_info();
223
            $sessionlock = self::get_session_lock_info();
220
            if (!empty($sessionlock['held'])) {
224
            if (!empty($sessionlock['held'])) {
221
                // The page displays the footer and the session has been closed.
225
                // The page displays the footer and the session has been closed.
222
                $sessionlocktext = "Session lock held: ".number_format($sessionlock['held'], 3)." secs";
226
                $sessionlocktext = "Session lock held: " . number_format($sessionlock['held'], 3) . " secs";
223
            } else {
227
            } else {
224
                // The session hasn't yet been closed and so we assume now with microtime.
228
                // The session hasn't yet been closed and so we assume now with microtime.
225
                $sessionlockheld = microtime(true) - $PERF->sessionlock['gained'];
229
                $sessionlockheld = microtime(true) - $PERF->sessionlock['gained'];
226
                $sessionlocktext = "Session lock open: ".number_format($sessionlockheld, 3)." secs";
230
                $sessionlocktext = "Session lock open: " . number_format($sessionlockheld, 3) . " secs";
227
            }
231
            }
228
            $info['txt'] .= $sessionlocktext;
232
            $info['txt'] .= $sessionlocktext;
229
            $info['html'] .= html_writer::div($sessionlocktext, "sessionlockstart");
233
            $info['html'] .= html_writer::div($sessionlocktext, "sessionlockstart");
230
            $sessionlockwaittext = "Session lock wait: ".number_format($sessionlock['wait'], 3)." secs";
234
            $sessionlockwaittext = "Session lock wait: " . number_format($sessionlock['wait'], 3) . " secs";
231
            $info['txt'] .= $sessionlockwaittext;
235
            $info['txt'] .= $sessionlockwaittext;
232
            $info['html'] .= html_writer::div($sessionlockwaittext, "sessionlockwait");
236
            $info['html'] .= html_writer::div($sessionlockwaittext, "sessionlockwait");
Línea 233... Línea 237...
233
        }
237
        }
Línea 238... Línea 242...
238
    /**
242
    /**
239
     * Get fully qualified name of session handler class.
243
     * Get fully qualified name of session handler class.
240
     *
244
     *
241
     * @return string The name of the handler class
245
     * @return string The name of the handler class
242
     */
246
     */
243
    public static function get_handler_class() {
247
    public static function get_handler_class()
-
 
248
    {
244
        global $CFG, $DB;
249
        global $CFG, $DB;
Línea 245... Línea 250...
245
 
250
 
246
        if (PHPUNIT_TEST) {
251
        if (PHPUNIT_TEST) {
247
            return \core\tests\session\mock_handler::class;
252
            return \core\tests\session\mock_handler::class;
Línea 255... Línea 260...
255
    }
260
    }
Línea 256... Línea 261...
256
 
261
 
257
    /**
262
    /**
258
     * Create handler instance.
263
     * Create handler instance.
259
     */
264
     */
-
 
265
    protected static function load_handler()
260
    protected static function load_handler() {
266
    {
261
        if (self::$handler) {
267
        if (self::$handler) {
262
            return;
268
            return;
Línea 263... Línea 269...
263
        }
269
        }
Línea 277... Línea 283...
277
     * special areas. Do NOT use for logout and session termination
283
     * special areas. Do NOT use for logout and session termination
278
     * in normal requests!
284
     * in normal requests!
279
     *
285
     *
280
     * @param mixed $newsid only used after initialising a user session, is this a new user session?
286
     * @param mixed $newsid only used after initialising a user session, is this a new user session?
281
     */
287
     */
282
    public static function init_empty_session(?bool $newsid = null) {
288
    public static function init_empty_session(?bool $newsid = null)
-
 
289
    {
283
        global $CFG;
290
        global $CFG;
Línea 284... Línea 291...
284
 
291
 
285
        if (isset($GLOBALS['SESSION']->notifications)) {
292
        if (isset($GLOBALS['SESSION']->notifications)) {
286
            // Backup notifications. These should be preserved across session changes until the user fetches and clears them.
293
            // Backup notifications. These should be preserved across session changes until the user fetches and clears them.
Línea 305... Línea 312...
305
            $GLOBALS['USER']->mnethostid = 1;
312
            $GLOBALS['USER']->mnethostid = 1;
306
        }
313
        }
Línea 307... Línea 314...
307
 
314
 
308
        // Link global $USER and $SESSION.
315
        // Link global $USER and $SESSION.
309
        $_SESSION = array();
316
        $_SESSION = array();
310
        $_SESSION['USER'] =& $GLOBALS['USER'];
317
        $_SESSION['USER'] = &$GLOBALS['USER'];
311
        $_SESSION['SESSION'] =& $GLOBALS['SESSION'];
318
        $_SESSION['SESSION'] = &$GLOBALS['SESSION'];
Línea 312... Línea 319...
312
    }
319
    }
313
 
320
 
314
    /**
321
    /**
315
     * Make sure all cookie and session related stuff is configured properly before session start.
322
     * Make sure all cookie and session related stuff is configured properly before session start.
-
 
323
     */
316
     */
324
    protected static function prepare_cookies()
Línea 317... Línea 325...
317
    protected static function prepare_cookies() {
325
    {
Línea 318... Línea 326...
318
        global $CFG;
326
        global $CFG;
Línea 325... Línea 333...
325
 
333
 
326
        // Set sessioncookie variable if it isn't already.
334
        // Set sessioncookie variable if it isn't already.
327
        if (!isset($CFG->sessioncookie)) {
335
        if (!isset($CFG->sessioncookie)) {
328
            $CFG->sessioncookie = '';
336
            $CFG->sessioncookie = '';
329
        }
337
        }
Línea 330... Línea 338...
330
        $sessionname = 'MoodleSession'.$CFG->sessioncookie;
338
        $sessionname = 'MoodleSession' . $CFG->sessioncookie;
331
 
339
 
332
        // Make sure cookie domain makes sense for this wwwroot.
340
        // Make sure cookie domain makes sense for this wwwroot.
333
        if (!isset($CFG->sessioncookiedomain)) {
341
        if (!isset($CFG->sessioncookiedomain)) {
334
            $CFG->sessioncookiedomain = '';
342
            $CFG->sessioncookiedomain = '';
335
        } else if ($CFG->sessioncookiedomain !== '') {
343
        } else if ($CFG->sessioncookiedomain !== '') {
336
            $host = parse_url($CFG->wwwroot, PHP_URL_HOST);
344
            $host = parse_url($CFG->wwwroot, PHP_URL_HOST);
337
            if ($CFG->sessioncookiedomain !== $host) {
345
            if ($CFG->sessioncookiedomain !== $host) {
338
                if (substr($CFG->sessioncookiedomain, 0, 1) === '.') {
346
                if (substr($CFG->sessioncookiedomain, 0, 1) === '.') {
339
                    if (!preg_match('|^.*'.preg_quote($CFG->sessioncookiedomain, '|').'$|', $host)) {
347
                    if (!preg_match('|^.*' . preg_quote($CFG->sessioncookiedomain, '|') . '$|', $host)) {
340
                        // Invalid domain - it must be end part of host.
348
                        // Invalid domain - it must be end part of host.
341
                        $CFG->sessioncookiedomain = '';
349
                        $CFG->sessioncookiedomain = '';
342
                    }
350
                    }
343
                } else {
351
                } else {
344
                    if (!preg_match('|^.*\.'.preg_quote($CFG->sessioncookiedomain, '|').'$|', $host)) {
352
                    if (!preg_match('|^.*\.' . preg_quote($CFG->sessioncookiedomain, '|') . '$|', $host)) {
345
                        // Invalid domain - it must be end part of host.
353
                        // Invalid domain - it must be end part of host.
346
                        $CFG->sessioncookiedomain = '';
354
                        $CFG->sessioncookiedomain = '';
347
                    }
355
                    }
Línea 352... Línea 360...
352
        // Make sure the cookiepath is valid for this wwwroot or autodetect if not specified.
360
        // Make sure the cookiepath is valid for this wwwroot or autodetect if not specified.
353
        if (!isset($CFG->sessioncookiepath)) {
361
        if (!isset($CFG->sessioncookiepath)) {
354
            $CFG->sessioncookiepath = '';
362
            $CFG->sessioncookiepath = '';
355
        }
363
        }
356
        if ($CFG->sessioncookiepath !== '/') {
364
        if ($CFG->sessioncookiepath !== '/') {
357
            $path = parse_url($CFG->wwwroot, PHP_URL_PATH).'/';
365
            $path = parse_url($CFG->wwwroot, PHP_URL_PATH) . '/';
358
            if ($CFG->sessioncookiepath === '') {
366
            if ($CFG->sessioncookiepath === '') {
359
                $CFG->sessioncookiepath = $path;
367
                $CFG->sessioncookiepath = $path;
360
            } else {
368
            } else {
361
                if (strpos($path, $CFG->sessioncookiepath) !== 0 or substr($CFG->sessioncookiepath, -1) !== '/') {
369
                if (strpos($path, $CFG->sessioncookiepath) !== 0 or substr($CFG->sessioncookiepath, -1) !== '/') {
362
                    $CFG->sessioncookiepath = $path;
370
                    $CFG->sessioncookiepath = $path;
Línea 400... Línea 408...
400
        ini_set('session.serialize_handler', 'php');  // We can move to 'php_serialize' after we require PHP 5.5.4 form Moodle.
408
        ini_set('session.serialize_handler', 'php');  // We can move to 'php_serialize' after we require PHP 5.5.4 form Moodle.
Línea 401... Línea 409...
401
 
409
 
402
        // Moodle does normal session timeouts, this is for leftovers only.
410
        // Moodle does normal session timeouts, this is for leftovers only.
403
        ini_set('session.gc_probability', 1);
411
        ini_set('session.gc_probability', 1);
404
        ini_set('session.gc_divisor', 1000);
412
        ini_set('session.gc_divisor', 1000);
405
        ini_set('session.gc_maxlifetime', 60*60*24*4);
413
        ini_set('session.gc_maxlifetime', 60 * 60 * 24 * 4);
Línea 406... Línea 414...
406
    }
414
    }
407
 
415
 
408
    /**
416
    /**
409
     * Initialise $_SESSION, handles google access
417
     * Initialise $_SESSION, handles google access
410
     * and sets up not-logged-in user properly.
418
     * and sets up not-logged-in user properly.
411
     *
419
     *
412
     * WARNING: $USER and $SESSION are set up later, do not use them yet!
420
     * WARNING: $USER and $SESSION are set up later, do not use them yet!
413
     *
421
     *
414
     * @param bool $newsid is this a new session in first http request?
422
     * @param bool $newsid is this a new session in first http request?
-
 
423
     */
415
     */
424
    protected static function initialise_user_session($newsid)
Línea 416... Línea 425...
416
    protected static function initialise_user_session($newsid) {
425
    {
417
        global $CFG;
426
        global $CFG;
418
 
427
 
Línea 426... Línea 435...
426
        $record = self::get_session_by_sid($sid);
435
        $record = self::get_session_by_sid($sid);
427
        if (!isset($record->sid)) {
436
        if (!isset($record->sid)) {
428
            if (!$newsid) {
437
            if (!$newsid) {
429
                if (!empty($_SESSION['USER']->id)) {
438
                if (!empty($_SESSION['USER']->id)) {
430
                    // This should not happen, just log it, we MUST not produce any output here!
439
                    // This should not happen, just log it, we MUST not produce any output here!
431
                    error_log("Cannot find session record $sid for user ".$_SESSION['USER']->id.", creating new session.");
440
                    error_log("Cannot find session record $sid for user " . $_SESSION['USER']->id . ", creating new session.");
432
                }
441
                }
433
                // Prevent session fixation attacks.
442
                // Prevent session fixation attacks.
434
                session_regenerate_id(true);
443
                session_regenerate_id(true);
435
            }
444
            }
436
            $_SESSION = array();
445
            $_SESSION = array();
Línea 448... Línea 457...
448
            $maxlifetime = $CFG->sessiontimeout;
457
            $maxlifetime = $CFG->sessiontimeout;
449
            $timeout = false;
458
            $timeout = false;
450
            if (isguestuser($userid) or empty($userid)) {
459
            if (isguestuser($userid) or empty($userid)) {
451
                // Ignore guest and not-logged in timeouts, there is very little risk here.
460
                // Ignore guest and not-logged in timeouts, there is very little risk here.
452
                $timeout = false;
461
                $timeout = false;
453
 
-
 
454
            } else if ($record->timemodified < di::get(clock::class)->time() - $maxlifetime) {
462
            } else if ($record->timemodified < di::get(clock::class)->time() - $maxlifetime) {
455
                $timeout = true;
463
                $timeout = true;
456
                $authsequence = get_enabled_auth_plugins(); // Auths, in sequence.
464
                $authsequence = get_enabled_auth_plugins(); // Auths, in sequence.
457
                foreach ($authsequence as $authname) {
465
                foreach ($authsequence as $authname) {
458
                    $authplugin = get_auth_plugin($authname);
466
                    $authplugin = get_auth_plugin($authname);
Línea 492... Línea 500...
492
 
500
 
493
                if ($record->timemodified == $record->timecreated) {
501
                if ($record->timemodified == $record->timecreated) {
494
                    // Always do first update of existing record.
502
                    // Always do first update of existing record.
495
                    $update->timemodified = $record->timemodified = $time;
503
                    $update->timemodified = $record->timemodified = $time;
496
                    $updated = true;
-
 
497
 
504
                    $updated = true;
498
                } else if ($record->timemodified < $time - $updatefreq) {
505
                } else if ($record->timemodified < $time - $updatefreq) {
499
                    // Update the session modified flag only once every 20 seconds.
506
                    // Update the session modified flag only once every 20 seconds.
500
                    $update->timemodified = $record->timemodified = $time;
507
                    $update->timemodified = $record->timemodified = $time;
501
                    $updated = true;
508
                    $updated = true;
Línea 532... Línea 539...
532
                $user = guest_user();
539
                $user = guest_user();
533
            }
540
            }
534
            $referer = get_local_referer(false);
541
            $referer = get_local_referer(false);
535
            if (!empty($CFG->guestloginbutton) and !$user and !empty($referer)) {
542
            if (!empty($CFG->guestloginbutton) and !$user and !empty($referer)) {
536
                // Automatically log in users coming from search engine results.
543
                // Automatically log in users coming from search engine results.
537
                if (strpos($referer, 'google') !== false ) {
544
                if (strpos($referer, 'google') !== false) {
538
                    $user = guest_user();
545
                    $user = guest_user();
539
                } else if (strpos($referer, 'altavista') !== false ) {
546
                } else if (strpos($referer, 'altavista') !== false) {
540
                    $user = guest_user();
547
                    $user = guest_user();
541
                }
548
                }
542
            }
549
            }
543
        }
550
        }
Línea 560... Línea 567...
560
     * Returns a single session record for this session id.
567
     * Returns a single session record for this session id.
561
     *
568
     *
562
     * @param string $sid
569
     * @param string $sid
563
     * @return \stdClass
570
     * @return \stdClass
564
     */
571
     */
565
    public static function get_session_by_sid(string $sid): \stdClass {
572
    public static function get_session_by_sid(string $sid): \stdClass
-
 
573
    {
566
        return self::$handler->get_session_by_sid($sid);
574
        return self::$handler->get_session_by_sid($sid);
567
    }
575
    }
Línea 568... Línea 576...
568
 
576
 
569
    /**
577
    /**
570
     * Returns all the session records for this user id.
578
     * Returns all the session records for this user id.
571
     *
579
     *
572
     * @param int $userid
580
     * @param int $userid
573
     * @return array
581
     * @return array
574
     */
582
     */
-
 
583
    public static function get_sessions_by_userid(int $userid): array
575
    public static function get_sessions_by_userid(int $userid): array {
584
    {
576
        return self::$handler->get_sessions_by_userid($userid);
585
        return self::$handler->get_sessions_by_userid($userid);
Línea 577... Línea 586...
577
    }
586
    }
578
 
587
 
579
    /**
588
    /**
580
     * Insert new empty session record.
589
     * Insert new empty session record.
581
     *
590
     *
582
     * @param int $userid
591
     * @param int $userid
583
     * @return \stdClass the new record
592
     * @return \stdClass the new record
-
 
593
     */
584
     */
594
    public static function add_session(int $userid): \stdClass
585
    public static function add_session(int $userid): \stdClass {
595
    {
Línea 586... Línea 596...
586
        return self::$handler->add_session($userid);
596
        return self::$handler->add_session($userid);
587
    }
597
    }
588
 
598
 
589
    /**
599
    /**
590
     * Update a session record.
600
     * Update a session record.
591
     *
601
     *
592
     * @param \stdClass $record
602
     * @param \stdClass $record
-
 
603
     * @return bool
593
     * @return bool
604
     */
594
     */
605
    public static function update_session(\stdClass $record): bool
Línea 595... Línea 606...
595
    public static function update_session(\stdClass $record): bool {
606
    {
596
        return self::$handler->update_session($record);
607
        return self::$handler->update_session($record);
597
    }
608
    }
598
 
609
 
599
    /**
610
    /**
600
     * Do various session security checks.
611
     * Do various session security checks.
601
     *
612
     *
-
 
613
     * WARNING: $USER and $SESSION are set up later, do not use them yet!
602
     * WARNING: $USER and $SESSION are set up later, do not use them yet!
614
     * @throws \core\session\exception
Línea 603... Línea 615...
603
     * @throws \core\session\exception
615
     */
604
     */
616
    protected static function check_security()
605
    protected static function check_security() {
617
    {
Línea 623... Línea 635...
623
 
635
 
624
    /**
636
    /**
625
     * Login user, to be called from complete_user_login() only.
637
     * Login user, to be called from complete_user_login() only.
626
     * @param \stdClass $user
638
     * @param \stdClass $user
627
     */
639
     */
-
 
640
    public static function login_user(\stdClass $user)
628
    public static function login_user(\stdClass $user) {
641
    {
Línea 629... Línea 642...
629
        global $DB;
642
        global $DB;
630
 
643
 
Línea 647... Línea 660...
647
     * Returns a valid setting for the SameSite cookie attribute.
660
     * Returns a valid setting for the SameSite cookie attribute.
648
     *
661
     *
649
     * @return string The desired setting for the SameSite attribute on the cookie. Empty string indicates the SameSite attribute
662
     * @return string The desired setting for the SameSite attribute on the cookie. Empty string indicates the SameSite attribute
650
     * should not be set at all.
663
     * should not be set at all.
651
     */
664
     */
652
    private static function should_use_samesite_none(): bool {
665
    private static function should_use_samesite_none(): bool
-
 
666
    {
653
        // We only want None or no attribute at this point. When we have cookie handling compatible with Lax,
667
        // We only want None or no attribute at this point. When we have cookie handling compatible with Lax,
654
        // we can look at checking a setting.
668
        // we can look at checking a setting.
Línea 655... Línea 669...
655
 
669
 
656
        // Browser support for none is not consistent yet. There are known issues with Safari, and IE11.
670
        // Browser support for none is not consistent yet. There are known issues with Safari, and IE11.
Línea 666... Línea 680...
666
 
680
 
667
    /**
681
    /**
668
     * Terminate current user session.
682
     * Terminate current user session.
669
     * @return void
683
     * @return void
670
     */
684
     */
-
 
685
    public static function terminate_current()
671
    public static function terminate_current() {
686
    {
Línea 672... Línea 687...
672
        global $DB;
687
        global $DB;
673
 
688
 
674
        if (!self::$sessionactive) {
689
        if (!self::$sessionactive) {
675
            self::init_empty_session();
690
            self::init_empty_session();
676
            self::$sessionactive = false;
691
            self::$sessionactive = false;
Línea 677... Línea 692...
677
            return;
692
            return;
678
        }
693
        }
679
 
694
 
680
        try {
695
        try {
681
            $DB->delete_records('external_tokens', array('sid'=>session_id(), 'tokentype'=>EXTERNAL_TOKEN_EMBEDDED));
696
            $DB->delete_records('external_tokens', array('sid' => session_id(), 'tokentype' => EXTERNAL_TOKEN_EMBEDDED));
Línea 682... Línea 697...
682
        } catch (\Exception $ignored) {
697
        } catch (\Exception $ignored) {
683
            // Probably install/upgrade - ignore this problem.
698
            // Probably install/upgrade - ignore this problem.
684
        }
699
        }
685
 
700
 
686
        // Initialize variable to pass-by-reference to headers_sent(&$file, &$line).
701
        // Initialize variable to pass-by-reference to headers_sent(&$file, &$line).
687
        $file = null;
702
        $file = null;
Línea 688... Línea 703...
688
        $line = null;
703
        $line = null;
689
        if (headers_sent($file, $line)) {
704
        if (headers_sent($file, $line)) {
690
            error_log('Cannot terminate session properly - headers were already sent in file: '.$file.' on line '.$line);
705
            error_log('Cannot terminate session properly - headers were already sent in file: ' . $file . ' on line ' . $line);
Línea 701... Línea 716...
701
 
716
 
702
    /**
717
    /**
703
     * No more changes in session expected.
718
     * No more changes in session expected.
704
     * Unblocks the sessions, other scripts may start executing in parallel.
719
     * Unblocks the sessions, other scripts may start executing in parallel.
705
     */
720
     */
-
 
721
    public static function write_close()
706
    public static function write_close() {
722
    {
Línea 707... Línea 723...
707
        global $PERF, $ME, $CFG;
723
        global $PERF, $ME, $CFG;
708
 
724
 
709
        if (self::$sessionactive) {
725
        if (self::$sessionactive) {
Línea 769... Línea 785...
769
     * In write_close the session is saved to the variable $sessionatclose
785
     * In write_close the session is saved to the variable $sessionatclose
770
     * If there is a difference between $sessionatclose and the current session,
786
     * If there is a difference between $sessionatclose and the current session,
771
     * it means a script has erroneously closed the session too early.
787
     * it means a script has erroneously closed the session too early.
772
     * Script is usually called in shutdown_manager
788
     * Script is usually called in shutdown_manager
773
     */
789
     */
774
    public static function check_mutated_closed_session() {
790
    public static function check_mutated_closed_session()
-
 
791
    {
775
        global $ME;
792
        global $ME;
Línea 776... Línea 793...
776
 
793
 
777
        // Session is still open, mutations are allowed.
794
        // Session is still open, mutations are allowed.
778
        if (self::$sessionactive) {
795
        if (self::$sessionactive) {
Línea 817... Línea 834...
817
     * Timeout evaluation is simplified, the auth hooks are not executed.
834
     * Timeout evaluation is simplified, the auth hooks are not executed.
818
     *
835
     *
819
     * @param string $sid
836
     * @param string $sid
820
     * @return bool
837
     * @return bool
821
     */
838
     */
822
    public static function session_exists($sid) {
839
    public static function session_exists($sid)
-
 
840
    {
823
        global $DB, $CFG;
841
        global $DB, $CFG;
Línea 824... Línea 842...
824
 
842
 
825
        if (empty($CFG->version)) {
843
        if (empty($CFG->version)) {
826
            // Not installed yet, do not try to access database.
844
            // Not installed yet, do not try to access database.
Línea 846... Línea 864...
846
 
864
 
847
    /**
865
    /**
848
     * Return the number of seconds remaining in the current session.
866
     * Return the number of seconds remaining in the current session.
849
     * @param string $sid
867
     * @param string $sid
850
     */
868
     */
-
 
869
    public static function time_remaining($sid)
851
    public static function time_remaining($sid) {
870
    {
Línea 852... Línea 871...
852
        global $DB, $CFG;
871
        global $DB, $CFG;
853
 
872
 
854
        if (empty($CFG->version)) {
873
        if (empty($CFG->version)) {
Línea 874... Línea 893...
874
 
893
 
875
    /**
894
    /**
876
     * Fake last access for given session, this prevents session timeout.
895
     * Fake last access for given session, this prevents session timeout.
877
     * @param string $sid
896
     * @param string $sid
878
     */
897
     */
-
 
898
    public static function touch_session($sid)
879
    public static function touch_session($sid) {
899
    {
880
        // Timeouts depend on core sessions table only, no need to update anything in external stores.
900
        // Timeouts depend on core sessions table only, no need to update anything in external stores.
881
        self::$handler->update_session((object) [
901
        self::$handler->update_session((object) [
882
            'sid' => $sid,
902
            'sid' => $sid,
883
            'timemodified' => di::get(clock::class)->time(),
903
            'timemodified' => di::get(clock::class)->time(),
Línea 893... Línea 913...
893
     */
913
     */
894
    #[\core\attribute\deprecated(
914
    #[\core\attribute\deprecated(
895
        replacement: 'destroy_all',
915
        replacement: 'destroy_all',
896
        since: '4.5',
916
        since: '4.5',
897
    )]
917
    )]
898
    public static function kill_all_sessions(): void {
918
    public static function kill_all_sessions(): void
-
 
919
    {
899
        \core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
920
        \core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
900
        self::destroy_all();
921
        self::destroy_all();
901
    }
922
    }
Línea 902... Línea 923...
902
 
923
 
Línea 910... Línea 931...
910
     */
931
     */
911
    #[\core\attribute\deprecated(
932
    #[\core\attribute\deprecated(
912
        replacement: 'destroy',
933
        replacement: 'destroy',
913
        since: '4.5',
934
        since: '4.5',
914
    )]
935
    )]
915
    public static function kill_session($sid): void {
936
    public static function kill_session($sid): void
-
 
937
    {
916
        \core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
938
        \core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
917
        self::destroy($sid);
939
        self::destroy($sid);
918
    }
940
    }
Línea 919... Línea 941...
919
 
941
 
Línea 927... Línea 949...
927
     */
949
     */
928
    #[\core\attribute\deprecated(
950
    #[\core\attribute\deprecated(
929
        replacement: 'destroy_by_auth_plugin',
951
        replacement: 'destroy_by_auth_plugin',
930
        since: '4.5',
952
        since: '4.5',
931
    )]
953
    )]
932
    public static function kill_sessions_for_auth_plugin(string $pluginname): void {
954
    public static function kill_sessions_for_auth_plugin(string $pluginname): void
-
 
955
    {
933
        \core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
956
        \core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
934
        self::destroy_by_auth_plugin($pluginname);
957
        self::destroy_by_auth_plugin($pluginname);
935
    }
958
    }
Línea 936... Línea 959...
936
 
959
 
Línea 941... Línea 964...
941
     * @param string $keepsid keep this sid if present
964
     * @param string $keepsid keep this sid if present
942
     * @deprecated since Moodle 4.5 See MDL-66161
965
     * @deprecated since Moodle 4.5 See MDL-66161
943
     * @todo Remove in MDL-81848
966
     * @todo Remove in MDL-81848
944
     */
967
     */
945
    #[\core\attribute\deprecated(
968
    #[\core\attribute\deprecated(
946
            replacement: 'destroy_user_sessions',
969
        replacement: 'destroy_user_sessions',
947
            since: '4.5',
970
        since: '4.5',
948
    )]
971
    )]
949
    public static function kill_user_sessions($userid, $keepsid = null) {
972
    public static function kill_user_sessions($userid, $keepsid = null)
-
 
973
    {
950
        \core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
974
        \core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
951
        self::destroy_user_sessions($userid, $keepsid);
975
        self::destroy_user_sessions($userid, $keepsid);
952
    }
976
    }
Línea 953... Línea 977...
953
 
977
 
954
    /**
978
    /**
955
     * Destroy all sessions for a given plugin.
979
     * Destroy all sessions for a given plugin.
956
     * Typically used when a plugin is disabled or uninstalled, so all sessions (users) for that plugin are logged out.
980
     * Typically used when a plugin is disabled or uninstalled, so all sessions (users) for that plugin are logged out.
957
     *
981
     *
958
     * @param string $pluginname Auth plugin name.
982
     * @param string $pluginname Auth plugin name.
959
     */
983
     */
-
 
984
    public static function destroy_by_auth_plugin(string $pluginname): void
960
    public static function destroy_by_auth_plugin(string $pluginname): void {
985
    {
961
        self::$handler->destroy_by_auth_plugin($pluginname);
986
        self::$handler->destroy_by_auth_plugin($pluginname);
Línea 962... Línea 987...
962
    }
987
    }
963
 
988
 
964
    /**
989
    /**
965
     * Destroy all sessions, and delete all the session data.
990
     * Destroy all sessions, and delete all the session data.
966
     *
991
     *
967
     * @return bool
992
     * @return bool
-
 
993
     */
968
     */
994
    public static function destroy_all(): bool
969
    public static function destroy_all(): bool {
995
    {
Línea 970... Línea 996...
970
        self::terminate_current();
996
        self::terminate_current();
971
        self::load_handler();
997
        self::load_handler();
972
 
998
 
973
        try {
999
        try {
974
            $result = self::$handler->destroy_all();
1000
            $result = self::$handler->destroy_all();
975
        } catch (\moodle_exception $ignored) {
1001
        } catch (\moodle_exception $ignored) {
Línea 976... Línea 1002...
976
            // Do not show any warnings - might be during upgrade/installation.
1002
            // Do not show any warnings - might be during upgrade/installation.
977
            $result = true;
1003
            $result = true;
Línea 978... Línea 1004...
978
        }
1004
        }
979
 
1005
 
980
         return $result;
1006
        return $result;
981
    }
1007
    }
982
 
1008
 
983
    /**
1009
    /**
984
     * Destroy a specific session and delete this session record for this session id.
1010
     * Destroy a specific session and delete this session record for this session id.
-
 
1011
     *
985
     *
1012
     * @param string $id
Línea 986... Línea 1013...
986
     * @param string $id
1013
     * @return bool
987
     * @return bool
1014
     */
988
     */
1015
    public static function destroy(string $id): bool
Línea 999... Línea 1026...
999
    /**
1026
    /**
1000
     * Destroy all sessions of given user unconditionally.
1027
     * Destroy all sessions of given user unconditionally.
1001
     * @param int $userid
1028
     * @param int $userid
1002
     * @param string $keepsid keep this sid if present
1029
     * @param string $keepsid keep this sid if present
1003
     */
1030
     */
1004
    public static function destroy_user_sessions($userid, $keepsid = null) {
1031
    public static function destroy_user_sessions($userid, $keepsid = null)
-
 
1032
    {
1005
        $sessions = self::get_sessions_by_userid($userid);
1033
        $sessions = self::get_sessions_by_userid($userid);
1006
        foreach ($sessions as $session) {
1034
        foreach ($sessions as $session) {
1007
            if ($keepsid and $keepsid === $session->sid) {
1035
            if ($keepsid and $keepsid === $session->sid) {
1008
                continue;
1036
                continue;
1009
            }
1037
            }
Línea 1023... Línea 1051...
1023
     *
1051
     *
1024
     * @param int $userid
1052
     * @param int $userid
1025
     * @param string $sid session id to be always keep, usually the current one
1053
     * @param string $sid session id to be always keep, usually the current one
1026
     * @return void
1054
     * @return void
1027
     */
1055
     */
1028
    public static function apply_concurrent_login_limit($userid, $sid = null) {
1056
    public static function apply_concurrent_login_limit($userid, $sid = null)
-
 
1057
    {
1029
        global $CFG, $DB;
1058
        global $CFG, $DB;
Línea 1030... Línea 1059...
1030
 
1059
 
1031
        // NOTE: the $sid parameter is here mainly to allow testing,
1060
        // NOTE: the $sid parameter is here mainly to allow testing,
Línea 1057... Línea 1086...
1057
                }
1086
                }
1058
            }
1087
            }
1059
        }
1088
        }
Línea 1060... Línea 1089...
1060
 
1089
 
1061
        // Order records by timecreated DESC.
1090
        // Order records by timecreated DESC.
1062
        usort($sessions, function($a, $b){
1091
        usort($sessions, function ($a, $b) {
1063
            return $b->timecreated <=> $a->timecreated;
1092
            return $b->timecreated <=> $a->timecreated;
Línea 1064... Línea 1093...
1064
        });
1093
        });
1065
 
1094
 
Línea 1075... Línea 1104...
1075
    /**
1104
    /**
1076
     * Set current user.
1105
     * Set current user.
1077
     *
1106
     *
1078
     * @param \stdClass $user record
1107
     * @param \stdClass $user record
1079
     */
1108
     */
1080
    public static function set_user(\stdClass $user) {
1109
    public static function set_user(\stdClass $user)
-
 
1110
    {
1081
        global $ADMIN;
1111
        global $ADMIN;
1082
        $GLOBALS['USER'] = $user;
1112
        $GLOBALS['USER'] = $user;
1083
        unset($GLOBALS['USER']->description); // Conserve memory.
1113
        unset($GLOBALS['USER']->description); // Conserve memory.
1084
        unset($GLOBALS['USER']->password);    // Improve security.
1114
        unset($GLOBALS['USER']->password);    // Improve security.
1085
        if (isset($GLOBALS['USER']->lang)) {
1115
        if (isset($GLOBALS['USER']->lang)) {
1086
            // Make sure it is a valid lang pack name.
1116
            // Make sure it is a valid lang pack name.
1087
            $GLOBALS['USER']->lang = clean_param($GLOBALS['USER']->lang, PARAM_LANG);
1117
            $GLOBALS['USER']->lang = clean_param($GLOBALS['USER']->lang, PARAM_LANG);
1088
        }
1118
        }
Línea 1089... Línea 1119...
1089
 
1119
 
1090
        // Relink session with global $USER just in case it got unlinked somehow.
1120
        // Relink session with global $USER just in case it got unlinked somehow.
Línea 1091... Línea 1121...
1091
        $_SESSION['USER'] =& $GLOBALS['USER'];
1121
        $_SESSION['USER'] = &$GLOBALS['USER'];
1092
 
1122
 
Línea 1093... Línea 1123...
1093
        // Nullify the $ADMIN tree global. If we're changing users, then this is now stale and must be generated again if needed.
1123
        // Nullify the $ADMIN tree global. If we're changing users, then this is now stale and must be generated again if needed.
Línea 1104... Línea 1134...
1104
     * Periodic timed-out session cleanup.
1134
     * Periodic timed-out session cleanup.
1105
     *
1135
     *
1106
     * @param int $maxlifetime Sessions that have not updated for the last max_lifetime seconds will be removed.
1136
     * @param int $maxlifetime Sessions that have not updated for the last max_lifetime seconds will be removed.
1107
     * @return void
1137
     * @return void
1108
     */
1138
     */
1109
    public static function gc(int $maxlifetime = 0): void {
1139
    public static function gc(int $maxlifetime = 0): void
-
 
1140
    {
1110
        global $CFG;
1141
        global $CFG;
Línea 1111... Línea 1142...
1111
 
1142
 
1112
        // If max lifetime is not provided, use the default session timeout.
1143
        // If max lifetime is not provided, use the default session timeout.
1113
        if ($maxlifetime == 0) {
1144
        if ($maxlifetime == 0) {
Línea 1118... Línea 1149...
1118
 
1149
 
1119
    /**
1150
    /**
1120
     * Is current $USER logged-in-as somebody else?
1151
     * Is current $USER logged-in-as somebody else?
1121
     * @return bool
1152
     * @return bool
1122
     */
1153
     */
-
 
1154
    public static function is_loggedinas()
1123
    public static function is_loggedinas() {
1155
    {
1124
        return !empty($GLOBALS['USER']->realuser);
1156
        return !empty($GLOBALS['USER']->realuser);
Línea 1125... Línea 1157...
1125
    }
1157
    }
1126
 
1158
 
1127
    /**
1159
    /**
1128
     * Returns the $USER object ignoring current login-as session
1160
     * Returns the $USER object ignoring current login-as session
1129
     * @return \stdClass user object
1161
     * @return \stdClass user object
-
 
1162
     */
1130
     */
1163
    public static function get_realuser()
1131
    public static function get_realuser() {
1164
    {
1132
        if (self::is_loggedinas()) {
1165
        if (self::is_loggedinas()) {
1133
            return $_SESSION['REALUSER'];
1166
            return $_SESSION['REALUSER'];
1134
        } else {
1167
        } else {
Línea 1141... Línea 1174...
1141
     * @param int $userid
1174
     * @param int $userid
1142
     * @param \context $context
1175
     * @param \context $context
1143
     * @param bool $generateevent Set to false to prevent the loginas event to be generated
1176
     * @param bool $generateevent Set to false to prevent the loginas event to be generated
1144
     * @return void
1177
     * @return void
1145
     */
1178
     */
1146
    public static function loginas($userid, \context $context, $generateevent = true) {
1179
    public static function loginas($userid, \context $context, $generateevent = true)
-
 
1180
    {
1147
        global $USER;
1181
        global $USER;
Línea 1148... Línea 1182...
1148
 
1182
 
1149
        if (self::is_loggedinas()) {
1183
        if (self::is_loggedinas()) {
1150
            return;
1184
            return;
Línea 1151... Línea 1185...
1151
        }
1185
        }
1152
 
1186
 
1153
        // Switch to fresh new $_SESSION.
1187
        // Switch to fresh new $_SESSION.
1154
        $_SESSION = array();
1188
        $_SESSION = array();
1155
        $_SESSION['REALSESSION'] = clone($GLOBALS['SESSION']);
1189
        $_SESSION['REALSESSION'] = clone ($GLOBALS['SESSION']);
Línea 1156... Línea 1190...
1156
        $GLOBALS['SESSION'] = new \stdClass();
1190
        $GLOBALS['SESSION'] = new \stdClass();
1157
        $_SESSION['SESSION'] =& $GLOBALS['SESSION'];
1191
        $_SESSION['SESSION'] = &$GLOBALS['SESSION'];
1158
 
1192
 
1159
        // Create the new $USER object with all details and reload needed capabilities.
1193
        // Create the new $USER object with all details and reload needed capabilities.
1160
        $_SESSION['REALUSER'] = clone($GLOBALS['USER']);
1194
        $_SESSION['REALUSER'] = clone ($GLOBALS['USER']);
Línea 1161... Línea 1195...
1161
        $user = get_complete_user_data('id', $userid);
1195
        $user = get_complete_user_data('id', $userid);
Línea 1209... Línea 1243...
1209
     * @param string $component The string component for the message to show on failure.
1243
     * @param string $component The string component for the message to show on failure.
1210
     * @param int $frequency The update frequency in seconds.
1244
     * @param int $frequency The update frequency in seconds.
1211
     * @param int $timeout The timeout of each request in seconds.
1245
     * @param int $timeout The timeout of each request in seconds.
1212
     * @throws \coding_exception IF the frequency is longer than the session lifetime.
1246
     * @throws \coding_exception IF the frequency is longer than the session lifetime.
1213
     */
1247
     */
1214
    public static function keepalive($identifier = 'sessionerroruser', $component = 'error', $frequency = null, $timeout = 0) {
1248
    public static function keepalive($identifier = 'sessionerroruser', $component = 'error', $frequency = null, $timeout = 0)
-
 
1249
    {
1215
        global $CFG, $PAGE;
1250
        global $CFG, $PAGE;
Línea 1216... Línea 1251...
1216
 
1251
 
1217
        if ($frequency) {
1252
        if ($frequency) {
1218
            if ($frequency > $CFG->sessiontimeout) {
1253
            if ($frequency > $CFG->sessiontimeout) {
Línea 1223... Línea 1258...
1223
            // A frequency of sessiontimeout / 10 matches the timeouts in core/network amd module.
1258
            // A frequency of sessiontimeout / 10 matches the timeouts in core/network amd module.
1224
            $frequency = $CFG->sessiontimeout / 10;
1259
            $frequency = $CFG->sessiontimeout / 10;
1225
        }
1260
        }
Línea 1226... Línea 1261...
1226
 
1261
 
1227
        $PAGE->requires->js_call_amd('core/network', 'keepalive', array(
1262
        $PAGE->requires->js_call_amd('core/network', 'keepalive', array(
1228
                $frequency,
1263
            $frequency,
1229
                $timeout,
1264
            $timeout,
1230
                $identifier,
1265
            $identifier,
1231
                $component
1266
            $component
1232
            ));
1267
        ));
Línea 1233... Línea 1268...
1233
    }
1268
    }
1234
 
1269
 
1235
    /**
1270
    /**
1236
     * Generate a new login token and store it in the session.
1271
     * Generate a new login token and store it in the session.
1237
     *
1272
     *
1238
     * @return array The current login state.
1273
     * @return array The current login state.
-
 
1274
     */
1239
     */
1275
    private static function create_login_token()
Línea 1240... Línea 1276...
1240
    private static function create_login_token() {
1276
    {
1241
        global $SESSION;
1277
        global $SESSION;
1242
 
1278
 
Línea 1263... Línea 1299...
1263
     * Logins will be rejected if they do not include this token as well as
1299
     * Logins will be rejected if they do not include this token as well as
1264
     * the username and password fields.
1300
     * the username and password fields.
1265
     *
1301
     *
1266
     * @return string The current login token.
1302
     * @return string The current login token.
1267
     */
1303
     */
1268
    public static function get_login_token() {
1304
    public static function get_login_token()
-
 
1305
    {
1269
        global $CFG, $SESSION;
1306
        global $CFG, $SESSION;
Línea 1270... Línea 1307...
1270
 
1307
 
Línea 1271... Línea 1308...
1271
        $state = false;
1308
        $state = false;
Línea 1299... Línea 1336...
1299
     *
1336
     *
1300
     * @param mixed $token The value submitted in the login form that we are validating.
1337
     * @param mixed $token The value submitted in the login form that we are validating.
1301
     *                     If false is passed for the token, this function will always return true.
1338
     *                     If false is passed for the token, this function will always return true.
1302
     * @return boolean If the submitted token is valid.
1339
     * @return boolean If the submitted token is valid.
1303
     */
1340
     */
1304
    public static function validate_login_token($token = false) {
1341
    public static function validate_login_token($token = false)
-
 
1342
    {
1305
        global $CFG, $SESSION;
1343
        global $CFG, $SESSION;
Línea 1306... Línea 1344...
1306
 
1344
 
1307
        if (!empty($CFG->alternateloginurl) || !empty($CFG->disablelogintoken)) {
1345
        if (!empty($CFG->alternateloginurl) || !empty($CFG->disablelogintoken)) {
1308
            // An external login page cannot generate the login token we need to protect CSRF on
1346
            // An external login page cannot generate the login token we need to protect CSRF on
Línea 1334... Línea 1372...
1334
    /**
1372
    /**
1335
     * Get the recent session locks array.
1373
     * Get the recent session locks array.
1336
     *
1374
     *
1337
     * @return array Recent session locks array.
1375
     * @return array Recent session locks array.
1338
     */
1376
     */
1339
    public static function get_recent_session_locks() {
1377
    public static function get_recent_session_locks()
-
 
1378
    {
1340
        global $SESSION;
1379
        global $SESSION;
Línea 1341... Línea 1380...
1341
 
1380
 
1342
        if (!isset($SESSION->recentsessionlocks)) {
1381
        if (!isset($SESSION->recentsessionlocks)) {
1343
            // This will hold the pages that blocks other page.
1382
            // This will hold the pages that blocks other page.
Línea 1352... Línea 1391...
1352
     *
1391
     *
1353
     * This function will store session lock info of all the pages visited.
1392
     * This function will store session lock info of all the pages visited.
1354
     *
1393
     *
1355
     * @param array $sessionlock Session lock array.
1394
     * @param array $sessionlock Session lock array.
1356
     */
1395
     */
1357
    public static function update_recent_session_locks($sessionlock) {
1396
    public static function update_recent_session_locks($sessionlock)
-
 
1397
    {
1358
        global $CFG, $SESSION;
1398
        global $CFG, $SESSION;
Línea 1359... Línea 1399...
1359
 
1399
 
1360
        if (empty($CFG->debugsessionlock)) {
1400
        if (empty($CFG->debugsessionlock)) {
1361
            return;
1401
            return;
Línea 1374... Línea 1414...
1374
    }
1414
    }
Línea 1375... Línea 1415...
1375
 
1415
 
1376
    /**
1416
    /**
1377
     * Reset recent session locks array if there is a time gap more than SESSION_RESET_GAP_THRESHOLD.
1417
     * Reset recent session locks array if there is a time gap more than SESSION_RESET_GAP_THRESHOLD.
1378
     */
1418
     */
-
 
1419
    public static function cleanup_recent_session_locks()
1379
    public static function cleanup_recent_session_locks() {
1420
    {
Línea 1380... Línea 1421...
1380
        global $SESSION;
1421
        global $SESSION;
Línea 1381... Línea 1422...
1381
 
1422
 
Línea 1405... Línea 1446...
1405
     * Look for a page whose lock was gained before that timestamp, and released after that timestamp.
1446
     * Look for a page whose lock was gained before that timestamp, and released after that timestamp.
1406
     *
1447
     *
1407
     * @param  float $time Time before session lock starts.
1448
     * @param  float $time Time before session lock starts.
1408
     * @return array|null
1449
     * @return array|null
1409
     */
1450
     */
1410
    public static function get_locked_page_at($time) {
1451
    public static function get_locked_page_at($time)
-
 
1452
    {
1411
        $recentsessionlocks = self::get_recent_session_locks();
1453
        $recentsessionlocks = self::get_recent_session_locks();
1412
        foreach ($recentsessionlocks as $recentsessionlock) {
1454
        foreach ($recentsessionlocks as $recentsessionlock) {
-
 
1455
            if (
1413
            if ($time >= $recentsessionlock['gained'] &&
1456
                $time >= $recentsessionlock['gained'] &&
1414
                $time <= $recentsessionlock['released']) {
1457
                $time <= $recentsessionlock['released']
-
 
1458
            ) {
1415
                return $recentsessionlock;
1459
                return $recentsessionlock;
1416
            }
1460
            }
1417
        }
1461
        }
1418
    }
1462
    }
Línea 1419... Línea 1463...
1419
 
1463
 
1420
    /**
1464
    /**
1421
     * Display the page which blocks other pages.
1465
     * Display the page which blocks other pages.
1422
     *
1466
     *
1423
     * @return string
1467
     * @return string
1424
     */
1468
     */
-
 
1469
    public static function display_blocking_page()
1425
    public static function display_blocking_page() {
1470
    {
Línea 1426... Línea 1471...
1426
        global $PERF;
1471
        global $PERF;
1427
 
1472
 
1428
        $page = self::get_locked_page_at($PERF->sessionlock['start']);
1473
        $page = self::get_locked_page_at($PERF->sessionlock['start']);
1429
        $output = "Script ".me()." was blocked for ";
1474
        $output = "Script " . me() . " was blocked for ";
1430
        $output .= number_format($PERF->sessionlock['wait'], 3);
1475
        $output .= number_format($PERF->sessionlock['wait'], 3);
1431
        if ($page != null) {
1476
        if ($page != null) {
1432
            $output .= " second(s) by script: ";
1477
            $output .= " second(s) by script: ";
Línea 1441... Línea 1486...
1441
    /**
1486
    /**
1442
     * Get session lock info of the current page.
1487
     * Get session lock info of the current page.
1443
     *
1488
     *
1444
     * @return array
1489
     * @return array
1445
     */
1490
     */
1446
    public static function get_session_lock_info() {
1491
    public static function get_session_lock_info()
-
 
1492
    {
1447
        global $PERF;
1493
        global $PERF;
Línea 1448... Línea 1494...
1448
 
1494
 
1449
        if (!isset($PERF->sessionlock)) {
1495
        if (!isset($PERF->sessionlock)) {
1450
            return null;
1496
            return null;
Línea 1453... Línea 1499...
1453
    }
1499
    }
Línea 1454... Línea 1500...
1454
 
1500
 
1455
    /**
1501
    /**
1456
     * Display debugging info about slow and blocked script.
1502
     * Display debugging info about slow and blocked script.
1457
     */
1503
     */
-
 
1504
    public static function sessionlock_debugging()
1458
    public static function sessionlock_debugging() {
1505
    {
Línea 1459... Línea 1506...
1459
        global $CFG, $PERF;
1506
        global $CFG, $PERF;
1460
 
1507
 
1461
        if (!empty($CFG->debugsessionlock)) {
1508
        if (!empty($CFG->debugsessionlock)) {
1462
            if (isset($PERF->sessionlock['held']) && $PERF->sessionlock['held'] > $CFG->debugsessionlock) {
1509
            if (isset($PERF->sessionlock['held']) && $PERF->sessionlock['held'] > $CFG->debugsessionlock) {
1463
                debugging("Script ".me()." locked the session for ".number_format($PERF->sessionlock['held'], 3)
1510
                debugging("Script " . me() . " locked the session for " . number_format($PERF->sessionlock['held'], 3)
Línea 1464... Línea 1511...
1464
                ." seconds, it should close the session using \core\session\manager::write_close().", DEBUG_NORMAL);
1511
                    . " seconds, it should close the session using \core\session\manager::write_close().", DEBUG_NORMAL);
1465
            }
1512
            }
1466
 
1513
 
1467
            if (isset($PERF->sessionlock['wait']) && $PERF->sessionlock['wait'] > $CFG->debugsessionlock) {
1514
            if (isset($PERF->sessionlock['wait']) && $PERF->sessionlock['wait'] > $CFG->debugsessionlock) {
1468
                $output = self::display_blocking_page();
1515
                $output = self::display_blocking_page();
1469
                debugging($output, DEBUG_DEVELOPER);
1516
                debugging($output, DEBUG_DEVELOPER);
Línea 1470... Línea 1517...
1470
            }
1517
            }
-
 
1518
        }
-
 
1519
    }
-
 
1520
 
-
 
1521
    /**
-
 
1522
     * Check if the session is currently active.
-
 
1523
     * 
-
 
1524
     * @return bool True if session is active, false otherwise
-
 
1525
     */
-
 
1526
    public static function is_session_active(): bool
-
 
1527
    {
1471
        }
1528
        return self::$sessionactive === true;
1472
    }
1529
    }
1473
 
1530
 
1474
    /**
1531
    /**
1475
     * Compares two arrays and outputs the difference.
1532
     * Compares two arrays and outputs the difference.
Línea 1482... Línea 1539...
1482
     *
1539
     *
1483
     * @param array $previous
1540
     * @param array $previous
1484
     * @param array $current
1541
     * @param array $current
1485
     * @return array
1542
     * @return array
1486
     */
1543
     */
1487
    private static function array_session_diff(array $previous, array $current): array {
1544
    private static function array_session_diff(array $previous, array $current): array
-
 
1545
    {
1488
        // To use array_udiff_uassoc, the first array must have the most keys; this ensures every key is checked.
1546
        // To use array_udiff_uassoc, the first array must have the most keys; this ensures every key is checked.
1489
        // To do this, we first need to sort them by the length of their keys.
1547
        // To do this, we first need to sort them by the length of their keys.
1490
        $arrays = [$current, $previous];
1548
        $arrays = [$current, $previous];
Línea 1491... Línea 1549...
1491
 
1549