‪TYPO3CMS  ‪main
RecoveryCodesTest.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 PHPUnit\Framework\Attributes\DataProvider;
21 use PHPUnit\Framework\Attributes\Test;
26 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
27 
28 final class ‪RecoveryCodesTest extends UnitTestCase
29 {
31 
32  protected function ‪setUp(): void
33  {
34  parent::setUp();
35 
37  $this->subject = GeneralUtility::makeInstance(RecoveryCodes::class, 'BE');
38  }
39 
40  protected function ‪tearDown(): void
41  {
43 
44  parent::tearDown();
45  }
46 
47  #[Test]
48  public function ‪generateRecoveryCodesTest(): void
49  {
50  ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['passwordHashing'] = [
51  'className' => NoopPasswordHash::class,
52  'options' => [],
53  ];
54 
55  $codes = $this->subject->generateRecoveryCodes();
56 
57  self::assertCount(8, $codes);
58 
59  $plainCodes = array_keys($codes);
60  $hashedCodes = array_values($codes);
61  $hashInstance = (new ‪NoopPasswordHash());
62 
63  foreach ($hashedCodes as $key => $code) {
64  self::assertTrue($hashInstance->isValidSaltedPW($code));
65  self::assertTrue($hashInstance->checkPassword((string)$plainCodes[$key], $code));
66  }
67  }
68 
69  #[Test]
71  {
72  $this->expectException(\InvalidArgumentException::class);
73  $this->expectExceptionCode(1613666803);
74  $this->subject->generatePlainRecoveryCodes(6);
75  }
76 
77  #[DataProvider('generatePlainRecoveryCodesTestDataProvider')]
78  #[Test]
79  public function ‪generatePlainRecoveryCodesTest(int $length, int $quantity): void
80  {
81  $recoveryCodes = $this->subject->generatePlainRecoveryCodes($length, $quantity);
82  self::assertCount($quantity, $recoveryCodes);
83  foreach ($recoveryCodes as $code) {
84  self::assertIsNumeric($code);
85  self::assertEquals($length, strlen($code));
86  }
87  }
88 
89  public static function ‪generatePlainRecoveryCodesTestDataProvider(): \Generator
90  {
91  yield 'Default 8 codes with 8 chars' => [8, 8];
92  yield '8 codes with 10 chars' => [8, 10];
93  yield '10 codes with 8 chars' => [10, 8];
94  yield '0 codes with 8 chars' => [8, 0];
95  yield '10 codes with 10 chars' => [10, 10];
96  }
97 
98  #[Test]
100  {
101  ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['passwordHashing'] = [
102  'className' => BcryptPasswordHash::class,
103  'options' => [
104  // Reduce default costs for quicker unit tests
105  'cost' => 10,
106  ],
107  ];
108 
109  $codes = $this->subject->generatedHashedRecoveryCodes(['12345678', '87654321']);
110 
111  self::assertTrue((new ‪BcryptPasswordHash())->isValidSaltedPW((string)$codes[0]));
112  self::assertCount(2, $codes);
113  }
114 
115  #[Test]
116  public function ‪verifyRecoveryCodeTest(): void
117  {
118  ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['passwordHashing'] = [
119  'className' => NoopPasswordHash::class,
120  'options' => [],
121  ];
122 
123  $recoveryCode = '18742989';
124  $codes = [];
125 
126  // False on empty codes
127  self::assertFalse($this->subject->verifyRecoveryCode($recoveryCode, $codes));
128 
129  $codes = $this->subject->generatedHashedRecoveryCodes(
130  array_merge([$recoveryCode], $this->subject->generatePlainRecoveryCodes(8, 2))
131  );
132 
133  // Recovery code can be verified
134  self::assertTrue($this->subject->verifyRecoveryCode($recoveryCode, $codes));
135  // Verified code is removed from available codes
136  self::assertCount(2, $codes);
137  // Recovery code can not be verified again
138  self::assertFalse($this->subject->verifyRecoveryCode($recoveryCode, $codes));
139  }
140 
141  #[Test]
143  {
144  $code = '18742989';
145  $codes = [(new ‪NoopPasswordHash())->getHashedPassword($code)];
146 
147  // Ensure we have another default hash instance
148  ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['passwordHashing'] = [
149  'className' => BcryptPasswordHash::class,
150  'options' => [
151  // Reduce default costs for quicker unit tests
152  'cost' => 10,
153  ],
154  ];
155 
156  self::assertTrue($this->subject->verifyRecoveryCode($code, $codes));
157  self::assertEmpty($codes);
158  }
159 }
‪TYPO3\CMS\Core\Crypto\PasswordHashing\BcryptPasswordHash
Definition: BcryptPasswordHash.php:32
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\RecoveryCodesTest\generateRecoveryCodesTest
‪generateRecoveryCodesTest()
Definition: RecoveryCodesTest.php:48
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\RecoveryCodesTest\setUp
‪setUp()
Definition: RecoveryCodesTest.php:32
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\RecoveryCodesTest\tearDown
‪tearDown()
Definition: RecoveryCodesTest.php:40
‪TYPO3\CMS\Core\Authentication\Mfa\Provider\RecoveryCodes
Definition: RecoveryCodes.php:30
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\RecoveryCodesTest\generatePlainRecoveryCodesThrowsExceptionOnInvalidLengthTest
‪generatePlainRecoveryCodesThrowsExceptionOnInvalidLengthTest()
Definition: RecoveryCodesTest.php:70
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\Fixtures\Crypto\PasswordHashing\NoopPasswordHash\unregisterNoopPasswordHash
‪static unregisterNoopPasswordHash()
Definition: NoopPasswordHash.php:72
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\Fixtures\Crypto\PasswordHashing\NoopPasswordHash
Definition: NoopPasswordHash.php:29
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\RecoveryCodesTest\generatePlainRecoveryCodesTest
‪generatePlainRecoveryCodesTest(int $length, int $quantity)
Definition: RecoveryCodesTest.php:79
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\RecoveryCodesTest\verifyRecoveryCodeTest
‪verifyRecoveryCodeTest()
Definition: RecoveryCodesTest.php:116
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\Fixtures\Crypto\PasswordHashing\NoopPasswordHash\registerNoopPasswordHash
‪static registerNoopPasswordHash()
Definition: NoopPasswordHash.php:67
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\RecoveryCodesTest\generatedHashedRecoveryCodesAreHashedWithDefaultHashInstanceTest
‪generatedHashedRecoveryCodesAreHashedWithDefaultHashInstanceTest()
Definition: RecoveryCodesTest.php:99
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\RecoveryCodesTest\$subject
‪RecoveryCodes $subject
Definition: RecoveryCodesTest.php:30
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\RecoveryCodesTest\generatePlainRecoveryCodesTestDataProvider
‪static generatePlainRecoveryCodesTestDataProvider()
Definition: RecoveryCodesTest.php:89
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\RecoveryCodesTest
Definition: RecoveryCodesTest.php:29
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Tests\Unit\Authentication\Mfa\Provider\RecoveryCodesTest\verifyRecoveryCodeUsesTheCorrectHashInstanceTest
‪verifyRecoveryCodeUsesTheCorrectHashInstanceTest()
Definition: RecoveryCodesTest.php:142
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52