ILIAS  trunk Revision v13.0_alpha-52-g21c366bbdd4
class.ilSessionReminderCheck.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
25use Psr\Http\Message\ResponseInterface;
27use ILIAS\Refinery\Factory as Refinery;
28
30{
31 public function __construct(
32 private GlobalHttpState $http,
33 private Refinery $refinery,
34 private ilLanguage $lng,
35 private ilDBInterface $db,
36 private ilIniFile $client_ini,
37 private ilLogger $logger,
38 private ClockInterface $clock,
39 private ilSetting $settings,
40 private ?TokenCipher $token_cipher
41 ) {
42 }
43
44 public function handle(): ResponseInterface
45 {
46 // disable session writing and extension of expiration time
48
49 $response = ['remind' => false];
50
51 if ($this->token_cipher === null) {
52 $this->logger->warning('Session reminder call without encryption key, please run the ILIAS setup (update).');
53 $response['message'] = 'ILIAS could not decrypt the session reminder token.';
54 return $this->toJsonResponse($response);
55 }
56
57 $token = $this->http->wrapper()->post()->retrieve(
58 'token',
59 $this->refinery->byTrying([
60 $this->refinery->kindlyTo()->string(),
61 $this->refinery->always('')
62 ])
63 );
64
65 // Invalid tokens are rejected before the database is accessed
66 $token_data = $token !== '' ? $this->token_cipher->decode($token) : null;
67 if ($token_data === null || $token_data['user_id'] <= 0) {
68 $this->logger->debug('Session reminder call with missing or invalid token');
69 $response['message'] = 'ILIAS could not decrypt the session reminder token.';
70 return $this->toJsonResponse($response);
71 }
72
73 $user_id = $token_data['user_id'];
74 $hash = $token_data['session_hash'];
75
76 $this->logger->debug('Session reminder call for user ' . $user_id . ' and session id hash: ' . $hash);
77
78 // The hash is compared in PHP: Hashing in the query would have to be done for every row by the database server
79 $res = $this->db->queryF(
80 'SELECT usess.session_id, usess.expires, usess.user_id, od.create_date ' .
81 'FROM usr_session usess ' .
82 'INNER JOIN object_data od ON od.obj_id = usess.user_id ' .
83 'WHERE usess.user_id = %s',
85 [$user_id]
86 );
87
88 $matches = [];
89 while ($row = $this->db->fetchAssoc($res)) {
90 $row_hash = hash(
91 'sha256',
92 implode('', [
93 $row['session_id'],
94 $row['user_id'],
95 $row['create_date']
96 ])
97 );
98 if (hash_equals($row_hash, $hash)) {
99 $matches[] = $row;
100 }
101 }
102
103 if ($matches === []) {
104 $response['message'] = 'ILIAS could not determine the session data.';
105 return $this->toJsonResponse($response);
106 }
107
108 if (count($matches) > 1) {
109 $response['message'] = 'The determined session data is not unique.';
110 return $this->toJsonResponse($response);
111 }
112
113 $data = $matches[0];
114 if (!$this->isAuthenticatedUsrSession($data)) {
115 $response['message'] = 'ILIAS could not fetch the session data or the corresponding user is no more authenticated.';
116 return $this->toJsonResponse($response);
117 }
118
119 $expiration_time = $data['expires'];
120 if ($expiration_time === null) {
121 $response['message'] = 'ILIAS could not determine the expiration time from the session data.';
122 return $this->toJsonResponse($response);
123 }
124 $expiration_time = (int) $data['expires'];
125
126 if ($this->isSessionAlreadyExpired($expiration_time)) {
127 $response['message'] = 'The session is already expired. The client should have received a remind command before.';
128 return $this->toJsonResponse($response);
129 }
130
132 $ilUser = ilObjectFactory::getInstanceByObjId((int) $data['user_id'], false);
133 if (!($ilUser instanceof ilObjUser)) {
134 $response['message'] = 'ILIAS could not fetch the session data or the corresponding user is no more authenticated.';
135 return $this->toJsonResponse($response);
136 }
137
138 $session_reminder = new ilSessionReminder(
139 $ilUser,
140 $this->clock,
141 $this->settings
142 );
143 $reminder_time = $expiration_time - ($session_reminder->getEffectiveLeadTime() * 60);
144 if ($reminder_time > $this->clock->now()->getTimestamp()) {
145 // session will expire in <lead_time> minutes
146 $response['message'] = 'Lead time not reached, yet. Current time: ' .
147 date('Y-m-d H:i:s') . ', Reminder time: ' . date('Y-m-d H:i:s', $reminder_time);
148 return $this->toJsonResponse($response);
149 }
150
151 $dateTime = new ilDateTime($expiration_time, IL_CAL_UNIX);
152 switch ($ilUser->getTimeFormat()) {
154 $formatted_expiration_time = $dateTime->get(IL_CAL_FKT_DATE, 'g:ia', $ilUser->getTimeZone());
155 break;
156
158 default:
159 $formatted_expiration_time = $dateTime->get(IL_CAL_FKT_DATE, 'H:i', $ilUser->getTimeZone());
160 break;
161 }
162
163 $response = [
164 'extend_url' => './ilias.php?baseClass=' . ilDashboardGUI::class,
165 'txt' => str_replace(
166 "\\n",
167 '%0A',
168 sprintf(
169 $this->lng->txt('session_reminder_alert'),
170 ilDatePresentation::secondsToString($expiration_time - $this->clock->now()->getTimestamp()),
171 $formatted_expiration_time,
172 $this->client_ini->readVariable('client', 'name') . ' | ' . ilUtil::_getHttpPath()
173 )
174 ),
175 'remind' => true
176 ];
177
178 return $this->toJsonResponse($response);
179 }
180
185 private function toJsonResponse($data): ResponseInterface
186 {
187 return $this->http->response()
188 ->withHeader(ResponseHeader::CONTENT_TYPE, 'application/json')
189 ->withBody(Streams::ofString(json_encode($data, JSON_THROW_ON_ERROR)));
190 }
191
192 private function isSessionAlreadyExpired(int $expirationTime): bool
193 {
194 return $expirationTime < $this->clock->now()->getTimestamp();
195 }
196
197 private function isAuthenticatedUsrSession(?array $data): bool
198 {
199 return (
200 is_array($data) &&
201 isset($data['user_id']) &&
202 (int) $data['user_id'] > 0 &&
203 (int) $data['user_id'] !== ANONYMOUS_USER_ID
204 );
205 }
206}
$settings
Definition: index.php:44
Encodes the opaque token the session reminder sends to sessioncheck.php.
Definition: TokenCipher.php:37
Builds data types.
Definition: Factory.php:36
Stream factory which enables the user to create streams without the knowledge of the concrete class.
Definition: Streams.php:32
const IL_CAL_UNIX
const IL_CAL_FKT_DATE
static secondsToString(int $seconds, bool $force_with_seconds=false, ?ilLanguage $a_lng=null)
converts seconds to string: Long: 7 days 4 hour(s) ...
@classDescription Date and time handling
INI file parser — compatibility wrapper.
language handling
Component logger with individual log levels by component id.
User class.
static getInstanceByObjId(?int $obj_id, bool $stop_on_error=true)
get an instance of an Ilias object by object id
__construct(private GlobalHttpState $http, private Refinery $refinery, private ilLanguage $lng, private ilDBInterface $db, private ilIniFile $client_ini, private ilLogger $logger, private ClockInterface $clock, private ilSetting $settings, private ?TokenCipher $token_cipher)
isSessionAlreadyExpired(int $expirationTime)
static enableWebAccessWithoutSession(bool $enable_web_access_without_session)
ILIAS Setting Class.
static _getHttpPath()
const ANONYMOUS_USER_ID
Definition: constants.php:27
Interface GlobalHttpState.
Interface ResponseHeader.
Interface ilDBInterface.
static http()
Fetches the global http state from ILIAS.
global $lng
Definition: privfeed.php:26
foreach($_POST as $key=> $value) $res
$data
Definition: storeScorm.php:45
$token
Definition: xapitoken.php:69
$response
Definition: xapitoken.php:92