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