[SECURITY] Password Reset Updates
[lhc/web/wiklou.git] / tests / phpunit / includes / user / PasswordResetTest.php
1 <?php
2
3 use MediaWiki\Auth\AuthManager;
4 use MediaWiki\Auth\TemporaryPasswordAuthenticationRequest;
5 use MediaWiki\Block\CompositeBlock;
6 use MediaWiki\Block\DatabaseBlock;
7 use MediaWiki\Block\SystemBlock;
8 use MediaWiki\Config\ServiceOptions;
9 use MediaWiki\Permissions\PermissionManager;
10 use Psr\Log\NullLogger;
11 use Wikimedia\Rdbms\ILoadBalancer;
12
13 /**
14 * @covers PasswordReset
15 * @group Database
16 */
17 class PasswordResetTest extends MediaWikiTestCase {
18 const VALID_IP = '1.2.3.4';
19 const VALID_EMAIL = 'foo@bar.baz';
20
21 /**
22 * @dataProvider provideIsAllowed
23 */
24 public function testIsAllowed( $passwordResetRoutes, $enableEmail,
25 $allowsAuthenticationDataChange, $canEditPrivate, $block, $globalBlock, $isAllowed
26 ) {
27 $config = $this->makeConfig( $enableEmail, $passwordResetRoutes, false );
28
29 $authManager = $this->getMockBuilder( AuthManager::class )->disableOriginalConstructor()
30 ->getMock();
31 $authManager->expects( $this->any() )->method( 'allowsAuthenticationDataChange' )
32 ->willReturn( $allowsAuthenticationDataChange ? Status::newGood() : Status::newFatal( 'foo' ) );
33
34 $user = $this->getMockBuilder( User::class )->getMock();
35 $user->expects( $this->any() )->method( 'getName' )->willReturn( 'Foo' );
36 $user->expects( $this->any() )->method( 'getBlock' )->willReturn( $block );
37 $user->expects( $this->any() )->method( 'getGlobalBlock' )->willReturn( $globalBlock );
38
39 $permissionManager = $this->getMockBuilder( PermissionManager::class )
40 ->disableOriginalConstructor()
41 ->getMock();
42 $permissionManager->method( 'userHasRight' )
43 ->with( $user, 'editmyprivateinfo' )
44 ->willReturn( $canEditPrivate );
45
46 $loadBalancer = $this->createMock( ILoadBalancer::class );
47
48 $passwordReset = new PasswordReset(
49 $config,
50 $authManager,
51 $permissionManager,
52 $loadBalancer,
53 new NullLogger()
54 );
55
56 $this->assertSame( $isAllowed, $passwordReset->isAllowed( $user )->isGood() );
57 }
58
59 public function provideIsAllowed() {
60 return [
61 'no routes' => [
62 'passwordResetRoutes' => [],
63 'enableEmail' => true,
64 'allowsAuthenticationDataChange' => true,
65 'canEditPrivate' => true,
66 'block' => null,
67 'globalBlock' => null,
68 'isAllowed' => false,
69 ],
70 'email disabled' => [
71 'passwordResetRoutes' => [ 'username' => true ],
72 'enableEmail' => false,
73 'allowsAuthenticationDataChange' => true,
74 'canEditPrivate' => true,
75 'block' => null,
76 'globalBlock' => null,
77 'isAllowed' => false,
78 ],
79 'auth data change disabled' => [
80 'passwordResetRoutes' => [ 'username' => true ],
81 'enableEmail' => true,
82 'allowsAuthenticationDataChange' => false,
83 'canEditPrivate' => true,
84 'block' => null,
85 'globalBlock' => null,
86 'isAllowed' => false,
87 ],
88 'cannot edit private data' => [
89 'passwordResetRoutes' => [ 'username' => true ],
90 'enableEmail' => true,
91 'allowsAuthenticationDataChange' => true,
92 'canEditPrivate' => false,
93 'block' => null,
94 'globalBlock' => null,
95 'isAllowed' => false,
96 ],
97 'blocked with account creation disabled' => [
98 'passwordResetRoutes' => [ 'username' => true ],
99 'enableEmail' => true,
100 'allowsAuthenticationDataChange' => true,
101 'canEditPrivate' => true,
102 'block' => new DatabaseBlock( [ 'createAccount' => true ] ),
103 'globalBlock' => null,
104 'isAllowed' => false,
105 ],
106 'blocked w/o account creation disabled' => [
107 'passwordResetRoutes' => [ 'username' => true ],
108 'enableEmail' => true,
109 'allowsAuthenticationDataChange' => true,
110 'canEditPrivate' => true,
111 'block' => new DatabaseBlock( [] ),
112 'globalBlock' => null,
113 'isAllowed' => true,
114 ],
115 'using blocked proxy' => [
116 'passwordResetRoutes' => [ 'username' => true ],
117 'enableEmail' => true,
118 'allowsAuthenticationDataChange' => true,
119 'canEditPrivate' => true,
120 'block' => new SystemBlock(
121 [ 'systemBlock' => 'proxy' ]
122 ),
123 'globalBlock' => null,
124 'isAllowed' => false,
125 ],
126 'globally blocked with account creation not disabled' => [
127 'passwordResetRoutes' => [ 'username' => true ],
128 'enableEmail' => true,
129 'allowsAuthenticationDataChange' => true,
130 'canEditPrivate' => true,
131 'block' => null,
132 'globalBlock' => new SystemBlock(
133 [ 'systemBlock' => 'global-block' ]
134 ),
135 'isAllowed' => true,
136 ],
137 'blocked via wgSoftBlockRanges' => [
138 'passwordResetRoutes' => [ 'username' => true ],
139 'enableEmail' => true,
140 'allowsAuthenticationDataChange' => true,
141 'canEditPrivate' => true,
142 'block' => new SystemBlock(
143 [ 'systemBlock' => 'wgSoftBlockRanges', 'anonOnly' => true ]
144 ),
145 'globalBlock' => null,
146 'isAllowed' => true,
147 ],
148 'blocked with an unknown system block type' => [
149 'passwordResetRoutes' => [ 'username' => true ],
150 'enableEmail' => true,
151 'allowsAuthenticationDataChange' => true,
152 'canEditPrivate' => true,
153 'block' => new SystemBlock( [ 'systemBlock' => 'unknown' ] ),
154 'globalBlock' => null,
155 'isAllowed' => false,
156 ],
157 'blocked with multiple blocks, all allowing password reset' => [
158 'passwordResetRoutes' => [ 'username' => true ],
159 'enableEmail' => true,
160 'allowsAuthenticationDataChange' => true,
161 'canEditPrivate' => true,
162 'block' => new CompositeBlock( [
163 'originalBlocks' => [
164 new SystemBlock( [ 'systemBlock' => 'wgSoftBlockRanges', 'anonOnly' => true ] ),
165 new Block( [] ),
166 ]
167 ] ),
168 'globalBlock' => null,
169 'isAllowed' => true,
170 ],
171 'blocked with multiple blocks, not all allowing password reset' => [
172 'passwordResetRoutes' => [ 'username' => true ],
173 'enableEmail' => true,
174 'allowsAuthenticationDataChange' => true,
175 'canEditPrivate' => true,
176 'block' => new CompositeBlock( [
177 'originalBlocks' => [
178 new SystemBlock( [ 'systemBlock' => 'wgSoftBlockRanges', 'anonOnly' => true ] ),
179 new SystemBlock( [ 'systemBlock' => 'proxy' ] ),
180 ]
181 ] ),
182 'globalBlock' => null,
183 'isAllowed' => false,
184 ],
185 'all OK' => [
186 'passwordResetRoutes' => [ 'username' => true ],
187 'enableEmail' => true,
188 'allowsAuthenticationDataChange' => true,
189 'canEditPrivate' => true,
190 'block' => null,
191 'globalBlock' => null,
192 'isAllowed' => true,
193 ],
194 ];
195 }
196
197 public function testExecute_notAllowed() {
198 $user = $this->createMock( User::class );
199 /** @var User $user */
200
201 $passwordReset = $this->getMockBuilder( PasswordReset::class )
202 ->disableOriginalConstructor()
203 ->setMethods( [ 'isAllowed' ] )
204 ->getMock();
205 $passwordReset->expects( $this->any() )
206 ->method( 'isAllowed' )
207 ->with( $user )
208 ->willReturn( Status::newFatal( 'somestatuscode' ) );
209 /** @var PasswordReset $passwordReset */
210
211 $this->expectException( \LogicException::class );
212 $passwordReset->execute( $user );
213 }
214
215 /**
216 * @dataProvider provideExecute
217 * @param string|bool $expectedError
218 * @param ServiceOptions $config
219 * @param User $performingUser
220 * @param PermissionManager $permissionManager
221 * @param AuthManager $authManager
222 * @param string|null $username
223 * @param string|null $email
224 * @param User[] $usersWithEmail
225 * @covers SendPasswordResetEmailUpdate
226 */
227 public function testExecute(
228 $expectedError,
229 ServiceOptions $config,
230 User $performingUser,
231 PermissionManager $permissionManager,
232 AuthManager $authManager,
233 $username = '',
234 $email = '',
235 array $usersWithEmail = []
236 ) {
237 // Unregister the hooks for proper unit testing
238 $this->mergeMwGlobalArrayValue( 'wgHooks', [
239 'User::mailPasswordInternal' => [],
240 'SpecialPasswordResetOnSubmit' => [],
241 ] );
242
243 $loadBalancer = $this->createMock( ILoadBalancer::class );
244
245 $users = $this->makeUsers();
246
247 $lookupUser = function ( $username ) use ( $users ) {
248 return $users[ $username ] ?? false;
249 };
250
251 $passwordReset = $this->getMockBuilder( PasswordReset::class )
252 ->setMethods( [ 'getUsersByEmail', 'isAllowed', 'lookupUser' ] )
253 ->setConstructorArgs( [
254 $config,
255 $authManager,
256 $permissionManager,
257 $loadBalancer,
258 new NullLogger()
259 ] )
260 ->getMock();
261 $passwordReset->method( 'getUsersByEmail' )->with( $email )
262 ->willReturn( array_map( $lookupUser, $usersWithEmail ) );
263 $passwordReset->method( 'isAllowed' )
264 ->willReturn( Status::newGood() );
265 $passwordReset->method( 'lookupUser' )
266 ->willReturnCallback( $lookupUser );
267
268 /** @var PasswordReset $passwordReset */
269 $status = $passwordReset->execute( $performingUser, $username, $email );
270 $this->assertStatus( $status, $expectedError );
271 }
272
273 public function provideExecute() {
274 $defaultConfig = $this->makeConfig( true, [ 'username' => true, 'email' => true ], false );
275 $emailRequiredConfig = $this->makeConfig( true, [ 'username' => true, 'email' => true ], true );
276 $performingUser = $this->makePerformingUser( self::VALID_IP, false );
277 $throttledUser = $this->makePerformingUser( self::VALID_IP, true );
278 $permissionManager = $this->makePermissionManager( $performingUser, true );
279
280 return [
281 'Throttled, pretend everything is ok' => [
282 'expectedError' => false,
283 'config' => $defaultConfig,
284 'performingUser' => $throttledUser,
285 'permissionManager' => $permissionManager,
286 'authManager' => $this->makeAuthManager(),
287 'username' => 'User1',
288 'email' => '',
289 'usersWithEmail' => [],
290 ],
291 'Throttled, email required for resets, is invalid, pretend everything is ok' => [
292 'expectedError' => false,
293 'config' => $emailRequiredConfig,
294 'performingUser' => $throttledUser,
295 'permissionManager' => $permissionManager,
296 'authManager' => $this->makeAuthManager(),
297 'username' => 'User1',
298 'email' => '[invalid email]',
299 'usersWithEmail' => [],
300 ],
301 'Invalid email, pretend everything is OK' => [
302 'expectedError' => false,
303 'config' => $defaultConfig,
304 'performingUser' => $performingUser,
305 'permissionManager' => $permissionManager,
306 'authManager' => $this->makeAuthManager(),
307 'username' => '',
308 'email' => '[invalid email]',
309 'usersWithEmail' => [],
310 ],
311 'No username, no email' => [
312 'expectedError' => 'passwordreset-nodata',
313 'config' => $defaultConfig,
314 'performingUser' => $performingUser,
315 'permissionManager' => $permissionManager,
316 'authManager' => $this->makeAuthManager(),
317 'username' => '',
318 'email' => '',
319 'usersWithEmail' => [],
320 ],
321 'Email route not enabled' => [
322 'expectedError' => 'passwordreset-nodata',
323 'config' => $this->makeConfig( true, [ 'username' => true ], false ),
324 'performingUser' => $performingUser,
325 'permissionManager' => $permissionManager,
326 'authManager' => $this->makeAuthManager(),
327 'username' => '',
328 'email' => self::VALID_EMAIL,
329 'usersWithEmail' => [],
330 ],
331 'Username route not enabled' => [
332 'expectedError' => 'passwordreset-nodata',
333 'config' => $this->makeConfig( true, [ 'email' => true ], false ),
334 'performingUser' => $performingUser,
335 'permissionManager' => $permissionManager,
336 'authManager' => $this->makeAuthManager(),
337 'username' => 'User1',
338 'email' => '',
339 'usersWithEmail' => [],
340 ],
341 'No routes enabled' => [
342 'expectedError' => 'passwordreset-nodata',
343 'config' => $this->makeConfig( true, [], false ),
344 'performingUser' => $performingUser,
345 'permissionManager' => $permissionManager,
346 'authManager' => $this->makeAuthManager(),
347 'username' => 'User1',
348 'email' => self::VALID_EMAIL,
349 'usersWithEmail' => [],
350 ],
351 'Email required for resets but is empty, pretend everything is OK' => [
352 'expectedError' => false,
353 'config' => $emailRequiredConfig,
354 'performingUser' => $performingUser,
355 'permissionManager' => $permissionManager,
356 'authManager' => $this->makeAuthManager(),
357 'username' => 'User1',
358 'email' => '',
359 'usersWithEmail' => [],
360 ],
361 'Email required for resets but is invalid, pretend everything is OK' => [
362 'expectedError' => false,
363 'config' => $emailRequiredConfig,
364 'performingUser' => $performingUser,
365 'permissionManager' => $permissionManager,
366 'authManager' => $this->makeAuthManager(),
367 'username' => 'User1',
368 'email' => '[invalid email]',
369 'usersWithEmail' => [],
370 ],
371 'Password email already sent within 24 hours, pretend everything is ok' => [
372 'expectedError' => false,
373 'config' => $defaultConfig,
374 'performingUser' => $performingUser,
375 'permissionManager' => $permissionManager,
376 'authManager' => $this->makeAuthManager( [ 'User1' ], 0, [], [ 'User1' ] ),
377 'username' => 'User1',
378 'email' => '',
379 'usersWithEmail' => [ 'User1' ],
380 ],
381 'No user by this username, pretend everything is OK' => [
382 'expectedError' => false,
383 'config' => $defaultConfig,
384 'performingUser' => $performingUser,
385 'permissionManager' => $permissionManager,
386 'authManager' => $this->makeAuthManager(),
387 'username' => 'Nonexistent user',
388 'email' => '',
389 'usersWithEmail' => [],
390 ],
391 'Username is not valid' => [
392 'expectedError' => 'noname',
393 'config' => $defaultConfig,
394 'performingUser' => $performingUser,
395 'permissionManager' => $permissionManager,
396 'authManager' => $this->makeAuthManager(),
397 'username' => 'Invalid|username',
398 'email' => '',
399 'usersWithEmail' => [],
400 ],
401 'If no users with this email found, pretend everything is OK' => [
402 'expectedError' => false,
403 'config' => $defaultConfig,
404 'performingUser' => $performingUser,
405 'permissionManager' => $permissionManager,
406 'authManager' => $this->makeAuthManager(),
407 'username' => '',
408 'email' => 'some@not.found.email',
409 'usersWithEmail' => [],
410 ],
411 'No email for the user, pretend everything is OK' => [
412 'expectedError' => false,
413 'config' => $defaultConfig,
414 'performingUser' => $performingUser,
415 'permissionManager' => $permissionManager,
416 'authManager' => $this->makeAuthManager(),
417 'username' => 'BadUser',
418 'email' => '',
419 'usersWithEmail' => [],
420 ],
421 'Email required for resets, no match' => [
422 'expectedError' => false,
423 'config' => $emailRequiredConfig,
424 'performingUser' => $performingUser,
425 'permissionManager' => $permissionManager,
426 'authManager' => $this->makeAuthManager(),
427 'username' => 'User1',
428 'email' => 'some@other.email',
429 'usersWithEmail' => [],
430 ],
431 "Couldn't determine the performing user's IP" => [
432 'expectedError' => 'badipaddress',
433 'config' => $defaultConfig,
434 'performingUser' => $this->makePerformingUser( null, false ),
435 'permissionManager' => $permissionManager,
436 'authManager' => $this->makeAuthManager(),
437 'username' => 'User1',
438 'email' => '',
439 'usersWithEmail' => [],
440 ],
441 'User is allowed, but ignored' => [
442 'expectedError' => 'passwordreset-ignored',
443 'config' => $defaultConfig,
444 'performingUser' => $performingUser,
445 'permissionManager' => $permissionManager,
446 'authManager' => $this->makeAuthManager( [ 'User1' ], 0, [ 'User1' ] ),
447 'username' => 'User1',
448 'email' => '',
449 'usersWithEmail' => [],
450 ],
451 'One of users is ignored' => [
452 'expectedError' => 'passwordreset-ignored',
453 'config' => $defaultConfig,
454 'performingUser' => $performingUser,
455 'permissionManager' => $permissionManager,
456 'authManager' => $this->makeAuthManager( [ 'User1', 'User2' ], 0, [ 'User2' ] ),
457 'username' => '',
458 'email' => self::VALID_EMAIL,
459 'usersWithEmail' => [ 'User1', 'User2' ],
460 ],
461 'User is rejected' => [
462 'expectedError' => 'rejected by test mock',
463 'config' => $defaultConfig,
464 'performingUser' => $performingUser,
465 'permissionManager' => $permissionManager,
466 'authManager' => $this->makeAuthManager(),
467 'username' => 'User1',
468 'email' => '',
469 'usersWithEmail' => [],
470 ],
471 'One of users is rejected' => [
472 'expectedError' => 'rejected by test mock',
473 'config' => $defaultConfig,
474 'performingUser' => $performingUser,
475 'permissionManager' => $permissionManager,
476 'authManager' => $this->makeAuthManager( [ 'User1' ] ),
477 'username' => '',
478 'email' => self::VALID_EMAIL,
479 'usersWithEmail' => [ 'User1', 'User2' ],
480 ],
481 'Reset one user via password' => [
482 'expectedError' => false,
483 'config' => $defaultConfig,
484 'performingUser' => $performingUser,
485 'permissionManager' => $permissionManager,
486 'authManager' => $this->makeAuthManager( [ 'User1' ], 1 ),
487 'username' => 'User1',
488 'email' => self::VALID_EMAIL,
489 // Make sure that only the user specified by username is reset
490 'usersWithEmail' => [ 'User1', 'User2' ],
491 ],
492 'Reset one user via email' => [
493 'expectedError' => false,
494 'config' => $defaultConfig,
495 'performingUser' => $performingUser,
496 'permissionManager' => $permissionManager,
497 'authManager' => $this->makeAuthManager( [ 'User1' ], 1 ),
498 'username' => '',
499 'email' => self::VALID_EMAIL,
500 'usersWithEmail' => [ 'User1' ],
501 ],
502 'Reset multiple users via email' => [
503 'expectedError' => false,
504 'config' => $defaultConfig,
505 'performingUser' => $performingUser,
506 'permissionManager' => $permissionManager,
507 'authManager' => $this->makeAuthManager( [ 'User1', 'User2' ], 2 ),
508 'username' => '',
509 'email' => self::VALID_EMAIL,
510 'usersWithEmail' => [ 'User1', 'User2' ],
511 ],
512 "Email is required for resets, user didn't opt in" => [
513 'expectedError' => false,
514 'config' => $emailRequiredConfig,
515 'performingUser' => $performingUser,
516 'permissionManager' => $permissionManager,
517 'authManager' => $this->makeAuthManager( [ 'User2' ], 1 ),
518 'username' => 'User2',
519 'email' => self::VALID_EMAIL,
520 'usersWithEmail' => [ 'User2' ],
521 ],
522 'Reset three users via email that did not opt in, multiple users with same email' => [
523 'expectedError' => false,
524 'config' => $emailRequiredConfig,
525 'performingUser' => $performingUser,
526 'permissionManager' => $permissionManager,
527 'authManager' => $this->makeAuthManager( [ 'User2', 'User3', 'User4' ], 3, [ 'User1' ] ),
528 'username' => '',
529 'email' => self::VALID_EMAIL,
530 'usersWithEmail' => [ 'User1', 'User2', 'User3', 'User4' ],
531 ],
532 ];
533 }
534
535 private function assertStatus( StatusValue $status, $error = false ) {
536 if ( $error === false ) {
537 $this->assertTrue( $status->isGood(), 'Expected status to be good' );
538 } else {
539 $this->assertFalse( $status->isGood(), 'Expected status to not be good' );
540 if ( is_string( $error ) ) {
541 $this->assertNotEmpty( $status->getErrors() );
542 $message = $status->getErrors()[0]['message'];
543 if ( $message instanceof MessageSpecifier ) {
544 $message = $message->getKey();
545 }
546 $this->assertSame( $error, $message );
547 }
548 }
549 }
550
551 private function makeConfig( $enableEmail, array $passwordResetRoutes, $emailForResets ) {
552 $hash = [
553 'AllowRequiringEmailForResets' => $emailForResets,
554 'EnableEmail' => $enableEmail,
555 'PasswordResetRoutes' => $passwordResetRoutes,
556 ];
557
558 return new ServiceOptions( PasswordReset::CONSTRUCTOR_OPTIONS, $hash );
559 }
560
561 /**
562 * @param string|null $ip
563 * @param bool $pingLimited
564 * @return User
565 */
566 private function makePerformingUser( $ip, $pingLimited ) : User {
567 $request = $this->getMockBuilder( WebRequest::class )
568 ->getMock();
569 $request->method( 'getIP' )
570 ->willReturn( $ip );
571 /** @var WebRequest $request */
572
573 $user = $this->getMockBuilder( User::class )
574 ->setMethods( [ 'getName', 'pingLimiter', 'getRequest' ] )
575 ->getMock();
576
577 $user->method( 'getName' )
578 ->willReturn( 'SomeUser' );
579 $user->method( 'pingLimiter' )
580 ->with( 'mailpassword' )
581 ->willReturn( $pingLimited );
582 $user->method( 'getRequest' )
583 ->willReturn( $request );
584
585 /** @var User $user */
586 return $user;
587 }
588
589 private function makePermissionManager( User $performingUser, $isAllowed ) : PermissionManager {
590 $permissionManager = $this->getMockBuilder( PermissionManager::class )
591 ->disableOriginalConstructor()
592 ->getMock();
593 $permissionManager->method( 'userHasRight' )
594 ->with( $performingUser, 'editmyprivateinfo' )
595 ->willReturn( $isAllowed );
596
597 /** @var PermissionManager $permissionManager */
598 return $permissionManager;
599 }
600
601 /**
602 * @param string[] $allowed Usernames that are allowed to send password reset email
603 * by AuthManager's allowsAuthenticationDataChange method.
604 * @param int $numUsersToAuth Number of users that will receive email
605 * @param string[] $ignored Usernames that are allowed but ignored by AuthManager's
606 * allowsAuthenticationDataChange method and will not receive password reset email.
607 * @param string[] $mailThrottledLimited Usernames that have already
608 * received the password reset email within a given time, and AuthManager
609 * changeAuthenticationData method will mark them as 'throttled-mailpassword.'
610 * @return AuthManager
611 */
612 private function makeAuthManager(
613 array $allowed = [],
614 $numUsersToAuth = 0,
615 array $ignored = [],
616 array $mailThrottledLimited = []
617 ) : AuthManager {
618 $authManager = $this->getMockBuilder( AuthManager::class )
619 ->disableOriginalConstructor()
620 ->getMock();
621 $authManager->method( 'allowsAuthenticationDataChange' )
622 ->willReturnCallback(
623 function ( TemporaryPasswordAuthenticationRequest $req )
624 use ( $allowed, $ignored, $mailThrottledLimited ) {
625 if ( in_array( $req->username, $mailThrottledLimited, true ) ) {
626 return Status::newGood( 'throttled-mailpassword' );
627 }
628
629 $value = in_array( $req->username, $ignored, true )
630 ? 'ignored'
631 : 'okie dokie';
632
633 return in_array( $req->username, $allowed, true )
634 ? Status::newGood( $value )
635 : Status::newFatal( 'rejected by test mock' );
636 } );
637 // changeAuthenticationData is executed in the deferred update class
638 // SendPasswordResetEmailUpdate
639 $authManager->expects( $this->exactly( $numUsersToAuth ) )
640 ->method( 'changeAuthenticationData' );
641
642 /** @var AuthManager $authManager */
643 return $authManager;
644 }
645
646 /**
647 * @return User[]
648 */
649 private function makeUsers() {
650 $user1 = $this->getMockBuilder( User::class )->getMock();
651 $user2 = $this->getMockBuilder( User::class )->getMock();
652 $user3 = $this->getMockBuilder( User::class )->getMock();
653 $user4 = $this->getMockBuilder( User::class )->getMock();
654 $user1->method( 'getName' )->willReturn( 'User1' );
655 $user2->method( 'getName' )->willReturn( 'User2' );
656 $user3->method( 'getName' )->willReturn( 'User3' );
657 $user4->method( 'getName' )->willReturn( 'User4' );
658 $user1->method( 'getId' )->willReturn( 1 );
659 $user2->method( 'getId' )->willReturn( 2 );
660 $user3->method( 'getId' )->willReturn( 3 );
661 $user4->method( 'getId' )->willReturn( 4 );
662 $user1->method( 'getEmail' )->willReturn( self::VALID_EMAIL );
663 $user2->method( 'getEmail' )->willReturn( self::VALID_EMAIL );
664 $user3->method( 'getEmail' )->willReturn( self::VALID_EMAIL );
665 $user4->method( 'getEmail' )->willReturn( self::VALID_EMAIL );
666
667 $user1->method( 'getBoolOption' )
668 ->with( 'requireemail' )
669 ->willReturn( true );
670
671 $badUser = $this->getMockBuilder( User::class )->getMock();
672 $badUser->method( 'getName' )->willReturn( 'BadUser' );
673 $badUser->method( 'getId' )->willReturn( 5 );
674 $badUser->method( 'getEmail' )->willReturn( null );
675
676 return [
677 'User1' => $user1,
678 'User2' => $user2,
679 'User3' => $user3,
680 'User4' => $user4,
681 'BadUser' => $badUser,
682 ];
683 }
684 }