‪TYPO3CMS  11.5
TotpProviderTest.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 
30 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
31 
32 class ‪TotpProviderTest extends FunctionalTestCase
33 {
36 
37  protected function ‪setUp(): void
38  {
39  parent::setUp();
40  $this->importCSVDataSet(__DIR__ . '/../../Fixtures/be_users.csv');
41  $this->user = $this->setUpBackendUser(1);
42  ‪$GLOBALS['LANG'] = $this->get(LanguageServiceFactory::class)->createFromUserPreferences($this->user);
43  $this->subject = $this->get(MfaProviderRegistry::class)->getProvider('totp');
44  }
45 
49  public function ‪canProcessTest(): void
50  {
51  self::assertFalse($this->subject->canProcess(new ‪ServerRequest('https://example.com', 'POST')));
52 
53  // Add necessary query parameter
54  self::assertTrue($this->subject->canProcess(
55  (new ‪ServerRequest('https://example.com', 'POST'))
56  ->withQueryParams(['totp' => '123456'])
57  ));
58  }
59 
63  public function ‪isActiveTest(): void
64  {
65  // No provider entry exists
66  self::assertFalse($this->subject->isActive(‪MfaProviderPropertyManager::create($this->subject, $this->user)));
67 
68  // Active state missing
69  $this->‪setupUser(['secret' => 'KRMVATZTJFZUC53FONXW2ZJB']);
70  self::assertFalse($this->subject->isActive(‪MfaProviderPropertyManager::create($this->subject, $this->user)));
71 
72  // Secret missing
73  $this->‪setupUser(['active' => true]);
74  self::assertFalse($this->subject->isActive(‪MfaProviderPropertyManager::create($this->subject, $this->user)));
75 
76  // Active provider
77  $this->‪setupUser(['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB']);
78  self::assertTrue($this->subject->isActive(‪MfaProviderPropertyManager::create($this->subject, $this->user)));
79  }
80 
84  public function ‪isLockedTest(): void
85  {
86  // No provider entry exists
87  self::assertFalse($this->subject->isLocked(‪MfaProviderPropertyManager::create($this->subject, $this->user)));
88 
89  // Provider is not locked
90  $this->‪setupUser(['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB', 'attempts' => 0]);
91  self::assertFalse($this->subject->isLocked(‪MfaProviderPropertyManager::create($this->subject, $this->user)));
92 
93  // Lock provider by setting attempts=3
94  $this->‪setupUser(['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB', 'attempts' => 3]);
95  self::assertTrue($this->subject->isLocked(‪MfaProviderPropertyManager::create($this->subject, $this->user)));
96  }
97 
101  public function ‪verifyTest(): void
102  {
103  $request = (new ‪ServerRequest('https://example.com', 'POST'));
104  $timestamp = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('date', 'timestamp');
105  $totp = GeneralUtility::makeInstance(
106  Totp::class,
107  'KRMVATZTJFZUC53FONXW2ZJB'
108  )->generateTotp((int)floor($timestamp / 30));
109 
110  // Provider is inactive (secret missing)
111  $this->‪setupUser(['active' => true]);
112  self::assertFalse(
113  $this->subject->verify(
114  $request->withQueryParams(['totp' => $totp]),
115  ‪MfaProviderPropertyManager::create($this->subject, $this->user)
116  )
117  );
118 
119  // Provider is locked (attempts=3)
120  $this->‪setupUser(['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB', 'attempts' => 3]);
121  self::assertFalse(
122  $this->subject->verify(
123  $request->withQueryParams(['totp' => $totp]),
124  ‪MfaProviderPropertyManager::create($this->subject, $this->user)
125  )
126  );
127 
128  // Wrong totp
129  $this->‪setupUser(['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB', 'attempts' => 0]);
130  self::assertFalse(
131  $this->subject->verify(
132  $request->withQueryParams(['totp' => '123456']),
133  ‪MfaProviderPropertyManager::create($this->subject, $this->user)
134  )
135  );
136 
137  // Correct totp
138  self::assertTrue(
139  $this->subject->verify(
140  $request->withQueryParams(['totp' => $totp]),
141  ‪MfaProviderPropertyManager::create($this->subject, $this->user)
142  )
143  );
144  }
145 
149  public function ‪activateTest(): void
150  {
151  $request = (new ‪ServerRequest('https://example.com', 'POST'));
152  $propertyManager = ‪MfaProviderPropertyManager::create($this->subject, $this->user);
153 
154  // Wrong totp
155  self::assertFalse($this->subject->activate($request->withParsedBody(['totp' => '123456']), $propertyManager));
156 
157  // Setup form data to activate provider
158  $secret = 'KRMVATZTJFZUC53FONXW2ZJB';
159  $timestamp = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('date', 'timestamp');
160  $parsedBody = [
161  'totp' => GeneralUtility::makeInstance(Totp::class, $secret)->generateTotp((int)floor($timestamp / 30)),
162  'secret' => $secret,
163  'checksum' => GeneralUtility::hmac($secret, 'totp-setup'),
164 
165  ];
166  self::assertTrue($this->subject->activate($request->withParsedBody($parsedBody), $propertyManager));
167  self::assertTrue($propertyManager->getProperty('active'));
168  self::assertEquals('KRMVATZTJFZUC53FONXW2ZJB', $propertyManager->getProperty('secret'));
169  }
170 
174  public function ‪deactivateTest(): void
175  {
176  $request = (new ‪ServerRequest('https://example.com', 'POST'));
177 
178  // No provider entry exists
179  self::assertFalse($this->subject->deactivate($request, ‪MfaProviderPropertyManager::create($this->subject, $this->user)));
180 
181  // Only an active provider can be deactivated
182  $this->‪setupUser(['active' => false, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB']);
183  self::assertFalse($this->subject->deactivate($request, ‪MfaProviderPropertyManager::create($this->subject, $this->user)));
184 
185  // Active provider is deactivated
186  $this->‪setupUser(['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB']);
187  self::assertTrue($this->subject->deactivate($request, ‪MfaProviderPropertyManager::create($this->subject, $this->user)));
188  }
189 
193  public function ‪unlockTest(): void
194  {
195  $request = (new ‪ServerRequest('https://example.com', 'POST'));
196 
197  // No provider entry exists
198  self::assertFalse($this->subject->unlock($request, ‪MfaProviderPropertyManager::create($this->subject, $this->user)));
199 
200  // Provider is inactive (missing secret)
201  $this->‪setupUser(['active' => true, 'attempts' => 3]);
202  self::assertFalse($this->subject->unlock($request, ‪MfaProviderPropertyManager::create($this->subject, $this->user)));
203 
204  // Provider is not locked
205  $this->‪setupUser(['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB', 'attempts' => 0]);
206  self::assertFalse($this->subject->unlock($request, ‪MfaProviderPropertyManager::create($this->subject, $this->user)));
207 
208  // Active and locked provider is unlocked
209  $this->‪setupUser(['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB', 'attempts' => 3]);
210  self::assertTrue($this->subject->unlock($request, ‪MfaProviderPropertyManager::create($this->subject, $this->user)));
211  }
212 
216  public function ‪updateTest(): void
217  {
218  $request = (new ‪ServerRequest('https://example.com', 'POST'));
219 
220  // No provider entry exists
221  self::assertFalse($this->subject->update($request, ‪MfaProviderPropertyManager::create($this->subject, $this->user)));
222 
223  // Provider is inactive (missing secret)
224  $this->‪setupUser(['active' => true, 'attempts' => 0]);
225  self::assertFalse($this->subject->update($request, ‪MfaProviderPropertyManager::create($this->subject, $this->user)));
226 
227  // Provider is locked (attempts=3)
228  $this->‪setupUser(['active' => true, 'attempts' => 3]);
229  self::assertFalse($this->subject->update($request, ‪MfaProviderPropertyManager::create($this->subject, $this->user)));
230 
231  // Active and unlocked provider is updated
232  $this->‪setupUser(['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB', 'attempts' => 0]);
233  $request = $request->withParsedBody(['name' => 'some name']);
234  self::assertTrue($this->subject->update($request, ‪MfaProviderPropertyManager::create($this->subject, $this->user)));
235  }
236 
240  public function ‪setupViewTest(): void
241  {
242  $request = (new ‪ServerRequest('https://example.com', 'POST'));
243  $propertyManager = ‪MfaProviderPropertyManager::create($this->subject, $this->user);
244  $response = $this->subject->handleRequest($request, $propertyManager, ‪MfaViewType::SETUP)->getBody()->getContents();
245 
246  self::assertMatchesRegularExpression('/<input.*id="totp"/s', $response);
247  self::assertMatchesRegularExpression('/<input.*id="secret"/s', $response);
248  self::assertMatchesRegularExpression('/<div.*id="qr-code"/s', $response);
249  self::assertMatchesRegularExpression('/<typo3-mfa-totp-url-info-button.*url="otpauth:\/\//s', $response);
250  }
251 
255  public function ‪editViewTest(): void
256  {
257  $request = (new ‪ServerRequest('https://example.com', 'POST'));
258  $this->‪setupUser(['name' => 'some name', 'updated' => 1616099471, 'lastUsed' => 1616099472]);
259  $propertyManager = ‪MfaProviderPropertyManager::create($this->subject, $this->user);
260  $response = $this->subject->handleRequest($request, $propertyManager, ‪MfaViewType::EDIT)->getBody()->getContents();
261 
262  self::assertMatchesRegularExpression('/<td>.*Name.*<td>.*some name/s', $response);
263  self::assertMatchesRegularExpression('/<td>.*Last updated.*<td>.*18-03-21/s', $response);
264  self::assertMatchesRegularExpression('/<td>.*Last used.*<td>.*18-03-21/s', $response);
265  }
266 
270  public function ‪authViewTest(): void
271  {
272  $request = (new ‪ServerRequest('https://example.com', 'POST'));
273  $this->‪setupUser(['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB', 'attempts' => 0]);
274  $propertyManager = ‪MfaProviderPropertyManager::create($this->subject, $this->user);
275  $response = $this->subject->handleRequest($request, $propertyManager, ‪MfaViewType::AUTH)->getBody()->getContents();
276 
277  self::assertMatchesRegularExpression('/<input.*id="totp"/s', $response);
278 
279  // Lock the provider by setting attempts=3
280  $this->‪setupUser(['active' => true, 'secret' => 'KRMVATZTJFZUC53FONXW2ZJB', 'attempts' => 3]);
281  $propertyManager = ‪MfaProviderPropertyManager::create($this->subject, $this->user);
282  $response = $this->subject->handleRequest($request, $propertyManager, ‪MfaViewType::AUTH)->getBody()->getContents();
283 
284  self::assertStringContainsString('The maximum attempts for this provider are exceeded.', $response);
285  }
286 
287  protected function ‪setupUser(array $properties = []): void
288  {
289  $this->user->user['mfa'] = json_encode(['totp' => $properties]);
290  }
291 }
‪TYPO3\CMS\Core\Localization\LanguageServiceFactory
Definition: LanguageServiceFactory.php:25
‪TYPO3\CMS\Core\Tests\Functional\Authentication\Mfa\Provider\TotpProviderTest\deactivateTest
‪deactivateTest()
Definition: TotpProviderTest.php:174
‪TYPO3\CMS\Core\Authentication\Mfa\MfaViewType\EDIT
‪const EDIT
Definition: MfaViewType.php:28
‪TYPO3\CMS\Core\Tests\Functional\Authentication\Mfa\Provider\TotpProviderTest\updateTest
‪updateTest()
Definition: TotpProviderTest.php:216
‪TYPO3\CMS\Core\Authentication\Mfa\MfaViewType\AUTH
‪const AUTH
Definition: MfaViewType.php:29
‪TYPO3\CMS\Core\Tests\Functional\Authentication\Mfa\Provider\TotpProviderTest\setupUser
‪setupUser(array $properties=[])
Definition: TotpProviderTest.php:287
‪TYPO3\CMS\Core\Tests\Functional\Authentication\Mfa\Provider\TotpProviderTest\isActiveTest
‪isActiveTest()
Definition: TotpProviderTest.php:63
‪TYPO3\CMS\Core\Tests\Functional\Authentication\Mfa\Provider\TotpProviderTest
Definition: TotpProviderTest.php:33
‪TYPO3\CMS\Core\Tests\Functional\Authentication\Mfa\Provider
Definition: RecoveryCodesProviderTest.php:18
‪TYPO3\CMS\Core\Authentication\Mfa\MfaViewType\SETUP
‪const SETUP
Definition: MfaViewType.php:27
‪TYPO3\CMS\Core\Authentication\Mfa\MfaProviderManifestInterface
Definition: MfaProviderManifestInterface.php:26
‪TYPO3\CMS\Core\Authentication\Mfa\Provider\Totp
Definition: Totp.php:30
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:53
‪TYPO3\CMS\Core\Tests\Functional\Authentication\Mfa\Provider\TotpProviderTest\activateTest
‪activateTest()
Definition: TotpProviderTest.php:149
‪TYPO3\CMS\Core\Tests\Functional\Authentication\Mfa\Provider\TotpProviderTest\editViewTest
‪editViewTest()
Definition: TotpProviderTest.php:255
‪TYPO3\CMS\Core\Http\ServerRequest
Definition: ServerRequest.php:37
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Core\Tests\Functional\Authentication\Mfa\Provider\TotpProviderTest\$subject
‪MfaProviderManifestInterface $subject
Definition: TotpProviderTest.php:35
‪TYPO3\CMS\Core\Authentication\Mfa\MfaProviderPropertyManager
Definition: MfaProviderPropertyManager.php:33
‪TYPO3\CMS\Core\Tests\Functional\Authentication\Mfa\Provider\TotpProviderTest\verifyTest
‪verifyTest()
Definition: TotpProviderTest.php:101
‪TYPO3\CMS\Core\Tests\Functional\Authentication\Mfa\Provider\TotpProviderTest\authViewTest
‪authViewTest()
Definition: TotpProviderTest.php:270
‪TYPO3\CMS\Core\Tests\Functional\Authentication\Mfa\Provider\TotpProviderTest\unlockTest
‪unlockTest()
Definition: TotpProviderTest.php:193
‪TYPO3\CMS\Core\Tests\Functional\Authentication\Mfa\Provider\TotpProviderTest\$user
‪BackendUserAuthentication $user
Definition: TotpProviderTest.php:34
‪$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\Core\Tests\Functional\Authentication\Mfa\Provider\TotpProviderTest\canProcessTest
‪canProcessTest()
Definition: TotpProviderTest.php:49
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\Tests\Functional\Authentication\Mfa\Provider\TotpProviderTest\isLockedTest
‪isLockedTest()
Definition: TotpProviderTest.php:84
‪TYPO3\CMS\Core\Tests\Functional\Authentication\Mfa\Provider\TotpProviderTest\setupViewTest
‪setupViewTest()
Definition: TotpProviderTest.php:240
‪TYPO3\CMS\Core\Authentication\Mfa\MfaProviderRegistry
Definition: MfaProviderRegistry.php:28
‪TYPO3\CMS\Core\Tests\Functional\Authentication\Mfa\Provider\TotpProviderTest\setUp
‪setUp()
Definition: TotpProviderTest.php:37