ILIAS  release_8 Revision v8.24
class.ilSessionControl.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
25{
30 public const DEFAULT_MAX_COUNT = 0;
31 public const DEFAULT_MIN_IDLE = 15;
32 public const DEFAULT_MAX_IDLE = 30;
35
41 private static array $setting_fields = array(
42 'session_max_count',
43 'session_min_idle',
44 'session_max_idle',
45 'session_max_idle_after_first_request',
46 'session_allow_client_maintenance',
47 'session_handling_type'
48 );
49
54 private const SESSION_TYPE_UNKNOWN = 0;
55 private const SESSION_TYPE_SYSTEM = 1;
56 private const SESSION_TYPE_ADMIN = 2;
57 private const SESSION_TYPE_USER = 3;
58 private const SESSION_TYPE_ANONYM = 4;
59
60 private const SESSION_TYPE_KEY = "SessionType";
67 public static array $session_types_controlled = array(
68 self::SESSION_TYPE_USER,
69 self::SESSION_TYPE_ANONYM
70 );
71
78 private static array $session_types_not_controlled = array(
79 self::SESSION_TYPE_UNKNOWN,
80 self::SESSION_TYPE_SYSTEM,
81 self::SESSION_TYPE_ADMIN
82 );
83
89 public static function handleLoginEvent(string $a_login, ilAuthSession $auth_session): bool
90 {
91 global $DIC;
92
93 $ilSetting = $DIC['ilSetting'];
94
95 $user_id = ilObjUser::_lookupId($a_login);
96
97 // we need the session type for the session statistics
98 // regardless of the current session handling type
99 switch (true) {
100 case isset($_ENV['SHELL']):
102 break;
103
104 case $user_id === ANONYMOUS_USER_ID:
106 break;
107
110 break;
111
112 default:
114 break;
115 }
116
117 ilSession::set(self::SESSION_TYPE_KEY, $type);
118 self::debug(__METHOD__ . " --> update sessions type to (" . $type . ")");
119
120 // do not handle login event in fixed duration mode
121 if ((int) $ilSetting->get('session_handling_type', (string) ilSession::SESSION_HANDLING_FIXED) !== ilSession::SESSION_HANDLING_LOAD_DEPENDENT) {
122 return true;
123 }
124
125 if (in_array($type, self::$session_types_controlled, true)) {
126 //TODO rework this, as it did return value of a void method call
127 self::checkCurrentSessionIsAllowed($auth_session, $user_id);
128 return true;
129 }
130 return false;
131 }
132
136 public static function handleLogoutEvent(): void
137 {
138 global $DIC;
139
140 $ilSetting = $DIC['ilSetting'];
141
142 // do not handle logout event in fixed duration mode
143 if ((int) $ilSetting->get('session_handling_type', '0') !== 1) {
144 return;
145 }
146
147 ilSession::set('SessionType', self::SESSION_TYPE_UNKNOWN);
148 self::debug(__METHOD__ . " --> reset sessions type to (" . ilSession::get('SessionType') . ")");
149
150 // session_destroy() is called in auth, so raw data will be updated
151
153 }
154
161 private static function checkCurrentSessionIsAllowed(ilAuthSession $auth, int $a_user_id): void
162 {
163 global $DIC;
164
165 $ilSetting = $DIC['ilSetting'];
166
167 $max_sessions = (int) $ilSetting->get('session_max_count', (string) self::DEFAULT_MAX_COUNT);
168
169 if ($max_sessions > 0) {
170 // get total number of sessions
171 $num_sessions = self::getExistingSessionCount(self::$session_types_controlled);
172
173 self::debug(__METHOD__ . "--> total existing sessions (" . $num_sessions . ")");
174
175 if (($num_sessions + 1) > $max_sessions) {
176 self::debug(__METHOD__ . ' --> limit for session pool reached, but try kicking some first request abidencer');
177
178 self::kickFirstRequestAbidencer(self::$session_types_controlled);
179
180 // get total number of sessions again
181 $num_sessions = self::getExistingSessionCount(self::$session_types_controlled);
182
183 if (($num_sessions + 1) > $max_sessions) {
184 self::debug(__METHOD__ . ' --> limit for session pool still reached so try kick one min idle session');
185
186 self::kickOneMinIdleSession(self::$session_types_controlled);
187
188 // get total number of sessions again
189 $num_sessions = self::getExistingSessionCount(self::$session_types_controlled);
190
191 if (($num_sessions + 1) > $max_sessions) {
192 self::debug(__METHOD__ . ' --> limit for session pool still reached so logout session (' . session_id() . ') and trigger event');
193
195
196 // as the session is opened and closed in one request, there
197 // is no proper session yet and we have to do this ourselves
199 session_id(),
200 ilSession::get(self::SESSION_TYPE_KEY),
201 time(),
202 $a_user_id
203 );
204
205 $auth->logout();
206
207 // Trigger reachedSessionPoolLimit Event
208 $ilAppEventHandler = $DIC['ilAppEventHandler'];
209 $ilAppEventHandler->raise(
210 'Services/Authentication',
211 'reachedSessionPoolLimit',
212 array()
213 );
214
215 // auth won't do this, we need to close session properly
216 // already done in new implementation
217 // session_destroy();
218
219 ilUtil::redirect('login.php?reached_session_limit=true');
220 } else {
221 self::debug(__METHOD__ . ' --> limit of session pool not reached anymore after kicking one min idle session');
222 }
223 } else {
224 self::debug(__METHOD__ . ' --> limit of session pool not reached anymore after kicking some first request abidencer');
225 }
226 } else {
227 self::debug(__METHOD__ . ' --> limit for session pool not reached yet');
228 }
229 } else {
230 self::debug(__METHOD__ . ' --> limit for session pool not set so check is bypassed');
231 }
232 }
233
237 public static function getExistingSessionCount(array $a_types): int
238 {
239 global $DIC;
240
241 $ilDB = $DIC['ilDB'];
242
243 $ts = time();
244
245 $query = "SELECT count(session_id) AS num_sessions FROM usr_session " .
246 "WHERE expires > %s " .
247 "AND " . $ilDB->in('type', $a_types, false, 'integer');
248
249 $res = $ilDB->queryF($query, array('integer'), array($ts));
250 return (int) $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)->num_sessions;
251 }
252
258 private static function kickOneMinIdleSession(array $a_types): void
259 {
260 global $DIC;
261
262 $ilDB = $DIC['ilDB'];
263 $ilSetting = $DIC['ilSetting'];
264
265 $ts = time();
266 $min_idle = (int) $ilSetting->get('session_min_idle', (string) self::DEFAULT_MIN_IDLE) * 60;
267 $max_idle = (int) $ilSetting->get('session_max_idle', (string) self::DEFAULT_MAX_IDLE) * 60;
268
269 $query = "SELECT session_id,expires FROM usr_session WHERE expires >= %s " .
270 "AND (expires - %s) < (%s - %s) " .
271 "AND " . $ilDB->in('type', $a_types, false, 'integer') . " ORDER BY expires";
272
273 $res = $ilDB->queryF(
274 $query,
275 array('integer', 'integer', 'integer', 'integer'),
276 array($ts, $ts, $max_idle, $min_idle)
277 );
278
279 if ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
280 ilSession::_destroy($row->session_id, ilSession::SESSION_CLOSE_IDLE, $row->expires);
281
282 self::debug(__METHOD__ . ' --> successfully deleted one min idle session');
283
284 return;
285 }
286 self::debug(__METHOD__ . ' --> no min idle session available for deletion');
287 }
288
293 private static function kickFirstRequestAbidencer(array $a_types): void
294 {
295 global $DIC;
296
297 $ilDB = $DIC['ilDB'];
298 $ilSetting = $DIC['ilSetting'];
299
300 $max_idle_after_first_request = (int) $ilSetting->get('session_max_idle_after_first_request') * 60;
301
302 if ((int) $max_idle_after_first_request === 0) {
303 return;
304 }
305
306 $query = "SELECT session_id,expires FROM usr_session WHERE " .
307 "(ctime - createtime) < %s " .
308 "AND (%s - createtime) > %s " .
309 "AND " . $ilDB->in('type', $a_types, false, 'integer');
310
311 $res = $ilDB->queryF(
312 $query,
313 array('integer', 'integer', 'integer'),
314 array($max_idle_after_first_request, time(), $max_idle_after_first_request)
315 );
316
317 $session_ids = array();
318 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
319 $session_ids[$row->session_id] = $row->expires;
320 }
322
323 self::debug(__METHOD__ . ' --> Finished kicking first request abidencer');
324 }
325
332 private static function isValidSession(string $a_sid): bool
333 {
334 global $DIC;
335
336 $ilDB = $DIC['ilDB'];
337
338 $query = "SELECT session_id, expires FROM usr_session " .
339 "WHERE session_id = %s";
340
341 $res = $ilDB->queryF($query, array('text'), array($a_sid));
342
343 $ts = time();
344
345 $sessions = array();
346
347 while ($row = $ilDB->fetchAssoc($res)) {
348 if ($row['expires'] > $ts) {
349 self::debug(__METHOD__ . ' --> Found a valid session with id (' . $a_sid . ')');
350 $sessions[] = $row;
351 } else {
352 self::debug(__METHOD__ . ' --> Found an expired session with id (' . $a_sid . ')');
353 }
354 }
355
356 if (count($sessions) === 1) {
357 self::debug(__METHOD__ . ' --> Exact one valid session found for session id (' . $a_sid . ')');
358
359 return true;
360 }
361
362 if (count($sessions) > 1) {
363 self::debug(__METHOD__ . ' --> Strange!!! More than one sessions found for given session id! (' . $a_sid . ')');
364 } else {
365 self::debug(__METHOD__ . ' --> No valid session found for session id (' . $a_sid . ')');
366 }
367
368 return false;
369 }
370
374 private static function removeSessionCookie(): void
375 {
376 ilUtil::setCookie(session_name(), 'deleted', true, true);
377 self::debug('Session cookie has been removed');
378 }
379
387 private static function checkAdministrationPermission(int $a_user_id): bool
388 {
389 if (!$a_user_id) {
390 return false;
391 }
392
393 global $DIC;
394
395 $rbacsystem = $DIC['rbacsystem'];
396
397 $access = $rbacsystem->checkAccessOfUser(
398 $a_user_id,
399 'read,visible',
401 );
402
403 return $access;
404 }
405
411 private static function debug(string $a_debug_log_message): void
412 {
413 global $DIC;
414
415 $logger = $DIC->logger()->auth();
416
417 $logger->debug($a_debug_log_message);
418 }
419
425 public static function getSettingFields(): array
426 {
428 }
429}
static _lookupId($a_user_str)
static handleLogoutEvent()
reset sessions type to unknown
static array $session_types_not_controlled
all session types that will be involved when count of sessions will be determined or when idleing ses...
static handleLoginEvent(string $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 checkCurrentSessionIsAllowed(ilAuthSession $auth, int $a_user_id)
checks wether the current session exhaust the limit of sessions when limit is reached it deletes "fir...
static isValidSession(string $a_sid)
checks if session exists for given id and if it is still valid
static checkAdministrationPermission(int $a_user_id)
checks wether a given user login relates to an user with administrative permissions
static array $setting_fields
all fieldnames that are saved in settings table
const SESSION_TYPE_UNKNOWN
session types from which one is assigned to each session
static debug(string $a_debug_log_message)
logs the given debug message in \ilLogger
static getSettingFields()
returns the array of setting fields
static getExistingSessionCount(array $a_types)
returns number of valid sessions relating to given session types
static removeSessionCookie()
removes a session cookie, so it is not sent by browser anymore
const DEFAULT_MAX_COUNT
default value for settings that have not been defined in setup or administration yet
static array $session_types_controlled
static kickOneMinIdleSession(array $a_types)
if sessions exist that relates to given session types and idled longer than min idle parameter,...
static kickFirstRequestAbidencer(array $a_types)
kicks sessions of users that abidence after login so people could not login and go for coffe break ;-...
static createRawEntry(string $a_session_id, int $a_session_type, int $a_timestamp, int $a_user_id)
Create raw data entry.
static get(string $a_var)
const SESSION_HANDLING_LOAD_DEPENDENT
const SESSION_HANDLING_FIXED
static setClosingContext(int $a_context)
set closing context (for statistics)
const SESSION_CLOSE_FIRST
static _destroy($a_session_id, ?int $a_closing_context=null, $a_expired_at=null)
Destroy session.
const SESSION_CLOSE_IDLE
static set(string $a_var, $a_val)
Set a value.
const SESSION_CLOSE_LIMIT
static redirect(string $a_script)
static setCookie(string $a_cookie_name, string $a_cookie_value='', bool $a_also_set_super_global=true, bool $a_set_cookie_invalid=false)
const ANONYMOUS_USER_ID
Definition: constants.php:27
const SYSTEM_FOLDER_ID
Definition: constants.php:35
global $DIC
Definition: feed.php:28
$res
Definition: ltiservices.php:69
$auth
Definition: metadata.php:76
global $ilSetting
Definition: privfeed.php:17
$query
$type