+ /** @var PasswordReset $passwordReset */
+ $status = $passwordReset->execute( $performingUser, $username, $email );
+ $this->assertStatus( $status, $expectedError );
+ }
+
+ public function provideExecute() {
+ $defaultConfig = $this->makeConfig( true, [ 'username' => true, 'email' => true ], false );
+ $emailRequiredConfig = $this->makeConfig( true, [ 'username' => true, 'email' => true ], true );
+ $performingUser = $this->makePerformingUser( self::VALID_IP, false );
+ $throttledUser = $this->makePerformingUser( self::VALID_IP, true );
+ $permissionManager = $this->makePermissionManager( $performingUser, true );
+
+ return [
+ 'Invalid email' => [
+ 'expectedError' => 'passwordreset-invalidemail',
+ 'config' => $defaultConfig,
+ 'performingUser' => $throttledUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager(),
+ 'username' => '',
+ 'email' => '[invalid email]',
+ 'usersWithEmail' => [],
+ ],
+ 'No username, no email' => [
+ 'expectedError' => 'passwordreset-nodata',
+ 'config' => $defaultConfig,
+ 'performingUser' => $throttledUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager(),
+ 'username' => '',
+ 'email' => '',
+ 'usersWithEmail' => [],
+ ],
+ 'Email route not enabled' => [
+ 'expectedError' => 'passwordreset-nodata',
+ 'config' => $this->makeConfig( true, [ 'username' => true ], false ),
+ 'performingUser' => $throttledUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager(),
+ 'username' => '',
+ 'email' => self::VALID_EMAIL,
+ 'usersWithEmail' => [],
+ ],
+ 'Username route not enabled' => [
+ 'expectedError' => 'passwordreset-nodata',
+ 'config' => $this->makeConfig( true, [ 'email' => true ], false ),
+ 'performingUser' => $throttledUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager(),
+ 'username' => 'User1',
+ 'email' => '',
+ 'usersWithEmail' => [],
+ ],
+ 'No routes enabled' => [
+ 'expectedError' => 'passwordreset-nodata',
+ 'config' => $this->makeConfig( true, [], false ),
+ 'performingUser' => $throttledUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager(),
+ 'username' => 'User1',
+ 'email' => self::VALID_EMAIL,
+ 'usersWithEmail' => [],
+ ],
+ 'Email reqiured for resets, but is empty' => [
+ 'expectedError' => 'passwordreset-username-email-required',
+ 'config' => $emailRequiredConfig,
+ 'performingUser' => $throttledUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager(),
+ 'username' => 'User1',
+ 'email' => '',
+ 'usersWithEmail' => [],
+ ],
+ 'Email reqiured for resets, is invalid' => [
+ 'expectedError' => 'passwordreset-invalidemail',
+ 'config' => $emailRequiredConfig,
+ 'performingUser' => $throttledUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager(),
+ 'username' => 'User1',
+ 'email' => '[invalid email]',
+ 'usersWithEmail' => [],
+ ],
+ 'Throttled' => [
+ 'expectedError' => 'actionthrottledtext',
+ 'config' => $defaultConfig,
+ 'performingUser' => $throttledUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager(),
+ 'username' => 'User1',
+ 'email' => '',
+ 'usersWithEmail' => [],
+ ],
+ 'No user by this username' => [
+ 'expectedError' => 'nosuchuser',
+ 'config' => $defaultConfig,
+ 'performingUser' => $performingUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager(),
+ 'username' => 'Nonexistent user',
+ 'email' => '',
+ 'usersWithEmail' => [],
+ ],
+ 'If no users with this email found, pretend everything is OK' => [
+ 'expectedError' => false,
+ 'config' => $defaultConfig,
+ 'performingUser' => $performingUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager(),
+ 'username' => '',
+ 'email' => 'some@not.found.email',
+ 'usersWithEmail' => [],
+ ],
+ 'No email for the user' => [
+ 'expectedError' => 'noemail',
+ 'config' => $defaultConfig,
+ 'performingUser' => $performingUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager(),
+ 'username' => 'BadUser',
+ 'email' => '',
+ 'usersWithEmail' => [],
+ ],
+ 'Email reqiured for resets, no match' => [
+ 'expectedError' => false,
+ 'config' => $emailRequiredConfig,
+ 'performingUser' => $performingUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager(),
+ 'username' => 'User1',
+ 'email' => 'some@other.email',
+ 'usersWithEmail' => [],
+ ],
+ "Couldn't determine the performing user's IP" => [
+ 'expectedError' => 'badipaddress',
+ 'config' => $defaultConfig,
+ 'performingUser' => $this->makePerformingUser( null, false ),
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager(),
+ 'username' => 'User1',
+ 'email' => '',
+ 'usersWithEmail' => [],
+ ],
+ 'User is allowed, but ignored' => [
+ 'expectedError' => 'passwordreset-ignored',
+ 'config' => $defaultConfig,
+ 'performingUser' => $performingUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager( [ 'User1' ], 0, [ 'User1' ] ),
+ 'username' => 'User1',
+ 'email' => '',
+ 'usersWithEmail' => [],
+ ],
+ 'One of users is ignored' => [
+ 'expectedError' => 'passwordreset-ignored',
+ 'config' => $defaultConfig,
+ 'performingUser' => $performingUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager( [ 'User1', 'User2' ], 0, [ 'User2' ] ),
+ 'username' => '',
+ 'email' => self::VALID_EMAIL,
+ 'usersWithEmail' => [ 'User1', 'User2' ],
+ ],
+ 'User is rejected' => [
+ 'expectedError' => 'rejected by test mock',
+ 'config' => $defaultConfig,
+ 'performingUser' => $performingUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager(),
+ 'username' => 'User1',
+ 'email' => '',
+ 'usersWithEmail' => [],
+ ],
+ 'One of users is rejected' => [
+ 'expectedError' => 'rejected by test mock',
+ 'config' => $defaultConfig,
+ 'performingUser' => $performingUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager( [ 'User1' ] ),
+ 'username' => '',
+ 'email' => self::VALID_EMAIL,
+ 'usersWithEmail' => [ 'User1', 'User2' ],
+ ],
+ 'Reset one user via password' => [
+ 'expectedError' => false,
+ 'config' => $defaultConfig,
+ 'performingUser' => $performingUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager( [ 'User1' ], 1 ),
+ 'username' => 'User1',
+ 'email' => self::VALID_EMAIL,
+ // Make sure that only the user specified by username is reset
+ 'usersWithEmail' => [ 'User1', 'User2' ],
+ ],
+ 'Reset one user via email' => [
+ 'expectedError' => false,
+ 'config' => $defaultConfig,
+ 'performingUser' => $performingUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager( [ 'User1' ], 1 ),
+ 'username' => '',
+ 'email' => self::VALID_EMAIL,
+ 'usersWithEmail' => [ 'User1' ],
+ ],
+ 'Reset multiple users via email' => [
+ 'expectedError' => false,
+ 'config' => $defaultConfig,
+ 'performingUser' => $performingUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager( [ 'User1', 'User2' ], 2 ),
+ 'username' => '',
+ 'email' => self::VALID_EMAIL,
+ 'usersWithEmail' => [ 'User1', 'User2' ],
+ ],
+ "Email is required for resets, user didn't opt in" => [
+ 'expectedError' => false,
+ 'config' => $emailRequiredConfig,
+ 'performingUser' => $performingUser,
+ 'permissionManager' => $permissionManager,
+ 'authManager' => $this->makeAuthManager( [ 'User2' ], 1 ),
+ 'username' => 'User2',
+ 'email' => self::VALID_EMAIL,
+ 'usersWithEmail' => [ 'User2' ],
+ ],
+ ];
+ }
+
+ private function assertStatus( StatusValue $status, $error = false ) {
+ if ( $error === false ) {
+ $this->assertTrue( $status->isGood(), 'Expected status to be good' );
+ } else {
+ $this->assertFalse( $status->isGood(), 'Expected status to not be good' );
+ if ( is_string( $error ) ) {
+ $this->assertNotEmpty( $status->getErrors() );
+ $message = $status->getErrors()[0]['message'];
+ if ( $message instanceof MessageSpecifier ) {
+ $message = $message->getKey();
+ }
+ $this->assertSame( $error, $message );
+ }
+ }
+ }
+
+ private function makeConfig( $enableEmail, array $passwordResetRoutes, $emailForResets ) {
+ $hash = [
+ 'AllowRequiringEmailForResets' => $emailForResets,
+ 'EnableEmail' => $enableEmail,
+ 'PasswordResetRoutes' => $passwordResetRoutes,
+ ];
+
+ return new ServiceOptions( PasswordReset::$constructorOptions, $hash );
+ }
+
+ /**
+ * @param string|null $ip
+ * @param bool $pingLimited
+ * @return User
+ */
+ private function makePerformingUser( $ip, $pingLimited ) : User {
+ $request = $this->getMockBuilder( WebRequest::class )