‪TYPO3CMS  11.5
BackendUserAuthenticator.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\ResponseInterface;
21 use Psr\Http\Message\ServerRequestInterface;
22 use Psr\Http\Server\RequestHandlerInterface;
23 use Psr\Log\LoggerAwareInterface;
24 use Psr\Log\LoggerAwareTrait;
25 use Symfony\Component\RateLimiter\LimiterInterface;
42 
49 {
50  use LoggerAwareTrait;
51 
57  protected ‪$publicRoutes = [
58  '/login',
59  '/login/frame',
60  '/login/password-reset/forget',
61  '/login/password-reset/initiate-reset',
62  '/login/password-reset/validate',
63  '/login/password-reset/finish',
64  '/install/server-response-check/host',
65  '/ajax/login',
66  '/ajax/logout',
67  '/ajax/login/preflight',
68  '/ajax/login/refresh',
69  '/ajax/login/timedout',
70  '/ajax/core/requirejs',
71  ];
72 
75 
76  public function ‪__construct(
80  ) {
81  parent::__construct(‪$context);
82  $this->languageServiceFactory = ‪$languageServiceFactory;
83  $this->rateLimiterFactory = ‪$rateLimiterFactory;
84  }
85 
93  public function ‪process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
94  {
96  $route = $request->getAttribute('route');
97 
98  // The global must be available very early, because methods below
99  // might trigger code which relies on it. See: #45625
100  ‪$GLOBALS['BE_USER'] = GeneralUtility::makeInstance(BackendUserAuthentication::class);
101  // Rate Limiting
102  $rateLimiter = $this->‪ensureLoginRateLimit(‪$GLOBALS['BE_USER'], $request);
103  // Whether multi-factor authentication is requested
104  $mfaRequested = $route->getOption('_identifier') === 'auth_mfa';
105  try {
106  ‪$GLOBALS['BE_USER']->start($request);
107  } catch (MfaRequiredException $mfaRequiredException) {
108  // If MFA is required and we are not already on the "auth_mfa"
109  // route, force the user to it for further authentication.
110  if (!$mfaRequested && !$this->‪isLoggedInBackendUserRequired($route)) {
111  return $this->‪redirectToMfaEndpoint(
112  'auth_mfa',
113  ‪$GLOBALS['BE_USER'],
114  $request,
115  ['identifier' => $mfaRequiredException->getProvider()->getIdentifier()]
116  );
117  }
118  }
119 
120  // Register the backend user as aspect and initializing workspace once for TSconfig conditions
121  $this->‪setBackendUserAspect(‪$GLOBALS['BE_USER'], (int)(‪$GLOBALS['BE_USER']->user['workspace_id'] ?? 0));
122  if ($this->‪isLoggedInBackendUserRequired($route)) {
123  if (!$this->context->getAspect('backend.user')->isLoggedIn()) {
124  $uri = GeneralUtility::makeInstance(UriBuilder::class)->buildUriWithRedirect(
125  'login',
126  [],
127  ‪RouteRedirect::createFromRoute($route, $request->getQueryParams())
128  );
129  $response = new RedirectResponse($uri);
130  return $this->‪enrichResponseWithHeadersAndCookieInformation($response, ‪$GLOBALS['BE_USER']);
131  }
132  if (!‪$GLOBALS['BE_USER']->isUserAllowedToLogin()) {
133  $content = GeneralUtility::makeInstance(ErrorPageController::class)->errorAction(
134  'Login Error',
135  'TYPO3 is in maintenance mode at the moment. Only administrators are allowed access.',
137  1294585860,
138  503
139  );
140  $response = new HtmlResponse($content, 503);
141  return $this->‪enrichResponseWithHeadersAndCookieInformation($response, ‪$GLOBALS['BE_USER']);
142  }
143  }
144  if ($this->context->getAspect('backend.user')->isLoggedIn()) {
145  ‪$GLOBALS['BE_USER']->initializeBackendLogin();
146  // Reset the limiter after successful login
147  if ($rateLimiter) {
148  $rateLimiter->reset();
149  }
150  // In case the current request is not targeted to authenticate against MFA, the "mfa"
151  // key is not yet set in session (indicating that MFA has already been passed) and it's
152  // no "switch-user" mode, check whether the user is required to set up MFA and redirect
153  // to the corresponding setup endpoint if not already on it.
154  if (!$mfaRequested
155  && !(bool)(‪$GLOBALS['BE_USER']->getSessionData('mfa') ?? false)
156  && !‪$GLOBALS['BE_USER']->getOriginalUserIdWhenInSwitchUserMode()
157  && ‪$GLOBALS['BE_USER']->isMfaSetupRequired()
158  && $route->getOption('_identifier') !== 'setup_mfa'
159  ) {
160  return $this->‪redirectToMfaEndpoint('setup_mfa', ‪$GLOBALS['BE_USER'], $request);
161  }
162  }
163  ‪$GLOBALS['LANG'] = $this->languageServiceFactory->createFromUserPreferences(‪$GLOBALS['BE_USER']);
164  // Re-setting the user and take the workspace from the user object now
165  $this->‪setBackendUserAspect(‪$GLOBALS['BE_USER']);
166  $response = $handler->handle($request);
168  return $this->‪enrichResponseWithHeadersAndCookieInformation($response, ‪$GLOBALS['BE_USER']);
169  }
170 
181  ResponseInterface $response,
182  ?BackendUserAuthentication $userAuthentication
183  ): ResponseInterface {
184  if ($userAuthentication) {
185  // If no backend user is logged-in, the cookie should be removed
186  if (!$this->context->getAspect('backend.user')->isLoggedIn()) {
187  $userAuthentication->removeCookie();
188  }
189  // Ensure to always apply a cookie
190  $response = $userAuthentication->appendCookieToResponse($response);
191  }
192  // Additional headers to never cache any PHP request should be sent at any time when
193  // accessing the TYPO3 Backend
194  $response = $this->‪applyHeadersToResponse($response);
195  return $response;
196  }
197 
201  protected function ‪sessionGarbageCollection(): void
202  {
203  ‪UserSessionManager::create('BE')->collectGarbage();
204  }
205 
209  protected function ‪redirectToMfaEndpoint(
210  string $endpoint,
211  BackendUserAuthentication $user,
212  ServerRequestInterface $request,
213  array $parameters = []
214  ): ResponseInterface {
215  // GLOBALS[LANG] needs to be set up, because the UriBuilder is generating a token, which in turn
216  // needs the FormProtectionFactory, which then builds a Message Closure with GLOBALS[LANG] (hacky, yes!)
217  ‪$GLOBALS['LANG'] = $this->languageServiceFactory->createFromUserPreferences($user);
218  $response = new RedirectResponse(
219  GeneralUtility::makeInstance(UriBuilder::class)->buildUriWithRedirect($endpoint, $parameters, ‪RouteRedirect::createFromRequest($request))
220  );
221  // Add necessary cookies and headers to the response so
222  // the already passed authentication step is not lost.
223  $response = $user->appendCookieToResponse($response);
224  $response = $this->‪applyHeadersToResponse($response);
225  return $response;
226  }
227 
235  protected function ‪isLoggedInBackendUserRequired(Route $route): bool
236  {
237  return in_array($route->getPath(), $this->publicRoutes, true) === false;
238  }
239 
240  protected function ‪ensureLoginRateLimit(‪BackendUserAuthentication $user, ServerRequestInterface $request): ?LimiterInterface
241  {
242  if (!$user->‪isActiveLogin($request)) {
243  return null;
244  }
245  $loginRateLimiter = $this->rateLimiterFactory->createLoginRateLimiter($user, $request);
246  $limit = $loginRateLimiter->consume();
247  if (!$limit->isAccepted()) {
248  $this->logger->debug('Login request has been rate limited for IP address {ipAddress}', ['ipAddress' => $request->getAttribute('normalizedParams')->getRemoteAddress()]);
249  $dateformat = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
250  $lockedUntil = $limit->getRetryAfter()->getTimestamp() > 0 ?
251  ' until ' . date($dateformat, $limit->getRetryAfter()->getTimestamp()) : '';
254  'The login is locked' . $lockedUntil . ' due to too many failed login attempts from your IP address.',
255  'Login Request Rate Limited',
256  1616175867
257  );
258  }
259  return $loginRateLimiter;
260  }
261 }
‪TYPO3\CMS\Core\Localization\LanguageServiceFactory
Definition: LanguageServiceFactory.php:25
‪TYPO3\CMS\Backend\Middleware\BackendUserAuthenticator\redirectToMfaEndpoint
‪redirectToMfaEndpoint(string $endpoint, BackendUserAuthentication $user, ServerRequestInterface $request, array $parameters=[])
Definition: BackendUserAuthenticator.php:208
‪TYPO3\CMS\Backend\Routing\Route\getPath
‪string getPath()
Definition: Route.php:51
‪TYPO3\CMS\Backend\Middleware\BackendUserAuthenticator\$publicRoutes
‪array $publicRoutes
Definition: BackendUserAuthenticator.php:56
‪TYPO3\CMS\Core\Messaging\AbstractMessage
Definition: AbstractMessage.php:26
‪TYPO3\CMS\Backend\Middleware
Definition: AdditionalResponseHeaders.php:18
‪TYPO3\CMS\Core\Controller\ErrorPageController
Definition: ErrorPageController.php:30
‪TYPO3\CMS\Backend\Middleware\BackendUserAuthenticator\enrichResponseWithHeadersAndCookieInformation
‪ResponseInterface enrichResponseWithHeadersAndCookieInformation(ResponseInterface $response, ?BackendUserAuthentication $userAuthentication)
Definition: BackendUserAuthenticator.php:179
‪TYPO3\CMS\Backend\Middleware\BackendUserAuthenticator\ensureLoginRateLimit
‪ensureLoginRateLimit(BackendUserAuthentication $user, ServerRequestInterface $request)
Definition: BackendUserAuthenticator.php:239
‪TYPO3\CMS\Backend\Middleware\BackendUserAuthenticator\$rateLimiterFactory
‪RateLimiterFactory $rateLimiterFactory
Definition: BackendUserAuthenticator.php:73
‪TYPO3\CMS\Backend\Middleware\BackendUserAuthenticator\isLoggedInBackendUserRequired
‪bool isLoggedInBackendUserRequired(Route $route)
Definition: BackendUserAuthenticator.php:234
‪TYPO3\CMS\Core\RateLimiter\RequestRateLimitedException
Definition: RequestRateLimitedException.php:25
‪TYPO3\CMS\Core\Middleware\BackendUserAuthenticator\setBackendUserAspect
‪setBackendUserAspect(?BackendUserAuthentication $user, int $alternativeWorkspaceId=null)
Definition: BackendUserAuthenticator.php:94
‪TYPO3\CMS\Backend\Middleware\BackendUserAuthenticator\process
‪ResponseInterface process(ServerRequestInterface $request, RequestHandlerInterface $handler)
Definition: BackendUserAuthenticator.php:92
‪TYPO3\CMS\Backend\Routing\Route
Definition: Route.php:24
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:53
‪TYPO3\CMS\Core\Authentication\AbstractUserAuthentication\appendCookieToResponse
‪ResponseInterface appendCookieToResponse(ResponseInterface $response)
Definition: AbstractUserAuthentication.php:277
‪TYPO3\CMS\Core\Middleware\BackendUserAuthenticator\$context
‪Context $context
Definition: BackendUserAuthenticator.php:52
‪TYPO3\CMS\Core\Middleware\BackendUserAuthenticator
Definition: BackendUserAuthenticator.php:49
‪TYPO3\CMS\Backend\Routing\RouteRedirect
Definition: RouteRedirect.php:30
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:40
‪TYPO3\CMS\Core\Authentication\Mfa\MfaRequiredException
Definition: MfaRequiredException.php:29
‪TYPO3\CMS\Backend\Middleware\BackendUserAuthenticator\sessionGarbageCollection
‪sessionGarbageCollection()
Definition: BackendUserAuthenticator.php:200
‪TYPO3\CMS\Core\Middleware\BackendUserAuthenticator\applyHeadersToResponse
‪ResponseInterface applyHeadersToResponse(ResponseInterface $response)
Definition: BackendUserAuthenticator.php:73
‪TYPO3\CMS\Core\Session\UserSessionManager\create
‪static static create(string $loginType, int $sessionLifetime=null, SessionManager $sessionManager=null, IpLocker $ipLocker=null)
Definition: UserSessionManager.php:392
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Core\Utility\HttpUtility\HTTP_STATUS_403
‪const HTTP_STATUS_403
Definition: HttpUtility.php:54
‪TYPO3\CMS\Core\Http\RedirectResponse
Definition: RedirectResponse.php:28
‪TYPO3\CMS\Core\Authentication\Mfa\MfaRequiredException\getProvider
‪getProvider()
Definition: MfaRequiredException.php:38
‪TYPO3\CMS\Core\RateLimiter\RateLimiterFactory
Definition: RateLimiterFactory.php:33
‪TYPO3\CMS\Backend\Middleware\BackendUserAuthenticator\$languageServiceFactory
‪LanguageServiceFactory $languageServiceFactory
Definition: BackendUserAuthenticator.php:72
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Backend\Middleware\BackendUserAuthenticator
Definition: BackendUserAuthenticator.php:49
‪TYPO3\CMS\Core\Authentication\AbstractUserAuthentication\isActiveLogin
‪isActiveLogin(ServerRequestInterface $request)
Definition: AbstractUserAuthentication.php:1135
‪TYPO3\CMS\Core\Authentication\AbstractUserAuthentication\removeCookie
‪removeCookie($cookieName=null)
Definition: AbstractUserAuthentication.php:906
‪TYPO3\CMS\Backend\Routing\RouteRedirect\createFromRoute
‪static createFromRoute(Route $route, array $parameters)
Definition: RouteRedirect.php:53
‪TYPO3\CMS\Core\Utility\HttpUtility
Definition: HttpUtility.php:22
‪TYPO3\CMS\Backend\Middleware\BackendUserAuthenticator\__construct
‪__construct(Context $context, LanguageServiceFactory $languageServiceFactory, RateLimiterFactory $rateLimiterFactory)
Definition: BackendUserAuthenticator.php:75
‪TYPO3\CMS\Backend\Routing\RouteRedirect\createFromRequest
‪static createFromRequest(ServerRequestInterface $request)
Definition: RouteRedirect.php:58
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\Session\UserSessionManager
Definition: UserSessionManager.php:38
‪TYPO3\CMS\Core\Messaging\AbstractMessage\ERROR
‪const ERROR
Definition: AbstractMessage.php:31
‪TYPO3\CMS\Core\Http\HtmlResponse
Definition: HtmlResponse.php:26