‪TYPO3CMS  11.5
MfaSetupControllerTest.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 Prophecy\PhpUnit\ProphecyTrait;
33 use TYPO3\CMS\Core\Page\PageRenderer;
35 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
36 
37 class ‪MfaSetupControllerTest extends FunctionalTestCase
38 {
39  use ProphecyTrait;
40 
42  protected ‪ServerRequest ‪$request;
43 
53  'SYS' => [
54  'systemMaintainers' => [],
55  ],
56  ];
57 
58  protected function ‪setUp(): void
59  {
60  parent::setUp();
61  $this->setUpBackendUserFromFixture(1);
62  ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['requireMfa'] = 1;
64 
65  $container = $this->getContainer();
66  $this->subject = new ‪MfaSetupController(
67  $container->get(UriBuilder::class),
68  $container->get(MfaProviderRegistry::class),
69  $container->get(ModuleTemplateFactory::class),
70  $container->get(AuthenticationStyleInformation::class),
71  $container->get(PageRenderer::class),
72  $this->prophesize(Logger::class)->reveal()
73  );
74 
75  $this->request = (new ‪ServerRequest())
76  ->withAttribute('applicationType', ‪SystemEnvironmentBuilder::REQUESTTYPE_BE);
77  }
78 
83  {
84  ‪$GLOBALS['BE_USER']->setAndSaveSessionData('mfa', true);
85 
86  $this->expectException(\InvalidArgumentException::class);
87  $this->expectExceptionCode(1632154036);
88 
90  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
91  $this->subject->handleRequest(‪$request);
92  }
93 
98  {
99  ‪$GLOBALS['BE_USER']->setAndSaveSessionData('backuserid', 123);
100 
101  $this->expectException(\InvalidArgumentException::class);
102  $this->expectExceptionCode(1632154036);
103 
105  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
106  $this->subject->handleRequest(‪$request);
107  }
108 
113  {
114  ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['requireMfa'] = 0;
115 
116  $this->expectException(\InvalidArgumentException::class);
117  $this->expectExceptionCode(1632154036);
118 
120  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
121  $this->subject->handleRequest(‪$request);
122  }
123 
128  {
129  ‪$GLOBALS['BE_USER']->user['mfa'] = json_encode(['totp' => ['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB']]);
130 
131  $this->expectException(\InvalidArgumentException::class);
132  $this->expectExceptionCode(1632154036);
133 
135  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
136  $this->subject->handleRequest(‪$request);
137  }
138 
142  public function ‪handleRequestReturns404OnInvalidAction(): void
143  {
144  ‪$request = $this->request->‪withQueryParams(['action' => 'unknown']);
145  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
146  $response = $this->subject->handleRequest(‪$request);
147 
148  self::assertEquals(404, $response->getStatusCode());
149  }
150 
154  public function ‪handleRequestReturns404OnWrongHttpMethod(): void
155  {
156  ‪$request = $this->request->‪withQueryParams(['action' => 'activate']);
157  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
158  $response = $this->subject->handleRequest(‪$request);
159 
160  self::assertEquals(404, $response->getStatusCode());
161  }
162 
166  public function ‪handleRequestFallsBackToSelectionView(): void
167  {
169  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
170  $response = $this->subject->handleRequest(‪$request);
171 
172  self::assertEquals(200, $response->getStatusCode());
173 
174  $responseContent = $response->getBody()->getContents();
175 
176  // Selection view is renderer
177  self::assertStringContainsString('Set up MFA', $responseContent);
178 
179  // Allowed default provider is rendered
180  self::assertMatchesRegularExpression('/<a.*class="list-group-item.*title="Set up Time-based one-time password".*>/s', $responseContent);
181 
182  // Non allowed default provider is not rendered
183  self::assertDoesNotMatchRegularExpression('/<a.*class="list-group-item.*title="Set up Recovery codes".*>/s', $responseContent);
184  }
185 
189  public function ‪handleRequestAddsRedirectParameters(): void
190  {
191  $queryParams = [
192  'action' => 'setup',
193  'identifier' => 'totp',
194  'redirect' => 'my_module',
195  'redirectParams' => 'some=param',
196  ];
197 
198  ‪$request = $this->request->‪withQueryParams($queryParams);
199  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
200  $response = $this->subject->handleRequest(‪$request);
201 
202  self::assertEquals(200, $response->getStatusCode());
203 
204  $responseContent = $response->getBody()->getContents();
205 
206  // Redirect params are kept
207  self::assertMatchesRegularExpression('/<form.*action="\/typo3\/setup\/mfa.*&amp;action=activate&amp;redirect=my_module&amp;redirectParams=some%3Dparam".*>/s', $responseContent);
208  self::assertMatchesRegularExpression('/<a.*title="Cancel".*href="\/typo3\/setup\/mfa.*&amp;redirect=my_module&amp;redirectParams=some%3Dparam".*>/s', $responseContent);
209  }
210 
214  public function ‪handleRequestReturnsSetupView(): void
215  {
216  $queryParams = [
217  'action' => 'setup',
218  'identifier' => 'totp',
219  ];
220 
221  ‪$request = $this->request->‪withQueryParams($queryParams);
222  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
223  $response = $this->subject->handleRequest(‪$request);
224 
225  self::assertEquals(200, $response->getStatusCode());
226 
227  $responseContent = $response->getBody()->getContents();
228 
229  // Auth view for provider is renderer
230  self::assertStringContainsString('Set up Time-based one-time password', $responseContent);
231 
232  // Ensure provider specific content is added as well
233  self::assertMatchesRegularExpression('/<div.*id="qr-code".*>/s', $responseContent);
234  self::assertMatchesRegularExpression('/<form.*name="setup".*id="mfaSetupController".*>/s', $responseContent);
235  self::assertMatchesRegularExpression('/<input.*id="totp"/s', $responseContent);
236  }
237 
242  {
243  $queryParams = [
244  'action' => 'activate',
245  'redirect' => 'web_list',
246  'redirectParams' => 'some=param',
247  ];
248 
249  ‪$request = $this->request->‪withMethod('POST')->withQueryParams($queryParams);
250  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
251  $response = $this->subject->handleRequest(‪$request);
252  $redirectUrl = parse_url($response->getHeaderLine('location'));
253 
254  self::assertEquals(302, $response->getStatusCode());
255  self::assertStringContainsString('/typo3/setup/mfa', $redirectUrl['path']);
256 
257  // Also redirect parameters are still kept
258  self::assertStringContainsString('redirect=web_list&redirectParams=some%3Dparam', $redirectUrl['query']);
259  }
260 
265  {
266  $queryParams = [
267  'action' => 'activate',
268  'redirect' => 'web_list',
269  'redirectParams' => 'some=param',
270  ];
271 
272  $parsedBody = [
273  'identifier' => 'recovery-codes',
274  ];
275 
276  ‪$request = $this->request->‪withMethod('POST')->withQueryParams($queryParams)->withParsedBody($parsedBody);
277  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
278  $response = $this->subject->handleRequest(‪$request);
279  $redirectUrl = parse_url($response->getHeaderLine('location'));
280 
281  self::assertEquals(302, $response->getStatusCode());
282  self::assertStringContainsString('/typo3/setup/mfa', $redirectUrl['path']);
283 
284  // Also redirect parameters are still kept
285  self::assertStringContainsString('redirect=web_list&redirectParams=some%3Dparam', $redirectUrl['query']);
286  }
287 
291  public function ‪handleRequestActivatesRequestedProvider(): void
292  {
293  $queryParams = [
294  'action' => 'activate',
295  'redirect' => 'web_list',
296  'redirectParams' => 'some=param',
297  ];
298 
299  $timestamp = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('date', 'timestamp');
300  $parsedBody = [
301  'identifier' => 'totp',
302  'totp' => GeneralUtility::makeInstance(
303  Totp::class,
304  'KRMVATZTJFZUC53FONXW2ZJB'
305  )->generateTotp((int)floor($timestamp / 30)),
306  'secret' => 'KRMVATZTJFZUC53FONXW2ZJB',
307  'checksum' => GeneralUtility::hmac('KRMVATZTJFZUC53FONXW2ZJB', 'totp-setup'),
308  ];
309 
310  ‪$request = $this->request->‪withMethod('POST')->withQueryParams($queryParams)->withParsedBody($parsedBody);
311  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
312  $response = $this->subject->handleRequest(‪$request);
313  $redirectUrl = parse_url($response->getHeaderLine('location'));
314 
315  // Successful activation will initiate a redirect to the login endpoint
316  self::assertEquals(302, $response->getStatusCode());
317  self::assertEquals('/typo3/login', $redirectUrl['path']);
318 
319  // Successful activation will set the "mfa" session key
320  self::assertTrue(‪$GLOBALS['BE_USER']->getSessionData('mfa'));
321 
322  // Successful activation will set "totp" as default provider
323  self::assertEquals('totp', ‪$GLOBALS['BE_USER']->uc['mfa']['defaultProvider']);
324 
325  // Successful activation will add a flash message
326  self::assertEquals(
327  'MFA setup successful',
328  GeneralUtility::makeInstance(FlashMessageService::class)->getMessageQueueByIdentifier()->getAllMessages()[0]->getTitle()
329  );
330 
331  // Flash message properly resolves the provider title
332  self::assertStringContainsString(
333  'You have successfully activated MFA provider Time-based one-time password.',
334  GeneralUtility::makeInstance(FlashMessageService::class)->getMessageQueueByIdentifier()->getAllMessages()[0]->getMessage()
335  );
336 
337  // Also redirect parameters are still kept
338  self::assertStringContainsString('redirect=web_list&redirectParams=some%3Dparam', $redirectUrl['query']);
339  }
340 
345  {
346  $queryParams = [
347  'action' => 'activate',
348  'redirect' => 'web_list',
349  'redirectParams' => 'some=param',
350  ];
351 
352  $parsedBody = [
353  'identifier' => 'totp',
354  'totp' => '123456', // invalid !!!
355  'secret' => 'KRMVATZTJFZUC53FONXW2ZJB',
356  'checksum' => GeneralUtility::hmac('KRMVATZTJFZUC53FONXW2ZJB', 'totp-setup'),
357  ];
358 
359  ‪$request = $this->request->‪withMethod('POST')->withQueryParams($queryParams)->withParsedBody($parsedBody);
360  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
361  $response = $this->subject->handleRequest(‪$request);
362  $redirectUrl = parse_url($response->getHeaderLine('location'));
363 
364  // Failure will redirect to setup view
365  self::assertEquals(302, $response->getStatusCode());
366  self::assertEquals('/typo3/setup/mfa', $redirectUrl['path']);
367 
368  // Failure will add "identifier" and "hasErrors" parameters
369  self::assertStringContainsString('identifier=totp&hasErrors=1', $redirectUrl['query']);
370 
371  // Also redirect parameters are still kept
372  self::assertStringContainsString('redirect=web_list&redirectParams=some%3Dparam', $redirectUrl['query']);
373  }
374 
378  public function ‪handleRequestCancelsSetup(): void
379  {
380  $queryParams = [
381  'action' => 'cancel',
382  'redirect' => 'web_list',
383  'redirectParams' => 'some=param',
384  ];
385 
386  ‪$request = $this->request->‪withQueryParams($queryParams);
387  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
388  $response = $this->subject->handleRequest(‪$request);
389  $redirectUrl = parse_url($response->getHeaderLine('location'));
390 
391  self::assertEquals(302, $response->getStatusCode());
392  self::assertEquals('/typo3/login', $redirectUrl['path']);
393 
394  // Also redirect parameters are still kept
395  self::assertStringContainsString('redirect=web_list&redirectParams=some%3Dparam', $redirectUrl['query']);
396  }
397 }
‪TYPO3\CMS\Backend\View\AuthenticationStyleInformation
Definition: AuthenticationStyleInformation.php:32
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\handleRequestThrowsExceptionWhenInSwitchUserMode
‪handleRequestThrowsExceptionWhenInSwitchUserMode()
Definition: MfaSetupControllerTest.php:96
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\setUp
‪setUp()
Definition: MfaSetupControllerTest.php:57
‪TYPO3\CMS\Core\Http\ServerRequest\withQueryParams
‪static withQueryParams(array $query)
Definition: ServerRequest.php:177
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\handleRequestActivatesRequestedProvider
‪handleRequestActivatesRequestedProvider()
Definition: MfaSetupControllerTest.php:290
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\handleRequestThrowsExceptionWhenMfaWasAlreadyPassed
‪handleRequestThrowsExceptionWhenMfaWasAlreadyPassed()
Definition: MfaSetupControllerTest.php:81
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\$configurationToUseInTestInstance
‪$configurationToUseInTestInstance
Definition: MfaSetupControllerTest.php:51
‪TYPO3\CMS\Backend\Template\ModuleTemplateFactory
Definition: ModuleTemplateFactory.php:29
‪TYPO3\CMS\Core\Core\SystemEnvironmentBuilder
Definition: SystemEnvironmentBuilder.php:41
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\handleRequestReturnsSetupView
‪handleRequestReturnsSetupView()
Definition: MfaSetupControllerTest.php:213
‪TYPO3\CMS\Core\Core\SystemEnvironmentBuilder\REQUESTTYPE_BE
‪const REQUESTTYPE_BE
Definition: SystemEnvironmentBuilder.php:45
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\handleRequestAddsRedirectParameters
‪handleRequestAddsRedirectParameters()
Definition: MfaSetupControllerTest.php:188
‪TYPO3\CMS\Backend\Controller\MfaSetupController
Definition: MfaSetupController.php:46
‪TYPO3\CMS\Core\Authentication\Mfa\Provider\Totp
Definition: Totp.php:30
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:53
‪TYPO3\CMS\Core\Core\Bootstrap\initializeLanguageObject
‪static initializeLanguageObject()
Definition: Bootstrap.php:598
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\handleRequestThrowsExceptionWhenMfaAlreadyActivated
‪handleRequestThrowsExceptionWhenMfaAlreadyActivated()
Definition: MfaSetupControllerTest.php:126
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\handleRequestThrowsExceptionWhenMfaNotRequired
‪handleRequestThrowsExceptionWhenMfaNotRequired()
Definition: MfaSetupControllerTest.php:111
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\handleRequestRedirectsToSetupOnInvalidProvider
‪handleRequestRedirectsToSetupOnInvalidProvider()
Definition: MfaSetupControllerTest.php:263
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:40
‪TYPO3\CMS\Core\Http\Request\withMethod
‪static withMethod($method)
Definition: Request.php:273
‪TYPO3\CMS\Core\Http\ServerRequest
Definition: ServerRequest.php:37
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\handleRequestRedirectsToSetupOnMissingProvider
‪handleRequestRedirectsToSetupOnMissingProvider()
Definition: MfaSetupControllerTest.php:240
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\handleRequestCancelsSetup
‪handleRequestCancelsSetup()
Definition: MfaSetupControllerTest.php:377
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\handleRequestRedirectsWithErrorOnActivationFailure
‪handleRequestRedirectsWithErrorOnActivationFailure()
Definition: MfaSetupControllerTest.php:343
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\$subject
‪MfaSetupController $subject
Definition: MfaSetupControllerTest.php:40
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\handleRequestReturns404OnWrongHttpMethod
‪handleRequestReturns404OnWrongHttpMethod()
Definition: MfaSetupControllerTest.php:153
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Core\Bootstrap
Definition: Bootstrap.php:70
‪TYPO3\CMS\Core\Log\Logger
Definition: Logger.php:27
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\handleRequestFallsBackToSelectionView
‪handleRequestFallsBackToSelectionView()
Definition: MfaSetupControllerTest.php:165
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest
Definition: MfaSetupControllerTest.php:38
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\handleRequestReturns404OnInvalidAction
‪handleRequestReturns404OnInvalidAction()
Definition: MfaSetupControllerTest.php:141
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaSetupControllerTest\$request
‪ServerRequest $request
Definition: MfaSetupControllerTest.php:41
‪TYPO3\CMS\Core\Messaging\FlashMessageService
Definition: FlashMessageService.php:27
‪TYPO3\CMS\Backend\Tests\Functional\Controller
Definition: EditDocumentControllerTest.php:18
‪TYPO3\CMS\Core\Authentication\Mfa\MfaProviderRegistry
Definition: MfaProviderRegistry.php:28