‪TYPO3CMS  ‪main
UserSessionManager.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\Http\Message\ServerRequestInterface;
21 use Psr\Log\LoggerAwareInterface;
22 use Psr\Log\LoggerAwareTrait;
30 
45 class ‪UserSessionManager implements LoggerAwareInterface
46 {
47  use LoggerAwareTrait;
49 
50  protected const ‪SESSION_ID_LENGTH = 32;
51  protected const ‪GARBAGE_COLLECTION_LIFETIME = 86400;
52  protected const ‪LIFETIME_OF_ANONYMOUS_SESSION_DATA = 86400;
53 
60  protected int ‪$sessionLifetime;
61 
65  protected string ‪$loginType;
66 
73  {
74  $this->sessionBackend = ‪$sessionBackend;
75  $this->sessionLifetime = ‪$sessionLifetime;
76  $this->ipLocker = ‪$ipLocker;
77  $this->loginType = ‪$loginType;
78  }
79 
81  {
83  $this->garbageCollectionForAnonymousSessions = ‪$garbageCollectionForAnonymousSessions;
84  }
85  }
86 
95  public function ‪createFromRequestOrAnonymous(ServerRequestInterface $request, string $cookieName): ‪UserSession
96  {
97  try {
98  $cookieValue = (string)($request->getCookieParams()[$cookieName] ?? '');
99  $scope = $this->getCookieScope($request->getAttribute('normalizedParams') ?? ‪NormalizedParams::createFromRequest($request));
100  $sessionId = ‪UserSession::resolveIdentifierFromJwt($cookieValue, $scope);
101  } catch (\‪Exception $exception) {
102  $this->logger->debug('Could not resolve session identifier from JWT', ['exception' => $exception]);
103  }
104  return $this->‪getSessionFromSessionId($sessionId ?? '') ?? $this->‪createAnonymousSession();
105  }
106 
111  {
112  $randomSessionId = $this->‪createSessionId();
113  return ‪UserSession::createNonFixated($randomSessionId);
114  }
115 
123  public function ‪createSessionFromStorage(string $sessionId): ‪UserSession
124  {
125  $this->logger->debug('Fetch session with identifier {session}', ['session' => sha1($sessionId)]);
126  $sessionRecord = $this->sessionBackend->‪get($sessionId);
127  return ‪UserSession::createFromRecord($sessionId, $sessionRecord);
128  }
129 
133  public function ‪hasExpired(‪UserSession $session): bool
134  {
135  return $this->sessionLifetime === 0 || ‪$GLOBALS['EXEC_TIME'] > $session->‪getLastUpdated() + ‪$this->sessionLifetime;
136  }
137 
143  public function ‪willExpire(‪UserSession $session, int $gracePeriod): bool
144  {
145  return ‪$GLOBALS['EXEC_TIME'] >= ($session->‪getLastUpdated() + ‪$this->sessionLifetime) - $gracePeriod;
146  }
147 
158  public function ‪fixateAnonymousSession(‪UserSession $session, bool $isPermanent = false): ‪UserSession
159  {
160  $sessionIpLock = $this->ipLocker->getSessionIpLock((string)GeneralUtility::getIndpEnv('REMOTE_ADDR'));
161  $sessionRecord = $session->‪toArray();
162  $sessionRecord['ses_iplock'] = $sessionIpLock;
163  // Ensure the user is not set, as this is always an anonymous session (see elevateToFixatedUserSession)
164  $sessionRecord['ses_userid'] = 0;
165  if ($isPermanent) {
166  $sessionRecord['ses_permanent'] = 1;
167  }
168  // The updated session record now also contains an updated timestamp (ses_tstamp)
169  $updatedSessionRecord = $this->sessionBackend->set($session->‪getIdentifier(), $sessionRecord);
170  return $this->‪recreateUserSession($session, $updatedSessionRecord);
171  }
172 
184  public function ‪elevateToFixatedUserSession(‪UserSession $session, int $userId, bool $isPermanent = false): ‪UserSession
185  {
186  $sessionId = $session->‪getIdentifier();
187  $this->logger->debug('Create session ses_id = {session}', ['session' => sha1($sessionId)]);
188  // Delete any session entry first
189  $this->sessionBackend->remove($sessionId);
190  // Re-create session entry
191  $sessionIpLock = $this->ipLocker->getSessionIpLock((string)GeneralUtility::getIndpEnv('REMOTE_ADDR'));
192  $sessionRecord = [
193  'ses_iplock' => $sessionIpLock,
194  'ses_userid' => $userId,
195  'ses_tstamp' => ‪$GLOBALS['EXEC_TIME'],
196  'ses_data' => '',
197  ];
198  if ($isPermanent) {
199  $sessionRecord['ses_permanent'] = 1;
200  }
201  $sessionRecord = $this->sessionBackend->set($sessionId, $sessionRecord);
202  return ‪UserSession::createFromRecord($sessionId, $sessionRecord, true);
203  }
204 
217  public function ‪regenerateSession(
218  string $sessionId,
219  array $existingSessionRecord = [],
220  bool $anonymous = false
221  ): ‪UserSession {
222  if (empty($existingSessionRecord)) {
223  $existingSessionRecord = $this->sessionBackend->‪get($sessionId);
224  }
225  if ($anonymous) {
226  $existingSessionRecord['ses_userid'] = 0;
227  }
228  // Update session record with new ID
229  $newSessionId = $this->‪createSessionId();
230  $this->sessionBackend->set($newSessionId, $existingSessionRecord);
231  $this->sessionBackend->remove($sessionId);
232  return ‪UserSession::createFromRecord($newSessionId, $existingSessionRecord, true);
233  }
234 
244  {
245  if ($session->‪needsUpdate()) {
246  // Update the session timestamp by writing a dummy update. (Backend will update the timestamp)
247  $this->sessionBackend->update($session->‪getIdentifier(), []);
248  $session = $this->‪recreateUserSession($session);
249  }
250  return $session;
251  }
252 
256  public function ‪isSessionPersisted(‪UserSession $session): bool
257  {
258  return $this->‪getSessionFromSessionId($session->‪getIdentifier()) !== null;
259  }
260 
264  public function ‪removeSession(‪UserSession $session): void
265  {
266  $this->sessionBackend->remove($session->‪getIdentifier());
267  }
268 
272  public function ‪updateSession(‪UserSession $session): ‪UserSession
273  {
274  $sessionRecord = $this->sessionBackend->update($session->‪getIdentifier(), $session->‪toArray());
275  return $this->‪recreateUserSession($session, $sessionRecord);
276  }
277 
281  public function ‪collectGarbage(int $garbageCollectionProbability = 1): void
282  {
283  // If we're lucky we'll get to clean up old sessions
284  if (random_int(0, mt_getrandmax()) % 100 <= $garbageCollectionProbability) {
285  $this->sessionBackend->collectGarbage(
286  $this->sessionLifetime > 0 ? $this->sessionLifetime : self::GARBAGE_COLLECTION_LIFETIME,
287  $this->garbageCollectionForAnonymousSessions
288  );
289  }
290  }
291 
295  protected function ‪createSessionId(): string
296  {
297  return GeneralUtility::makeInstance(Random::class)->generateRandomHexString(self::SESSION_ID_LENGTH);
298  }
299 
306  protected function ‪getSessionFromSessionId(string $id): ?‪UserSession
307  {
308  if ($id === '') {
309  return null;
310  }
311  try {
312  $sessionRecord = $this->sessionBackend->‪get($id);
313  if ($sessionRecord === []) {
314  return null;
315  }
316  // If the session does not match the current IP lock, it should be treated as invalid
317  // and a new session should be created.
318  if ($this->ipLocker->validateRemoteAddressAgainstSessionIpLock(
319  (string)GeneralUtility::getIndpEnv('REMOTE_ADDR'),
320  $sessionRecord['ses_iplock']
321  )) {
322  return ‪UserSession::createFromRecord($id, $sessionRecord);
323  }
324  } catch (‪SessionNotFoundException $e) {
325  return null;
326  }
327 
328  return null;
329  }
330 
345  public static function ‪create(string ‪$loginType, int ‪$sessionLifetime = null, ‪SessionManager $sessionManager = null, ‪IpLocker ‪$ipLocker = null): self
346  {
347  $sessionManager = $sessionManager ?? GeneralUtility::makeInstance(SessionManager::class);
348  ‪$ipLocker = ‪$ipLocker ?? GeneralUtility::makeInstance(
349  IpLocker::class,
350  (int)(‪$GLOBALS['TYPO3_CONF_VARS'][‪$loginType]['lockIP'] ?? 0),
351  (int)(‪$GLOBALS['TYPO3_CONF_VARS'][‪$loginType]['lockIPv6'] ?? 0)
352  );
353  $lifetime = (int)(‪$GLOBALS['TYPO3_CONF_VARS'][‪$loginType]['lifetime'] ?? 0);
354  ‪$sessionLifetime = ‪$sessionLifetime ?? (int)‪$GLOBALS['TYPO3_CONF_VARS'][‪$loginType]['sessionTimeout'];
355  if (‪$sessionLifetime > 0 && $sessionLifetime < $lifetime && $lifetime > 0) {
356  // If server session timeout is non-zero but less than client session timeout: Copy this value instead.
357  ‪$sessionLifetime = $lifetime;
358  }
359  $object = GeneralUtility::makeInstance(
360  self::class,
361  $sessionManager->getSessionBackend(‪$loginType),
363  ‪$ipLocker,
365  );
366  if (‪$loginType === 'FE') {
367  $object->setGarbageCollectionTimeoutForAnonymousSessions((int)(‪$GLOBALS['TYPO3_CONF_VARS']['FE']['sessionDataLifetime'] ?? 0));
368  }
369  return $object;
370  }
371 
379  protected function ‪recreateUserSession(‪UserSession $session, array $sessionRecord = null): ‪UserSession
380  {
382  $session->‪getIdentifier(),
383  $sessionRecord ?? $this->sessionBackend->get($session->‪getIdentifier()),
384  $session->‪isNew() // keep state (required to emit e.g. cookies)
385  );
386  }
387 }
‪TYPO3\CMS\Core\Session\UserSessionManager\hasExpired
‪hasExpired(UserSession $session)
Definition: UserSessionManager.php:133
‪TYPO3\CMS\Core\Session\UserSessionManager\$sessionLifetime
‪int $sessionLifetime
Definition: UserSessionManager.php:60
‪TYPO3\CMS\Core\Session\UserSessionManager\willExpire
‪willExpire(UserSession $session, int $gracePeriod)
Definition: UserSessionManager.php:143
‪TYPO3\CMS\Core\Session\UserSession\getIdentifier
‪string getIdentifier()
Definition: UserSession.php:68
‪TYPO3\CMS\Core\Session\UserSession\toArray
‪array toArray()
Definition: UserSession.php:293
‪TYPO3\CMS\Core\Session\UserSession\createNonFixated
‪static createNonFixated(string $identifier)
Definition: UserSession.php:243
‪TYPO3\CMS\Core\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Core\Session\UserSession
Definition: UserSession.php:45
‪TYPO3\CMS\Core\Session\UserSession\createFromRecord
‪static createFromRecord(string $id, array $record, bool $markAsNew=false)
Definition: UserSession.php:223
‪TYPO3\CMS\Core\Session\SessionManager
Definition: SessionManager.php:41
‪TYPO3\CMS\Core\Session\UserSessionManager\fixateAnonymousSession
‪UserSession fixateAnonymousSession(UserSession $session, bool $isPermanent=false)
Definition: UserSessionManager.php:158
‪TYPO3\CMS\Core\Session\UserSessionManager\elevateToFixatedUserSession
‪UserSession elevateToFixatedUserSession(UserSession $session, int $userId, bool $isPermanent=false)
Definition: UserSessionManager.php:184
‪TYPO3\CMS\Core\Session\UserSessionManager\LIFETIME_OF_ANONYMOUS_SESSION_DATA
‪const LIFETIME_OF_ANONYMOUS_SESSION_DATA
Definition: UserSessionManager.php:52
‪TYPO3\CMS\Core\Session
‪TYPO3\CMS\Core\Session\UserSessionManager\SESSION_ID_LENGTH
‪const SESSION_ID_LENGTH
Definition: UserSessionManager.php:50
‪TYPO3\CMS\Core\Session\UserSessionManager\$garbageCollectionForAnonymousSessions
‪int $garbageCollectionForAnonymousSessions
Definition: UserSessionManager.php:62
‪TYPO3\CMS\Core\Session\UserSessionManager\__construct
‪__construct(SessionBackendInterface $sessionBackend, int $sessionLifetime, IpLocker $ipLocker, string $loginType)
Definition: UserSessionManager.php:72
‪TYPO3\CMS\Core\Session\UserSession\isNew
‪isNew()
Definition: UserSession.php:177
‪TYPO3\CMS\Core\Session\UserSession\get
‪get(string $key)
Definition: UserSession.php:122
‪TYPO3\CMS\Core\Session\UserSessionManager\removeSession
‪removeSession(UserSession $session)
Definition: UserSessionManager.php:264
‪TYPO3\CMS\Core\Session\UserSessionManager\getSessionFromSessionId
‪UserSession null getSessionFromSessionId(string $id)
Definition: UserSessionManager.php:306
‪TYPO3\CMS\Core\Session\UserSessionManager\regenerateSession
‪regenerateSession(string $sessionId, array $existingSessionRecord=[], bool $anonymous=false)
Definition: UserSessionManager.php:217
‪TYPO3\CMS\Core\Session\UserSession\resolveIdentifierFromJwt
‪static non empty string null resolveIdentifierFromJwt(string $cookieValue, CookieScope $scope)
Definition: UserSession.php:261
‪TYPO3\CMS\Core\Session\Backend\SessionBackendInterface
Definition: SessionBackendInterface.php:28
‪TYPO3\CMS\Core\Session\UserSessionManager\$sessionBackend
‪SessionBackendInterface $sessionBackend
Definition: UserSessionManager.php:63
‪TYPO3\CMS\Core\Session\UserSessionManager\create
‪static static create(string $loginType, int $sessionLifetime=null, SessionManager $sessionManager=null, IpLocker $ipLocker=null)
Definition: UserSessionManager.php:345
‪TYPO3\CMS\Core\Session\UserSessionManager\updateSessionTimestamp
‪UserSession updateSessionTimestamp(UserSession $session)
Definition: UserSessionManager.php:243
‪TYPO3\CMS\Core\Session\UserSessionManager\GARBAGE_COLLECTION_LIFETIME
‪const GARBAGE_COLLECTION_LIFETIME
Definition: UserSessionManager.php:51
‪TYPO3\CMS\Core\Session\UserSessionManager\createAnonymousSession
‪createAnonymousSession()
Definition: UserSessionManager.php:110
‪TYPO3\CMS\Core\Session\UserSessionManager\updateSession
‪updateSession(UserSession $session)
Definition: UserSessionManager.php:272
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Session\UserSessionManager\$ipLocker
‪IpLocker $ipLocker
Definition: UserSessionManager.php:64
‪TYPO3\CMS\Core\Crypto\Random
Definition: Random.php:27
‪TYPO3\CMS\Core\Authentication\IpLocker
Definition: IpLocker.php:26
‪TYPO3\CMS\Core\Session\UserSession\getLastUpdated
‪int getLastUpdated()
Definition: UserSession.php:84
‪TYPO3\CMS\Core\Session\UserSessionManager\recreateUserSession
‪recreateUserSession(UserSession $session, array $sessionRecord=null)
Definition: UserSessionManager.php:379
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Session\UserSessionManager\isSessionPersisted
‪isSessionPersisted(UserSession $session)
Definition: UserSessionManager.php:256
‪TYPO3\CMS\Core\Session\UserSessionManager\createFromRequestOrAnonymous
‪UserSession createFromRequestOrAnonymous(ServerRequestInterface $request, string $cookieName)
Definition: UserSessionManager.php:95
‪TYPO3\CMS\Core\Session\UserSessionManager\setGarbageCollectionTimeoutForAnonymousSessions
‪setGarbageCollectionTimeoutForAnonymousSessions(int $garbageCollectionForAnonymousSessions=0)
Definition: UserSessionManager.php:80
‪TYPO3\CMS\Core\Session\UserSessionManager\collectGarbage
‪collectGarbage(int $garbageCollectionProbability=1)
Definition: UserSessionManager.php:281
‪TYPO3\CMS\Core\Session\UserSessionManager\$loginType
‪string $loginType
Definition: UserSessionManager.php:65
‪TYPO3\CMS\Core\Http\NormalizedParams\createFromRequest
‪static static createFromRequest(ServerRequestInterface $request, array $systemConfiguration=null)
Definition: NormalizedParams.php:840
‪TYPO3\CMS\Core\Session\UserSessionManager\createSessionFromStorage
‪UserSession createSessionFromStorage(string $sessionId)
Definition: UserSessionManager.php:123
‪TYPO3\CMS\Core\Session\Backend\Exception\SessionNotFoundException
Definition: SessionNotFoundException.php:23
‪TYPO3\CMS\Core\Session\UserSessionManager
Definition: UserSessionManager.php:46
‪TYPO3\CMS\Core\Session\UserSessionManager\createSessionId
‪createSessionId()
Definition: UserSessionManager.php:295
‪TYPO3\CMS\Core\Session\UserSession\needsUpdate
‪needsUpdate()
Definition: UserSession.php:193
‪TYPO3\CMS\Core\Http\NormalizedParams
Definition: NormalizedParams.php:38