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 
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 
60  self::SESSION_TYPE_USER,
61  self::SESSION_TYPE_ANONYM
62  );
63 
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 
118  self::removeSessionCookie();
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']):
171  $type = self::SESSION_TYPE_SYSTEM;
172  break;
173 
174  case $user_id == ANONYMOUS_USER_ID:
175  $type = self::SESSION_TYPE_ANONYM;
176  break;
177 
178  case self::checkAdministrationPermission($user_id):
179  $type = self::SESSION_TYPE_ADMIN;
180  break;
181 
182  default:
183  $type = self::SESSION_TYPE_USER;
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 
217  self::removeSessionCookie();
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  {
509  return self::$setting_fields;
510  }
511 }
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.
$auth
Definition: metadata.php:48
$_COOKIE['client_id']
Definition: server.php:9
$_SESSION["AccountId"]
$type
static removeSessionCookie()
removes a session cookie, so it is not sent by browser anymore
$_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
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
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
Create styles array
The data for the language used.
static initSession()
mark session with type regarding to the context.
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 $lng
Definition: privfeed.php:17
global $ilDB
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
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