ILIAS  release_7 Revision v7.30-3-g800a261c036
class.ilSessionControl.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4
12{
17 const INTERNAL_DEBUG = false;
18
24 const DEFAULT_MIN_IDLE = 15;
25 const DEFAULT_MAX_IDLE = 30;
28
34 private static $setting_fields = array(
35 'session_max_count',
36 'session_min_idle',
37 'session_max_idle',
38 'session_max_idle_after_first_request',
39 'session_allow_client_maintenance',
40 'session_handling_type'
41 );
42
52
59 public static $session_types_controlled = array(
60 self::SESSION_TYPE_USER,
61 self::SESSION_TYPE_ANONYM
62 );
63
70 private static $session_types_not_controlled = array(
71 self::SESSION_TYPE_UNKNOWN,
72 self::SESSION_TYPE_SYSTEM,
73 self::SESSION_TYPE_ADMIN
74 );
75
85 public static function checkExpiredSession()
86 {
87 global $DIC;
88
89 $ilSetting = $DIC['ilSetting'];
90
91 // do not check session in fixed duration mode
92 if ($ilSetting->get('session_handling_type', 0) != 1) {
93 return;
94 }
95
96 // check for expired sessions makes sense
97 // only when public section is not enabled
98 // because it is not possible to determine
99 // wether the sid cookie relates to a session of an
100 // authenticated user or a anonymous user
101 // when the session dataset has allready been deleted
102
103 if (!$ilSetting->get("pub_section")) {
104 global $DIC;
105
106 $lng = $DIC['lng'];
107
108 $sid = null;
109
110 if (!isset($_COOKIE[session_name()]) || !strlen($_COOKIE[session_name()])) {
111 self::debug('Browser did not send a sid cookie');
112 } else {
113 $sid = $_COOKIE[session_name()];
114
115 self::debug('Browser sent sid cookie with value (' . $sid . ')');
116
117 if (!self::isValidSession($sid)) {
118 self::debug('remove session cookie for (' . $sid . ') and trigger event');
119
120 // raw data will be updated (later) with garbage collection [destroyExpired()]
121
123
124 // Trigger expiredSessionDetected Event
125 global $DIC;
126
127 $ilAppEventHandler = $DIC['ilAppEventHandler'];
128 $ilAppEventHandler->raise(
129 'Services/Authentication',
130 'expiredSessionDetected',
131 array()
132 );
133
134 ilUtil::redirect('login.php?expired=true' . '&target=' . $_GET['target']);
135 }
136 }
137 }
138 }
139
144 public static function initSession()
145 {
146 global $DIC;
147
148 $ilSetting = $DIC['ilSetting'];
149
150 // do not init session type in fixed duration mode
151 if ($ilSetting->get('session_handling_type', 0) != 1) {
152 return;
153 }
154
155 if (!isset($_SESSION['SessionType'])) {
156 $_SESSION['SessionType'] = self::SESSION_TYPE_UNKNOWN;
157 self::debug(__METHOD__ . " --> init session with type (" . $_SESSION['SessionType'] . ")");
158 } else {
159 self::debug(__METHOD__ . " --> keep sessions type on (" . $_SESSION['SessionType'] . ")");
160 }
161 }
162
168 public static function handleLoginEvent($a_login, ilAuthSession $auth_session)
169 {
170 global $DIC;
171
172 $ilSetting = $DIC['ilSetting'];
173
174 require_once 'Services/User/classes/class.ilObjUser.php';
175 $user_id = ilObjUser::_lookupId($a_login);
176
177 // we need the session type for the session statistics
178 // regardless of the current session handling type
179 switch (true) {
180 case isset($_ENV['SHELL']):
182 break;
183
184 case $user_id == ANONYMOUS_USER_ID:
186 break;
187
190 break;
191
192 default:
194 break;
195 }
196
197 $_SESSION['SessionType'] = $type;
198 self::debug(__METHOD__ . " --> update sessions type to (" . $type . ")");
199
200 // do not handle login event in fixed duration mode
201 if ($ilSetting->get('session_handling_type', 0) != 1) {
202 return true;
203 }
204
205 if (in_array($type, self::$session_types_controlled)) {
206 return self::checkCurrentSessionIsAllowed($auth_session, $user_id);
207 }
208 }
209
213 public static function handleLogoutEvent()
214 {
215 global $DIC;
216
217 $ilSetting = $DIC['ilSetting'];
218
219 // do not handle logout event in fixed duration mode
220 if ($ilSetting->get('session_handling_type', 0) != 1) {
221 return;
222 }
223
224 $_SESSION['SessionType'] = self::SESSION_TYPE_UNKNOWN;
225 self::debug(__METHOD__ . " --> reset sessions type to (" . $_SESSION['SessionType'] . ")");
226
227 // session_destroy() is called in auth, so raw data will be updated
228
230 }
231
242 private static function checkCurrentSessionIsAllowed(ilAuthSession $auth, $a_user_id)
243 {
244 global $DIC;
245
246 $ilSetting = $DIC['ilSetting'];
247
248 $max_sessions = (int) $ilSetting->get('session_max_count', self::DEFAULT_MAX_COUNT);
249
250 if ($max_sessions > 0) {
251 // get total number of sessions
252 $num_sessions = self::getExistingSessionCount(self::$session_types_controlled);
253
254 self::debug(__METHOD__ . "--> total existing sessions (" . $num_sessions . ")");
255
256 if (($num_sessions + 1) > $max_sessions) {
257 self::debug(__METHOD__ . ' --> limit for session pool reached, but try kicking some first request abidencer');
258
259 self::kickFirstRequestAbidencer(self::$session_types_controlled);
260
261 // get total number of sessions again
262 $num_sessions = self::getExistingSessionCount(self::$session_types_controlled);
263
264 if (($num_sessions + 1) > $max_sessions) {
265 self::debug(__METHOD__ . ' --> limit for session pool still reached so try kick one min idle session');
266
267 self::kickOneMinIdleSession(self::$session_types_controlled);
268
269 // get total number of sessions again
270 $num_sessions = self::getExistingSessionCount(self::$session_types_controlled);
271
272 if (($num_sessions + 1) > $max_sessions) {
273 self::debug(__METHOD__ . ' --> limit for session pool still reached so logout session (' . session_id() . ') and trigger event');
274
276
277 // as the session is opened and closed in one request, there
278 // is no proper session yet and we have to do this ourselves
280 session_id(),
281 $_SESSION['SessionType'],
282 time(),
283 $a_user_id
284 );
285
286 $auth->logout();
287
288 // Trigger reachedSessionPoolLimit Event
289 global $DIC;
290
291 $ilAppEventHandler = $DIC['ilAppEventHandler'];
292 $ilAppEventHandler->raise(
293 'Services/Authentication',
294 'reachedSessionPoolLimit',
295 array()
296 );
297
298 // auth won't do this, we need to close session properly
299 // already done in new implementation
300 // session_destroy();
301
302 ilUtil::redirect('login.php?reached_session_limit=true');
303 } else {
304 self::debug(__METHOD__ . ' --> limit of session pool not reached anymore after kicking one min idle session');
305 }
306 } else {
307 self::debug(__METHOD__ . ' --> limit of session pool not reached anymore after kicking some first request abidencer');
308 }
309 } else {
310 self::debug(__METHOD__ . ' --> limit for session pool not reached yet');
311 }
312 } else {
313 self::debug(__METHOD__ . ' --> limit for session pool not set so check is bypassed');
314 }
315 }
316
324 public static function getExistingSessionCount(array $a_types)
325 {
326 global $DIC;
327
328 $ilDB = $DIC['ilDB'];
329
330 $ts = time();
331
332 $query = "SELECT count(session_id) AS num_sessions FROM usr_session " .
333 "WHERE expires > %s " .
334 "AND " . $ilDB->in('type', $a_types, false, 'integer');
335
336 $res = $ilDB->queryF($query, array('integer'), array($ts));
337 $row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT);
338
339 return $row->num_sessions;
340 }
341
352 private static function kickOneMinIdleSession(array $a_types)
353 {
354 global $DIC;
355
356 $ilDB = $DIC['ilDB'];
357 $ilSetting = $DIC['ilSetting'];
358
359 $ts = time();
360 $min_idle = (int) $ilSetting->get('session_min_idle', self::DEFAULT_MIN_IDLE) * 60;
361 $max_idle = (int) $ilSetting->get('session_max_idle', self::DEFAULT_MAX_IDLE) * 60;
362
363 $query = "SELECT session_id,expires FROM usr_session WHERE expires >= %s " .
364 "AND (expires - %s) < (%s - %s) " .
365 "AND " . $ilDB->in('type', $a_types, false, 'integer') . " ORDER BY expires";
366
367 $res = $ilDB->queryF(
368 $query,
369 array('integer', 'integer', 'integer', 'integer'),
370 array($ts, $ts, $max_idle, $min_idle)
371 );
372
373 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
374 ilSession::_destroy($row->session_id, ilSession::SESSION_CLOSE_IDLE, $row->expires);
375
376 self::debug(__METHOD__ . ' --> successfully deleted one min idle session');
377
378 return true;
379 }
380
381 self::debug(__METHOD__ . ' --> no min idle session available for deletion');
382
383 return false;
384 }
385
394 private static function kickFirstRequestAbidencer(array $a_types)
395 {
396 global $DIC;
397
398 $ilDB = $DIC['ilDB'];
399 $ilSetting = $DIC['ilSetting'];
400
401 $max_idle_after_first_request = (int) $ilSetting->get('session_max_idle_after_first_request') * 60;
402
403 if ((int) $max_idle_after_first_request == 0) {
404 return;
405 }
406
407 $query = "SELECT session_id,expires FROM usr_session WHERE " .
408 "(ctime - createtime) < %s " .
409 "AND (%s - createtime) > %s " .
410 "AND " . $ilDB->in('type', $a_types, false, 'integer');
411
412 $res = $ilDB->queryF(
413 $query,
414 array('integer', 'integer', 'integer'),
415 array($max_idle_after_first_request, time(), $max_idle_after_first_request)
416 );
417
418 $session_ids = array();
419 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
420 $session_ids[$row->session_id] = $row->expires;
421 }
423
424 self::debug(__METHOD__ . ' --> Finished kicking first request abidencer');
425 }
426
436 private static function isValidSession($a_sid)
437 {
438 global $DIC;
439
440 $ilDB = $DIC['ilDB'];
441 $ilSetting = $DIC['ilSetting'];
442
443 $query = "SELECT session_id, expires FROM usr_session " .
444 "WHERE session_id = %s";
445
446 $res = $ilDB->queryF($query, array('text'), array($a_sid));
447
448 $ts = time();
449
450 $sessions = array();
451
452 while ($row = $ilDB->fetchAssoc($res)) {
453 if ($row['expires'] > $ts) {
454 self::debug(__METHOD__ . ' --> Found a valid session with id (' . $a_sid . ')');
455 $sessions[] = $row;
456 } else {
457 self::debug(__METHOD__ . ' --> Found an expired session with id (' . $a_sid . ')');
458 }
459 }
460
461 if (count($sessions) == 1) {
462 self::debug(__METHOD__ . ' --> Exact one valid session found for session id (' . $a_sid . ')');
463
464 return true;
465 } else {
466 if (count($sessions) > 1) {
467 self::debug(__METHOD__ . ' --> Strange!!! More than one sessions found for given session id! (' . $a_sid . ')');
468 } else {
469 self::debug(__METHOD__ . ' --> No valid session found for session id (' . $a_sid . ')');
470 }
471
472 return false;
473 }
474 }
475
479 private static function removeSessionCookie()
480 {
481 ilUtil::setCookie(session_name(), 'deleted', true, true);
482 self::debug('Session cookie has been removed');
483 }
484
493 private static function checkAdministrationPermission($a_user_id)
494 {
495 if (!(int) $a_user_id) {
496 return false;
497 }
498
499 global $DIC;
500
501 $rbacsystem = $DIC['rbacsystem'];
502
503 $access = $rbacsystem->checkAccessOfUser(
504 $a_user_id,
505 'read,visible',
507 );
508
509 return $access;
510 }
511
518 private static function debug($a_debug_log_message)
519 {
520 global $DIC;
521
522 $ilLog = $DIC['ilLog'];
523
524 if (DEVMODE) {
525 $ilLog->write($a_debug_log_message, 'message');
526 }
527
528 if (self::INTERNAL_DEBUG) {
529 error_log($a_debug_log_message . "\n", 3, 'session.log');
530 }
531 }
532
538 public static function getSettingFields()
539 {
541 }
542}
$_GET["client_id"]
$_SESSION["AccountId"]
An exception for terminatinating execution or to throw for unit testing.
isValidSession($ext_uid, $soap_pw, $new_user)
isValidSession
static _lookupId($a_user_str)
Lookup id by login.
const INTERNAL_DEBUG
this controls the debuggin into a separate logfile (.
static handleLogoutEvent()
reset sessions type to unknown
static isValidSession($a_sid)
checks if session exists for given id and if it is still valid
static $session_types_not_controlled
all session types that will be involved when count of sessions will be determined or when idleing ses...
static initSession()
mark session with type regarding to the context.
static $setting_fields
all fieldnames that are saved in settings table
const SESSION_TYPE_UNKNOWN
session types from which one is assigned to each session
static getSettingFields()
returns the array of setting fields
static handleLoginEvent($a_login, ilAuthSession $auth_session)
when current session is allowed to be created it marks it with type regarding to the sessions user co...
static checkExpiredSession()
checks for possibly expired session should be called from ilAuthUtils::__initAuth() so it's called be...
static debug($a_debug_log_message)
logs the given debug message in ilLog
static getExistingSessionCount(array $a_types)
returns number of valid sessions relating to given session types
static checkCurrentSessionIsAllowed(ilAuthSession $auth, $a_user_id)
checks wether the current session exhaust the limit of sessions when limit is reached it deletes "fir...
static removeSessionCookie()
removes a session cookie, so it is not sent by browser anymore
const DEFAULT_MAX_COUNT
default value for settings that have not been defined in setup or administration yet
static checkAdministrationPermission($a_user_id)
checks wether a given user login relates to an user with administrative permissions
static kickOneMinIdleSession(array $a_types)
if sessions exist that relates to given session types and idled longer than min idle parameter,...
static kickFirstRequestAbidencer(array $a_types)
kicks sessions of users that abidence after login so people could not login and go for coffe break ;-...
static createRawEntry($a_session_id, $a_session_type, $a_timestamp, $a_user_id)
Create raw data entry.
static setClosingContext($a_context)
set closing context (for statistics)
static _destroy($a_session_id, $a_closing_context=null, $a_expired_at=null)
Destroy session.
const SESSION_CLOSE_FIRST
const SESSION_CLOSE_IDLE
const SESSION_CLOSE_LIMIT
static setCookie($a_cookie_name, $a_cookie_value='', $a_also_set_super_global=true, $a_set_cookie_invalid=false)
static redirect($a_script)
const ANONYMOUS_USER_ID
Definition: constants.php:25
const SYSTEM_FOLDER_ID
Definition: constants.php:33
global $DIC
Definition: goto.php:24
$auth
Definition: metadata.php:59
global $ilSetting
Definition: privfeed.php:17
$query
$type
$lng
foreach($_POST as $key=> $value) $res
global $ilDB
$_COOKIE[session_name()]
Definition: xapitoken.php:37