[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/lib/classes/session/ -> manager.php (source)

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  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/>.
  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  
  25  namespace core\session;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  /**
  30   * Session manager, this is the public Moodle API for sessions.
  31   *
  32   * Following PHP functions MUST NOT be used directly:
  33   * - session_start() - not necessary, lib/setup.php starts session automatically,
  34   *   use define('NO_MOODLE_COOKIE', true) if session not necessary.
  35   * - session_write_close() - use \core\session\manager::write_close() instead.
  36   * - session_destroy() - use require_logout() instead.
  37   *
  38   * @package    core
  39   * @copyright  2013 Petr Skoda {@link http://skodak.org}
  40   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   */
  42  class manager {
  43      /** @var handler $handler active session handler instance */
  44      protected static $handler;
  45  
  46      /** @var bool $sessionactive Is the session active? */
  47      protected static $sessionactive = null;
  48  
  49      /**
  50       * Start user session.
  51       *
  52       * Note: This is intended to be called only from lib/setup.php!
  53       */
  54      public static function start() {
  55          global $CFG, $DB;
  56  
  57          if (isset(self::$sessionactive)) {
  58              debugging('Session was already started!', DEBUG_DEVELOPER);
  59              return;
  60          }
  61  
  62          self::load_handler();
  63  
  64          // Init the session handler only if everything initialised properly in lib/setup.php file
  65          // and the session is actually required.
  66          if (empty($DB) or empty($CFG->version) or !defined('NO_MOODLE_COOKIES') or NO_MOODLE_COOKIES or CLI_SCRIPT) {
  67              self::$sessionactive = false;
  68              self::init_empty_session();
  69              return;
  70          }
  71  
  72          try {
  73              self::$handler->init();
  74              self::prepare_cookies();
  75              $newsid = empty($_COOKIE[session_name()]);
  76  
  77              self::$handler->start();
  78  
  79              self::initialise_user_session($newsid);
  80              self::check_security();
  81  
  82              // Link global $USER and $SESSION,
  83              // this is tricky because PHP does not allow references to references
  84              // and global keyword uses internally once reference to the $GLOBALS array.
  85              // The solution is to use the $GLOBALS['USER'] and $GLOBALS['$SESSION']
  86              // as the main storage of data and put references to $_SESSION.
  87              $GLOBALS['USER'] = $_SESSION['USER'];
  88              $_SESSION['USER'] =& $GLOBALS['USER'];
  89              $GLOBALS['SESSION'] = $_SESSION['SESSION'];
  90              $_SESSION['SESSION'] =& $GLOBALS['SESSION'];
  91  
  92          } catch (\Exception $ex) {
  93              @session_write_close();
  94              self::init_empty_session();
  95              self::$sessionactive = false;
  96              throw $ex;
  97          }
  98  
  99          self::$sessionactive = true;
 100      }
 101  
 102      /**
 103       * Returns current page performance info.
 104       *
 105       * @return array perf info
 106       */
 107      public static function get_performance_info() {
 108          if (!session_id()) {
 109              return array();
 110          }
 111  
 112          self::load_handler();
 113          $size = display_size(strlen(session_encode()));
 114          $handler = get_class(self::$handler);
 115  
 116          $info = array();
 117          $info['size'] = $size;
 118          $info['html'] = "<span class=\"sessionsize\">Session ($handler): $size</span> ";
 119          $info['txt'] = "Session ($handler): $size ";
 120  
 121          return $info;
 122      }
 123  
 124      /**
 125       * Create handler instance.
 126       */
 127      protected static function load_handler() {
 128          global $CFG, $DB;
 129  
 130          if (self::$handler) {
 131              return;
 132          }
 133  
 134          // Find out which handler to use.
 135          if (PHPUNIT_TEST) {
 136              $class = '\core\session\file';
 137  
 138          } else if (!empty($CFG->session_handler_class)) {
 139              $class = $CFG->session_handler_class;
 140  
 141          } else if (!empty($CFG->dbsessions) and $DB->session_lock_supported()) {
 142              $class = '\core\session\database';
 143  
 144          } else {
 145              $class = '\core\session\file';
 146          }
 147          self::$handler = new $class();
 148      }
 149  
 150      /**
 151       * Empty current session, fill it with not-logged-in user info.
 152       *
 153       * This is intended for installation scripts, unit tests and other
 154       * special areas. Do NOT use for logout and session termination
 155       * in normal requests!
 156       */
 157      public static function init_empty_session() {
 158          global $CFG;
 159  
 160          $GLOBALS['SESSION'] = new \stdClass();
 161  
 162          $GLOBALS['USER'] = new \stdClass();
 163          $GLOBALS['USER']->id = 0;
 164          if (isset($CFG->mnet_localhost_id)) {
 165              $GLOBALS['USER']->mnethostid = $CFG->mnet_localhost_id;
 166          } else {
 167              // Not installed yet, the future host id will be most probably 1.
 168              $GLOBALS['USER']->mnethostid = 1;
 169          }
 170  
 171          // Link global $USER and $SESSION.
 172          $_SESSION = array();
 173          $_SESSION['USER'] =& $GLOBALS['USER'];
 174          $_SESSION['SESSION'] =& $GLOBALS['SESSION'];
 175      }
 176  
 177      /**
 178       * Make sure all cookie and session related stuff is configured properly before session start.
 179       */
 180      protected static function prepare_cookies() {
 181          global $CFG;
 182  
 183          if (!isset($CFG->cookiesecure) or (!is_https() and empty($CFG->sslproxy))) {
 184              $CFG->cookiesecure = 0;
 185          }
 186  
 187          if (!isset($CFG->cookiehttponly)) {
 188              $CFG->cookiehttponly = 0;
 189          }
 190  
 191          // Set sessioncookie variable if it isn't already.
 192          if (!isset($CFG->sessioncookie)) {
 193              $CFG->sessioncookie = '';
 194          }
 195          $sessionname = 'MoodleSession'.$CFG->sessioncookie;
 196  
 197          // Make sure cookie domain makes sense for this wwwroot.
 198          if (!isset($CFG->sessioncookiedomain)) {
 199              $CFG->sessioncookiedomain = '';
 200          } else if ($CFG->sessioncookiedomain !== '') {
 201              $host = parse_url($CFG->wwwroot, PHP_URL_HOST);
 202              if ($CFG->sessioncookiedomain !== $host) {
 203                  if (substr($CFG->sessioncookiedomain, 0, 1) === '.') {
 204                      if (!preg_match('|^.*'.preg_quote($CFG->sessioncookiedomain, '|').'$|', $host)) {
 205                          // Invalid domain - it must be end part of host.
 206                          $CFG->sessioncookiedomain = '';
 207                      }
 208                  } else {
 209                      if (!preg_match('|^.*\.'.preg_quote($CFG->sessioncookiedomain, '|').'$|', $host)) {
 210                          // Invalid domain - it must be end part of host.
 211                          $CFG->sessioncookiedomain = '';
 212                      }
 213                  }
 214              }
 215          }
 216  
 217          // Make sure the cookiepath is valid for this wwwroot or autodetect if not specified.
 218          if (!isset($CFG->sessioncookiepath)) {
 219              $CFG->sessioncookiepath = '';
 220          }
 221          if ($CFG->sessioncookiepath !== '/') {
 222              $path = parse_url($CFG->wwwroot, PHP_URL_PATH).'/';
 223              if ($CFG->sessioncookiepath === '') {
 224                  $CFG->sessioncookiepath = $path;
 225              } else {
 226                  if (strpos($path, $CFG->sessioncookiepath) !== 0 or substr($CFG->sessioncookiepath, -1) !== '/') {
 227                      $CFG->sessioncookiepath = $path;
 228                  }
 229              }
 230          }
 231  
 232          // Discard session ID from POST, GET and globals to tighten security,
 233          // this is session fixation prevention.
 234          unset($GLOBALS[$sessionname]);
 235          unset($_GET[$sessionname]);
 236          unset($_POST[$sessionname]);
 237          unset($_REQUEST[$sessionname]);
 238  
 239          // Compatibility hack for non-browser access to our web interface.
 240          if (!empty($_COOKIE[$sessionname]) && $_COOKIE[$sessionname] == "deleted") {
 241              unset($_COOKIE[$sessionname]);
 242          }
 243  
 244          // Set configuration.
 245          session_name($sessionname);
 246          session_set_cookie_params(0, $CFG->sessioncookiepath, $CFG->sessioncookiedomain, $CFG->cookiesecure, $CFG->cookiehttponly);
 247          ini_set('session.use_trans_sid', '0');
 248          ini_set('session.use_only_cookies', '1');
 249          ini_set('session.hash_function', '0');        // For now MD5 - we do not have room for sha-1 in sessions table.
 250          ini_set('session.use_strict_mode', '0');      // We have custom protection in session init.
 251          ini_set('session.serialize_handler', 'php');  // We can move to 'php_serialize' after we require PHP 5.5.4 form Moodle.
 252  
 253          // Moodle does normal session timeouts, this is for leftovers only.
 254          ini_set('session.gc_probability', 1);
 255          ini_set('session.gc_divisor', 1000);
 256          ini_set('session.gc_maxlifetime', 60*60*24*4);
 257      }
 258  
 259      /**
 260       * Initialise $_SESSION, handles google access
 261       * and sets up not-logged-in user properly.
 262       *
 263       * WARNING: $USER and $SESSION are set up later, do not use them yet!
 264       *
 265       * @param bool $newsid is this a new session in first http request?
 266       */
 267      protected static function initialise_user_session($newsid) {
 268          global $CFG, $DB;
 269  
 270          $sid = session_id();
 271          if (!$sid) {
 272              // No session, very weird.
 273              error_log('Missing session ID, session not started!');
 274              self::init_empty_session();
 275              return;
 276          }
 277  
 278          if (!$record = $DB->get_record('sessions', array('sid'=>$sid), 'id, sid, state, userid, lastip, timecreated, timemodified')) {
 279              if (!$newsid) {
 280                  if (!empty($_SESSION['USER']->id)) {
 281                      // This should not happen, just log it, we MUST not produce any output here!
 282                      error_log("Cannot find session record $sid for user ".$_SESSION['USER']->id.", creating new session.");
 283                  }
 284                  // Prevent session fixation attacks.
 285                  session_regenerate_id(true);
 286              }
 287              $_SESSION = array();
 288          }
 289          unset($sid);
 290  
 291          if (isset($_SESSION['USER']->id)) {
 292              if (!empty($_SESSION['USER']->realuser)) {
 293                  $userid = $_SESSION['USER']->realuser;
 294              } else {
 295                  $userid = $_SESSION['USER']->id;
 296              }
 297  
 298              // Verify timeout first.
 299              $maxlifetime = $CFG->sessiontimeout;
 300              $timeout = false;
 301              if (isguestuser($userid) or empty($userid)) {
 302                  // Ignore guest and not-logged in timeouts, there is very little risk here.
 303                  $timeout = false;
 304  
 305              } else if ($record->timemodified < time() - $maxlifetime) {
 306                  $timeout = true;
 307                  $authsequence = get_enabled_auth_plugins(); // Auths, in sequence.
 308                  foreach ($authsequence as $authname) {
 309                      $authplugin = get_auth_plugin($authname);
 310                      if ($authplugin->ignore_timeout_hook($_SESSION['USER'], $record->sid, $record->timecreated, $record->timemodified)) {
 311                          $timeout = false;
 312                          break;
 313                      }
 314                  }
 315              }
 316  
 317              if ($timeout) {
 318                  session_regenerate_id(true);
 319                  $_SESSION = array();
 320                  $DB->delete_records('sessions', array('id'=>$record->id));
 321  
 322              } else {
 323                  // Update session tracking record.
 324  
 325                  $update = new \stdClass();
 326                  $updated = false;
 327  
 328                  if ($record->userid != $userid) {
 329                      $update->userid = $record->userid = $userid;
 330                      $updated = true;
 331                  }
 332  
 333                  $ip = getremoteaddr();
 334                  if ($record->lastip != $ip) {
 335                      $update->lastip = $record->lastip = $ip;
 336                      $updated = true;
 337                  }
 338  
 339                  $updatefreq = empty($CFG->session_update_timemodified_frequency) ? 20 : $CFG->session_update_timemodified_frequency;
 340  
 341                  if ($record->timemodified == $record->timecreated) {
 342                      // Always do first update of existing record.
 343                      $update->timemodified = $record->timemodified = time();
 344                      $updated = true;
 345  
 346                  } else if ($record->timemodified < time() - $updatefreq) {
 347                      // Update the session modified flag only once every 20 seconds.
 348                      $update->timemodified = $record->timemodified = time();
 349                      $updated = true;
 350                  }
 351  
 352                  if ($updated) {
 353                      $update->id = $record->id;
 354                      $DB->update_record('sessions', $update);
 355                  }
 356  
 357                  return;
 358              }
 359          } else {
 360              if ($record) {
 361                  // This happens when people switch session handlers...
 362                  session_regenerate_id(true);
 363                  $_SESSION = array();
 364                  $DB->delete_records('sessions', array('id'=>$record->id));
 365              }
 366          }
 367          unset($record);
 368  
 369          $timedout = false;
 370          if (!isset($_SESSION['SESSION'])) {
 371              $_SESSION['SESSION'] = new \stdClass();
 372              if (!$newsid) {
 373                  $timedout = true;
 374              }
 375          }
 376  
 377          $user = null;
 378  
 379          if (!empty($CFG->opentogoogle)) {
 380              if (is_web_crawler()) {
 381                  $user = guest_user();
 382              }
 383              if (!empty($CFG->guestloginbutton) and !$user and !empty($_SERVER['HTTP_REFERER'])) {
 384                  // Automatically log in users coming from search engine results.
 385                  if (strpos($_SERVER['HTTP_REFERER'], 'google') !== false ) {
 386                      $user = guest_user();
 387                  } else if (strpos($_SERVER['HTTP_REFERER'], 'altavista') !== false ) {
 388                      $user = guest_user();
 389                  }
 390              }
 391          }
 392  
 393          // Setup $USER and insert the session tracking record.
 394          if ($user) {
 395              self::set_user($user);
 396              self::add_session_record($user->id);
 397          } else {
 398              self::init_empty_session();
 399              self::add_session_record(0);
 400          }
 401  
 402          if ($timedout) {
 403              $_SESSION['SESSION']->has_timed_out = true;
 404          }
 405      }
 406  
 407      /**
 408       * Insert new empty session record.
 409       * @param int $userid
 410       * @return \stdClass the new record
 411       */
 412      protected static function add_session_record($userid) {
 413          global $DB;
 414          $record = new \stdClass();
 415          $record->state       = 0;
 416          $record->sid         = session_id();
 417          $record->sessdata    = null;
 418          $record->userid      = $userid;
 419          $record->timecreated = $record->timemodified = time();
 420          $record->firstip     = $record->lastip = getremoteaddr();
 421  
 422          $record->id = $DB->insert_record('sessions', $record);
 423  
 424          return $record;
 425      }
 426  
 427      /**
 428       * Do various session security checks.
 429       *
 430       * WARNING: $USER and $SESSION are set up later, do not use them yet!
 431       */
 432      protected static function check_security() {
 433          global $CFG;
 434  
 435          if (!empty($_SESSION['USER']->id) and !empty($CFG->tracksessionip)) {
 436              // Make sure current IP matches the one for this session.
 437              $remoteaddr = getremoteaddr();
 438  
 439              if (empty($_SESSION['USER']->sessionip)) {
 440                  $_SESSION['USER']->sessionip = $remoteaddr;
 441              }
 442  
 443              if ($_SESSION['USER']->sessionip != $remoteaddr) {
 444                  // This is a security feature - terminate the session in case of any doubt.
 445                  self::terminate_current();
 446                  throw new exception('sessionipnomatch2', 'error');
 447              }
 448          }
 449      }
 450  
 451      /**
 452       * Login user, to be called from complete_user_login() only.
 453       * @param \stdClass $user
 454       */
 455      public static function login_user(\stdClass $user) {
 456          global $DB;
 457  
 458          // Regenerate session id and delete old session,
 459          // this helps prevent session fixation attacks from the same domain.
 460  
 461          $sid = session_id();
 462          session_regenerate_id(true);
 463          $DB->delete_records('sessions', array('sid'=>$sid));
 464          self::add_session_record($user->id);
 465  
 466          // Let enrol plugins deal with new enrolments if necessary.
 467          enrol_check_plugins($user);
 468  
 469          // Setup $USER object.
 470          self::set_user($user);
 471      }
 472  
 473      /**
 474       * Terminate current user session.
 475       * @return void
 476       */
 477      public static function terminate_current() {
 478          global $DB;
 479  
 480          if (!self::$sessionactive) {
 481              self::init_empty_session();
 482              self::$sessionactive = false;
 483              return;
 484          }
 485  
 486          try {
 487              $DB->delete_records('external_tokens', array('sid'=>session_id(), 'tokentype'=>EXTERNAL_TOKEN_EMBEDDED));
 488          } catch (\Exception $ignored) {
 489              // Probably install/upgrade - ignore this problem.
 490          }
 491  
 492          // Initialize variable to pass-by-reference to headers_sent(&$file, &$line).
 493          $file = null;
 494          $line = null;
 495          if (headers_sent($file, $line)) {
 496              error_log('Cannot terminate session properly - headers were already sent in file: '.$file.' on line '.$line);
 497          }
 498  
 499          // Write new empty session and make sure the old one is deleted.
 500          $sid = session_id();
 501          session_regenerate_id(true);
 502          $DB->delete_records('sessions', array('sid'=>$sid));
 503          self::init_empty_session();
 504          self::add_session_record($_SESSION['USER']->id); // Do not use $USER here because it may not be set up yet.
 505          session_write_close();
 506          self::$sessionactive = false;
 507      }
 508  
 509      /**
 510       * No more changes in session expected.
 511       * Unblocks the sessions, other scripts may start executing in parallel.
 512       */
 513      public static function write_close() {
 514          if (self::$sessionactive) {
 515              session_write_close();
 516          } else {
 517              if (session_id()) {
 518                  @session_write_close();
 519              }
 520          }
 521          self::$sessionactive = false;
 522      }
 523  
 524      /**
 525       * Does the PHP session with given id exist?
 526       *
 527       * The session must exist both in session table and actual
 528       * session backend and the session must not be timed out.
 529       *
 530       * Timeout evaluation is simplified, the auth hooks are not executed.
 531       *
 532       * @param string $sid
 533       * @return bool
 534       */
 535      public static function session_exists($sid) {
 536          global $DB, $CFG;
 537  
 538          if (empty($CFG->version)) {
 539              // Not installed yet, do not try to access database.
 540              return false;
 541          }
 542  
 543          // Note: add sessions->state checking here if it gets implemented.
 544          if (!$record = $DB->get_record('sessions', array('sid' => $sid), 'id, userid, timemodified')) {
 545              return false;
 546          }
 547  
 548          if (empty($record->userid) or isguestuser($record->userid)) {
 549              // Ignore guest and not-logged-in timeouts, there is very little risk here.
 550          } else if ($record->timemodified < time() - $CFG->sessiontimeout) {
 551              return false;
 552          }
 553  
 554          // There is no need the existence of handler storage in public API.
 555          self::load_handler();
 556          return self::$handler->session_exists($sid);
 557      }
 558  
 559      /**
 560       * Fake last access for given session, this prevents session timeout.
 561       * @param string $sid
 562       */
 563      public static function touch_session($sid) {
 564          global $DB;
 565  
 566          // Timeouts depend on core sessions table only, no need to update anything in external stores.
 567  
 568          $sql = "UPDATE {sessions} SET timemodified = :now WHERE sid = :sid";
 569          $DB->execute($sql, array('now'=>time(), 'sid'=>$sid));
 570      }
 571  
 572      /**
 573       * Terminate all sessions unconditionally.
 574       */
 575      public static function kill_all_sessions() {
 576          global $DB;
 577  
 578          self::terminate_current();
 579  
 580          self::load_handler();
 581          self::$handler->kill_all_sessions();
 582  
 583          try {
 584              $DB->delete_records('sessions');
 585          } catch (\dml_exception $ignored) {
 586              // Do not show any warnings - might be during upgrade/installation.
 587          }
 588      }
 589  
 590      /**
 591       * Terminate give session unconditionally.
 592       * @param string $sid
 593       */
 594      public static function kill_session($sid) {
 595          global $DB;
 596  
 597          self::load_handler();
 598  
 599          if ($sid === session_id()) {
 600              self::write_close();
 601          }
 602  
 603          self::$handler->kill_session($sid);
 604  
 605          $DB->delete_records('sessions', array('sid'=>$sid));
 606      }
 607  
 608      /**
 609       * Terminate all sessions of given user unconditionally.
 610       * @param int $userid
 611       */
 612      public static function kill_user_sessions($userid) {
 613          global $DB;
 614  
 615          $sessions = $DB->get_records('sessions', array('userid'=>$userid), 'id DESC', 'id, sid');
 616          foreach ($sessions as $session) {
 617              self::kill_session($session->sid);
 618          }
 619      }
 620  
 621      /**
 622       * Set current user.
 623       *
 624       * @param \stdClass $user record
 625       */
 626      public static function set_user(\stdClass $user) {
 627          $GLOBALS['USER'] = $user;
 628          unset($GLOBALS['USER']->description); // Conserve memory.
 629          unset($GLOBALS['USER']->password);    // Improve security.
 630          if (isset($GLOBALS['USER']->lang)) {
 631              // Make sure it is a valid lang pack name.
 632              $GLOBALS['USER']->lang = clean_param($GLOBALS['USER']->lang, PARAM_LANG);
 633          }
 634  
 635          // Relink session with global $USER just in case it got unlinked somehow.
 636          $_SESSION['USER'] =& $GLOBALS['USER'];
 637  
 638          // Init session key.
 639          sesskey();
 640      }
 641  
 642      /**
 643       * Periodic timed-out session cleanup.
 644       */
 645      public static function gc() {
 646          global $CFG, $DB;
 647  
 648          // This may take a long time...
 649          \core_php_time_limit::raise();
 650  
 651          $maxlifetime = $CFG->sessiontimeout;
 652  
 653          try {
 654              // Kill all sessions of deleted and suspended users without any hesitation.
 655              $rs = $DB->get_recordset_select('sessions', "userid IN (SELECT id FROM {user} WHERE deleted <> 0 OR suspended <> 0)", array(), 'id DESC', 'id, sid');
 656              foreach ($rs as $session) {
 657                  self::kill_session($session->sid);
 658              }
 659              $rs->close();
 660  
 661              // Kill sessions of users with disabled plugins.
 662              $auth_sequence = get_enabled_auth_plugins(true);
 663              $auth_sequence = array_flip($auth_sequence);
 664              unset($auth_sequence['nologin']); // No login means user cannot login.
 665              $auth_sequence = array_flip($auth_sequence);
 666  
 667              list($notplugins, $params) = $DB->get_in_or_equal($auth_sequence, SQL_PARAMS_QM, '', false);
 668              $rs = $DB->get_recordset_select('sessions', "userid IN (SELECT id FROM {user} WHERE auth $notplugins)", $params, 'id DESC', 'id, sid');
 669              foreach ($rs as $session) {
 670                  self::kill_session($session->sid);
 671              }
 672              $rs->close();
 673  
 674              // Now get a list of time-out candidates - real users only.
 675              $sql = "SELECT u.*, s.sid, s.timecreated AS s_timecreated, s.timemodified AS s_timemodified
 676                        FROM {user} u
 677                        JOIN {sessions} s ON s.userid = u.id
 678                       WHERE s.timemodified < :purgebefore AND u.id <> :guestid";
 679              $params = array('purgebefore' => (time() - $maxlifetime), 'guestid'=>$CFG->siteguest);
 680  
 681              $authplugins = array();
 682              foreach ($auth_sequence as $authname) {
 683                  $authplugins[$authname] = get_auth_plugin($authname);
 684              }
 685              $rs = $DB->get_recordset_sql($sql, $params);
 686              foreach ($rs as $user) {
 687                  foreach ($authplugins as $authplugin) {
 688                      /** @var \auth_plugin_base $authplugin*/
 689                      if ($authplugin->ignore_timeout_hook($user, $user->sid, $user->s_timecreated, $user->s_timemodified)) {
 690                          continue;
 691                      }
 692                  }
 693                  self::kill_session($user->sid);
 694              }
 695              $rs->close();
 696  
 697              // Delete expired sessions for guest user account, give them larger timeout, there is no security risk here.
 698              $params = array('purgebefore' => (time() - ($maxlifetime * 5)), 'guestid'=>$CFG->siteguest);
 699              $rs = $DB->get_recordset_select('sessions', 'userid = :guestid AND timemodified < :purgebefore', $params, 'id DESC', 'id, sid');
 700              foreach ($rs as $session) {
 701                  self::kill_session($session->sid);
 702              }
 703              $rs->close();
 704  
 705              // Delete expired sessions for userid = 0 (not logged in), better kill them asap to release memory.
 706              $params = array('purgebefore' => (time() - $maxlifetime));
 707              $rs = $DB->get_recordset_select('sessions', 'userid = 0 AND timemodified < :purgebefore', $params, 'id DESC', 'id, sid');
 708              foreach ($rs as $session) {
 709                  self::kill_session($session->sid);
 710              }
 711              $rs->close();
 712  
 713              // Cleanup letfovers from the first browser access because it may set multiple cookies and then use only one.
 714              $params = array('purgebefore' => (time() - 60*3));
 715              $rs = $DB->get_recordset_select('sessions', 'userid = 0 AND timemodified = timecreated AND timemodified < :purgebefore', $params, 'id ASC', 'id, sid');
 716              foreach ($rs as $session) {
 717                  self::kill_session($session->sid);
 718              }
 719              $rs->close();
 720  
 721          } catch (\Exception $ex) {
 722              debugging('Error gc-ing sessions: '.$ex->getMessage(), DEBUG_NORMAL, $ex->getTrace());
 723          }
 724      }
 725  
 726      /**
 727       * Is current $USER logged-in-as somebody else?
 728       * @return bool
 729       */
 730      public static function is_loggedinas() {
 731          return !empty($GLOBALS['USER']->realuser);
 732      }
 733  
 734      /**
 735       * Returns the $USER object ignoring current login-as session
 736       * @return \stdClass user object
 737       */
 738      public static function get_realuser() {
 739          if (self::is_loggedinas()) {
 740              return $_SESSION['REALUSER'];
 741          } else {
 742              return $GLOBALS['USER'];
 743          }
 744      }
 745  
 746      /**
 747       * Login as another user - no security checks here.
 748       * @param int $userid
 749       * @param \context $context
 750       * @return void
 751       */
 752      public static function loginas($userid, \context $context) {
 753          global $USER;
 754  
 755          if (self::is_loggedinas()) {
 756              return;
 757          }
 758  
 759          // Switch to fresh new $_SESSION.
 760          $_SESSION = array();
 761          $_SESSION['REALSESSION'] = clone($GLOBALS['SESSION']);
 762          $GLOBALS['SESSION'] = new \stdClass();
 763          $_SESSION['SESSION'] =& $GLOBALS['SESSION'];
 764  
 765          // Create the new $USER object with all details and reload needed capabilities.
 766          $_SESSION['REALUSER'] = clone($GLOBALS['USER']);
 767          $user = get_complete_user_data('id', $userid);
 768          $user->realuser       = $_SESSION['REALUSER']->id;
 769          $user->loginascontext = $context;
 770  
 771          // Let enrol plugins deal with new enrolments if necessary.
 772          enrol_check_plugins($user);
 773  
 774          // Create event before $USER is updated.
 775          $event = \core\event\user_loggedinas::create(
 776              array(
 777                  'objectid' => $USER->id,
 778                  'context' => $context,
 779                  'relateduserid' => $userid,
 780                  'other' => array(
 781                      'originalusername' => fullname($USER, true),
 782                      'loggedinasusername' => fullname($user, true)
 783                  )
 784              )
 785          );
 786          // Set up global $USER.
 787          \core\session\manager::set_user($user);
 788          $event->trigger();
 789      }
 790  }


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1