ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
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 
23  const DEFAULT_MAX_COUNT = 0;
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 
49  const SESSION_TYPE_ADMIN = 2;
50  const SESSION_TYPE_USER = 3;
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 
122  self::removeSessionCookie();
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']):
181  $type = self::SESSION_TYPE_SYSTEM;
182  break;
183 
184  case $user_id == ANONYMOUS_USER_ID:
185  $type = self::SESSION_TYPE_ANONYM;
186  break;
187 
188  case self::checkAdministrationPermission($user_id):
189  $type = self::SESSION_TYPE_ADMIN;
190  break;
191 
192  default:
193  $type = self::SESSION_TYPE_USER;
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 
229  self::removeSessionCookie();
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));
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)) {
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',
506  SYSTEM_FOLDER_ID
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  {
540  return self::$setting_fields;
541  }
542 }
const SESSION_CLOSE_IDLE
const DEFAULT_MAX_COUNT
default value for settings that have not been defined in setup or administration yet ...
static _destroy($a_session_id, $a_closing_context=null, $a_expired_at=null)
Destroy session.
static $setting_fields
all fieldnames that are saved in settings table
logout()
Logout user => stop session.
$_COOKIE['client_id']
Definition: server.php:9
$_SESSION["AccountId"]
$type
static removeSessionCookie()
removes a session cookie, so it is not sent by browser anymore
global $DIC
Definition: saml.php:7
$_GET["client_id"]
static _lookupId($a_user_str)
Lookup id by login.
static isValidSession($a_sid)
checks if session exists for given id and if it is still valid
static getSettingFields()
returns the array of setting fields
$auth
Definition: fileserver.php:48
const INTERNAL_DEBUG
this controls the debuggin into a separate logfile (.
static setCookie($a_cookie_name, $a_cookie_value='', $a_also_set_super_global=true, $a_set_cookie_invalid=false)
static checkAdministrationPermission($a_user_id)
checks wether a given user login relates to an user with administrative permissions ...
foreach($_POST as $key=> $value) $res
$lng
static checkExpiredSession()
checks for possibly expired session should be called from ilAuthUtils::__initAuth() so it&#39;s called be...
$query
static createRawEntry($a_session_id, $a_session_type, $a_timestamp, $a_user_id)
Create raw data entry.
const SESSION_CLOSE_LIMIT
static initSession()
mark session with type regarding to the context.
$row
static getExistingSessionCount(array $a_types)
returns number of valid sessions relating to given session types
static debug($a_debug_log_message)
logs the given debug message in ilLog
static setClosingContext($a_context)
set closing context (for statistics)
static $session_types_not_controlled
all session types that will be involved when count of sessions will be determined or when idleing ses...
global $ilSetting
Definition: privfeed.php:17
global $ilDB
static handleLogoutEvent()
reset sessions type to unknown
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 redirect($a_script)
static kickFirstRequestAbidencer(array $a_types)
kicks sessions of users that abidence after login so people could not login and go for coffe break ;-...
const SESSION_CLOSE_FIRST
static kickOneMinIdleSession(array $a_types)
if sessions exist that relates to given session types and idled longer than min idle parameter...
static checkCurrentSessionIsAllowed(ilAuthSession $auth, $a_user_id)
checks wether the current session exhaust the limit of sessions when limit is reached it deletes "fir...
const SESSION_TYPE_UNKNOWN
session types from which one is assigned to each session
isValidSession($ext_uid, $soap_pw, $new_user)
isValidSession