[TASK] Mitigate argon2i hash issues
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Authentication / AuthenticationServiceTest.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Core\Tests\Unit\Authentication;
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
18 use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
19 use TYPO3\CMS\Core\Authentication\AuthenticationService;
20 use TYPO3\CMS\Core\Crypto\PasswordHashing\InvalidPasswordHashException;
21 use TYPO3\CMS\Core\Log\Logger;
22 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
23
24 /**
25 * Test case
26 */
27 class AuthenticationServiceTest extends UnitTestCase
28 {
29 /**
30 * @var bool Reset singletons created by subject
31 */
32 protected $resetSingletonInstances = true;
33
34 /**
35 * Date provider for processLoginReturnsCorrectData
36 *
37 * @return array
38 */
39 public function processLoginDataProvider(): array
40 {
41 return [
42 'Backend login with securityLevel "normal"' => [
43 'normal',
44 [
45 'status' => 'login',
46 'uname' => 'admin',
47 'uident' => 'password',
48 ],
49 [
50 'status' => 'login',
51 'uname' => 'admin',
52 'uident' => 'password',
53 'uident_text' => 'password',
54 ]
55 ],
56 'Frontend login with securityLevel "normal"' => [
57 'normal',
58 [
59 'status' => 'login',
60 'uname' => 'admin',
61 'uident' => 'password',
62 ],
63 [
64 'status' => 'login',
65 'uname' => 'admin',
66 'uident' => 'password',
67 'uident_text' => 'password',
68 ]
69 ],
70 ];
71 }
72
73 /**
74 * @test
75 * @dataProvider processLoginDataProvider
76 */
77 public function processLoginReturnsCorrectData($passwordSubmissionStrategy, $loginData, $expectedProcessedData): void
78 {
79 $subject = new AuthenticationService();
80 // Login data is modified by reference
81 $subject->processLoginData($loginData, $passwordSubmissionStrategy);
82 $this->assertEquals($expectedProcessedData, $loginData);
83 }
84
85 /**
86 * @test
87 */
88 public function authUserReturns100IfSubmittedPasswordIsEmpty(): void
89 {
90 $subject = new AuthenticationService();
91 $subject->initAuth('mode', ['uident_text' => '', 'uname' => 'user'], [], null);
92 $this->assertSame(100, $subject->authUser([]));
93 }
94
95 /**
96 * @test
97 */
98 public function authUserReturns100IfUserSubmittedUsernameIsEmpty(): void
99 {
100 $subject = new AuthenticationService();
101 $subject->initAuth('mode', ['uident_text' => 'foo', 'uname' => ''], [], null);
102 $this->assertSame(100, $subject->authUser([]));
103 }
104
105 /**
106 * @test
107 */
108 public function authUserThrowsExceptionIfUserTableIsNotSet(): void
109 {
110 $subject = new AuthenticationService();
111 $subject->initAuth('mode', ['uident_text' => 'password', 'uname' => 'user'], [], null);
112 $this->expectException(\RuntimeException::class);
113 $this->expectExceptionCode(1533159150);
114 $subject->authUser([]);
115 }
116
117 /**
118 * @test
119 */
120 public function authUserThrowsExceptionIfPasswordInDbDoesNotResolveToAValidHash(): void
121 {
122 $subject = new AuthenticationService();
123 $pObjProphecy = $this->prophesize(AbstractUserAuthentication::class);
124 $loggerProphecy = $this->prophesize(Logger::class);
125 $subject->setLogger($loggerProphecy->reveal());
126 $subject->initAuth(
127 'authUserBE',
128 [
129 'uident_text' => 'password',
130 'uname' => 'lolli'
131 ],
132 [
133 'db_user' => ['table' => 'be_users'],
134 'HTTP_HOST' => ''
135 ],
136 $pObjProphecy->reveal()
137 );
138 $dbUser = [
139 'password' => 'aPlainTextPassword',
140 'lockToDomain' => ''
141 ];
142 $this->expectException(InvalidPasswordHashException::class);
143 $this->expectExceptionCode(1533818591);
144 $subject->authUser($dbUser);
145 }
146
147 /**
148 * @test
149 */
150 public function authUserReturns0IfPasswordDoesNotMatch(): void
151 {
152 $subject = new AuthenticationService();
153 $pObjProphecy = $this->prophesize(AbstractUserAuthentication::class);
154 $loggerProphecy = $this->prophesize(Logger::class);
155 $subject->setLogger($loggerProphecy->reveal());
156 $subject->initAuth(
157 'authUserBE',
158 [
159 'uident_text' => 'notMyPassword',
160 'uname' => 'lolli'
161 ],
162 [
163 'db_user' => ['table' => 'be_users'],
164 'HTTP_HOST' => '',
165 ],
166 $pObjProphecy->reveal()
167 );
168 $dbUser = [
169 // a phpass hash of 'myPassword'
170 'password' => '$P$C/2Vr3ywuuPo5C7cs75YBnVhgBWpMP1',
171 'lockToDomain' => ''
172 ];
173 $this->assertSame(0, $subject->authUser($dbUser));
174 }
175
176 /**
177 * @test
178 */
179 public function authUserReturns200IfPasswordMatch(): void
180 {
181 $subject = new AuthenticationService();
182 $pObjProphecy = $this->prophesize(AbstractUserAuthentication::class);
183 $loggerProphecy = $this->prophesize(Logger::class);
184 $subject->setLogger($loggerProphecy->reveal());
185 $subject->initAuth(
186 'authUserBE',
187 [
188 'uident_text' => 'myPassword',
189 'uname' => 'lolli'
190 ],
191 [
192 'db_user' => ['table' => 'be_users'],
193 'HTTP_HOST' => ''
194 ],
195 $pObjProphecy->reveal()
196 );
197 $dbUser = [
198 // an argon2i hash of 'myPassword'
199 'password' => '$argon2i$v=19$m=16384,t=16,p=2$Ty9zOFVWdDBVQmlWTldVbg$kiVbkrYeTvgNg84i97WZBMQszmza66IohBxUtOnzRvU',
200 'lockToDomain' => ''
201 ];
202 $this->assertSame(200, $subject->authUser($dbUser));
203 }
204
205 /**
206 * @test
207 */
208 public function authUserReturns0IfPasswordMatchButDomainLockDoesNotMatch(): void
209 {
210 $subject = new AuthenticationService();
211 $pObjProphecy = $this->prophesize(AbstractUserAuthentication::class);
212 $loggerProphecy = $this->prophesize(Logger::class);
213 $subject->setLogger($loggerProphecy->reveal());
214 $subject->initAuth(
215 'authUserBE',
216 [
217 'uident_text' => 'myPassword',
218 'uname' => 'lolli'
219 ],
220 [
221 'db_user' => [
222 'table' => 'be_users',
223 'username_column' => 'username',
224 ],
225 'REMOTE_HOST' => '',
226 'HTTP_HOST' => 'example.com',
227 ],
228 $pObjProphecy->reveal()
229 );
230 $dbUser = [
231 // an argon2i hash of 'myPassword'
232 'password' => '$argon2i$v=19$m=16384,t=16,p=2$Ty9zOFVWdDBVQmlWTldVbg$kiVbkrYeTvgNg84i97WZBMQszmza66IohBxUtOnzRvU',
233 'username' => 'lolli',
234 'lockToDomain' => 'not.example.com'
235 ];
236 $this->assertSame(0, $subject->authUser($dbUser));
237 }
238 }