‪TYPO3CMS  11.5
MfaSetupController.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\Log\LoggerInterface;
36 use TYPO3\CMS\Core\Page\PageRenderer;
39 
46 {
47  protected const ‪ACTION_METHOD_MAP = [
48  'setup' => 'GET',
49  'activate' => 'POST',
50  'cancel' => 'GET',
51  ];
52 
54  protected PageRenderer ‪$pageRenderer;
55  protected LoggerInterface ‪$logger;
56 
57  public function ‪__construct(
62  PageRenderer ‪$pageRenderer,
63  LoggerInterface ‪$logger
64  ) {
66  $this->authenticationStyleInformation = ‪$authenticationStyleInformation;
67  $this->pageRenderer = ‪$pageRenderer;
68  $this->logger = ‪$logger;
69  }
70 
71  public function ‪handleRequest(ServerRequestInterface $request): ResponseInterface
72  {
73  $this->moduleTemplate = $this->moduleTemplateFactory->create($request);
74  $action = (string)($request->getQueryParams()['action'] ?? 'setup');
75 
76  $backendUser = $this->‪getBackendUser();
77  if (($backendUser->getSessionData('mfa') ?? false)
78  || $backendUser->getOriginalUserIdWhenInSwitchUserMode() !== null
79  || !$backendUser->isMfaSetupRequired()
80  || $this->mfaProviderRegistry->hasActiveProviders($backendUser)
81  ) {
82  // Since the current user either did already pass MFA, is in "switch-user" mode,
83  // is not required to set up MFA or has already activated a provider, throw an
84  // exception to prevent the endpoint from being called unintentionally by custom code.
85  throw new \InvalidArgumentException('MFA setup is not necessary. Do not call this endpoint on your own.', 1632154036);
86  }
87 
88  $actionMethod = self::ACTION_METHOD_MAP[$action] ?? null;
89  if ($actionMethod !== null && $request->getMethod() === $actionMethod) {
90  return $this->{$action . 'Action'}($request);
91  }
92  return new ‪HtmlResponse('', 404);
93  }
94 
99  public function ‪setupAction(ServerRequestInterface $request): ResponseInterface
100  {
101  $this->moduleTemplate->setTitle('TYPO3 CMS Login: ' . (‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ?? ''));
102 
103  $identifier = (string)($request->getQueryParams()['identifier'] ?? '');
104  if ($identifier === '' || !$this->‪isValidIdentifier($identifier)) {
105  return new ‪HtmlResponse($this->‪renderSelectionView($request));
106  }
107  $mfaProvider = $this->mfaProviderRegistry->getProvider($identifier);
108  $this->‪log('Required MFA setup initiated', $mfaProvider);
109  return new ‪HtmlResponse($this->‪renderSetupView($request, $mfaProvider));
110  }
111 
116  public function ‪activateAction(ServerRequestInterface $request): ResponseInterface
117  {
118  $identifier = (string)($request->getParsedBody()['identifier'] ?? '');
119  if ($identifier === '' || !$this->‪isValidIdentifier($identifier)) {
120  // Return to selection view in case no valid identifier is given
121  return new ‪RedirectResponse($this->uriBuilder->buildUriWithRedirect('setup_mfa', [], ‪RouteRedirect::createFromRequest($request)));
122  }
123  $mfaProvider = $this->mfaProviderRegistry->getProvider($identifier);
124  $backendUser = $this->‪getBackendUser();
125  $propertyManager = ‪MfaProviderPropertyManager::create($mfaProvider, $backendUser);
126  // Check whether activation operation was successful and the provider is now active.
127  if (!$mfaProvider->activate($request, $propertyManager) || !$mfaProvider->isActive($propertyManager)) {
128  $this->‪log('Required MFA setup failed', $mfaProvider);
129  return new ‪RedirectResponse(
130  $this->uriBuilder->buildUriWithRedirect(
131  'setup_mfa',
132  [
133  'identifier' => $mfaProvider->getIdentifier(),
134  'hasErrors' => true,
135  ],
137  )
138  );
139  }
140  $this->‪log('Required MFA setup successful', $mfaProvider);
141  // Set the activated provider as the default provider, store the "mfa" key in the session data,
142  // add a flash message to the session and finally initiate a redirect to the login, on which
143  // possible redirect parameters are evaluated again.
144  $backendUser->uc['mfa']['defaultProvider'] = $mfaProvider->getIdentifier();
145  $backendUser->writeUC();
146  $backendUser->setAndSaveSessionData('mfa', true);
147  $this->‪addSuccessMessage($mfaProvider->getTitle());
148  return new ‪RedirectResponse($this->uriBuilder->buildUriWithRedirect('login', [], ‪RouteRedirect::createFromRequest($request)));
149  }
150 
156  public function ‪cancelAction(ServerRequestInterface $request): ResponseInterface
157  {
158  $this->‪log('Required MFA setup canceled');
159  $this->‪getBackendUser()->logoff();
160  return new ‪RedirectResponse($this->uriBuilder->buildUriWithRedirect('login', [], ‪RouteRedirect::createFromRequest($request)));
161  }
162 
166  protected function ‪renderSelectionView(ServerRequestInterface $request): string
167  {
168  $recommendedProvider = $this->‪getRecommendedProvider();
169  $providers = array_filter($this->allowedProviders, static function ($provider) use ($recommendedProvider) {
170  // Remove the recommended provider and providers, which can not be used as default, e.g. recovery codes
171  return $provider->isDefaultProviderAllowed()
172  && ($recommendedProvider === null || $provider->getIdentifier() !== $recommendedProvider->getIdentifier());
173  });
174  $view = $this->‪initializeView($request, 'Selection');
175  $view->assignMultiple([
176  'recommendedProvider' => $recommendedProvider,
177  'providers' => $providers,
178  ]);
179  $this->moduleTemplate->setContent($view->render());
180  return $this->moduleTemplate->renderContent();
181  }
182 
186  protected function ‪renderSetupView(
187  ServerRequestInterface $request,
189  ): string {
190  $propertyManager = ‪MfaProviderPropertyManager::create($mfaProvider, $this->‪getBackendUser());
191  $providerResponse = $mfaProvider->‪handleRequest($request, $propertyManager, ‪MfaViewType::SETUP);
192  $view = $this->‪initializeView($request, 'Setup');
193  $view->assignMultiple([
194  'provider' => $mfaProvider,
195  'providerContent' => $providerResponse->getBody(),
196  'hasErrors' => (bool)($request->getQueryParams()['hasErrors'] ?? false),
197  ]);
198  $this->moduleTemplate->setContent($view->render());
199  return $this->moduleTemplate->renderContent();
200  }
201 
205  protected function ‪initializeView(ServerRequestInterface $request, string $templateName): ‪StandaloneView
206  {
207  $view = $this->moduleTemplate->getView();
208  $view->‪setTemplateRootPaths(['EXT:backend/Resources/Private/Templates/Mfa/Standalone']);
209  $view->setPartialRootPaths(['EXT:backend/Resources/Private/Partials']);
210  $view->setLayoutRootPaths(['EXT:backend/Resources/Private/Layouts']);
211  $view->setTemplate($templateName);
212  $view->assignMultiple([
213  'redirect' => $request->getQueryParams()['redirect'] ?? '',
214  'redirectParams' => $request->getQueryParams()['redirectParams'] ?? '',
215  'siteName' => ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'],
216  'footerNote' => $this->authenticationStyleInformation->getFooterNote(),
217  ]);
219  return $view;
220  }
221 
222  protected function ‪addCustomAuthenticationFormStyles(): void
223  {
224  if (($backgroundImageStyles = $this->authenticationStyleInformation->getBackgroundImageStyles()) !== '') {
225  $this->pageRenderer->addCssInlineBlock('loginBackgroundImage', $backgroundImageStyles);
226  }
227  if (($highlightColorStyles = $this->authenticationStyleInformation->getHighlightColorStyles()) !== '') {
228  $this->pageRenderer->addCssInlineBlock('loginHighlightColor', $highlightColorStyles);
229  }
230  }
231 
236  protected function ‪isValidIdentifier(string $identifier): bool
237  {
238  return parent::isValidIdentifier($identifier)
239  && $this->mfaProviderRegistry->getProvider($identifier)->isDefaultProviderAllowed();
240  }
241 
246  protected function ‪addSuccessMessage(string $mfaProviderTitle): void
247  {
248  $lang = $this->‪getLanguageService();
249  GeneralUtility::makeInstance(FlashMessageService::class)->getMessageQueueByIdentifier()->enqueue(
250  GeneralUtility::makeInstance(
251  FlashMessage::class,
252  sprintf($lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_mfa.xlf:standalone.setup.success.message'), $lang->sL($mfaProviderTitle)),
253  $lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_mfa.xlf:standalone.setup.success.title'),
255  true
256  )
257  );
258  }
259 
263  protected function ‪log(string $message, ?‪MfaProviderManifestInterface $mfaProvider = null): void
264  {
265  $user = $this->‪getBackendUser();
266  $context = [
267  'user' => [
268  'uid' => $user->user[$user->userid_column],
269  'username' => $user->user[$user->username_column],
270  ],
271  ];
272  if ($mfaProvider !== null) {
273  $context['provider'] = $mfaProvider->getIdentifier();
274  }
275  $this->logger->debug($message, $context);
276  }
277 }
‪TYPO3\CMS\Backend\Controller\MfaSetupController\handleRequest
‪handleRequest(ServerRequestInterface $request)
Definition: MfaSetupController.php:71
‪TYPO3\CMS\Backend\View\AuthenticationStyleInformation
Definition: AuthenticationStyleInformation.php:32
‪TYPO3\CMS\Backend\Controller\MfaSetupController\renderSelectionView
‪renderSelectionView(ServerRequestInterface $request)
Definition: MfaSetupController.php:166
‪TYPO3\CMS\Backend\Controller\MfaSetupController\$authenticationStyleInformation
‪AuthenticationStyleInformation $authenticationStyleInformation
Definition: MfaSetupController.php:53
‪TYPO3\CMS\Core\Messaging\AbstractMessage
Definition: AbstractMessage.php:26
‪TYPO3\CMS\Backend\Controller\AbstractMfaController\getBackendUser
‪getBackendUser()
Definition: AbstractMfaController.php:122
‪TYPO3\CMS\Backend\Template\ModuleTemplateFactory
Definition: ModuleTemplateFactory.php:29
‪TYPO3\CMS\Backend\Controller\MfaSetupController\__construct
‪__construct(UriBuilder $uriBuilder, MfaProviderRegistry $mfaProviderRegistry, ModuleTemplateFactory $moduleTemplateFactory, AuthenticationStyleInformation $authenticationStyleInformation, PageRenderer $pageRenderer, LoggerInterface $logger)
Definition: MfaSetupController.php:57
‪TYPO3\CMS\Backend\Controller\MfaSetupController\initializeView
‪initializeView(ServerRequestInterface $request, string $templateName)
Definition: MfaSetupController.php:205
‪TYPO3\CMS\Backend\Controller\MfaSetupController\renderSetupView
‪renderSetupView(ServerRequestInterface $request, MfaProviderManifestInterface $mfaProvider)
Definition: MfaSetupController.php:186
‪TYPO3\CMS\Backend\Controller\AbstractMfaController\$moduleTemplateFactory
‪ModuleTemplateFactory $moduleTemplateFactory
Definition: AbstractMfaController.php:40
‪TYPO3\CMS\Core\Authentication\Mfa\MfaViewType\SETUP
‪const SETUP
Definition: MfaViewType.php:27
‪TYPO3\CMS\Backend\Controller\AbstractMfaController\getLanguageService
‪getLanguageService()
Definition: AbstractMfaController.php:127
‪TYPO3\CMS\Core\Authentication\Mfa\MfaProviderManifestInterface
Definition: MfaProviderManifestInterface.php:26
‪TYPO3\CMS\Backend\Controller\MfaSetupController
Definition: MfaSetupController.php:46
‪TYPO3\CMS\Backend\Controller\MfaSetupController\$logger
‪LoggerInterface $logger
Definition: MfaSetupController.php:55
‪TYPO3\CMS\Backend\Controller\AbstractMfaController\$mfaProviderRegistry
‪MfaProviderRegistry $mfaProviderRegistry
Definition: AbstractMfaController.php:39
‪TYPO3\CMS\Backend\Controller\MfaSetupController\setupAction
‪setupAction(ServerRequestInterface $request)
Definition: MfaSetupController.php:99
‪TYPO3\CMS\Backend\Controller\MfaSetupController\cancelAction
‪cancelAction(ServerRequestInterface $request)
Definition: MfaSetupController.php:156
‪TYPO3\CMS\Backend\Controller\AbstractMfaController\getRecommendedProvider
‪getRecommendedProvider()
Definition: AbstractMfaController.php:102
‪TYPO3\CMS\Backend\Routing\RouteRedirect
Definition: RouteRedirect.php:30
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:40
‪TYPO3\CMS\Backend\Controller\MfaSetupController\addCustomAuthenticationFormStyles
‪addCustomAuthenticationFormStyles()
Definition: MfaSetupController.php:222
‪TYPO3\CMS\Core\Authentication\Mfa\MfaProviderPropertyManager
Definition: MfaProviderPropertyManager.php:33
‪TYPO3\CMS\Backend\Controller\MfaSetupController\addSuccessMessage
‪addSuccessMessage(string $mfaProviderTitle)
Definition: MfaSetupController.php:246
‪TYPO3\CMS\Core\Http\RedirectResponse
Definition: RedirectResponse.php:28
‪TYPO3\CMS\Core\Messaging\AbstractMessage\OK
‪const OK
Definition: AbstractMessage.php:29
‪TYPO3\CMS\Backend\Controller\MfaSetupController\activateAction
‪activateAction(ServerRequestInterface $request)
Definition: MfaSetupController.php:116
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:26
‪TYPO3\CMS\Fluid\View\StandaloneView
Definition: StandaloneView.php:31
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Authentication\Mfa\MfaViewType
Definition: MfaViewType.php:26
‪TYPO3\CMS\Core\Authentication\Mfa\MfaProviderPropertyManager\create
‪static MfaProviderPropertyManager create(MfaProviderManifestInterface $provider, AbstractUserAuthentication $user)
Definition: MfaProviderPropertyManager.php:224
‪TYPO3\CMS\Backend\Controller\MfaSetupController\log
‪log(string $message, ?MfaProviderManifestInterface $mfaProvider=null)
Definition: MfaSetupController.php:263
‪TYPO3\CMS\Core\Authentication\Mfa\MfaProviderInterface\handleRequest
‪ResponseInterface handleRequest(ServerRequestInterface $request, MfaProviderPropertyManager $propertyManager, string $type)
‪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\Backend\Controller
Definition: AboutController.php:16
‪TYPO3\CMS\Backend\Controller\MfaSetupController\ACTION_METHOD_MAP
‪const ACTION_METHOD_MAP
Definition: MfaSetupController.php:47
‪TYPO3\CMS\Core\Messaging\FlashMessageService
Definition: FlashMessageService.php:27
‪TYPO3\CMS\Backend\Controller\MfaSetupController\$pageRenderer
‪PageRenderer $pageRenderer
Definition: MfaSetupController.php:54
‪TYPO3\CMS\Backend\Controller\MfaSetupController\isValidIdentifier
‪isValidIdentifier(string $identifier)
Definition: MfaSetupController.php:236
‪TYPO3\CMS\Backend\Controller\AbstractMfaController\$uriBuilder
‪UriBuilder $uriBuilder
Definition: AbstractMfaController.php:38
‪TYPO3\CMS\Core\Http\HtmlResponse
Definition: HtmlResponse.php:26
‪TYPO3\CMS\Core\Authentication\Mfa\MfaProviderRegistry
Definition: MfaProviderRegistry.php:28
‪TYPO3\CMS\Backend\Controller\AbstractMfaController
Definition: AbstractMfaController.php:37
‪TYPO3\CMS\Fluid\View\AbstractTemplateView\setTemplateRootPaths
‪setTemplateRootPaths(array $templateRootPaths)
Definition: AbstractTemplateView.php:124