Remove unused messages
[lhc/web/wiklou.git] / tests / phpunit / includes / auth / LegacyHookPreAuthenticationProviderTest.php
1 <?php
2
3 namespace MediaWiki\Auth;
4
5 /**
6 * @group AuthManager
7 * @group Database
8 * @covers MediaWiki\Auth\LegacyHookPreAuthenticationProvider
9 */
10 class LegacyHookPreAuthenticationProviderTest extends \MediaWikiTestCase {
11 /**
12 * Get an instance of the provider
13 * @return LegacyHookPreAuthenticationProvider
14 */
15 protected function getProvider() {
16 $request = $this->getMock( 'FauxRequest', [ 'getIP' ] );
17 $request->expects( $this->any() )->method( 'getIP' )->will( $this->returnValue( '127.0.0.42' ) );
18
19 $manager = new AuthManager(
20 $request, \ConfigFactory::getDefaultInstance()->makeConfig( 'main' )
21 );
22
23 $provider = new LegacyHookPreAuthenticationProvider();
24 $provider->setManager( $manager );
25 $provider->setLogger( new \Psr\Log\NullLogger() );
26 $provider->setConfig( new \HashConfig( [
27 'PasswordAttemptThrottle' => [ 'count' => 23, 'seconds' => 42 ],
28 ] ) );
29 return $provider;
30 }
31
32 /**
33 * Sets a mock on a hook
34 * @param string $hook
35 * @param object $expect From $this->once(), $this->never(), etc.
36 * @return object $mock->expects( $expect )->method( ... ).
37 */
38 protected function hook( $hook, $expect ) {
39 $mock = $this->getMock( __CLASS__, [ "on$hook" ] );
40 $this->mergeMwGlobalArrayValue( 'wgHooks', [
41 $hook => [ $mock ],
42 ] );
43 return $mock->expects( $expect )->method( "on$hook" );
44 }
45
46 /**
47 * Unsets a hook
48 * @param string $hook
49 */
50 protected function unhook( $hook ) {
51 $this->mergeMwGlobalArrayValue( 'wgHooks', [
52 $hook => [],
53 ] );
54 }
55
56 // Stubs for hooks taking reference parameters
57 public function onLoginUserMigrated( $user, &$msg ) {
58 }
59 public function onAbortLogin( $user, $password, &$abort, &$msg ) {
60 }
61 public function onAbortNewAccount( $user, &$abortError, &$abortStatus ) {
62 }
63 public function onAbortAutoAccount( $user, &$abortError ) {
64 }
65
66 /**
67 * @dataProvider provideTestForAuthentication
68 * @param string|null $username
69 * @param string|null $password
70 * @param string|null $msgForLoginUserMigrated
71 * @param int|null $abortForAbortLogin
72 * @param string|null $msgForAbortLogin
73 * @param string|null $failMsg
74 * @param array $failParams
75 */
76 public function testTestForAuthentication(
77 $username, $password,
78 $msgForLoginUserMigrated, $abortForAbortLogin, $msgForAbortLogin,
79 $failMsg, $failParams = []
80 ) {
81 $reqs = [];
82 if ( $username === null ) {
83 $this->hook( 'LoginUserMigrated', $this->never() );
84 $this->hook( 'AbortLogin', $this->never() );
85 } else {
86 if ( $password === null ) {
87 $req = $this->getMockForAbstractClass( AuthenticationRequest::class );
88 } else {
89 $req = new PasswordAuthenticationRequest();
90 $req->action = AuthManager::ACTION_LOGIN;
91 $req->password = $password;
92 }
93 $req->username = $username;
94 $reqs[get_class( $req )] = $req;
95
96 $h = $this->hook( 'LoginUserMigrated', $this->once() );
97 if ( $msgForLoginUserMigrated !== null ) {
98 $h->will( $this->returnCallback(
99 function ( $user, &$msg ) use ( $username, $msgForLoginUserMigrated ) {
100 $this->assertInstanceOf( 'User', $user );
101 $this->assertSame( $username, $user->getName() );
102 $msg = $msgForLoginUserMigrated;
103 return false;
104 }
105 ) );
106 $this->hook( 'AbortLogin', $this->never() );
107 } else {
108 $h->will( $this->returnCallback(
109 function ( $user, &$msg ) use ( $username ) {
110 $this->assertInstanceOf( 'User', $user );
111 $this->assertSame( $username, $user->getName() );
112 return true;
113 }
114 ) );
115 $h2 = $this->hook( 'AbortLogin', $this->once() );
116 if ( $abortForAbortLogin !== null ) {
117 $h2->will( $this->returnCallback(
118 function ( $user, $pass, &$abort, &$msg )
119 use ( $username, $password, $abortForAbortLogin, $msgForAbortLogin )
120 {
121 $this->assertInstanceOf( 'User', $user );
122 $this->assertSame( $username, $user->getName() );
123 if ( $password !== null ) {
124 $this->assertSame( $password, $pass );
125 } else {
126 $this->assertInternalType( 'string', $pass );
127 }
128 $abort = $abortForAbortLogin;
129 $msg = $msgForAbortLogin;
130 return false;
131 }
132 ) );
133 } else {
134 $h2->will( $this->returnCallback(
135 function ( $user, $pass, &$abort, &$msg ) use ( $username, $password ) {
136 $this->assertInstanceOf( 'User', $user );
137 $this->assertSame( $username, $user->getName() );
138 if ( $password !== null ) {
139 $this->assertSame( $password, $pass );
140 } else {
141 $this->assertInternalType( 'string', $pass );
142 }
143 return true;
144 }
145 ) );
146 }
147 }
148 }
149 unset( $h, $h2 );
150
151 $status = $this->getProvider()->testForAuthentication( $reqs );
152
153 $this->unhook( 'LoginUserMigrated' );
154 $this->unhook( 'AbortLogin' );
155
156 if ( $failMsg === null ) {
157 $this->assertEquals( \StatusValue::newGood(), $status, 'should succeed' );
158 } else {
159 $this->assertInstanceOf( 'StatusValue', $status, 'should fail (type)' );
160 $this->assertFalse( $status->isOk(), 'should fail (ok)' );
161 $errors = $status->getErrors();
162 $this->assertEquals( $failMsg, $errors[0]['message'], 'should fail (message)' );
163 $this->assertEquals( $failParams, $errors[0]['params'], 'should fail (params)' );
164 }
165 }
166
167 public static function provideTestForAuthentication() {
168 return [
169 'No valid requests' => [
170 null, null, null, null, null, null
171 ],
172 'No hook errors' => [
173 'User', 'PaSsWoRd', null, null, null, null
174 ],
175 'No hook errors, no password' => [
176 'User', null, null, null, null, null
177 ],
178 'LoginUserMigrated no message' => [
179 'User', 'PaSsWoRd', false, null, null, 'login-migrated-generic'
180 ],
181 'LoginUserMigrated with message' => [
182 'User', 'PaSsWoRd', 'LUM-abort', null, null, 'LUM-abort'
183 ],
184 'LoginUserMigrated with message and params' => [
185 'User', 'PaSsWoRd', [ 'LUM-abort', 'foo' ], null, null, 'LUM-abort', [ 'foo' ]
186 ],
187 'AbortLogin, SUCCESS' => [
188 'User', 'PaSsWoRd', null, \LoginForm::SUCCESS, null, null
189 ],
190 'AbortLogin, NEED_TOKEN, no message' => [
191 'User', 'PaSsWoRd', null, \LoginForm::NEED_TOKEN, null, 'nocookiesforlogin'
192 ],
193 'AbortLogin, NEED_TOKEN, with message' => [
194 'User', 'PaSsWoRd', null, \LoginForm::NEED_TOKEN, 'needtoken', 'needtoken'
195 ],
196 'AbortLogin, WRONG_TOKEN, no message' => [
197 'User', 'PaSsWoRd', null, \LoginForm::WRONG_TOKEN, null, 'sessionfailure'
198 ],
199 'AbortLogin, WRONG_TOKEN, with message' => [
200 'User', 'PaSsWoRd', null, \LoginForm::WRONG_TOKEN, 'wrongtoken', 'wrongtoken'
201 ],
202 'AbortLogin, ILLEGAL, no message' => [
203 'User', 'PaSsWoRd', null, \LoginForm::ILLEGAL, null, 'noname'
204 ],
205 'AbortLogin, ILLEGAL, with message' => [
206 'User', 'PaSsWoRd', null, \LoginForm::ILLEGAL, 'badname', 'badname'
207 ],
208 'AbortLogin, NO_NAME, no message' => [
209 'User', 'PaSsWoRd', null, \LoginForm::NO_NAME, null, 'noname'
210 ],
211 'AbortLogin, NO_NAME, with message' => [
212 'User', 'PaSsWoRd', null, \LoginForm::NO_NAME, 'badname', 'badname'
213 ],
214 'AbortLogin, WRONG_PASS, no message' => [
215 'User', 'PaSsWoRd', null, \LoginForm::WRONG_PASS, null, 'wrongpassword'
216 ],
217 'AbortLogin, WRONG_PASS, with message' => [
218 'User', 'PaSsWoRd', null, \LoginForm::WRONG_PASS, 'badpass', 'badpass'
219 ],
220 'AbortLogin, WRONG_PLUGIN_PASS, no message' => [
221 'User', 'PaSsWoRd', null, \LoginForm::WRONG_PLUGIN_PASS, null, 'wrongpassword'
222 ],
223 'AbortLogin, WRONG_PLUGIN_PASS, with message' => [
224 'User', 'PaSsWoRd', null, \LoginForm::WRONG_PLUGIN_PASS, 'badpass', 'badpass'
225 ],
226 'AbortLogin, NOT_EXISTS, no message' => [
227 "User'", 'A', null, \LoginForm::NOT_EXISTS, null, 'nosuchusershort', [ 'User&#39;' ]
228 ],
229 'AbortLogin, NOT_EXISTS, with message' => [
230 "User'", 'A', null, \LoginForm::NOT_EXISTS, 'badname', 'badname', [ 'User&#39;' ]
231 ],
232 'AbortLogin, EMPTY_PASS, no message' => [
233 'User', 'PaSsWoRd', null, \LoginForm::EMPTY_PASS, null, 'wrongpasswordempty'
234 ],
235 'AbortLogin, EMPTY_PASS, with message' => [
236 'User', 'PaSsWoRd', null, \LoginForm::EMPTY_PASS, 'badpass', 'badpass'
237 ],
238 'AbortLogin, RESET_PASS, no message' => [
239 'User', 'PaSsWoRd', null, \LoginForm::RESET_PASS, null, 'resetpass_announce'
240 ],
241 'AbortLogin, RESET_PASS, with message' => [
242 'User', 'PaSsWoRd', null, \LoginForm::RESET_PASS, 'resetpass', 'resetpass'
243 ],
244 'AbortLogin, THROTTLED, no message' => [
245 'User', 'PaSsWoRd', null, \LoginForm::THROTTLED, null, 'login-throttled',
246 [ \Message::durationParam( 42 ) ]
247 ],
248 'AbortLogin, THROTTLED, with message' => [
249 'User', 'PaSsWoRd', null, \LoginForm::THROTTLED, 't', 't',
250 [ \Message::durationParam( 42 ) ]
251 ],
252 'AbortLogin, USER_BLOCKED, no message' => [
253 "User'", 'P', null, \LoginForm::USER_BLOCKED, null, 'login-userblocked', [ 'User&#39;' ]
254 ],
255 'AbortLogin, USER_BLOCKED, with message' => [
256 "User'", 'P', null, \LoginForm::USER_BLOCKED, 'blocked', 'blocked', [ 'User&#39;' ]
257 ],
258 'AbortLogin, ABORTED, no message' => [
259 "User'", 'P', null, \LoginForm::ABORTED, null, 'login-abort-generic', [ 'User&#39;' ]
260 ],
261 'AbortLogin, ABORTED, with message' => [
262 "User'", 'P', null, \LoginForm::ABORTED, 'aborted', 'aborted', [ 'User&#39;' ]
263 ],
264 'AbortLogin, USER_MIGRATED, no message' => [
265 'User', 'P', null, \LoginForm::USER_MIGRATED, null, 'login-migrated-generic'
266 ],
267 'AbortLogin, USER_MIGRATED, with message' => [
268 'User', 'P', null, \LoginForm::USER_MIGRATED, 'migrated', 'migrated'
269 ],
270 'AbortLogin, USER_MIGRATED, with message and params' => [
271 'User', 'P', null, \LoginForm::USER_MIGRATED, [ 'migrated', 'foo' ],
272 'migrated', [ 'foo' ]
273 ],
274 ];
275 }
276
277 /**
278 * @dataProvider provideTestForAccountCreation
279 * @param string $msg
280 * @param Status|null $status
281 * @param StatusValue Result
282 */
283 public function testTestForAccountCreation( $msg, $status, $result ) {
284 $this->hook( 'AbortNewAccount', $this->once() )
285 ->will( $this->returnCallback( function ( $user, &$error, &$abortStatus )
286 use ( $msg, $status )
287 {
288 $this->assertInstanceOf( 'User', $user );
289 $this->assertSame( 'User', $user->getName() );
290 $error = $msg;
291 $abortStatus = $status;
292 return $error === null && $status === null;
293 } ) );
294
295 $user = \User::newFromName( 'User' );
296 $creator = \User::newFromName( 'UTSysop' );
297 $ret = $this->getProvider()->testForAccountCreation( $user, $creator, [] );
298
299 $this->unhook( 'AbortNewAccount' );
300
301 $this->assertEquals( $result, $ret );
302 }
303
304 public static function provideTestForAccountCreation() {
305 return [
306 'No hook errors' => [
307 null, null, \StatusValue::newGood()
308 ],
309 'AbortNewAccount, old style' => [
310 'foobar', null, \StatusValue::newFatal(
311 \Message::newFromKey( 'createaccount-hook-aborted' )->rawParams( 'foobar' )
312 )
313 ],
314 'AbortNewAccount, new style' => [
315 'foobar',
316 \Status::newFatal( 'aborted!', 'param' ),
317 \StatusValue::newFatal( 'aborted!', 'param' )
318 ],
319 ];
320 }
321
322 /**
323 * @dataProvider provideTestUserForCreation
324 * @param string|null $error
325 * @param string|null $failMsg
326 */
327 public function testTestUserForCreation( $error, $failMsg ) {
328 $testUser = self::getTestUser()->getUser();
329 $provider = $this->getProvider();
330 $options = [ 'flags' => \User::READ_LOCKING, 'creating' => true ];
331
332 $this->hook( 'AbortNewAccount', $this->never() );
333 $this->hook( 'AbortAutoAccount', $this->once() )
334 ->will( $this->returnCallback( function ( $user, &$abortError ) use ( $testUser, $error ) {
335 $this->assertInstanceOf( 'User', $user );
336 $this->assertSame( $testUser->getName(), $user->getName() );
337 $abortError = $error;
338 return $error === null;
339 } ) );
340 $status = $provider->testUserForCreation(
341 $testUser, AuthManager::AUTOCREATE_SOURCE_SESSION, $options
342 );
343 $this->unhook( 'AbortNewAccount' );
344 $this->unhook( 'AbortAutoAccount' );
345 if ( $failMsg === null ) {
346 $this->assertEquals( \StatusValue::newGood(), $status, 'should succeed' );
347 } else {
348 $this->assertInstanceOf( 'StatusValue', $status, 'should fail (type)' );
349 $this->assertFalse( $status->isOk(), 'should fail (ok)' );
350 $errors = $status->getErrors();
351 $this->assertEquals( $failMsg, $errors[0]['message'], 'should fail (message)' );
352 }
353
354 $this->hook( 'AbortAutoAccount', $this->never() );
355 $this->hook( 'AbortNewAccount', $this->never() );
356 $status = $provider->testUserForCreation( $testUser, false, $options );
357 $this->unhook( 'AbortNewAccount' );
358 $this->unhook( 'AbortAutoAccount' );
359 $this->assertEquals( \StatusValue::newGood(), $status, 'should succeed' );
360 }
361
362 public static function provideTestUserForCreation() {
363 return [
364 'Success' => [ null, null ],
365 'Fail, no message' => [ false, 'login-abort-generic' ],
366 'Fail, with message' => [ 'fail', 'fail' ],
367 ];
368 }
369 }