[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |