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