Proyectos de Subversion Moodle

Rev

Rev 1 | Ir a la última revisión | Mostrar el archivo completo | | | Autoría | Ultima modificación | Ver Log |

Rev 1 Rev 1441
Línea 12... Línea 12...
12
// GNU General Public License for more details.
12
// GNU General Public License for more details.
13
//
13
//
14
// You should have received a copy of the GNU General Public License
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
Línea 16... Línea -...
16
 
-
 
17
/**
-
 
18
 * Session manager class.
-
 
19
 *
-
 
20
 * @package    core
-
 
21
 * @copyright  2013 Petr Skoda {@link http://skodak.org}
-
 
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-
 
23
 */
-
 
24
 
16
 
Línea 25... Línea 17...
25
namespace core\session;
17
namespace core\session;
26
 
18
 
27
defined('MOODLE_INTERNAL') || die();
19
use core\clock;
Línea 28... Línea 20...
28
 
20
use core\di;
29
use html_writer;
21
use html_writer;
30
 
22
 
Línea 87... Línea 79...
87
     *                              itself as read only.
79
     *                              itself as read only.
88
     */
80
     */
89
    public static function restart_with_write_lock(bool $readonlysession) {
81
    public static function restart_with_write_lock(bool $readonlysession) {
90
        global $CFG;
82
        global $CFG;
Línea 91... Línea 83...
91
 
83
 
92
        if (!empty($CFG->enable_read_only_sessions_debug)) {
84
        if (!empty($CFG->enable_read_only_sessions) || !empty($CFG->enable_read_only_sessions_debug)) {
93
            self::$requireslockdebug = !$readonlysession;
85
            self::$requireslockdebug = !$readonlysession;
Línea 94... Línea 86...
94
        }
86
        }
95
 
87
 
Línea 159... Línea 151...
159
            self::prepare_cookies();
151
            self::prepare_cookies();
160
            $isnewsession = empty($_COOKIE[session_name()]);
152
            $isnewsession = empty($_COOKIE[session_name()]);
Línea 161... Línea 153...
161
 
153
 
162
            if (!self::$handler->start()) {
154
            if (!self::$handler->start()) {
163
                // Could not successfully start/recover session.
155
                // Could not successfully start/recover session.
164
                throw new \core\session\exception(get_string('servererror'));
156
                throw new \core\session\exception('sessionstarterror', 'error');
Línea 165... Línea 157...
165
            }
157
            }
166
 
158
 
167
            if ($requireslock) {
159
            if ($requireslock) {
Línea 250... Línea 242...
250
     */
242
     */
251
    public static function get_handler_class() {
243
    public static function get_handler_class() {
252
        global $CFG, $DB;
244
        global $CFG, $DB;
Línea 253... Línea 245...
253
 
245
 
254
        if (PHPUNIT_TEST) {
246
        if (PHPUNIT_TEST) {
255
            return '\core\session\file';
247
            return \core\tests\session\mock_handler::class;
256
        } else if (!empty($CFG->session_handler_class)) {
248
        } else if (!empty($CFG->session_handler_class)) {
257
            return $CFG->session_handler_class;
249
            return $CFG->session_handler_class;
258
        } else if (!empty($CFG->dbsessions) and $DB->session_lock_supported()) {
250
        } else if (!empty($CFG->dbsessions) && $DB->session_lock_supported()) {
259
            return '\core\session\database';
251
            return database::class;
Línea 260... Línea 252...
260
        }
252
        }
261
 
253
 
Línea 262... Línea 254...
262
        return '\core\session\file';
254
        return file::class;
263
    }
255
    }
264
 
256
 
Línea 271... Línea 263...
271
        }
263
        }
Línea 272... Línea 264...
272
 
264
 
273
        // Find out which handler to use.
265
        // Find out which handler to use.
274
        $class = self::get_handler_class();
266
        $class = self::get_handler_class();
-
 
267
        self::$handler = new $class();
-
 
268
        if (!self::$handler instanceof \core\session\handler) {
-
 
269
            throw new exception("$class must implement the \core\session\handler");
275
        self::$handler = new $class();
270
        }
Línea 276... Línea 271...
276
    }
271
    }
277
 
272
 
278
    /**
273
    /**
Línea 417... Línea 412...
417
     * WARNING: $USER and $SESSION are set up later, do not use them yet!
412
     * WARNING: $USER and $SESSION are set up later, do not use them yet!
418
     *
413
     *
419
     * @param bool $newsid is this a new session in first http request?
414
     * @param bool $newsid is this a new session in first http request?
420
     */
415
     */
421
    protected static function initialise_user_session($newsid) {
416
    protected static function initialise_user_session($newsid) {
422
        global $CFG, $DB;
417
        global $CFG;
Línea 423... Línea 418...
423
 
418
 
424
        $sid = session_id();
419
        $sid = session_id();
425
        if (!$sid) {
420
        if (!$sid) {
426
            // No session, very weird.
421
            // No session, very weird.
427
            error_log('Missing session ID, session not started!');
422
            error_log('Missing session ID, session not started!');
428
            self::init_empty_session($newsid);
423
            self::init_empty_session($newsid);
429
            return;
424
            return;
430
        }
-
 
431
 
425
        }
-
 
426
        $record = self::get_session_by_sid($sid);
432
        if (!$record = $DB->get_record('sessions', array('sid'=>$sid), 'id, sid, state, userid, lastip, timecreated, timemodified')) {
427
        if (!isset($record->sid)) {
433
            if (!$newsid) {
428
            if (!$newsid) {
434
                if (!empty($_SESSION['USER']->id)) {
429
                if (!empty($_SESSION['USER']->id)) {
435
                    // This should not happen, just log it, we MUST not produce any output here!
430
                    // This should not happen, just log it, we MUST not produce any output here!
436
                    error_log("Cannot find session record $sid for user ".$_SESSION['USER']->id.", creating new session.");
431
                    error_log("Cannot find session record $sid for user ".$_SESSION['USER']->id.", creating new session.");
Línea 454... Línea 449...
454
            $timeout = false;
449
            $timeout = false;
455
            if (isguestuser($userid) or empty($userid)) {
450
            if (isguestuser($userid) or empty($userid)) {
456
                // Ignore guest and not-logged in timeouts, there is very little risk here.
451
                // Ignore guest and not-logged in timeouts, there is very little risk here.
457
                $timeout = false;
452
                $timeout = false;
Línea 458... Línea 453...
458
 
453
 
459
            } else if ($record->timemodified < time() - $maxlifetime) {
454
            } else if ($record->timemodified < di::get(clock::class)->time() - $maxlifetime) {
460
                $timeout = true;
455
                $timeout = true;
461
                $authsequence = get_enabled_auth_plugins(); // Auths, in sequence.
456
                $authsequence = get_enabled_auth_plugins(); // Auths, in sequence.
462
                foreach ($authsequence as $authname) {
457
                foreach ($authsequence as $authname) {
463
                    $authplugin = get_auth_plugin($authname);
458
                    $authplugin = get_auth_plugin($authname);
Línea 472... Línea 467...
472
                if (defined('NO_SESSION_UPDATE') && NO_SESSION_UPDATE) {
467
                if (defined('NO_SESSION_UPDATE') && NO_SESSION_UPDATE) {
473
                    return;
468
                    return;
474
                }
469
                }
475
                session_regenerate_id(true);
470
                session_regenerate_id(true);
476
                $_SESSION = array();
471
                $_SESSION = array();
477
                $DB->delete_records('sessions', array('id'=>$record->id));
472
                self::destroy($record->sid);
478
 
-
 
479
            } else {
473
            } else {
480
                // Update session tracking record.
474
                // Update session tracking record.
481
 
-
 
482
                $update = new \stdClass();
475
                $update = new \stdClass();
483
                $updated = false;
476
                $updated = false;
Línea 484... Línea 477...
484
 
477
 
485
                if ($record->userid != $userid) {
478
                if ($record->userid != $userid) {
Línea 491... Línea 484...
491
                if ($record->lastip != $ip) {
484
                if ($record->lastip != $ip) {
492
                    $update->lastip = $record->lastip = $ip;
485
                    $update->lastip = $record->lastip = $ip;
493
                    $updated = true;
486
                    $updated = true;
494
                }
487
                }
Línea -... Línea 488...
-
 
488
 
-
 
489
                $time = di::get(clock::class)->time();
495
 
490
 
Línea 496... Línea 491...
496
                $updatefreq = empty($CFG->session_update_timemodified_frequency) ? 20 : $CFG->session_update_timemodified_frequency;
491
                $updatefreq = empty($CFG->session_update_timemodified_frequency) ? 20 : $CFG->session_update_timemodified_frequency;
497
 
492
 
498
                if ($record->timemodified == $record->timecreated) {
493
                if ($record->timemodified == $record->timecreated) {
499
                    // Always do first update of existing record.
494
                    // Always do first update of existing record.
Línea 500... Línea 495...
500
                    $update->timemodified = $record->timemodified = time();
495
                    $update->timemodified = $record->timemodified = $time;
501
                    $updated = true;
496
                    $updated = true;
502
 
497
 
503
                } else if ($record->timemodified < time() - $updatefreq) {
498
                } else if ($record->timemodified < $time - $updatefreq) {
504
                    // Update the session modified flag only once every 20 seconds.
499
                    // Update the session modified flag only once every 20 seconds.
Línea 505... Línea 500...
505
                    $update->timemodified = $record->timemodified = time();
500
                    $update->timemodified = $record->timemodified = $time;
506
                    $updated = true;
501
                    $updated = true;
-
 
502
                }
507
                }
503
 
508
 
504
                if ($updated && (!defined('NO_SESSION_UPDATE') || !NO_SESSION_UPDATE)) {
Línea 509... Línea 505...
509
                if ($updated && (!defined('NO_SESSION_UPDATE') || !NO_SESSION_UPDATE)) {
505
                    $update->id = $record->id;
510
                    $update->id = $record->id;
506
                    $update->userid = $record->userid;
511
                    $DB->update_record('sessions', $update);
-
 
512
                }
507
                    self::$handler->update_session($update);
513
 
508
                }
514
                return;
509
 
515
            }
510
                return;
516
        } else {
511
            }
517
            if ($record) {
-
 
518
                // This happens when people switch session handlers...
512
        } else if (isset($record->sid)) {
519
                session_regenerate_id(true);
513
            // This happens when people switch session handlers...
Línea 520... Línea 514...
520
                $_SESSION = array();
514
            session_regenerate_id(true);
521
                $DB->delete_records('sessions', array('id'=>$record->id));
515
            $_SESSION = [];
Línea 549... Línea 543...
549
        }
543
        }
Línea 550... Línea 544...
550
 
544
 
551
        // Setup $USER and insert the session tracking record.
545
        // Setup $USER and insert the session tracking record.
552
        if ($user) {
546
        if ($user) {
553
            self::set_user($user);
547
            self::set_user($user);
554
            self::add_session_record($user->id);
548
            self::add_session($user->id);
555
        } else {
549
        } else {
556
            self::init_empty_session($newsid);
550
            self::init_empty_session($newsid);
557
            self::add_session_record(0);
551
            self::add_session(0);
Línea 558... Línea 552...
558
        }
552
        }
559
 
553
 
560
        if ($timedout) {
554
        if ($timedout) {
561
            $_SESSION['SESSION']->has_timed_out = true;
555
            $_SESSION['SESSION']->has_timed_out = true;
Línea 562... Línea 556...
562
        }
556
        }
-
 
557
    }
-
 
558
 
-
 
559
    /**
-
 
560
     * Returns a single session record for this session id.
-
 
561
     *
-
 
562
     * @param string $sid
-
 
563
     * @return \stdClass
-
 
564
     */
-
 
565
    public static function get_session_by_sid(string $sid): \stdClass {
-
 
566
        return self::$handler->get_session_by_sid($sid);
-
 
567
    }
-
 
568
 
-
 
569
    /**
-
 
570
     * Returns all the session records for this user id.
-
 
571
     *
-
 
572
     * @param int $userid
-
 
573
     * @return array
-
 
574
     */
-
 
575
    public static function get_sessions_by_userid(int $userid): array {
-
 
576
        return self::$handler->get_sessions_by_userid($userid);
563
    }
577
    }
-
 
578
 
564
 
579
    /**
565
    /**
580
     * Insert new empty session record.
566
     * Insert new empty session record.
581
     *
567
     * @param int $userid
582
     * @param int $userid
568
     * @return \stdClass the new record
-
 
569
     */
-
 
570
    protected static function add_session_record($userid) {
-
 
571
        global $DB;
583
     * @return \stdClass the new record
572
        $record = new \stdClass();
-
 
573
        $record->state       = 0;
-
 
574
        $record->sid         = session_id();
-
 
575
        $record->sessdata    = null;
-
 
576
        $record->userid      = $userid;
584
     */
577
        $record->timecreated = $record->timemodified = time();
-
 
Línea -... Línea 585...
-
 
585
    public static function add_session(int $userid): \stdClass {
-
 
586
        return self::$handler->add_session($userid);
-
 
587
    }
-
 
588
 
578
        $record->firstip     = $record->lastip = getremoteaddr();
589
    /**
-
 
590
     * Update a session record.
-
 
591
     *
-
 
592
     * @param \stdClass $record
579
 
593
     * @return bool
Línea 580... Línea 594...
580
        $record->id = $DB->insert_record('sessions', $record);
594
     */
581
 
595
    public static function update_session(\stdClass $record): bool {
582
        return $record;
596
        return self::$handler->update_session($record);
Línea 617... Línea 631...
617
        // Regenerate session id and delete old session,
631
        // Regenerate session id and delete old session,
618
        // this helps prevent session fixation attacks from the same domain.
632
        // this helps prevent session fixation attacks from the same domain.
Línea 619... Línea 633...
619
 
633
 
620
        $sid = session_id();
634
        $sid = session_id();
621
        session_regenerate_id(true);
635
        session_regenerate_id(true);
622
        $DB->delete_records('sessions', array('sid'=>$sid));
636
        self::destroy($sid);
Línea 623... Línea 637...
623
        self::add_session_record($user->id);
637
        self::add_session($user->id);
624
 
638
 
Línea 625... Línea 639...
625
        // Let enrol plugins deal with new enrolments if necessary.
639
        // Let enrol plugins deal with new enrolments if necessary.
Línea 677... Línea 691...
677
        }
691
        }
Línea 678... Línea 692...
678
 
692
 
679
        // Write new empty session and make sure the old one is deleted.
693
        // Write new empty session and make sure the old one is deleted.
680
        $sid = session_id();
694
        $sid = session_id();
681
        session_regenerate_id(true);
695
        session_regenerate_id(true);
682
        $DB->delete_records('sessions', array('sid'=>$sid));
696
        self::destroy($sid);
683
        self::init_empty_session();
697
        self::init_empty_session();
684
        self::add_session_record($_SESSION['USER']->id); // Do not use $USER here because it may not be set up yet.
698
        self::add_session($_SESSION['USER']->id); // Do not use $USER here because it may not be set up yet.
685
        self::write_close();
699
        self::write_close();
Línea 686... Línea 700...
686
    }
700
    }
687
 
701
 
Línea 812... Línea 826...
812
            // Not installed yet, do not try to access database.
826
            // Not installed yet, do not try to access database.
813
            return false;
827
            return false;
814
        }
828
        }
Línea 815... Línea 829...
815
 
829
 
816
        // Note: add sessions->state checking here if it gets implemented.
830
        // Note: add sessions->state checking here if it gets implemented.
-
 
831
        $record = self::get_session_by_sid($sid);
817
        if (!$record = $DB->get_record('sessions', array('sid' => $sid), 'id, userid, timemodified')) {
832
        if (!isset($record->sid)) {
818
            return false;
833
            return false;
Línea 819... Línea 834...
819
        }
834
        }
820
 
835
 
821
        if (empty($record->userid) or isguestuser($record->userid)) {
836
        if (empty($record->userid) or isguestuser($record->userid)) {
822
            // Ignore guest and not-logged-in timeouts, there is very little risk here.
837
            // Ignore guest and not-logged-in timeouts, there is very little risk here.
823
        } else if ($record->timemodified < time() - $CFG->sessiontimeout) {
838
        } else if ($record->timemodified < di::get(clock::class)->time() - $CFG->sessiontimeout) {
Línea 824... Línea 839...
824
            return false;
839
            return false;
825
        }
840
        }
Línea 840... Línea 855...
840
            // Not installed yet, do not try to access database.
855
            // Not installed yet, do not try to access database.
841
            return ['userid' => 0, 'timeremaining' => $CFG->sessiontimeout];
856
            return ['userid' => 0, 'timeremaining' => $CFG->sessiontimeout];
842
        }
857
        }
Línea 843... Línea 858...
843
 
858
 
844
        // Note: add sessions->state checking here if it gets implemented.
859
        // Note: add sessions->state checking here if it gets implemented.
845
        if (!$record = $DB->get_record('sessions', array('sid' => $sid), 'id, userid, timemodified')) {
860
        if (!$record = self::get_session_by_sid($sid)) {
846
            return ['userid' => 0, 'timeremaining' => $CFG->sessiontimeout];
861
            return ['userid' => 0, 'timeremaining' => $CFG->sessiontimeout];
Línea 847... Línea 862...
847
        }
862
        }
848
 
863
 
849
        if (empty($record->userid) or isguestuser($record->userid)) {
864
        if (empty($record->userid) or isguestuser($record->userid)) {
850
            // Ignore guest and not-logged-in timeouts, there is very little risk here.
865
            // Ignore guest and not-logged-in timeouts, there is very little risk here.
-
 
866
            return ['userid' => 0, 'timeremaining' => $CFG->sessiontimeout];
-
 
867
        } else {
851
            return ['userid' => 0, 'timeremaining' => $CFG->sessiontimeout];
868
            return [
-
 
869
                'userid' => $record->userid,
852
        } else {
870
                'timeremaining' => $CFG->sessiontimeout - (di::get(clock::class)->time() - $record->timemodified),
853
            return ['userid' => $record->userid, 'timeremaining' => $CFG->sessiontimeout - (time() - $record->timemodified)];
871
            ];
Línea 854... Línea 872...
854
        }
872
        }
855
    }
873
    }
856
 
874
 
857
    /**
875
    /**
858
     * Fake last access for given session, this prevents session timeout.
876
     * Fake last access for given session, this prevents session timeout.
859
     * @param string $sid
-
 
860
     */
-
 
861
    public static function touch_session($sid) {
877
     * @param string $sid
862
        global $DB;
-
 
863
 
878
     */
-
 
879
    public static function touch_session($sid) {
864
        // Timeouts depend on core sessions table only, no need to update anything in external stores.
880
        // Timeouts depend on core sessions table only, no need to update anything in external stores.
-
 
881
        self::$handler->update_session((object) [
865
 
882
            'sid' => $sid,
Línea 866... Línea 883...
866
        $sql = "UPDATE {sessions} SET timemodified = :now WHERE sid = :sid";
883
            'timemodified' => di::get(clock::class)->time(),
867
        $DB->execute($sql, array('now'=>time(), 'sid'=>$sid));
884
        ]);
-
 
885
    }
-
 
886
 
-
 
887
    /**
-
 
888
     * Terminate all sessions unconditionally.
868
    }
889
     *
-
 
890
     * @return void
-
 
891
     * @deprecated since Moodle 4.5 See MDL-66161
-
 
892
     * @todo Remove in MDL-81848
-
 
893
     */
869
 
894
    #[\core\attribute\deprecated(
-
 
895
        replacement: 'destroy_all',
870
    /**
896
        since: '4.5',
-
 
897
    )]
Línea -... Línea 898...
-
 
898
    public static function kill_all_sessions(): void {
-
 
899
        \core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
-
 
900
        self::destroy_all();
-
 
901
    }
-
 
902
 
-
 
903
    /**
-
 
904
     * Terminate give session unconditionally.
-
 
905
     *
-
 
906
     * @param string $sid
-
 
907
     * @return void
-
 
908
     * @deprecated since Moodle 4.5 See MDL-66161
-
 
909
     * @todo Remove in MDL-81848
-
 
910
     */
-
 
911
    #[\core\attribute\deprecated(
871
     * Terminate all sessions unconditionally.
912
        replacement: 'destroy',
-
 
913
        since: '4.5',
Línea -... Línea 914...
-
 
914
    )]
-
 
915
    public static function kill_session($sid): void {
-
 
916
        \core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
-
 
917
        self::destroy($sid);
-
 
918
    }
-
 
919
 
-
 
920
    /**
-
 
921
     * Kill sessions of users with disabled plugins.
-
 
922
     *
-
 
923
     * @param string $pluginname
-
 
924
     * @return void
-
 
925
     * @deprecated since Moodle 4.5 See MDL-66161
-
 
926
     * @todo Remove in MDL-81848
-
 
927
     */
-
 
928
    #[\core\attribute\deprecated(
-
 
929
        replacement: 'destroy_by_auth_plugin',
-
 
930
        since: '4.5',
-
 
931
    )]
-
 
932
    public static function kill_sessions_for_auth_plugin(string $pluginname): void {
-
 
933
        \core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
-
 
934
        self::destroy_by_auth_plugin($pluginname);
-
 
935
    }
-
 
936
 
-
 
937
    /**
-
 
938
     * Terminate all sessions of given user unconditionally.
-
 
939
     *
-
 
940
     * @param int $userid
-
 
941
     * @param string $keepsid keep this sid if present
-
 
942
     * @deprecated since Moodle 4.5 See MDL-66161
-
 
943
     * @todo Remove in MDL-81848
-
 
944
     */
-
 
945
    #[\core\attribute\deprecated(
-
 
946
            replacement: 'destroy_user_sessions',
-
 
947
            since: '4.5',
-
 
948
    )]
-
 
949
    public static function kill_user_sessions($userid, $keepsid = null) {
-
 
950
        \core\deprecation::emit_deprecation([self::class, __FUNCTION__]);
-
 
951
        self::destroy_user_sessions($userid, $keepsid);
-
 
952
    }
-
 
953
 
-
 
954
    /**
-
 
955
     * 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.
-
 
957
     *
-
 
958
     * @param string $pluginname Auth plugin name.
-
 
959
     */
-
 
960
    public static function destroy_by_auth_plugin(string $pluginname): void {
-
 
961
        self::$handler->destroy_by_auth_plugin($pluginname);
-
 
962
    }
-
 
963
 
-
 
964
    /**
872
     */
965
     * Destroy all sessions, and delete all the session data.
873
    public static function kill_all_sessions() {
-
 
Línea 874... Línea 966...
874
        global $DB;
966
     *
875
 
967
     * @return bool
876
        self::terminate_current();
968
     */
877
 
969
    public static function destroy_all(): bool {
-
 
970
        self::terminate_current();
878
        self::load_handler();
971
        self::load_handler();
-
 
972
 
-
 
973
        try {
879
        self::$handler->kill_all_sessions();
974
            $result = self::$handler->destroy_all();
Línea 880... Línea 975...
880
 
975
        } catch (\moodle_exception $ignored) {
881
        try {
976
            // Do not show any warnings - might be during upgrade/installation.
-
 
977
            $result = true;
882
            $DB->delete_records('sessions');
978
        }
-
 
979
 
883
        } catch (\dml_exception $ignored) {
980
         return $result;
884
            // Do not show any warnings - might be during upgrade/installation.
981
    }
885
        }
-
 
886
    }
-
 
887
 
982
 
Línea 888... Línea 983...
888
    /**
983
    /**
889
     * Terminate give session unconditionally.
984
     * Destroy a specific session and delete this session record for this session id.
890
     * @param string $sid
985
     *
Línea 891... Línea 986...
891
     */
986
     * @param string $id
892
    public static function kill_session($sid) {
-
 
893
        global $DB;
-
 
894
 
987
     * @return bool
Línea 895... Línea 988...
895
        self::load_handler();
988
     */
896
 
989
    public static function destroy(string $id): bool {
897
        if ($sid === session_id()) {
990
        self::load_handler();
898
            self::write_close();
991
 
899
        }
992
        if ($id === session_id()) {
900
 
993
            self::write_close();
901
        self::$handler->kill_session($sid);
-
 
902
 
-
 
903
        $DB->delete_records('sessions', array('sid'=>$sid));
994
        }
904
    }
995
 
905
 
996
        return self::$handler->destroy($id);
906
    /**
997
    }
907
     * Terminate all sessions of given user unconditionally.
998
 
908
     * @param int $userid
999
    /**
909
     * @param string $keepsid keep this sid if present
1000
     * Destroy all sessions of given user unconditionally.
910
     */
1001
     * @param int $userid
Línea 911... Línea 1002...
911
    public static function kill_user_sessions($userid, $keepsid = null) {
1002
     * @param string $keepsid keep this sid if present
912
        global $DB;
1003
     */
Línea 947... Línea 1038...
947
 
1038
 
948
        if (empty($CFG->limitconcurrentlogins) or $CFG->limitconcurrentlogins < 0) {
1039
        if (empty($CFG->limitconcurrentlogins) or $CFG->limitconcurrentlogins < 0) {
949
            return;
1040
            return;
Línea 950... Línea 1041...
950
        }
1041
        }
-
 
1042
 
-
 
1043
        $sessions = self::get_sessions_by_userid($userid);
Línea 951... Línea 1044...
951
 
1044
 
952
        $count = $DB->count_records('sessions', array('userid' => $userid));
1045
        $count = count($sessions);
953
 
1046
 
Línea 954... Línea 1047...
954
        if ($count <= $CFG->limitconcurrentlogins) {
1047
        if ($count <= $CFG->limitconcurrentlogins) {
955
            return;
-
 
956
        }
-
 
957
 
1048
            return;
-
 
1049
        }
958
        $i = 0;
1050
 
959
        $select = "userid = :userid";
1051
        $i = 0;
960
        $params = array('userid' => $userid);
1052
        if ($sid) {
961
        if ($sid) {
1053
            foreach ($sessions as $key => $session) {
962
            if ($DB->record_exists('sessions', array('sid' => $sid, 'userid' => $userid))) {
1054
                if ($session->sid == $sid && $session->userid == $userid) {
963
                $select .= " AND sid <> :sid";
1055
                    $i = 1;
Línea -... Línea 1056...
-
 
1056
                    unset($sessions[$key]);
-
 
1057
                }
964
                $params['sid'] = $sid;
1058
            }
-
 
1059
        }
-
 
1060
 
965
                $i = 1;
1061
        // Order records by timecreated DESC.
966
            }
1062
        usort($sessions, function($a, $b){
967
        }
1063
            return $b->timecreated <=> $a->timecreated;
968
 
1064
        });
969
        $sessions = $DB->get_records_select('sessions', $select, $params, 'timecreated DESC', 'id, sid');
1065
 
970
        foreach ($sessions as $session) {
1066
        foreach ($sessions as $session) {
971
            $i++;
1067
            $i++;
972
            if ($i <= $CFG->limitconcurrentlogins) {
1068
            if ($i <= $CFG->limitconcurrentlogins) {
Línea 973... Línea 1069...
973
                continue;
1069
                continue;
974
            }
1070
            }
Línea 1004... Línea 1100...
1004
        set_access_log_user();
1100
        set_access_log_user();
1005
    }
1101
    }
Línea 1006... Línea 1102...
1006
 
1102
 
1007
    /**
1103
    /**
-
 
1104
     * Periodic timed-out session cleanup.
-
 
1105
     *
-
 
1106
     * @param int $maxlifetime Sessions that have not updated for the last max_lifetime seconds will be removed.
1008
     * Periodic timed-out session cleanup.
1107
     * @return void
1009
     */
1108
     */
1010
    public static function gc() {
1109
    public static function gc(int $maxlifetime = 0): void {
1011
        global $CFG, $DB;
-
 
1012
 
-
 
1013
        // This may take a long time...
-
 
1014
        \core_php_time_limit::raise();
-
 
1015
 
-
 
1016
        $maxlifetime = $CFG->sessiontimeout;
-
 
1017
 
-
 
1018
        try {
-
 
1019
            // Kill all sessions of deleted and suspended users without any hesitation.
-
 
1020
            $rs = $DB->get_recordset_select('sessions', "userid IN (SELECT id FROM {user} WHERE deleted <> 0 OR suspended <> 0)", array(), 'id DESC', 'id, sid');
-
 
1021
            foreach ($rs as $session) {
-
 
1022
                self::kill_session($session->sid);
-
 
1023
            }
-
 
1024
            $rs->close();
-
 
1025
 
-
 
1026
            // Kill sessions of users with disabled plugins.
-
 
1027
            $authsequence = get_enabled_auth_plugins();
-
 
1028
            $authsequence = array_flip($authsequence);
-
 
1029
            unset($authsequence['nologin']); // No login means user cannot login.
-
 
1030
            $authsequence = array_flip($authsequence);
-
 
1031
 
-
 
1032
            list($notplugins, $params) = $DB->get_in_or_equal($authsequence, SQL_PARAMS_QM, '', false);
-
 
1033
            $rs = $DB->get_recordset_select('sessions', "userid IN (SELECT id FROM {user} WHERE auth $notplugins)", $params, 'id DESC', 'id, sid');
-
 
1034
            foreach ($rs as $session) {
-
 
1035
                self::kill_session($session->sid);
-
 
1036
            }
-
 
1037
            $rs->close();
-
 
1038
 
-
 
1039
            // Now get a list of time-out candidates - real users only.
-
 
1040
            $sql = "SELECT u.*, s.sid, s.timecreated AS s_timecreated, s.timemodified AS s_timemodified
-
 
1041
                      FROM {user} u
-
 
1042
                      JOIN {sessions} s ON s.userid = u.id
-
 
1043
                     WHERE s.timemodified < :purgebefore AND u.id <> :guestid";
-
 
1044
            $params = array('purgebefore' => (time() - $maxlifetime), 'guestid'=>$CFG->siteguest);
-
 
1045
 
-
 
1046
            $authplugins = array();
-
 
1047
            foreach ($authsequence as $authname) {
-
 
1048
                $authplugins[$authname] = get_auth_plugin($authname);
-
 
1049
            }
-
 
1050
            $rs = $DB->get_recordset_sql($sql, $params);
-
 
1051
            foreach ($rs as $user) {
-
 
1052
                foreach ($authplugins as $authplugin) {
-
 
1053
                    /** @var \auth_plugin_base $authplugin*/
-
 
1054
                    if ($authplugin->ignore_timeout_hook($user, $user->sid, $user->s_timecreated, $user->s_timemodified)) {
-
 
1055
                        continue 2;
-
 
1056
                    }
-
 
1057
                }
-
 
1058
                self::kill_session($user->sid);
-
 
1059
            }
-
 
1060
            $rs->close();
-
 
1061
 
-
 
1062
            // Delete expired sessions for guest user account, give them larger timeout, there is no security risk here.
-
 
1063
            $params = array('purgebefore' => (time() - ($maxlifetime * 5)), 'guestid'=>$CFG->siteguest);
-
 
1064
            $rs = $DB->get_recordset_select('sessions', 'userid = :guestid AND timemodified < :purgebefore', $params, 'id DESC', 'id, sid');
-
 
1065
            foreach ($rs as $session) {
-
 
1066
                self::kill_session($session->sid);
-
 
1067
            }
-
 
1068
            $rs->close();
-
 
1069
 
-
 
1070
            // Delete expired sessions for userid = 0 (not logged in), better kill them asap to release memory.
-
 
1071
            $params = array('purgebefore' => (time() - $maxlifetime));
-
 
1072
            $rs = $DB->get_recordset_select('sessions', 'userid = 0 AND timemodified < :purgebefore', $params, 'id DESC', 'id, sid');
-
 
1073
            foreach ($rs as $session) {
-
 
1074
                self::kill_session($session->sid);
-
 
1075
            }
-
 
1076
            $rs->close();
-
 
1077
 
-
 
1078
            // Cleanup letfovers from the first browser access because it may set multiple cookies and then use only one.
-
 
1079
            $params = array('purgebefore' => (time() - 60*3));
-
 
1080
            $rs = $DB->get_recordset_select('sessions', 'userid = 0 AND timemodified = timecreated AND timemodified < :purgebefore', $params, 'id ASC', 'id, sid');
-
 
1081
            foreach ($rs as $session) {
-
 
1082
                self::kill_session($session->sid);
-
 
1083
            }
-
 
Línea -... Línea 1110...
-
 
1110
        global $CFG;
1084
            $rs->close();
1111
 
1085
 
1112
        // If max lifetime is not provided, use the default session timeout.
1086
        } catch (\Exception $ex) {
1113
        if ($maxlifetime == 0) {
-
 
1114
            $maxlifetime = $CFG->sessiontimeout;
1087
            debugging('Error gc-ing sessions: '.$ex->getMessage(), DEBUG_NORMAL, $ex->getTrace());
1115
        }
Línea 1088... Línea 1116...
1088
        }
1116
        self::$handler->gc($maxlifetime);
1089
    }
1117
    }
1090
 
1118
 
Línea 1212... Línea 1240...
1212
    private static function create_login_token() {
1240
    private static function create_login_token() {
1213
        global $SESSION;
1241
        global $SESSION;
Línea 1214... Línea 1242...
1214
 
1242
 
1215
        $state = [
1243
        $state = [
1216
            'token' => random_string(32),
1244
            'token' => random_string(32),
1217
            'created' => time() // Server time - not user time.
1245
            'created' => di::get(clock::class)->time(), // Server time - not user time.
Línea 1218... Línea 1246...
1218
        ];
1246
        ];
1219
 
1247
 
1220
        if (!isset($SESSION->logintoken)) {
1248
        if (!isset($SESSION->logintoken)) {
Línea 1252... Línea 1280...
1252
        if (empty($state)) {
1280
        if (empty($state)) {
1253
            $state = self::create_login_token();
1281
            $state = self::create_login_token();
1254
        }
1282
        }
Línea 1255... Línea 1283...
1255
 
1283
 
1256
        // Check token lifespan.
1284
        // Check token lifespan.
1257
        if ($state['created'] < (time() - $CFG->sessiontimeout)) {
1285
        if ($state['created'] < (di::get(clock::class)->time() - $CFG->sessiontimeout)) {
1258
            $state = self::create_login_token();
1286
            $state = self::create_login_token();
Línea 1259... Línea 1287...
1259
        }
1287
        }
1260
 
1288