ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.ilSession.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
26 class ilSession
27 {
35  public const SESSION_HANDLING_FIXED = 0;
36 
45 
51  public const SESSION_CLOSE_USER = 1; // manual logout
52  public const SESSION_CLOSE_EXPIRE = 2; // has expired
53  public const SESSION_CLOSE_FIRST = 3; // kicked by session control (first abidencer)
54  public const SESSION_CLOSE_IDLE = 4; // kickey by session control (ilde time)
55  public const SESSION_CLOSE_LIMIT = 5; // kicked by session control (limit reached)
56  public const SESSION_CLOSE_LOGIN = 6; // anonymous => login
57  public const SESSION_CLOSE_PUBLIC = 7; // => anonymous
58  public const SESSION_CLOSE_TIME = 8; // account time limit reached
59  public const SESSION_CLOSE_IP = 9; // wrong ip
60  public const SESSION_CLOSE_SIMUL = 10; // simultaneous login
61  public const SESSION_CLOSE_INACTIVE = 11; // inactive account
62 
63  private static ?int $closing_context = null;
64 
65  protected static bool $enable_web_access_without_session = false;
66 
76  public static function _getData(string $a_session_id): string
77  {
78  if (!$a_session_id) {
79  // fix for php #70520
80  return '';
81  }
82  global $DIC;
83 
84  $ilDB = $DIC['ilDB'];
85 
86  $q = "SELECT data FROM usr_session WHERE session_id = " .
87  $ilDB->quote($a_session_id, "text");
88  $set = $ilDB->query($q);
89  $rec = $ilDB->fetchAssoc($set);
90  if (!is_array($rec)) {
91  return '';
92  }
93 
94  // fix for php #70520
95  return (string) $rec["data"];
96  }
97 
103  public static function lookupExpireTime(string $a_session_id): int
104  {
105  global $DIC;
106 
107  $ilDB = $DIC['ilDB'];
108 
109  $query = 'SELECT expires FROM usr_session WHERE session_id = ' .
110  $ilDB->quote($a_session_id, 'text');
111  $res = $ilDB->query($query);
112  if ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
113  return (int) $row->expires;
114  }
115  return 0;
116  }
117 
118  public static function _writeData(string $a_session_id, string $a_data): bool
119  {
120  global $DIC;
121 
123  $ilDB = $DIC['ilDB'];
125  $ilClientIniFile = $DIC['ilClientIniFile'];
126 
127  if (self::isWebAccessWithoutSessionEnabled()) {
128  // Prevent session data written for web access checker
129  // when no cookie was sent (e.g. for pdf files linking others).
130  // This would result in new session records for each request.
131  return true;
132  }
133 
134  if (!$a_session_id) {
135  return true;
136  }
137 
138  $now = time();
139 
140  // prepare session data
141  $fields = [
142  'user_id' => [ilDBConstants::T_INTEGER, (int) (self::get('_authsession_user_id') ?? 0)],
143  'expires' => [ilDBConstants::T_INTEGER, self::getExpireValue()],
144  'data' => [ilDBConstants::T_CLOB, $a_data],
145  'ctime' => [ilDBConstants::T_INTEGER, $now],
146  'type' => [ilDBConstants::T_INTEGER, (int) (self::get('SessionType') ?? 0)]
147  ];
148  if ($ilClientIniFile->readVariable('session', 'save_ip')) {
149  $fields['remote_addr'] = [ilDBConstants::T_TEXT, $_SERVER['REMOTE_ADDR'] ?? ''];
150  }
151 
152  if (self::_exists($a_session_id)) {
153  // note that we do this only when inserting the new record
154  // updating may get us other contexts for the same session, especially ilContextWAC, which we do not want
155  if (class_exists('ilContext') && ilContext::isSessionMainContext()) {
156  $fields['context'] = [ilDBConstants::T_TEXT, ilContext::getType()];
157  }
158  $ilDB->update(
159  'usr_session',
160  $fields,
161  ['session_id' => [ilDBConstants::T_TEXT, $a_session_id]]
162  );
163  } else {
164  $fields['session_id'] = [ilDBConstants::T_TEXT, $a_session_id];
165  $fields['createtime'] = [ilDBConstants::T_INTEGER, $now];
166 
167  // note that we do this only when inserting the new record
168  // updating may get us other contexts for the same session, especially ilContextWAC, which we do not want
169  if (class_exists('ilContext')) {
170  $fields['context'] = [ilDBConstants::T_TEXT, ilContext::getType()];
171  }
172 
173  $insert_fields = implode(', ', array_keys($fields));
174  $insert_values = implode(
175  ', ',
176  array_map(
177  static fn(string $type, $value): string => $ilDB->quote($value, $type),
178  array_column($fields, 0),
179  array_column($fields, 1)
180  )
181  );
182 
183  $update_fields = array_filter(
184  $fields,
185  static fn(string $field): bool => !in_array($field, ['session_id', 'user_id', 'createtime'], true),
186  ARRAY_FILTER_USE_KEY
187  );
188  $update_values = implode(
189  ', ',
190  array_map(
191  static fn(string $field, string $type, $value): string => $field . ' = ' . $ilDB->quote(
192  $value,
193  $type
194  ),
195  array_keys($update_fields),
196  array_column($update_fields, 0),
197  array_column($update_fields, 1)
198  )
199  );
200 
201  $ilDB->manipulate(
202  'INSERT INTO usr_session (' . $insert_fields . ') '
203  . 'VALUES (' . $insert_values . ') '
204  . 'ON DUPLICATE KEY UPDATE ' . $update_values
205  );
206 
207  // check type against session control
208  $type = (int) $fields['type'][1];
209  if (in_array($type, ilSessionControl::$session_types_controlled, true)) {
211  $fields['session_id'][1],
212  $type,
213  $fields['createtime'][1],
214  $fields['user_id'][1]
215  );
216  }
217  }
218 
219  if (!$DIC->cron()->manager()->isJobActive('auth_destroy_expired_sessions')) {
220  // finally delete deprecated sessions
221  $random = new ilRandom();
222  if ($random->int(0, 50) === 2) {
223  // get time _before_ destroying expired sessions
224  self::_destroyExpiredSessions();
226  }
227  }
228 
229  return true;
230  }
231 
232 
233 
240  public static function _exists(string $a_session_id): bool
241  {
242  if (!$a_session_id) {
243  return false;
244  }
245  global $DIC;
246 
247  $ilDB = $DIC['ilDB'];
248 
249  $q = "SELECT 1 FROM usr_session WHERE session_id = " . $ilDB->quote($a_session_id, "text");
250  $set = $ilDB->query($q);
251 
252  return $ilDB->numRows($set) > 0;
253  }
254 
262  public static function _destroy($a_session_id, ?int $a_closing_context = null, $a_expired_at = null): bool
263  {
264  global $DIC;
265 
266  $ilDB = $DIC['ilDB'];
267 
268  if (!$a_closing_context) {
269  $a_closing_context = self::$closing_context;
270  }
271 
272  ilSessionStatistics::closeRawEntry($a_session_id, $a_closing_context, $a_expired_at);
273 
274  if (!is_array($a_session_id)) {
275  $q = "DELETE FROM usr_session WHERE session_id = " .
276  $ilDB->quote($a_session_id, "text");
277  } else {
278  // array: id => timestamp - so we get rid of timestamps
279  if ($a_expired_at) {
280  $a_session_id = array_keys($a_session_id);
281  }
282  $q = "DELETE FROM usr_session WHERE " .
283  $ilDB->in("session_id", $a_session_id, false, "text");
284  }
285 
286  ilSessionIStorage::destroySession($a_session_id);
287 
288  $ilDB->manipulate($q);
289 
290  if (ilContext::usesHTTP()) {
291  try {
292  // only delete session cookie if it is set in the current request
293  if ($DIC->http()->wrapper()->cookie()->has(session_name()) &&
294  $DIC->http()->wrapper()->cookie()->retrieve(
295  session_name(),
296  $DIC->refinery()->kindlyTo()->string()
297  ) === $a_session_id) {
298  $cookieJar = $DIC->http()->cookieJar()->without(session_name());
299  $cookieJar->renderIntoResponseHeader($DIC->http()->response());
300  }
301  } catch (\Throwable $e) {
302  // ignore
303  // this is needed for "header already" sent errors when the random cleanup of expired sessions is triggered
304  }
305  }
306 
307  return true;
308  }
309 
315  public static function _destroyByUserId(int $a_user_id): bool
316  {
317  global $DIC;
318 
319  $ilDB = $DIC['ilDB'];
320 
321  $q = "DELETE FROM usr_session WHERE user_id = " .
322  $ilDB->quote($a_user_id, "integer");
323  $ilDB->manipulate($q);
324 
325  return true;
326  }
327 
332  public static function _destroyExpiredSessions(): int
333  {
334  global $DIC;
335 
336  $ilDB = $DIC['ilDB'];
337 
338  $q = 'SELECT session_id, expires FROM usr_session WHERE expires < ' . $ilDB->quote(time(), ilDBConstants::T_INTEGER);
339  $res = $ilDB->query($q);
340  $ids = [];
341  while ($row = $ilDB->fetchAssoc($res)) {
342  $ids[$row['session_id']] = (int) $row['expires'];
343  }
344  if ($ids !== []) {
345  self::_destroy($ids, self::SESSION_CLOSE_EXPIRE, true);
346  }
347 
348  return count($ids);
349  }
350 
357  public static function _duplicate(string $a_session_id): string
358  {
359  global $DIC;
360 
361  $ilDB = $DIC['ilDB'];
362 
363  // Create new session id
364  $new_session = $a_session_id;
365  do {
366  $new_session = md5($new_session);
367  $q = "SELECT * FROM usr_session WHERE " .
368  "session_id = " . $ilDB->quote($new_session, "text");
369  $res = $ilDB->query($q);
370  } while ($ilDB->fetchAssoc($res));
371 
372  $query = "SELECT * FROM usr_session " .
373  "WHERE session_id = " . $ilDB->quote($a_session_id, "text");
374  $res = $ilDB->query($query);
375 
376  if ($row = $ilDB->fetchObject($res)) {
377  self::_writeData($new_session, $row->data);
378  return $new_session;
379  }
380  //TODO check if throwing an excpetion might be a better choice
381  return "";
382  }
383 
393  public static function getExpireValue(bool $fixedMode = false): int
394  {
395  global $DIC;
396 
397  if ($fixedMode) {
398  // fixed session
399  return time() + self::getIdleValue($fixedMode);
400  }
401 
403  $ilSetting = $DIC['ilSetting'];
404  if ($ilSetting->get('session_handling_type', (string) self::SESSION_HANDLING_FIXED) === (string) self::SESSION_HANDLING_FIXED) {
405  return time() + self::getIdleValue($fixedMode);
406  }
407 
408  if ($ilSetting->get('session_handling_type', (string) self::SESSION_HANDLING_FIXED) === (string) self::SESSION_HANDLING_LOAD_DEPENDENT) {
409  // load dependent session settings
410  $max_idle = (int) ($ilSetting->get('session_max_idle') ?? ilSessionControl::DEFAULT_MAX_IDLE);
411  return time() + $max_idle * 60;
412  }
413  return time() + ilSessionControl::DEFAULT_MAX_IDLE * 60;
414  }
415 
423  public static function getIdleValue(bool $fixedMode = false): int
424  {
425  global $DIC;
426 
427  $ilSetting = $DIC['ilSetting'];
428  $ilClientIniFile = $DIC['ilClientIniFile'];
429 
430  if ($fixedMode || $ilSetting->get('session_handling_type', (string) self::SESSION_HANDLING_FIXED) === (string) self::SESSION_HANDLING_FIXED) {
431  // fixed session
432  return (int) $ilClientIniFile->readVariable('session', 'expire');
433  }
434 
435  if ($ilSetting->get('session_handling_type', (string) self::SESSION_HANDLING_FIXED) === (string) self::SESSION_HANDLING_LOAD_DEPENDENT) {
436  // load dependent session settings
437  return ((int) $ilSetting->get('session_max_idle', (string) (ilSessionControl::DEFAULT_MAX_IDLE))) * 60;
438  }
440  }
441 
449  public static function getSessionExpireValue(): int
450  {
451  return self::getIdleValue(true);
452  }
453 
457  public static function set(string $a_var, $a_val): void
458  {
459  $_SESSION[$a_var] = $a_val;
460  }
461 
465  public static function get(string $a_var)
466  {
467  return $_SESSION[$a_var] ?? null;
468  }
469 
470  public static function has($a_var): bool
471  {
472  return isset($_SESSION[$a_var]);
473  }
474 
478  public static function clear(string $a_var): void
479  {
480  if (isset($_SESSION[$a_var])) {
481  unset($_SESSION[$a_var]);
482  }
483  }
484 
485  public static function dumpToString(): string
486  {
487  return print_r($_SESSION, true);
488  }
489 
493  public static function setClosingContext(int $a_context): void
494  {
495  self::$closing_context = $a_context;
496  }
497 
501  public static function getClosingContext(): int
502  {
503  return self::$closing_context;
504  }
505 
506 
507 
511  public static function isWebAccessWithoutSessionEnabled(): bool
512  {
513  return self::$enable_web_access_without_session;
514  }
515 
519  public static function enableWebAccessWithoutSession(bool $enable_web_access_without_session): void
520  {
521  self::$enable_web_access_without_session = $enable_web_access_without_session;
522  }
523 }
const SESSION_CLOSE_IDLE
$res
Definition: ltiservices.php:69
static enableWebAccessWithoutSession(bool $enable_web_access_without_session)
static _duplicate(string $a_session_id)
Duplicate session.
static _destroyByUserId(int $a_user_id)
Destroy session.
static dumpToString()
static createRawEntry(string $a_session_id, int $a_session_type, int $a_timestamp, int $a_user_id)
Create raw data entry.
static _getData(string $a_session_id)
Get session data from table.
static usesHTTP()
Uses HTTP aka browser.
const SESSION_CLOSE_INACTIVE
static _exists(string $a_session_id)
Check whether session exists.
static bool $enable_web_access_without_session
const SESSION_CLOSE_LOGIN
const SESSION_CLOSE_TIME
static isSessionMainContext()
Context that are not only temporary in a session (e.g.
static lookupExpireTime(string $a_session_id)
Lookup expire time for a specific session.
const SESSION_HANDLING_FIXED
static _destroyExpiredSessions()
Destroy expired sessions.
static aggretateRaw(int $a_now)
Aggregate raw session data (older than given time)
const SESSION_CLOSE_EXPIRE
global $DIC
Definition: feed.php:28
const SESSION_CLOSE_USER
$_SERVER['HTTP_HOST']
Definition: raiseError.php:10
static closeRawEntry($a_session_id, ?int $a_context=null, $a_expired_at=null)
Close raw data entry.
static has($a_var)
static destroySession($a_session_id)
Destroy session(s).
get(string $key, Refinery\Transformation $t)
Get passed parameter, if not data passed, get key from http request.
static isWebAccessWithoutSessionEnabled()
const SESSION_CLOSE_LIMIT
const SESSION_CLOSE_PUBLIC
static _destroy($a_session_id, ?int $a_closing_context=null, $a_expired_at=null)
Destroy session.
static array $session_types_controlled
const SESSION_CLOSE_SIMUL
global $ilSetting
Definition: privfeed.php:18
static setClosingContext(int $a_context)
set closing context (for statistics)
Wrapper for generation of random numbers, strings, bytes.
$q
Definition: shib_logout.php:21
static getIdleValue(bool $fixedMode=false)
Returns the idle time in seconds.
const SESSION_HANDLING_LOAD_DEPENDENT
static getType()
Get context type.
static getClosingContext()
get closing context (for statistics)
static getSessionExpireValue()
Returns the session expiration value.
static clear(string $a_var)
const SESSION_CLOSE_FIRST
const SESSION_CLOSE_IP
static int $closing_context