ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
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 $ilSetting;
88
89 // do not check session in fixed duration mode
90 if ($ilSetting->get('session_handling_type', 0) != 1) {
91 return;
92 }
93
94 // check for expired sessions makes sense
95 // only when public section is not enabled
96 // because it is not possible to determine
97 // wether the sid cookie relates to a session of an
98 // authenticated user or a anonymous user
99 // when the session dataset has allready been deleted
100
101 if (!$ilSetting->get("pub_section")) {
102 global $lng;
103
104 $sid = null;
105
106 if (!isset($_COOKIE[session_name()]) || !strlen($_COOKIE[session_name()])) {
107 self::debug('Browser did not send a sid cookie');
108 } else {
109 $sid = $_COOKIE[session_name()];
110
111 self::debug('Browser sent sid cookie with value (' . $sid . ')');
112
113 if (!self::isValidSession($sid)) {
114 self::debug('remove session cookie for (' . $sid . ') and trigger event');
115
116 // raw data will be updated (later) with garbage collection [destroyExpired()]
117
119
120 // Trigger expiredSessionDetected Event
121 global $ilAppEventHandler;
122 $ilAppEventHandler->raise(
123 'Services/Authentication',
124 'expiredSessionDetected',
125 array()
126 );
127
128 ilUtil::redirect('login.php?expired=true' . '&target=' . $_GET['target']);
129 }
130 }
131 }
132 }
133
138 public static function initSession()
139 {
140 global $ilSetting;
141
142 // do not init session type in fixed duration mode
143 if ($ilSetting->get('session_handling_type', 0) != 1) {
144 return;
145 }
146
147 if (!isset($_SESSION['SessionType'])) {
148 $_SESSION['SessionType'] = self::SESSION_TYPE_UNKNOWN;
149 self::debug(__METHOD__ . " --> init session with type (" . $_SESSION['SessionType'] . ")");
150 } else {
151 self::debug(__METHOD__ . " --> keep sessions type on (" . $_SESSION['SessionType'] . ")");
152 }
153 }
154
160 public static function handleLoginEvent($a_login, ilAuthSession $auth_session)
161 {
162 global $ilSetting;
163
164 require_once 'Services/User/classes/class.ilObjUser.php';
165 $user_id = ilObjUser::_lookupId($a_login);
166
167 // we need the session type for the session statistics
168 // regardless of the current session handling type
169 switch (true) {
170 case isset($_ENV['SHELL']):
172 break;
173
174 case $user_id == ANONYMOUS_USER_ID:
176 break;
177
180 break;
181
182 default:
184 break;
185 }
186
187 $_SESSION['SessionType'] = $type;
188 self::debug(__METHOD__ . " --> update sessions type to (" . $type . ")");
189
190 // do not handle login event in fixed duration mode
191 if ($ilSetting->get('session_handling_type', 0) != 1) {
192 return true;
193 }
194
195 if (in_array($type, self::$session_types_controlled)) {
196 return self::checkCurrentSessionIsAllowed($auth_session, $user_id);
197 }
198 }
199
203 public static function handleLogoutEvent()
204 {
205 global $ilSetting;
206
207 // do not handle logout event in fixed duration mode
208 if ($ilSetting->get('session_handling_type', 0) != 1) {
209 return;
210 }
211
212 $_SESSION['SessionType'] = self::SESSION_TYPE_UNKNOWN;
213 self::debug(__METHOD__ . " --> reset sessions type to (" . $_SESSION['SessionType'] . ")");
214
215 // session_destroy() is called in auth, so raw data will be updated
216
218 }
219
230 private static function checkCurrentSessionIsAllowed(ilAuthSession $auth, $a_user_id)
231 {
232 global $ilSetting;
233
234 $max_sessions = (int) $ilSetting->get('session_max_count', self::DEFAULT_MAX_COUNT);
235
236 if ($max_sessions > 0) {
237 // get total number of sessions
238 $num_sessions = self::getExistingSessionCount(self::$session_types_controlled);
239
240 self::debug(__METHOD__ . "--> total existing sessions (" . $num_sessions . ")");
241
242 if (($num_sessions + 1) > $max_sessions) {
243 self::debug(__METHOD__ . ' --> limit for session pool reached, but try kicking some first request abidencer');
244
245 self::kickFirstRequestAbidencer(self::$session_types_controlled);
246
247 // get total number of sessions again
248 $num_sessions = self::getExistingSessionCount(self::$session_types_controlled);
249
250 if (($num_sessions + 1) > $max_sessions) {
251 self::debug(__METHOD__ . ' --> limit for session pool still reached so try kick one min idle session');
252
253 self::kickOneMinIdleSession(self::$session_types_controlled);
254
255 // get total number of sessions again
256 $num_sessions = self::getExistingSessionCount(self::$session_types_controlled);
257
258 if (($num_sessions + 1) > $max_sessions) {
259 self::debug(__METHOD__ . ' --> limit for session pool still reached so logout session (' . session_id() . ') and trigger event');
260
262
263 // as the session is opened and closed in one request, there
264 // is no proper session yet and we have to do this ourselves
266 session_id(),
267 $_SESSION['SessionType'],
268 time(),
269 $a_user_id
270 );
271
272 $auth->logout();
273
274 // Trigger reachedSessionPoolLimit Event
275 global $ilAppEventHandler;
276 $ilAppEventHandler->raise(
277 'Services/Authentication',
278 'reachedSessionPoolLimit',
279 array()
280 );
281
282 // auth won't do this, we need to close session properly
283 // already done in new implementation
284 // session_destroy();
285
286 ilUtil::redirect('login.php?reached_session_limit=true');
287 } else {
288 self::debug(__METHOD__ . ' --> limit of session pool not reached anymore after kicking one min idle session');
289 }
290 } else {
291 self::debug(__METHOD__ . ' --> limit of session pool not reached anymore after kicking some first request abidencer');
292 }
293 } else {
294 self::debug(__METHOD__ . ' --> limit for session pool not reached yet');
295 }
296 } else {
297 self::debug(__METHOD__ . ' --> limit for session pool not set so check is bypassed');
298 }
299 }
300
308 public static function getExistingSessionCount(array $a_types)
309 {
310 global $ilDB;
311
312 $ts = time();
313
314 $query = "SELECT count(session_id) AS num_sessions FROM usr_session " .
315 "WHERE expires > %s " .
316 "AND " . $ilDB->in('type', $a_types, false, 'integer');
317
318 $res = $ilDB->queryF($query, array('integer'), array($ts));
320
321 return $row->num_sessions;
322 }
323
334 private static function kickOneMinIdleSession(array $a_types)
335 {
336 global $ilDB, $ilSetting;
337
338 $ts = time();
339 $min_idle = (int) $ilSetting->get('session_min_idle', self::DEFAULT_MIN_IDLE) * 60;
340 $max_idle = (int) $ilSetting->get('session_max_idle', self::DEFAULT_MAX_IDLE) * 60;
341
342 $query = "SELECT session_id,expires FROM usr_session WHERE expires >= %s " .
343 "AND (expires - %s) < (%s - %s) " .
344 "AND " . $ilDB->in('type', $a_types, false, 'integer') . " ORDER BY expires";
345
346 $res = $ilDB->queryF(
347 $query,
348 array('integer', 'integer', 'integer', 'integer'),
349 array($ts, $ts, $max_idle, $min_idle)
350 );
351
352 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
354
355 self::debug(__METHOD__ . ' --> successfully deleted one min idle session');
356
357 return true;
358 }
359
360 self::debug(__METHOD__ . ' --> no min idle session available for deletion');
361
362 return false;
363 }
364
373 private static function kickFirstRequestAbidencer(array $a_types)
374 {
375 global $ilDB, $ilSetting;
376
377 $max_idle_after_first_request = (int) $ilSetting->get('session_max_idle_after_first_request') * 60;
378
379 if ((int) $max_idle_after_first_request == 0) {
380 return;
381 }
382
383 $query = "SELECT session_id,expires FROM usr_session WHERE " .
384 "(ctime - createtime) < %s " .
385 "AND (%s - createtime) > %s " .
386 "AND " . $ilDB->in('type', $a_types, false, 'integer');
387
388 $res = $ilDB->queryF(
389 $query,
390 array('integer', 'integer', 'integer'),
391 array($max_idle_after_first_request, time(), $max_idle_after_first_request)
392 );
393
394 $session_ids = array();
395 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
396 $session_ids[$row->session_id] = $row->expires;
397 }
399
400 self::debug(__METHOD__ . ' --> Finished kicking first request abidencer');
401 }
402
412 private static function isValidSession($a_sid)
413 {
414 global $ilDB, $ilSetting;
415
416 $query = "SELECT session_id, expires FROM usr_session " .
417 "WHERE session_id = %s";
418
419 $res = $ilDB->queryF($query, array('text'), array($a_sid));
420
421 $ts = time();
422
423 $sessions = array();
424
425 while ($row = $ilDB->fetchAssoc($res)) {
426 if ($row['expires'] > $ts) {
427 self::debug(__METHOD__ . ' --> Found a valid session with id (' . $a_sid . ')');
428 $sessions[] = $row;
429 } else {
430 self::debug(__METHOD__ . ' --> Found an expired session with id (' . $a_sid . ')');
431 }
432 }
433
434 if (count($sessions) == 1) {
435 self::debug(__METHOD__ . ' --> Exact one valid session found for session id (' . $a_sid . ')');
436
437 return true;
438 } else {
439 if (count($sessions) > 1) {
440 self::debug(__METHOD__ . ' --> Strange!!! More than one sessions found for given session id! (' . $a_sid . ')');
441 } else {
442 self::debug(__METHOD__ . ' --> No valid session found for session id (' . $a_sid . ')');
443 }
444
445 return false;
446 }
447 }
448
452 private static function removeSessionCookie()
453 {
454 ilUtil::setCookie(session_name(), 'deleted', true, true);
455 self::debug('Session cookie has been removed');
456 }
457
466 private static function checkAdministrationPermission($a_user_id)
467 {
468 if (!(int) $a_user_id) {
469 return false;
470 }
471
472 global $rbacsystem;
473
474 $access = $rbacsystem->checkAccessOfUser(
475 $a_user_id,
476 'read,visible',
477 SYSTEM_FOLDER_ID
478 );
479
480 return $access;
481 }
482
489 private static function debug($a_debug_log_message)
490 {
491 global $ilLog;
492
493 if (DEVMODE) {
494 $ilLog->write($a_debug_log_message, 'message');
495 }
496
497 if (self::INTERNAL_DEBUG) {
498 error_log($a_debug_log_message . "\n", 3, 'session.log');
499 }
500 }
501
507 public static function getSettingFields()
508 {
510 }
511}
$auth
Definition: metadata.php:48
$_COOKIE['client_id']
Definition: server.php:9
$_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)
global $lng
Definition: privfeed.php:17
global $ilSetting
Definition: privfeed.php:17
$query
$type
foreach($_POST as $key=> $value) $res
global $ilDB