ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
class.ilSession.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
21 class ilSession
22 {
28  public const int SESSION_CLOSE_USER = 1; // manual logout
29  public const int SESSION_CLOSE_EXPIRE = 2; // has expired
30  public const int SESSION_CLOSE_LOGIN = 6; // anonymous => login
31  public const int SESSION_CLOSE_PUBLIC = 7; // => anonymous
32  public const int SESSION_CLOSE_TIME = 8; // account time limit reached
33  public const int SESSION_CLOSE_IP = 9; // wrong ip
34  public const int SESSION_CLOSE_SIMUL = 10; // simultaneous login
35  public const int SESSION_CLOSE_INACTIVE = 11; // inactive account
36 
37  private static ?int $closing_context = null;
38 
39  protected static bool $enable_web_access_without_session = false;
40 
50  public static function _getData(string $a_session_id): string
51  {
52  if (!$a_session_id) {
53  // fix for php #70520
54  return '';
55  }
56  global $DIC;
57 
58  $ilDB = $DIC['ilDB'];
59 
60  $q = 'SELECT data FROM usr_session WHERE session_id = ' .
61  $ilDB->quote($a_session_id, 'text');
62  $set = $ilDB->query($q);
63  $rec = $ilDB->fetchAssoc($set);
64  if (!is_array($rec)) {
65  return '';
66  }
67 
68  // fix for php #70520
69  return (string) $rec['data'];
70  }
71 
77  public static function lookupExpireTime(string $a_session_id): int
78  {
79  global $DIC;
80 
81  $ilDB = $DIC['ilDB'];
82 
83  $query = 'SELECT expires FROM usr_session WHERE session_id = ' .
84  $ilDB->quote($a_session_id, 'text');
85  $res = $ilDB->query($query);
86  if ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
87  return (int) $row->expires;
88  }
89  return 0;
90  }
91 
92  public static function _writeData(string $a_session_id, string $a_data): bool
93  {
94  global $DIC;
95 
97  $ilDB = $DIC['ilDB'];
99  $ilClientIniFile = $DIC['ilClientIniFile'];
100 
101  if (self::isWebAccessWithoutSessionEnabled()) {
102  // Prevent session data written for web access checker
103  // when no cookie was sent (e.g. for pdf files linking others).
104  // This would result in new session records for each request.
105  return true;
106  }
107 
108  if (!$a_session_id) {
109  return true;
110  }
111 
112  $now = time();
113 
114  // prepare session data
115  $fields = [
116  'user_id' => [ilDBConstants::T_INTEGER, (int) (self::get('_authsession_user_id') ?? 0)],
117  'expires' => [ilDBConstants::T_INTEGER, self::getExpireValue()],
118  'data' => [ilDBConstants::T_CLOB, $a_data],
119  'ctime' => [ilDBConstants::T_INTEGER, $now],
120  'type' => [ilDBConstants::T_INTEGER, (int) (self::get('SessionType') ?? 0)]
121  ];
122  if ($ilClientIniFile->readVariable('session', 'save_ip')) {
123  $fields['remote_addr'] = [ilDBConstants::T_TEXT, $_SERVER['REMOTE_ADDR'] ?? ''];
124  }
125 
126  if (self::_exists($a_session_id)) {
127  // note that we do this only when inserting the new record
128  // updating may get us other contexts for the same session, especially ilContextWAC, which we do not want
129  if (class_exists('ilContext') && ilContext::isSessionMainContext()) {
130  $fields['context'] = [ilDBConstants::T_TEXT, ilContext::getType()];
131  }
132  $ilDB->update(
133  'usr_session',
134  $fields,
135  ['session_id' => [ilDBConstants::T_TEXT, $a_session_id]]
136  );
137  } else {
138  $fields['session_id'] = [ilDBConstants::T_TEXT, $a_session_id];
139  $fields['createtime'] = [ilDBConstants::T_INTEGER, $now];
140 
141  // note that we do this only when inserting the new record
142  // updating may get us other contexts for the same session, especially ilContextWAC, which we do not want
143  if (class_exists('ilContext')) {
144  $fields['context'] = [ilDBConstants::T_TEXT, ilContext::getType()];
145  }
146 
147  $insert_fields = implode(', ', array_keys($fields));
148  $insert_values = implode(
149  ', ',
150  array_map(
151  static fn(string $type, $value): string => $ilDB->quote($value, $type),
152  array_column($fields, 0),
153  array_column($fields, 1)
154  )
155  );
156 
157  $update_fields = array_filter(
158  $fields,
159  static fn(string $field): bool => !in_array($field, ['session_id', 'user_id', 'createtime'], true),
160  ARRAY_FILTER_USE_KEY
161  );
162  $update_values = implode(
163  ', ',
164  array_map(
165  static fn(string $field, string $type, $value): string => $field . ' = ' . $ilDB->quote(
166  $value,
167  $type
168  ),
169  array_keys($update_fields),
170  array_column($update_fields, 0),
171  array_column($update_fields, 1)
172  )
173  );
174 
175  $ilDB->manipulate(
176  'INSERT INTO usr_session (' . $insert_fields . ') '
177  . 'VALUES (' . $insert_values . ') '
178  . 'ON DUPLICATE KEY UPDATE ' . $update_values
179  );
180 
181  // check type against session control
182  $type = (int) $fields['type'][1];
183  if (in_array($type, ilSessionControl::$session_types_controlled, true)) {
185  $fields['session_id'][1],
186  $type,
187  $fields['createtime'][1],
188  $fields['user_id'][1]
189  );
190  }
191  }
192 
193  if (!$DIC->cron()->manager()->isJobActive('auth_destroy_expired_sessions')) {
194  $r = new \Random\Randomizer();
195  if ($r->getInt(0, 50) === 2) {
196  // get time _before_ destroying expired sessions
197  self::_destroyExpiredSessions();
199  }
200  }
201 
202  return true;
203  }
204 
205  public static function _exists(string $a_session_id): bool
206  {
207  if (!$a_session_id) {
208  return false;
209  }
210  global $DIC;
211 
212  $ilDB = $DIC['ilDB'];
213 
214  $q = 'SELECT 1 FROM usr_session WHERE session_id = ' . $ilDB->quote($a_session_id, 'text');
215  $set = $ilDB->query($q);
216 
217  return $ilDB->numRows($set) > 0;
218  }
219 
227  public static function _destroy($a_session_id, ?int $a_closing_context = null, $a_expired_at = null): bool
228  {
229  global $DIC;
230 
231  $ilDB = $DIC['ilDB'];
232 
233  if (!$a_closing_context) {
234  $a_closing_context = self::$closing_context;
235  }
236 
237  ilSessionStatistics::closeRawEntry($a_session_id, $a_closing_context, $a_expired_at);
238 
239  if (is_array($a_session_id)) {
240  // array: id => timestamp - so we get rid of timestamps
241  if ($a_expired_at) {
242  $a_session_id = array_keys($a_session_id);
243  }
244  $q = 'DELETE FROM usr_session WHERE ' .
245  $ilDB->in('session_id', $a_session_id, false, 'text');
246  } else {
247  $q = 'DELETE FROM usr_session WHERE session_id = ' .
248  $ilDB->quote($a_session_id, 'text');
249  }
250 
251  ilSessionIStorage::destroySession($a_session_id);
252 
253  $ilDB->manipulate($q);
254 
255  if (ilContext::usesHTTP()) {
256  try {
257  // only delete session cookie if it is set in the current request
258  if ($DIC->http()->wrapper()->cookie()->has(session_name()) &&
259  $DIC->http()->wrapper()->cookie()->retrieve(
260  session_name(),
261  $DIC->refinery()->kindlyTo()->string()
262  ) === $a_session_id) {
263  $cookieJar = $DIC->http()->cookieJar()->without(session_name());
264  $cookieJar->renderIntoResponseHeader($DIC->http()->response());
265  }
266  } catch (Throwable) {
267  // ignore
268  // this is needed for "header already" sent errors when the random cleanup of expired sessions is triggered
269  }
270  }
271 
272  return true;
273  }
274 
280  public static function _destroyByUserId(int $a_user_id): bool
281  {
282  global $DIC;
283 
284  $ilDB = $DIC['ilDB'];
285 
286  $q = 'DELETE FROM usr_session WHERE user_id = ' .
287  $ilDB->quote($a_user_id, 'integer');
288  $ilDB->manipulate($q);
289 
290  return true;
291  }
292 
297  public static function _destroyExpiredSessions(): int
298  {
299  global $DIC;
300 
301  $ilDB = $DIC['ilDB'];
302 
303  $q = 'SELECT session_id, expires FROM usr_session WHERE expires < ' . $ilDB->quote(time(), ilDBConstants::T_INTEGER);
304  $res = $ilDB->query($q);
305  $ids = [];
306  while ($row = $ilDB->fetchAssoc($res)) {
307  $ids[$row['session_id']] = (int) $row['expires'];
308  }
309  if ($ids !== []) {
310  self::_destroy($ids, self::SESSION_CLOSE_EXPIRE, true);
311  }
312 
313  return count($ids);
314  }
315 
322  public static function _duplicate(string $a_session_id): string
323  {
324  global $DIC;
325 
326  $ilDB = $DIC['ilDB'];
327 
328  // Create new session id
329  $new_session = $a_session_id;
330  do {
331  $new_session = md5($new_session);
332  $q = 'SELECT * FROM usr_session WHERE ' .
333  'session_id = ' . $ilDB->quote($new_session, 'text');
334  $res = $ilDB->query($q);
335  } while ($ilDB->fetchAssoc($res));
336 
337  $query = 'SELECT * FROM usr_session ' .
338  'WHERE session_id = ' . $ilDB->quote($a_session_id, 'text');
339  $res = $ilDB->query($query);
340 
341  if ($row = $ilDB->fetchObject($res)) {
342  self::_writeData($new_session, $row->data);
343  return $new_session;
344  }
345  //TODO check if throwing an excpetion might be a better choice
346  return '';
347  }
348 
352  public static function getExpireValue(): int
353  {
354  return time() + self::getIdleValue();
355  }
356 
360  public static function getIdleValue(): int
361  {
362  global $DIC;
363 
364  $ilClientIniFile = $DIC['ilClientIniFile'];
365 
366  return (int) $ilClientIniFile->readVariable('session', 'expire');
367  }
368 
372  public static function getSessionExpireValue(): int
373  {
374  return self::getIdleValue();
375  }
376 
380  public static function set(string $a_var, $a_val): void
381  {
382  $_SESSION[$a_var] = $a_val;
383  }
384 
388  public static function get(string $a_var)
389  {
390  return $_SESSION[$a_var] ?? null;
391  }
392 
393  public static function has($a_var): bool
394  {
395  return isset($_SESSION[$a_var]);
396  }
397 
398  public static function clear(string $a_var): void
399  {
400  if (isset($_SESSION[$a_var])) {
401  unset($_SESSION[$a_var]);
402  }
403  }
404 
405  public static function dumpToString(): string
406  {
407  return print_r($_SESSION, true);
408  }
409 
413  public static function setClosingContext(int $a_context): void
414  {
415  self::$closing_context = $a_context;
416  }
417 
421  public static function getClosingContext(): int
422  {
423  return self::$closing_context;
424  }
425 
426  public static function isWebAccessWithoutSessionEnabled(): bool
427  {
428  return self::$enable_web_access_without_session;
429  }
430 
431  public static function enableWebAccessWithoutSession(bool $enable_web_access_without_session): void
432  {
433  self::$enable_web_access_without_session = $enable_web_access_without_session;
434  }
435 }
$res
Definition: ltiservices.php:66
static enableWebAccessWithoutSession(bool $enable_web_access_without_session)
static _duplicate(string $a_session_id)
Duplicate session.
const int SESSION_CLOSE_INACTIVE
const int SESSION_CLOSE_TIME
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.
const int SESSION_CLOSE_LOGIN
static usesHTTP()
Uses HTTP aka browser.
static _exists(string $a_session_id)
static bool $enable_web_access_without_session
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.
static getIdleValue()
Returns the idle time in seconds.
static getExpireValue()
Returns the expiration timestamp in seconds.
static _destroyExpiredSessions()
Destroy expired sessions.
static aggretateRaw(int $a_now)
Aggregate raw session data (older than given time)
const int SESSION_CLOSE_SIMUL
const int SESSION_CLOSE_PUBLIC
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
$_SERVER['HTTP_HOST']
Definition: raiseError.php:26
get(string $class_name)
const int SESSION_CLOSE_USER
global $DIC
Definition: shib_login.php:26
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).
static isWebAccessWithoutSessionEnabled()
const int SESSION_CLOSE_IP
static _destroy($a_session_id, ?int $a_closing_context=null, $a_expired_at=null)
Destroy session.
static array $session_types_controlled
const int SESSION_CLOSE_EXPIRE
static setClosingContext(int $a_context)
set closing context (for statistics)
$q
Definition: shib_logout.php:23
static getType()
Get context type.
static getClosingContext()
get closing context (for statistics)
static getSessionExpireValue()
Returns the session expiration value.
static clear(string $a_var)
static int $closing_context
$r