3 // This file is part of Moodle - http://moodle.org/
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
21 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
22 * @copyright 2008, 2009 Petr Skoda {@link http://skodak.org}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') ||
die();
29 * Factory method returning moodle_session object.
30 * @return moodle_session
32 function session_get_instance() {
35 static $session = null;
37 if (is_null($session)) {
38 if (empty($CFG->sessiontimeout
)) {
39 $CFG->sessiontimeout
= 7200;
42 if (defined('SESSION_CUSTOM_CLASS')) {
43 // this is a hook for webservices, key based login, etc.
44 if (defined('SESSION_CUSTOM_FILE')) {
45 require_once($CFG->dirroot
.SESSION_CUSTOM_FILE
);
47 $session_class = SESSION_CUSTOM_CLASS
;
48 $session = new $session_class();
50 } else if ((!isset($CFG->dbsessions
) or $CFG->dbsessions
) and $DB->session_lock_supported()) {
51 // default recommended session type
52 $session = new database_session();
55 // legacy limited file based storage - some features and auth plugins will not work, sorry
56 $session = new legacy_file_session();
64 * Moodle session abstraction
68 * @copyright 2008 Petr Skoda {@link http://skodak.org}
69 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
71 interface moodle_session
{
73 * Terminate current session
76 public function terminate_current();
79 * No more changes in session expected.
80 * Unblocks the sessions, other scripts may start executing in parallel.
83 public function write_close();
86 * Check for existing session with id $sid
87 * @param unknown_type $sid
88 * @return boolean true if session found.
90 public function session_exists($sid);
94 * Class handling all session and cookies related stuff.
98 * @copyright 2009 Petr Skoda {@link http://skodak.org}
99 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
101 abstract class session_stub
implements moodle_session
{
102 protected $justloggedout;
104 public function __construct() {
107 if (NO_MOODLE_COOKIES
) {
108 // session not used at all
110 $_SESSION['SESSION'] = new stdClass();
111 $_SESSION['USER'] = new stdClass();
114 $this->prepare_cookies();
115 $this->init_session_storage();
117 $newsession = empty($_COOKIE['MoodleSession'.$CFG->sessioncookie
]);
119 ini_set('session.use_trans_sid', '0');
121 session_name('MoodleSession'.$CFG->sessioncookie
);
122 session_set_cookie_params(0, $CFG->sessioncookiepath
, $CFG->sessioncookiedomain
, $CFG->cookiesecure
, $CFG->cookiehttponly
);
124 if (!isset($_SESSION['SESSION'])) {
125 $_SESSION['SESSION'] = new stdClass();
126 if (!$newsession and !$this->justloggedout
) {
127 $_SESSION['SESSION']->has_timed_out
= true;
130 if (!isset($_SESSION['USER'])) {
131 $_SESSION['USER'] = new stdClass();
135 $this->check_user_initialised();
137 $this->check_security();
141 * Terminates active moodle session
143 public function terminate_current() {
144 global $CFG, $SESSION, $USER, $DB;
147 $DB->delete_records('external_tokens', array('sid'=>session_id(), 'tokentype'=>EXTERNAL_TOKEN_EMBEDDED
));
148 } catch (Exception
$ignored) {
149 // probably install/upgrade - ignore this problem
152 if (NO_MOODLE_COOKIES
) {
156 // Initialize variable to pass-by-reference to headers_sent(&$file, &$line)
158 $_SESSION['SESSION'] = new stdClass();
159 $_SESSION['USER'] = new stdClass();
160 $_SESSION['USER']->id
= 0;
161 if (isset($CFG->mnet_localhost_id
)) {
162 $_SESSION['USER']->mnethostid
= $CFG->mnet_localhost_id
;
164 $SESSION = $_SESSION['SESSION']; // this may not work properly
165 $USER = $_SESSION['USER']; // this may not work properly
169 if (headers_sent($file, $line)) {
170 error_log('Can not terminate session properly - headers were already sent in file: '.$file.' on line '.$line);
173 // now let's try to get a new session id and delete the old one
174 $this->justloggedout
= true;
175 session_regenerate_id(true);
176 $this->justloggedout
= false;
178 // write the new session
179 session_write_close();
183 * No more changes in session expected.
184 * Unblocks the sessions, other scripts may start executing in parallel.
187 public function write_close() {
188 if (NO_MOODLE_COOKIES
) {
192 session_write_close();
196 * Initialise $USER object, handles google access
197 * and sets up not logged in user properly.
201 protected function check_user_initialised() {
204 if (isset($_SESSION['USER']->id
)) {
205 // already set up $USER
211 if (!empty($CFG->opentogoogle
) and !NO_MOODLE_COOKIES
) {
212 if (is_web_crawler()) {
213 $user = guest_user();
215 if (!empty($CFG->guestloginbutton
) and !$user and !empty($_SERVER['HTTP_REFERER'])) {
216 // automaticaly log in users coming from search engine results
217 if (strpos($_SERVER['HTTP_REFERER'], 'google') !== false ) {
218 $user = guest_user();
219 } else if (strpos($_SERVER['HTTP_REFERER'], 'altavista') !== false ) {
220 $user = guest_user();
226 $user = new stdClass();
227 $user->id
= 0; // to enable proper function of $CFG->notloggedinroleid hack
228 if (isset($CFG->mnet_localhost_id
)) {
229 $user->mnethostid
= $CFG->mnet_localhost_id
;
231 $user->mnethostid
= 1;
234 session_set_user($user);
238 * Does various session security checks
241 protected function check_security() {
244 if (NO_MOODLE_COOKIES
) {
248 if (!empty($_SESSION['USER']->id
) and !empty($CFG->tracksessionip
)) {
249 /// Make sure current IP matches the one for this session
250 $remoteaddr = getremoteaddr();
252 if (empty($_SESSION['USER']->sessionip
)) {
253 $_SESSION['USER']->sessionip
= $remoteaddr;
256 if ($_SESSION['USER']->sessionip
!= $remoteaddr) {
257 // this is a security feature - terminate the session in case of any doubt
258 $this->terminate_current();
259 print_error('sessionipnomatch2', 'error');
265 * Prepare cookies and various system settings
267 protected function prepare_cookies() {
270 if (!isset($CFG->cookiesecure
) or (strpos($CFG->wwwroot
, 'https://') !== 0 and empty($CFG->sslproxy
))) {
271 $CFG->cookiesecure
= 0;
274 if (!isset($CFG->cookiehttponly
)) {
275 $CFG->cookiehttponly
= 0;
278 /// Set sessioncookie and sessioncookiepath variable if it isn't already
279 if (!isset($CFG->sessioncookie
)) {
280 $CFG->sessioncookie
= '';
283 // make sure cookie domain makes sense for this wwwroot
284 if (!isset($CFG->sessioncookiedomain
)) {
285 $CFG->sessioncookiedomain
= '';
286 } else if ($CFG->sessioncookiedomain
!== '') {
287 $host = parse_url($CFG->wwwroot
, PHP_URL_HOST
);
288 if ($CFG->sessioncookiedomain
!== $host) {
289 if (substr($CFG->sessioncookiedomain
, 0, 1) === '.') {
290 if (!preg_match('|^.*'.preg_quote($CFG->sessioncookiedomain
, '|').'$|', $host)) {
291 // invalid domain - it must be end part of host
292 $CFG->sessioncookiedomain
= '';
295 if (!preg_match('|^.*\.'.preg_quote($CFG->sessioncookiedomain
, '|').'$|', $host)) {
296 // invalid domain - it must be end part of host
297 $CFG->sessioncookiedomain
= '';
303 // make sure the cookiepath is valid for this wwwroot or autodetect if not specified
304 if (!isset($CFG->sessioncookiepath
)) {
305 $CFG->sessioncookiepath
= '';
307 if ($CFG->sessioncookiepath
!== '/') {
308 $path = parse_url($CFG->wwwroot
, PHP_URL_PATH
).'/';
309 if ($CFG->sessioncookiepath
=== '') {
310 $CFG->sessioncookiepath
= $path;
312 if (strpos($path, $CFG->sessioncookiepath
) !== 0 or substr($CFG->sessioncookiepath
, -1) !== '/') {
313 $CFG->sessioncookiepath
= $path;
318 //discard session ID from POST, GET and globals to tighten security,
319 //this is session fixation prevention
320 unset($
{'MoodleSession'.$CFG->sessioncookie
});
321 unset($_GET['MoodleSession'.$CFG->sessioncookie
]);
322 unset($_POST['MoodleSession'.$CFG->sessioncookie
]);
323 unset($_REQUEST['MoodleSession'.$CFG->sessioncookie
]);
325 //compatibility hack for Moodle Cron, cookies not deleted, but set to "deleted" - should not be needed with NO_MOODLE_COOKIES in cron.php now
326 if (!empty($_COOKIE['MoodleSession'.$CFG->sessioncookie
]) && $_COOKIE['MoodleSession'.$CFG->sessioncookie
] == "deleted") {
327 unset($_COOKIE['MoodleSession'.$CFG->sessioncookie
]);
332 * Inits session storage.
334 protected abstract function init_session_storage();
338 * Legacy moodle sessions stored in files, not recommended any more.
341 * @subpackage session
342 * @copyright 2009 Petr Skoda {@link http://skodak.org}
343 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
345 class legacy_file_session
extends session_stub
{
346 protected function init_session_storage() {
349 ini_set('session.save_handler', 'files');
351 // Some distros disable GC by setting probability to 0
352 // overriding the PHP default of 1
353 // (gc_probability is divided by gc_divisor, which defaults to 1000)
354 if (ini_get('session.gc_probability') == 0) {
355 ini_set('session.gc_probability', 1);
358 ini_set('session.gc_maxlifetime', $CFG->sessiontimeout
);
360 // make sure sessions dir exists and is writable, throws exception if not
361 make_upload_directory('sessions');
363 // Need to disable debugging since disk_free_space()
364 // will fail on very large partitions (see MDL-19222)
365 $freespace = @disk_free_space
($CFG->dataroot
.'/sessions');
366 if (!($freespace > 2048) and $freespace !== false) {
367 print_error('sessiondiskfull', 'error');
369 ini_set('session.save_path', $CFG->dataroot
.'/sessions');
372 * Check for existing session with id $sid
373 * @param unknown_type $sid
374 * @return boolean true if session found.
376 public function session_exists($sid){
379 $sid = clean_param($sid, PARAM_FILE
);
380 $sessionfile = "$CFG->dataroot/sessions/sess_$sid";
381 return file_exists($sessionfile);
386 * Recommended moodle session storage.
389 * @subpackage session
390 * @copyright 2009 Petr Skoda {@link http://skodak.org}
391 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
393 class database_session
extends session_stub
{
394 protected $record = null;
395 protected $database = null;
397 public function __construct() {
399 $this->database
= $DB;
400 parent
::__construct();
402 if (!empty($this->record
->state
)) {
403 // something is very wrong
404 session_kill($this->record
->sid
);
406 if ($this->record
->state
== 9) {
407 print_error('dbsessionmysqlpacketsize', 'error');
412 public function session_exists($sid){
415 $sql = "SELECT * FROM {sessions} WHERE timemodified < ? AND sid=? AND state=?";
416 $params = array(time() +
$CFG->sessiontimeout
, $sid, 0);
418 return $this->database
->record_exists_sql($sql, $params);
419 } catch (dml_exception
$ex) {
420 error_log('Error checking existance of database session');
425 protected function init_session_storage() {
428 // gc only from CRON - individual user timeouts now checked during each access
429 ini_set('session.gc_probability', 0);
431 ini_set('session.gc_maxlifetime', $CFG->sessiontimeout
);
433 $result = session_set_save_handler(array($this, 'handler_open'),
434 array($this, 'handler_close'),
435 array($this, 'handler_read'),
436 array($this, 'handler_write'),
437 array($this, 'handler_destroy'),
438 array($this, 'handler_gc'));
440 print_error('dbsessionhandlerproblem', 'error');
444 public function handler_open($save_path, $session_name) {
448 public function handler_close() {
449 if (isset($this->record
->id
)) {
450 $this->database
->release_session_lock($this->record
->id
);
452 $this->record
= null;
456 public function handler_read($sid) {
459 if ($this->record
and $this->record
->sid
!= $sid) {
460 error_log('Weird error reading database session - mismatched sid');
465 if ($record = $this->database
->get_record('sessions', array('sid'=>$sid))) {
466 $this->database
->get_session_lock($record->id
);
469 $record = new stdClass();
472 $record->sessdata
= null;
474 $record->timecreated
= $record->timemodified
= time();
475 $record->firstip
= $record->lastip
= getremoteaddr();
476 $record->id
= $this->database
->insert_record_raw('sessions', $record);
478 $this->database
->get_session_lock($record->id
);
480 } catch (dml_exception
$ex) {
481 error_log('Can not read or insert database sessions');
486 if ($record->timemodified +
$CFG->sessiontimeout
< time()) {
487 $ignoretimeout = false;
488 if (!empty($record->userid
)) { // skips not logged in
489 if ($user = $this->database
->get_record('user', array('id'=>$record->userid
))) {
490 if (!isguestuser($user)) {
491 $authsequence = get_enabled_auth_plugins(); // auths, in sequence
492 foreach($authsequence as $authname) {
493 $authplugin = get_auth_plugin($authname);
494 if ($authplugin->ignore_timeout_hook($user, $record->sid
, $record->timecreated
, $record->timemodified
)) {
495 $ignoretimeout = true;
502 if ($ignoretimeout) {
504 $record->timemodified
= time();
506 $this->database
->update_record('sessions', $record);
507 } catch (dml_exception
$ex) {
508 error_log('Can not refresh database session');
514 $record->sessdata
= null;
516 $record->timecreated
= $record->timemodified
= time();
517 $record->firstip
= $record->lastip
= getremoteaddr();
519 $this->database
->update_record('sessions', $record);
520 } catch (dml_exception
$ex) {
521 error_log('Can not time out database session');
527 $data = is_null($record->sessdata
) ?
'' : base64_decode($record->sessdata
);
529 unset($record->sessdata
); // conserve memory
530 $this->record
= $record;
535 public function handler_write($sid, $session_data) {
538 // TODO: MDL-20625 we need to rollback all active transactions and log error if any open needed
541 if (!empty($USER->realuser
)) {
542 $userid = $USER->realuser
;
543 } else if (!empty($USER->id
)) {
547 if (isset($this->record
->id
)) {
548 $record = new stdClass();
550 $record->sid
= $sid; // might be regenerating sid
551 $this->record
->sessdata
= base64_encode($session_data); // there might be some binary mess :-(
552 $this->record
->userid
= $userid;
553 $this->record
->timemodified
= time();
554 $this->record
->lastip
= getremoteaddr();
556 // TODO: verify session changed before doing update,
557 // also make sure the timemodified field is changed only every 10s if nothing else changes MDL-20462
560 $this->database
->update_record_raw('sessions', $this->record
);
561 } catch (dml_exception
$ex) {
562 if ($this->database
->get_dbfamily() === 'mysql') {
564 $this->database
->set_field('sessions', 'state', 9, array('id'=>$this->record
->id
));
565 } catch (Exception
$ignored) {
568 error_log('Can not write database session - please verify max_allowed_packet is at least 4M!');
570 error_log('Can not write database session');
575 // session already destroyed
576 $record = new stdClass();
579 $record->sessdata
= base64_encode($session_data); // there might be some binary mess :-(
580 $record->userid
= $userid;
581 $record->timecreated
= $record->timemodified
= time();
582 $record->firstip
= $record->lastip
= getremoteaddr();
583 $record->id
= $this->database
->insert_record_raw('sessions', $record);
584 $this->record
= $record;
587 $this->database
->get_session_lock($this->record
->id
);
588 } catch (dml_exception
$ex) {
589 error_log('Can not write new database session');
596 public function handler_destroy($sid) {
599 if (isset($this->record
->id
) and $this->record
->sid
=== $sid) {
600 $this->database
->release_session_lock($this->record
->id
);
601 $this->record
= null;
607 public function handler_gc($ignored_maxlifetime) {
614 * returns true if legacy session used.
615 * @return bool true if legacy(==file) based session used
617 function session_is_legacy() {
619 return ((isset($CFG->dbsessions
) and !$CFG->dbsessions
) or !$DB->session_lock_supported());
623 * Terminates all sessions, auth hooks are not executed.
624 * Useful in upgrade scripts.
626 function session_kill_all() {
629 // always check db table - custom session classes use sessions table
631 $DB->delete_records('sessions');
632 } catch (dml_exception
$ignored) {
633 // do not show any warnings - might be during upgrade/installation
636 if (session_is_legacy()) {
637 $sessiondir = "$CFG->dataroot/sessions";
638 if (is_dir($sessiondir)) {
639 foreach (glob("$sessiondir/sess_*") as $filename) {
647 * Mark session as accessed, prevents timeouts.
650 function session_touch($sid) {
653 // always check db table - custom session classes use sessions table
655 $sql = "UPDATE {sessions} SET timemodified=? WHERE sid=?";
656 $params = array(time(), $sid);
657 $DB->execute($sql, $params);
658 } catch (dml_exception
$ignored) {
659 // do not show any warnings - might be during upgrade/installation
662 if (session_is_legacy()) {
663 $sid = clean_param($sid, PARAM_FILE
);
664 $sessionfile = clean_param("$CFG->dataroot/sessions/sess_$sid", PARAM_FILE
);
665 if (file_exists($sessionfile)) {
666 // if the file is locked it means that it will be updated anyway
667 @touch
($sessionfile);
673 * Terminates one sessions, auth hooks are not executed.
675 * @param string $sid session id
677 function session_kill($sid) {
680 // always check db table - custom session classes use sessions table
682 $DB->delete_records('sessions', array('sid'=>$sid));
683 } catch (dml_exception
$ignored) {
684 // do not show any warnings - might be during upgrade/installation
687 if (session_is_legacy()) {
688 $sid = clean_param($sid, PARAM_FILE
);
689 $sessionfile = "$CFG->dataroot/sessions/sess_$sid";
690 if (file_exists($sessionfile)) {
691 @unlink
($sessionfile);
697 * Terminates all sessions of one user, auth hooks are not executed.
698 * NOTE: This can not work for file based sessions!
700 * @param int $userid user id
702 function session_kill_user($userid) {
705 // always check db table - custom session classes use sessions table
707 $DB->delete_records('sessions', array('userid'=>$userid));
708 } catch (dml_exception
$ignored) {
709 // do not show any warnings - might be during upgrade/installation
712 if (session_is_legacy()) {
718 * Session garbage collection
719 * - verify timeout for all users
720 * - kill sessions of all deleted users
721 * - kill sessions of users with disabled plugins or 'nologin' plugin
723 * NOTE: this can not work when legacy file sessions used!
725 function session_gc() {
728 $maxlifetime = $CFG->sessiontimeout
;
731 /// kill all sessions of deleted users
732 $DB->delete_records_select('sessions', "userid IN (SELECT id FROM {user} WHERE deleted <> 0)");
734 /// kill sessions of users with disabled plugins
735 $auth_sequence = get_enabled_auth_plugins(true);
736 $auth_sequence = array_flip($auth_sequence);
737 unset($auth_sequence['nologin']); // no login allowed
738 $auth_sequence = array_flip($auth_sequence);
740 list($notplugins, $params) = $DB->get_in_or_equal($auth_sequence, SQL_PARAMS_QM
, '', false);
741 $DB->delete_records_select('sessions', "userid IN (SELECT id FROM {user} WHERE auth $notplugins)", $params);
743 /// now get a list of time-out candidates
744 $sql = "SELECT u.*, s.sid, s.timecreated AS s_timecreated, s.timemodified AS s_timemodified
746 JOIN {sessions} s ON s.userid = u.id
747 WHERE s.timemodified + ? < ? AND u.id <> ?";
748 $params = array($maxlifetime, time(), $CFG->siteguest
);
750 $authplugins = array();
751 foreach($auth_sequence as $authname) {
752 $authplugins[$authname] = get_auth_plugin($authname);
754 $rs = $DB->get_recordset_sql($sql, $params);
755 foreach ($rs as $user) {
756 foreach ($authplugins as $authplugin) {
757 if ($authplugin->ignore_timeout_hook($user, $user->sid
, $user->s_timecreated
, $user->s_timemodified
)) {
761 $DB->delete_records('sessions', array('sid'=>$user->sid
));
765 $purgebefore = time() - $maxlifetime;
766 // delete expired sessions for guest user account
767 $DB->delete_records_select('sessions', 'userid = ? AND timemodified < ?', array($CFG->siteguest
, $purgebefore));
768 // delete expired sessions for userid = 0 (not logged in)
769 $DB->delete_records_select('sessions', 'userid = 0 AND timemodified < ?', array($purgebefore));
770 } catch (dml_exception
$ex) {
771 error_log('Error gc-ing sessions');
776 * Makes sure that $USER->sesskey exists, if $USER itself exists. It sets a new sesskey
777 * if one does not already exist, but does not overwrite existing sesskeys. Returns the
778 * sesskey string if $USER exists, or boolean false if not.
784 // note: do not use $USER because it may not be initialised yet
785 if (empty($_SESSION['USER']->sesskey
)) {
786 $_SESSION['USER']->sesskey
= random_string(10);
789 return $_SESSION['USER']->sesskey
;
794 * Check the sesskey and return true of false for whether it is valid.
795 * (You might like to imagine this function is called sesskey_is_valid().)
797 * Every script that lets the user perform a significant action (that is,
798 * changes data in the database) should check the sesskey before doing the action.
799 * Depending on your code flow, you may want to use the {@link require_sesskey()}
802 * @param string $sesskey The sesskey value to check (optional). Normally leave this blank
803 * and this function will do required_param('sesskey', ...).
804 * @return bool whether the sesskey sent in the request matches the one stored in the session.
806 function confirm_sesskey($sesskey=NULL) {
809 if (!empty($USER->ignoresesskey
)) {
813 if (empty($sesskey)) {
814 $sesskey = required_param('sesskey', PARAM_RAW
); // Check script parameters
817 return (sesskey() === $sesskey);
821 * Check the session key using {@link confirm_sesskey()},
822 * and cause a fatal error if it does not match.
824 function require_sesskey() {
825 if (!confirm_sesskey()) {
826 print_error('invalidsesskey');
831 * Sets a moodle cookie with a weakly encrypted username
833 * @param string $username to encrypt and place in a cookie, '' means delete current cookie
836 function set_moodle_cookie($username) {
839 if (NO_MOODLE_COOKIES
) {
843 if (empty($CFG->rememberusername
)) {
844 // erase current and do not store permanent cookies
848 if ($username === 'guest') {
849 // keep previous cookie in case of guest account login
853 $cookiename = 'MOODLEID_'.$CFG->sessioncookie
;
856 setcookie($cookiename, '', time() - HOURSECS
, $CFG->sessioncookiepath
, $CFG->sessioncookiedomain
, $CFG->cookiesecure
, $CFG->cookiehttponly
);
858 if ($username !== '') {
859 // set username cookie for 60 days
860 setcookie($cookiename, rc4encrypt($username), time()+
(DAYSECS
*60), $CFG->sessioncookiepath
, $CFG->sessioncookiedomain
, $CFG->cookiesecure
, $CFG->cookiehttponly
);
865 * Gets a moodle cookie with a weakly encrypted username
867 * @return string username
869 function get_moodle_cookie() {
872 if (NO_MOODLE_COOKIES
) {
876 if (empty($CFG->rememberusername
)) {
880 $cookiename = 'MOODLEID_'.$CFG->sessioncookie
;
882 if (empty($_COOKIE[$cookiename])) {
885 $username = rc4decrypt($_COOKIE[$cookiename]);
886 if ($username === 'guest' or $username === 'nobody') {
887 // backwards compatibility - we do not set these cookies any more
896 * Setup $USER object - called during login, loginas, etc.
898 * Call sync_user_enrolments() manually after log-in, or log-in-as.
900 * @param stdClass $user full user record object
903 function session_set_user($user) {
904 $_SESSION['USER'] = $user;
905 unset($_SESSION['USER']->description
); // conserve memory
906 sesskey(); // init session key
910 * Is current $USER logged-in-as somebody else?
913 function session_is_loggedinas() {
914 return !empty($_SESSION['USER']->realuser
);
918 * Returns the $USER object ignoring current login-as session
919 * @return stdClass user object
921 function session_get_realuser() {
922 if (session_is_loggedinas()) {
923 return $_SESSION['REALUSER'];
925 return $_SESSION['USER'];
930 * Login as another user - no security checks here.
932 * @param stdClass $context
935 function session_loginas($userid, $context) {
936 if (session_is_loggedinas()) {
940 // switch to fresh new $SESSION
941 $_SESSION['REALSESSION'] = $_SESSION['SESSION'];
942 $_SESSION['SESSION'] = new stdClass();
944 /// Create the new $USER object with all details and reload needed capabilities
945 $_SESSION['REALUSER'] = $_SESSION['USER'];
946 $user = get_complete_user_data('id', $userid);
947 $user->realuser
= $_SESSION['REALUSER']->id
;
948 $user->loginascontext
= $context;
950 // let enrol plugins deal with new enrolments if necessary
951 enrol_check_plugins($user);
952 // set up global $USER
953 session_set_user($user);
957 * Sets up current user and course environment (lang, etc.) in cron.
958 * Do not use outside of cron script!
960 * @param stdClass $user full user object, null means default cron user (admin)
961 * @param $course full course record, null means $SITE
964 function cron_setup_user($user = NULL, $course = NULL) {
965 global $CFG, $SITE, $PAGE;
967 static $cronuser = NULL;
968 static $cronsession = NULL;
970 if (empty($cronuser)) {
971 /// ignore admins timezone, language and locale - use site default instead!
972 $cronuser = get_admin();
973 $cronuser->timezone
= $CFG->timezone
;
974 $cronuser->lang
= '';
975 $cronuser->theme
= '';
976 unset($cronuser->description
);
978 $cronsession = new stdClass();
982 // cached default cron user (==modified admin for now)
983 session_set_user($cronuser);
984 $_SESSION['SESSION'] = $cronsession;
987 // emulate real user session - needed for caps in cron
988 if ($_SESSION['USER']->id
!= $user->id
) {
989 session_set_user($user);
990 $_SESSION['SESSION'] = new stdClass();
994 // TODO MDL-19774 relying on global $PAGE in cron is a bad idea.
995 // Temporary hack so that cron does not give fatal errors.
996 $PAGE = new moodle_page();
998 $PAGE->set_course($course);
1000 $PAGE->set_course($SITE);
1003 // TODO: it should be possible to improve perf by caching some limited number of users here ;-)