public function terminate_current();
/**
- * Terminates all sessions.
+ * Terminates all sessions, auth hooks are not executed.
+ * Useful in ugrade scripts.
*/
public function terminate_all();
*/
public function write_close();
+ /**
+ * Session garbage collection
+ * - verify timeout for all users
+ * - kill sessions of all deleted users
+ * - kill sessions of users with disabled plugins or 'nologin' plugin
+ */
+ public function gc();
}
/**
}
/**
- * Initialise $USER object, handles google access.
+ * Initialise $USER object, handles google access
+ * and sets up not logged in user properly.
*
* @return void
*/
ini_set('session.save_path', $CFG->dataroot .'/sessions');
}
+ /**
+ * Terminates all sessions, auth hooks are not executed.
+ * Useful in ugrade scripts.
+ */
public function terminate_all() {
// TODO
}
+
+ /**
+ * Session garbage collection
+ * - verify timeout for all users
+ * - kill sessions of all deleted users
+ * - kill sessions of users with disabled plugins or 'nologin' plugin
+ */
+ public function gc() {
+ // difficult/slow
+ }
+
}
/**
protected $record = null;
protected $database = null;
+ public function __construct() {
+ global $DB;
+ $this->database = $DB;
+ parent::__construct();
+ }
+
protected function init_session_storage() {
global $CFG;
- // ini_get('session.gc_probability') == 0 means we rely on cron cleanup only
- // TODO: implement cron db session cleanup
+ // gc only from CRON - individual user timeouts now checked during each access
+ ini_set('session.gc_probability', 0);
if (empty($CFG->sessiontimeout)) {
$CFG->sessiontimeout = 7200;
}
}
+ /**
+ * Terminates all sessions, auth hooks are not executed.
+ * Useful in ugrade scripts.
+ */
public function terminate_all() {
try {
// do not show any warnings - might be during upgrade/installation
$this->database->delete_records('sessions');
} catch (dml_exception $ignored) {
-
}
}
- public function handler_open($save_path, $session_name) {
- global $DB;
+ /**
+ * Session garbage collection
+ * - verify timeout for all users
+ * - kill sessions of all deleted users
+ * - kill sessions of users with disabled plugins or 'nologin' plugin
+ */
+ public function gc() {
+ global $CFG;
+ $maxlifetime = $CFG->sessiontimeout;
- $this->database = $DB;
+ if (empty($CFG->rolesactive)) {
+ return;
+ }
+
+ try {
+ /// kill all sessions of deleted users
+ $this->database->delete_records_select('sessions', "userid IN (SELECT id FROM {user} WHERE deleted <> 0)");
+
+ /// kill sessions of users with disabled plugins
+ $auth_sequence = get_enabled_auth_plugins(true);
+ $auth_sequence = array_flip($auth_sequence);
+ unset($auth_sequence['nologin']); // no login allowed
+ $auth_sequence = array_flip($auth_sequence);
+ $notplugins = null;
+ list($notplugins, $params) = $this->database->get_in_or_equal($auth_sequence, SQL_PARAMS_QM, '', false);
+ $this->database->delete_records_select('sessions', "userid IN (SELECT id FROM {user} WHERE auth $notplugins)", $params);
+
+ /// now get a list of time-out candidates
+ $sql = "SELECT s.*, u.auth
+ FROM {sessions} s
+ JOIN {user} u ON u.id = s.userid
+ WHERE s.timemodified + ? < ?";
+ $params = array($maxlifetime, time());
+
+ $authplugins = array();
+ foreach($auth_sequence as $authname) {
+ $authplugins[$authname] = get_auth_plugin($authname);
+ }
+ $records = $this->database->get_records_sql($sql, $params);
+ foreach ($records as $record) {
+ foreach ($authplugins as $authplugin) {
+ if ($authplugin->ignore_timeout($record->userid, $records->auth, $record->timecreated, $record->timemodified)) {
+ continue;
+ }
+ }
+ $this->database->delete_records('sessions', array('id'=>$record->id));
+ }
+ } catch (dml_exception $ex) {
+ error_log('Error gc-ing sessions');
+ }
+ }
+
+ public function handler_open($save_path, $session_name) {
return true;
}
try {
if ($record = $this->database->get_record('sessions', array('sid'=>$sid))) {
$this->database->get_session_lock($record->id);
-
+
} else {
$record = new object();
$record->state = 0;
// verify timeout
if ($record->timemodified + $CFG->sessiontimeout < time()) {
- // TODO: implement auth plugin timeout hook (see gc)
- $record->state = 0;
- $record->sessdata = null;
- $record->sessdatahash = null;
- $record->userid = 0;
- $record->timecreated = $record->timemodified = time();
- $record->firstip = $record->lastip = getremoteaddr();
- try {
- $this->database->update_record('sessions', $record);
- } catch (dml_exception $ex) {
- error_log('Can not time out database session');
- return '';
+ $ignoretimeout = false;
+ $authsequence = get_enabled_auth_plugins(); // auths, in sequence
+ foreach($authsequence as $authname) {
+ $authplugin = get_auth_plugin($authname);
+ if ($authplugin->ignore_timeout($record->userid, $records->auth, $record->timecreated, $record->timemodified)) {
+ $ignoretimeout = true;
+ break;
+ }
+ }
+ if ($ignoretimeout) {
+ //refresh session
+ $record->timemodified = time();
+ try {
+ $this->database->update_record('sessions', $record);
+ } catch (dml_exception $ex) {
+ error_log('Can not refresh database session');
+ return '';
+ }
+ } else {
+ //time out session
+ $record->state = 0;
+ $record->sessdata = null;
+ $record->sessdatahash = null;
+ $record->userid = 0;
+ $record->timecreated = $record->timemodified = time();
+ $record->firstip = $record->lastip = getremoteaddr();
+ try {
+ $this->database->update_record('sessions', $record);
+ } catch (dml_exception $ex) {
+ error_log('Can not time out database session');
+ return '';
+ }
}
}
}
public function handler_gc($ignored_maxlifetime) {
- global $CFG;
- $maxlifetime = $CFG->sessiontimeout;
-
- $select = "timemodified + :maxlifetime < :now";
- $params = array('now'=>time(), 'maxlifetime'=>$maxlifetime);
-
- // TODO: add auth plugin hook that would allow extending of max lifetime
-
- try {
- $this->database->delete_records_select('sessions', $select, $params);
- } catch (dml_exception $ex) {
- error_log('Can not garbage collect database sessions.');
- }
-
+ $this->gc();
return true;
}
*/
function session_set_user($user) {
$_SESSION['USER'] = $user;
- check_enrolment_plugins($_SESSION['USER']);
- load_all_capabilities();
+ unset($_SESSION['USER']->description); // conserve memory
+ if (!isset($_SESSION['USER']->access)) {
+ // check enrolments and load caps only once
+ check_enrolment_plugins($_SESSION['USER']);
+ load_all_capabilities();
+ }
sesskey(); // init session key
}
/// ignore admins timezone, language and locale - use site deafult instead!
$cronuser = get_admin();
$cronuser->timezone = $CFG->timezone;
- $cronuser->lang = '';
- $cronuser->theme = '';
+ $cronuser->lang = '';
+ $cronuser->theme = '';
+ unset($cronuser->description);
$cronsession = array();
}