‪TYPO3CMS  11.5
MfaControllerTest.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 ‪MfaControllerTest 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);
63 
64  $container = $this->getContainer();
65  $this->subject = new ‪MfaController(
66  $container->get(UriBuilder::class),
67  $container->get(MfaProviderRegistry::class),
68  $container->get(ModuleTemplateFactory::class),
69  $container->get(AuthenticationStyleInformation::class),
70  $container->get(PageRenderer::class),
71  );
72  $this->subject->setLogger($this->prophesize(Logger::class)->reveal());
73 
74  $this->request = (new ‪ServerRequest())
75  ->withAttribute('applicationType', ‪SystemEnvironmentBuilder::REQUESTTYPE_BE);
76  }
77 
78  protected function ‪tearDown(): void
79  {
81  parent::tearDown();
82  }
83 
88  {
89  $this->expectException(\InvalidArgumentException::class);
90  $this->expectExceptionCode(1611879244);
91 
92  ‪$request = $this->request->‪withQueryParams(['action' => 'unknown']);
93  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
94  $this->subject->handleRequest(‪$request);
95  }
96 
101  {
102  $this->expectException(\InvalidArgumentException::class);
103  $this->expectExceptionCode(1611879242);
104 
105  ‪$request = $this->request->‪withQueryParams(['action' => 'auth']);
106  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
107  $this->subject->handleRequest(‪$request);
108  }
109 
114  {
115  $this->expectException(\InvalidArgumentException::class);
116  $this->expectExceptionCode(1611879242);
117 
118  $queryParams = [
119  'action' => 'auth',
120  'identifier' => 'totp',
121  ];
122 
123  ‪$request = $this->request->‪withQueryParams($queryParams);
124  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
125  $this->subject->handleRequest(‪$request);
126  }
127 
131  public function ‪handleRequestReturnsAuthViewTest(): void
132  {
133  ‪$GLOBALS['BE_USER']->user['mfa'] = json_encode([
134  'totp' => ['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB'],
135  ]);
136 
137  $queryParams = [
138  'action' => 'auth',
139  'identifier' => 'totp',
140  ];
141 
142  ‪$request = $this->request->‪withQueryParams($queryParams);
143  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
144  $response = $this->subject->handleRequest(‪$request);
145 
146  self::assertEquals(200, $response->getStatusCode());
147 
148  $responseContent = $response->getBody()->getContents();
149 
150  // Auth view for provider is renderer
151  self::assertStringContainsString('Time-based one-time password', $responseContent);
152  self::assertMatchesRegularExpression('/<form.*name="verify".*id="mfaController">/s', $responseContent);
153 
154  // Ensure provider specific content is added as well
155  self::assertMatchesRegularExpression('/<input.*id="totp"/s', $responseContent);
156  }
157 
161  public function ‪handleRequestReturnsLockedAuthViewTest(): void
162  {
163  ‪$GLOBALS['BE_USER']->user['mfa'] = json_encode([
164  'totp' => ['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB', 'attempts' => 3],
165  ]);
166 
167  $queryParams = [
168  'action' => 'auth',
169  'identifier' => 'totp',
170  ];
171 
172  ‪$request = $this->request->‪withQueryParams($queryParams);
173  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
174  $response = $this->subject->handleRequest(‪$request);
175 
176  self::assertEquals(200, $response->getStatusCode());
177  self::assertStringContainsString('This provider is temporarily locked!', $response->getBody()->getContents());
178  }
179 
184  {
185  ‪$GLOBALS['BE_USER']->user['mfa'] = json_encode([
186  'totp' => ['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB'],
187  'recovery-codes' => ['active' => true, 'codes' => ['some-code']],
188  ]);
189 
190  $queryParams = [
191  'action' => 'auth',
192  'identifier' => 'totp',
193  ];
194 
195  ‪$request = $this->request->‪withQueryParams($queryParams);
196  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
197  $response = $this->subject->handleRequest(‪$request);
198 
199  self::assertEquals(200, $response->getStatusCode());
200 
201  $responseContent = $response->getBody()->getContents();
202  self::assertStringContainsString('Alternative providers', $responseContent);
203  self::assertMatchesRegularExpression('/<a.*title="Use Recovery codes"/s', $responseContent);
204  }
205 
210  {
211  ‪$GLOBALS['BE_USER']->user['mfa'] = json_encode([
212  'totp' => ['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB'],
213  ]);
214 
215  $queryParams = [
216  'action' => 'verify',
217  'identifier' => 'totp',
218  ];
219 
220  // The "totp" parameter is missing, therefore the TotpProvider will return false on ->canProcess()
221  ‪$request = $this->request->‪withQueryParams($queryParams);
222  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
223  $response = $this->subject->handleRequest(‪$request);
224 
225  self::assertEquals(302, $response->getStatusCode());
226  self::assertEquals('/typo3/login', parse_url($response->getHeaderLine('location'))['path']);
227  }
228 
233  {
234  ‪$GLOBALS['BE_USER']->user['mfa'] = json_encode([
235  'totp' => ['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB', 'attempts' => 3],
236  ]);
237 
238  $queryParams = [
239  'action' => 'verify',
240  'identifier' => 'totp',
241  'totp' => '123456',
242  ];
243 
244  ‪$request = $this->request->‪withQueryParams($queryParams);
245  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
246  $response = $this->subject->handleRequest(‪$request);
247 
248  self::assertEquals(302, $response->getStatusCode());
249  self::assertEquals('/typo3/login', parse_url($response->getHeaderLine('location'))['path']);
250  }
251 
256  {
257  ‪$GLOBALS['BE_USER']->user['mfa'] = json_encode([
258  'totp' => ['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB'],
259  ]);
260 
261  $queryParams = [
262  'action' => 'verify',
263  'identifier' => 'totp',
264  'totp' => '123456',
265  ];
266 
267  ‪$request = $this->request->‪withQueryParams($queryParams);
268  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
269  $response = $this->subject->handleRequest(‪$request);
270 
271  self::assertEquals(302, $response->getStatusCode());
272  self::assertEquals('/typo3/auth/mfa', parse_url($response->getHeaderLine('location'))['path']);
273  }
274 
279  {
280  ‪$GLOBALS['BE_USER']->user['mfa'] = json_encode([
281  'totp' => ['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB'],
282  ]);
283 
284  $timestamp = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('date', 'timestamp');
285  $totp = GeneralUtility::makeInstance(
286  Totp::class,
287  'KRMVATZTJFZUC53FONXW2ZJB'
288  )->generateTotp((int)floor($timestamp / 30));
289 
290  $queryParams = [
291  'action' => 'verify',
292  'identifier' => 'totp',
293  'totp' => $totp,
294  ];
295 
296  // Ensure mfa session key is not set
297  self::assertFalse((bool)‪$GLOBALS['BE_USER']->getSessionData('mfa'));
298 
299  ‪$request = $this->request->‪withQueryParams($queryParams);
300  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
301  $response = $this->subject->handleRequest(‪$request);
302 
303  // Session key is set - User is authenticated
304  self::assertTrue(‪$GLOBALS['BE_USER']->getSessionData('mfa'));
305 
306  // Redirect back to login
307  self::assertEquals(302, $response->getStatusCode());
308  self::assertEquals('/typo3/login', parse_url($response->getHeaderLine('location'))['path']);
309  }
310 
314  public function ‪handleRequestRedirectsToLoginOnCancelTest(): void
315  {
316  ‪$request = $this->request->‪withQueryParams(['action' => 'cancel']);
317  ‪$GLOBALS['TYPO3_REQUEST'] = ‪$request;
318  $response = $this->subject->handleRequest(‪$request);
319 
320  self::assertEquals(302, $response->getStatusCode());
321  self::assertEquals('/typo3/login', parse_url($response->getHeaderLine('location'))['path']);
322  }
323 }
‪TYPO3\CMS\Backend\View\AuthenticationStyleInformation
Definition: AuthenticationStyleInformation.php:32
‪TYPO3\CMS\Core\Http\ServerRequest\withQueryParams
‪static withQueryParams(array $query)
Definition: ServerRequest.php:177
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaControllerTest\handleRequestRedirectsToLoginOnCancelTest
‪handleRequestRedirectsToLoginOnCancelTest()
Definition: MfaControllerTest.php:313
‪TYPO3\CMS\Backend\Template\ModuleTemplateFactory
Definition: ModuleTemplateFactory.php:29
‪TYPO3\CMS\Core\Core\SystemEnvironmentBuilder
Definition: SystemEnvironmentBuilder.php:41
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaControllerTest\$request
‪ServerRequest $request
Definition: MfaControllerTest.php:41
‪TYPO3\CMS\Core\Core\SystemEnvironmentBuilder\REQUESTTYPE_BE
‪const REQUESTTYPE_BE
Definition: SystemEnvironmentBuilder.php:45
‪TYPO3\CMS\Core\FormProtection\FormProtectionFactory\purgeInstances
‪static purgeInstances()
Definition: FormProtectionFactory.php:231
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaControllerTest\setUp
‪setUp()
Definition: MfaControllerTest.php:57
‪TYPO3\CMS\Core\Authentication\Mfa\Provider\Totp
Definition: Totp.php:30
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaControllerTest\handleRequestReturnsLockedAuthViewTest
‪handleRequestReturnsLockedAuthViewTest()
Definition: MfaControllerTest.php:160
‪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\MfaControllerTest\$configurationToUseInTestInstance
‪$configurationToUseInTestInstance
Definition: MfaControllerTest.php:51
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:40
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaControllerTest
Definition: MfaControllerTest.php:38
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaControllerTest\handleRequestRedirectsToLoginOnLockedProviderRequestTest
‪handleRequestRedirectsToLoginOnLockedProviderRequestTest()
Definition: MfaControllerTest.php:231
‪TYPO3\CMS\Core\Http\ServerRequest
Definition: ServerRequest.php:37
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaControllerTest\handleRequestRedirectsToAuthViewOnUnsuccessfulAuthenticationTest
‪handleRequestRedirectsToAuthViewOnUnsuccessfulAuthenticationTest()
Definition: MfaControllerTest.php:254
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaControllerTest\handleRequestThrowsExceptionOnInvalidActionTest
‪handleRequestThrowsExceptionOnInvalidActionTest()
Definition: MfaControllerTest.php:86
‪TYPO3\CMS\Backend\Controller\MfaController
Definition: MfaController.php:46
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaControllerTest\$subject
‪MfaController $subject
Definition: MfaControllerTest.php:40
‪TYPO3\CMS\Core\FormProtection\FormProtectionFactory
Definition: FormProtectionFactory.php:48
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaControllerTest\handleRequestRedirectsToLoginOnInvalidRequestTest
‪handleRequestRedirectsToLoginOnInvalidRequestTest()
Definition: MfaControllerTest.php:208
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaControllerTest\tearDown
‪tearDown()
Definition: MfaControllerTest.php:77
‪TYPO3\CMS\Core\Core\Bootstrap
Definition: Bootstrap.php:70
‪TYPO3\CMS\Core\Log\Logger
Definition: Logger.php:27
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaControllerTest\handleRequestSetsSessionKeyOnSuccessfulAuthenticationTest
‪handleRequestSetsSessionKeyOnSuccessfulAuthenticationTest()
Definition: MfaControllerTest.php:277
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaControllerTest\handleRequestThrowsExceptionOnInactiveProviderTest
‪handleRequestThrowsExceptionOnInactiveProviderTest()
Definition: MfaControllerTest.php:112
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaControllerTest\handleRequestReturnsAuthViewTest
‪handleRequestReturnsAuthViewTest()
Definition: MfaControllerTest.php:130
‪TYPO3\CMS\Backend\Tests\Functional\Controller
Definition: EditDocumentControllerTest.php:18
‪TYPO3\CMS\Core\Authentication\Mfa\MfaProviderRegistry
Definition: MfaProviderRegistry.php:28
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaControllerTest\handleRequestThrowsExceptionOnMissingProviderTest
‪handleRequestThrowsExceptionOnMissingProviderTest()
Definition: MfaControllerTest.php:99
‪TYPO3\CMS\Backend\Tests\Functional\Controller\MfaControllerTest\handleRequestReturnsAlternativeProvidersInAuthViewTest
‪handleRequestReturnsAlternativeProvidersInAuthViewTest()
Definition: MfaControllerTest.php:182