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