ILIAS  Release_4_1_x_branch Revision 61804
 All Data Structures Namespaces Files Functions Variables Groups Pages
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  );
41 
48  const SESSION_TYPE_ADMIN = 2;
49  const SESSION_TYPE_USER = 3;
51 
58  private static $session_types_controlled = array(
59  self::SESSION_TYPE_USER,
60  self::SESSION_TYPE_ANONYM
61  );
62 
69  private static $session_types_not_controlled = array(
70  self::SESSION_TYPE_UNKNOWN,
71  self::SESSION_TYPE_SYSTEM,
72  self::SESSION_TYPE_ADMIN
73  );
74 
84  public static function checkExpiredSession()
85  {
86  global $ilSetting;
87 
88  // do not check session in fixed duration mode
89  if( $ilSetting->get('session_handling_type', 0) != 1 )
90  return;
91 
92  // check for expired sessions makes sense
93  // only when public section is not enabled
94  // because it is not possible to determine
95  // wether the sid cookie relates to a session of an
96  // authenticated user or a anonymous user
97  // when the session dataset has allready been deleted
98 
99  if(!$ilSetting->get("pub_section"))
100  {
101  global $lng;
102 
103  $sid = null;
104 
105  if( !isset($_COOKIE[session_name()]) || !strlen($_COOKIE[session_name()]) )
106  {
107  self::debug('Browser did not send a sid cookie');
108  }
109  else
110  {
111  $sid = $_COOKIE[session_name()];
112 
113  self::debug('Browser sent sid cookie with value ('.$sid.')');
114 
115  if(!self::isValidSession($sid))
116  {
117  self::debug('remove session cookie for ('.$sid.') and trigger event');
118 
120 
121  // Trigger expiredSessionDetected Event
122  global $ilAppEventHandler;
123  $ilAppEventHandler->raise(
124  'Services/Authentication', 'expiredSessionDetected', array()
125  );
126 
127  ilUtil::redirect('login.php?expired=true'.'&target='.$_GET['target']);
128  }
129  }
130  }
131  }
132 
137  public static function initSession()
138  {
139  global $ilSetting;
140 
141  // do not init session type in fixed duration mode
142  if( $ilSetting->get('session_handling_type', 0) != 1 )
143  return;
144 
145  if( !isset($_SESSION['SessionType']) )
146  {
147  $_SESSION['SessionType'] = self::SESSION_TYPE_UNKNOWN;
148  self::debug(__METHOD__." --> init session with type (".$_SESSION['SessionType'].")");
149  }
150  else
151  {
152  self::debug(__METHOD__." --> keep sessions type on (".$_SESSION['SessionType'].")");
153  }
154  }
155 
161  public static function handleLoginEvent($a_login, $a_auth)
162  {
163  global $ilSetting;
164 
165  // do not handle login event in fixed duration mode
166  if( $ilSetting->get('session_handling_type', 0) != 1 )
167  return;
168 
169 
170  $user_id = ilObjUser::_lookupId($a_login);
171 
172  switch(true)
173  {
174  case isset($_ENV['SHELL']):
175 
176  $_SESSION['SessionType'] = self::SESSION_TYPE_SYSTEM;
177  self::debug(__METHOD__." --> update sessions type to (".$_SESSION['SessionType'].")");
178  break;
179 
181 
182  $_SESSION['SessionType'] = self::SESSION_TYPE_ADMIN;
183  self::debug(__METHOD__." --> update sessions type to (".$_SESSION['SessionType'].")");
184  break;
185 
186  case $user_id == ANONYMOUS_USER_ID:
187 
188  $_SESSION['SessionType'] = self::SESSION_TYPE_ANONYM;
189  self::debug(__METHOD__." --> update sessions type to (".$_SESSION['SessionType'].")");
191  break;
192 
193  default:
194 
195  $_SESSION['SessionType'] = self::SESSION_TYPE_USER;
196  self::debug(__METHOD__." --> update sessions type to (".$_SESSION['SessionType'].")");
198  break;
199  }
200  }
201 
205  public static function handleLogoutEvent()
206  {
207  global $ilSetting;
208 
209  // do not handle logout event in fixed duration mode
210  if( $ilSetting->get('session_handling_type', 0) != 1 )
211  return;
212 
213  $_SESSION['SessionType'] = self::SESSION_TYPE_UNKNOWN;
214  self::debug(__METHOD__." --> reset sessions type to (".$_SESSION['SessionType'].")");
215 
217  }
218 
229  private static function checkCurrentSessionIsAllowed(Auth $a_auth)
230  {
231  global $ilSetting;
232 
233  $max_sessions = (int)$ilSetting->get('session_max_count', DEFAULT_MAX_COUNT);
234 
235  if($max_sessions > 0)
236  {
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  {
244  self::debug(__METHOD__.' --> limit for session pool reached, but try kicking some first request abidencer');
245 
246  self::kickFirstRequestAbidencer(self::$session_types_controlled);
247 
248  // get total number of sessions again
249  $num_sessions = self::getExistingSessionCount(self::$session_types_controlled);
250 
251  if(($num_sessions + 1) > $max_sessions)
252  {
253  self::debug(__METHOD__.' --> limit for session pool still reached so try kick one min idle session');
254 
255  self::kickOneMinIdleSession(self::$session_types_controlled);
256 
257  // get total number of sessions again
258  $num_sessions = self::getExistingSessionCount(self::$session_types_controlled);
259 
260  if(($num_sessions + 1) > $max_sessions)
261  {
262  self::debug(__METHOD__.' --> limit for session pool still reached so logout session ('.session_id().') and trigger event');
263 
264  $a_auth->logout();
265 
266  // Trigger reachedSessionPoolLimit Event
267  global $ilAppEventHandler;
268  $ilAppEventHandler->raise(
269  'Services/Authentication', 'reachedSessionPoolLimit', array()
270  );
271 
272  ilUtil::redirect('login.php?reached_session_limit=true');
273  }
274  else
275  {
276  self::debug(__METHOD__.' --> limit of session pool not reached anymore after kicking one min idle session');
277  }
278  }
279  else
280  {
281  self::debug(__METHOD__.' --> limit of session pool not reached anymore after kicking some first request abidencer');
282  }
283  }
284  else
285  {
286  self::debug(__METHOD__.' --> limit for session pool not reached yet');
287  }
288  }
289  else
290  {
291  self::debug(__METHOD__.' --> limit for session pool not set so check is bypassed');
292  }
293  }
294 
302  private static function getExistingSessionCount(array $a_types)
303  {
304  global $ilDB;
305 
306  $ts = time();
307 
308  $query = "SELECT count(session_id) AS num_sessions FROM usr_session ".
309  "WHERE expires > %s ".
310  "AND ".$ilDB->in('type', $a_types, false, 'integer');
311 
312  $res = $ilDB->queryF($query, array('integer'), array($ts));
313  $row = $res->fetchRow(DB_FETCHMODE_OBJECT);
314 
315  return $row->num_sessions;
316  }
317 
327  private static function kickOneMinIdleSession(array $a_types)
328  {
329  global $ilDB, $ilSetting;
330 
331  $ts = time();
332  $min_idle = (int)$ilSetting->get('session_min_idle', self::DEFAULT_MIN_IDLE) * 60;
333  $max_idle = (int)$ilSetting->get('session_max_idle', self::DEFAULT_MAX_IDLE) * 60;
334 
335  $query = "SELECT session_id FROM usr_session WHERE expires >= %s " .
336  "AND (expires - %s) < (%s - %s) " .
337  "AND ".$ilDB->in('type', $a_types, false, 'integer');
338  "ORDER BY expires";
339 
340  $res = $ilDB->queryF(
341  $query,
342  array('integer', 'integer', 'integer', 'integer'),
343  array($ts, $ts, $max_idle, $min_idle)
344  );
345 
346  while( $row = $res->fetchRow(DB_FETCHMODE_OBJECT) )
347  {
348  $sid = $row->session_id;
349  ilSession::_destroy($sid);
350 
351  self::debug(__METHOD__.' --> successfully deleted one min idle session');
352 
353  break;
354  }
355 
356  self::debug(__METHOD__.' --> no min idle session available for deletion');
357  }
358 
367  private static function kickFirstRequestAbidencer(array $a_types)
368  {
369  global $ilDB, $ilSetting;
370 
371  $max_idle_after_first_request = (int)$ilSetting->get('session_max_idle_after_first_request') * 60;
372 
373  if((int)$max_idle_after_first_request == 0) return;
374 
375  $query = "DELETE FROM usr_session WHERE " .
376  "(ctime - createtime) < %s " .
377  "AND (%s - createtime) > %s " .
378  "AND ".$ilDB->in('type', $a_types, false, 'integer');
379 
380  $ilDB->manipulateF( $query,
381  array('integer', 'integer', 'integer'),
382  array($max_idle_after_first_request, time(), $max_idle_after_first_request)
383  );
384 
385  self::debug(__METHOD__.' --> Finished kicking first request abidencer');
386  }
387 
397  private static function isValidSession($a_sid)
398  {
399  global $ilDB, $ilSetting;
400 
401  $query = "SELECT session_id, expires FROM usr_session ".
402  "WHERE session_id = %s";
403 
404  $res = $ilDB->queryF($query, array('text'), array($a_sid));
405 
406  $ts = time();
407 
408  $sessions = array();
409 
410  while( $row = $ilDB->fetchAssoc($res) )
411  {
412  if( $row['expires'] > $ts )
413  {
414  self::debug(__METHOD__.' --> Found a valid session with id ('.$a_sid.')');
415  $sessions[] = $row;
416  }
417  else
418  {
419  self::debug(__METHOD__.' --> Found an expired session with id ('.$a_sid.')');
420  }
421  }
422 
423  if(count($sessions) == 1)
424  {
425  self::debug(__METHOD__.' --> Exact one valid session found for session id ('.$a_sid.')');
426 
427  return true;
428  }
429  else
430  {
431  if($sess_count > 1)
432  self::debug(__METHOD__.' --> Strange!!! More than one sessions found for given session id! ('.$a_sid.')');
433  else self::debug(__METHOD__.' --> No valid session found for session id ('.$a_sid.')');
434 
435  return false;
436  }
437  }
438 
442  private static function removeSessionCookie()
443  {
444  ilUtil::setCookie(session_name(),'deleted',true,true);
445  self::debug('Session cookie has been removed');
446  }
447 
456  private static function checkAdministrationPermission($a_user_id)
457  {
458  if( !(int)$a_user_id ) return false;
459 
460  global $rbacsystem;
461 
462  $access = $rbacsystem->checkAccessOfUser(
463  $a_user_id, 'read,visible', SYSTEM_FOLDER_ID
464  );
465 
466  return $access;
467  }
468 
475  private static function debug($a_debug_log_message)
476  {
477  global $ilLog;
478 
479  if(DEVMODE) $ilLog->write($a_debug_log_message, 'message');
480 
481  if(self::INTERNAL_DEBUG) error_log($a_debug_log_message."\n", 3, 'session.log');
482  }
483 
489  public static function getSettingFields()
490  {
491  return self::$setting_fields;
492  }
493 
494 }
495 
496 
497 ?>