It is not entirely meaningless. It might be an indicator that the number of calls to a method is intentionally unlimited. This is similar to e.g. an @inheritDoc PHPDoc comment that marks a method as being "intentionally undocumented". However, what's the meaning of being "intentionally unconstrained"? Let's just not have any constraint then. I feel all these ->expects( $this->any() ) bloat the test code so much that it's never worth it. Change-Id: I9925e7706bd03e1666f6eb0b284cb42b0dd3be23
715 lines
24 KiB
PHP
715 lines
24 KiB
PHP
<?php
|
|
|
|
use MediaWiki\Auth\AuthManager;
|
|
use MediaWiki\Auth\TemporaryPasswordAuthenticationRequest;
|
|
use MediaWiki\Block\CompositeBlock;
|
|
use MediaWiki\Block\DatabaseBlock;
|
|
use MediaWiki\Block\SystemBlock;
|
|
use MediaWiki\Config\ServiceOptions;
|
|
use MediaWiki\MediaWikiServices;
|
|
use MediaWiki\Permissions\PermissionManager;
|
|
use MediaWiki\User\UserFactory;
|
|
use MediaWiki\User\UserOptionsLookup;
|
|
use Psr\Log\NullLogger;
|
|
use Wikimedia\Rdbms\ILoadBalancer;
|
|
|
|
/**
|
|
* @covers PasswordReset
|
|
* @group Database
|
|
*/
|
|
class PasswordResetTest extends MediaWikiIntegrationTestCase {
|
|
private const VALID_IP = '1.2.3.4';
|
|
private const VALID_EMAIL = 'foo@bar.baz';
|
|
|
|
/**
|
|
* @dataProvider provideIsAllowed
|
|
*/
|
|
public function testIsAllowed( $passwordResetRoutes, $enableEmail,
|
|
$allowsAuthenticationDataChange, $canEditPrivate, $block, $globalBlock, $isAllowed
|
|
) {
|
|
$config = $this->makeConfig( $enableEmail, $passwordResetRoutes, false );
|
|
|
|
$authManager = $this->getMockBuilder( AuthManager::class )->disableOriginalConstructor()
|
|
->getMock();
|
|
$authManager->method( 'allowsAuthenticationDataChange' )
|
|
->willReturn( $allowsAuthenticationDataChange ? Status::newGood() : Status::newFatal( 'foo' ) );
|
|
|
|
$user = $this->getMockBuilder( User::class )->getMock();
|
|
$user->method( 'getName' )->willReturn( 'Foo' );
|
|
$user->method( 'getBlock' )->willReturn( $block );
|
|
$user->method( 'getGlobalBlock' )->willReturn( $globalBlock );
|
|
|
|
$permissionManager = $this->getMockBuilder( PermissionManager::class )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
$permissionManager->method( 'userHasRight' )
|
|
->with( $user, 'editmyprivateinfo' )
|
|
->willReturn( $canEditPrivate );
|
|
|
|
$loadBalancer = $this->createMock( ILoadBalancer::class );
|
|
|
|
$hookContainer = $this->createHookContainer();
|
|
|
|
$mwServices = MediaWikiServices::getInstance();
|
|
$passwordReset = new PasswordReset(
|
|
$config,
|
|
new NullLogger(),
|
|
$authManager,
|
|
$hookContainer,
|
|
$loadBalancer,
|
|
$permissionManager,
|
|
$mwServices->getUserFactory(),
|
|
$mwServices->getUserNameUtils(),
|
|
$mwServices->getUserOptionsLookup()
|
|
);
|
|
|
|
$this->assertSame( $isAllowed, $passwordReset->isAllowed( $user )->isGood() );
|
|
}
|
|
|
|
public function provideIsAllowed() {
|
|
return [
|
|
'no routes' => [
|
|
'passwordResetRoutes' => [],
|
|
'enableEmail' => true,
|
|
'allowsAuthenticationDataChange' => true,
|
|
'canEditPrivate' => true,
|
|
'block' => null,
|
|
'globalBlock' => null,
|
|
'isAllowed' => false,
|
|
],
|
|
'email disabled' => [
|
|
'passwordResetRoutes' => [ 'username' => true ],
|
|
'enableEmail' => false,
|
|
'allowsAuthenticationDataChange' => true,
|
|
'canEditPrivate' => true,
|
|
'block' => null,
|
|
'globalBlock' => null,
|
|
'isAllowed' => false,
|
|
],
|
|
'auth data change disabled' => [
|
|
'passwordResetRoutes' => [ 'username' => true ],
|
|
'enableEmail' => true,
|
|
'allowsAuthenticationDataChange' => false,
|
|
'canEditPrivate' => true,
|
|
'block' => null,
|
|
'globalBlock' => null,
|
|
'isAllowed' => false,
|
|
],
|
|
'cannot edit private data' => [
|
|
'passwordResetRoutes' => [ 'username' => true ],
|
|
'enableEmail' => true,
|
|
'allowsAuthenticationDataChange' => true,
|
|
'canEditPrivate' => false,
|
|
'block' => null,
|
|
'globalBlock' => null,
|
|
'isAllowed' => false,
|
|
],
|
|
'blocked with account creation disabled' => [
|
|
'passwordResetRoutes' => [ 'username' => true ],
|
|
'enableEmail' => true,
|
|
'allowsAuthenticationDataChange' => true,
|
|
'canEditPrivate' => true,
|
|
'block' => new DatabaseBlock( [ 'createAccount' => true ] ),
|
|
'globalBlock' => null,
|
|
'isAllowed' => false,
|
|
],
|
|
'blocked w/o account creation disabled' => [
|
|
'passwordResetRoutes' => [ 'username' => true ],
|
|
'enableEmail' => true,
|
|
'allowsAuthenticationDataChange' => true,
|
|
'canEditPrivate' => true,
|
|
'block' => new DatabaseBlock( [] ),
|
|
'globalBlock' => null,
|
|
'isAllowed' => true,
|
|
],
|
|
'using blocked proxy' => [
|
|
'passwordResetRoutes' => [ 'username' => true ],
|
|
'enableEmail' => true,
|
|
'allowsAuthenticationDataChange' => true,
|
|
'canEditPrivate' => true,
|
|
'block' => new SystemBlock(
|
|
[ 'systemBlock' => 'proxy' ]
|
|
),
|
|
'globalBlock' => null,
|
|
'isAllowed' => false,
|
|
],
|
|
'globally blocked with account creation not disabled' => [
|
|
'passwordResetRoutes' => [ 'username' => true ],
|
|
'enableEmail' => true,
|
|
'allowsAuthenticationDataChange' => true,
|
|
'canEditPrivate' => true,
|
|
'block' => null,
|
|
'globalBlock' => new SystemBlock(
|
|
[ 'systemBlock' => 'global-block' ]
|
|
),
|
|
'isAllowed' => true,
|
|
],
|
|
'blocked via wgSoftBlockRanges' => [
|
|
'passwordResetRoutes' => [ 'username' => true ],
|
|
'enableEmail' => true,
|
|
'allowsAuthenticationDataChange' => true,
|
|
'canEditPrivate' => true,
|
|
'block' => new SystemBlock(
|
|
[ 'systemBlock' => 'wgSoftBlockRanges', 'anonOnly' => true ]
|
|
),
|
|
'globalBlock' => null,
|
|
'isAllowed' => true,
|
|
],
|
|
'blocked with an unknown system block type' => [
|
|
'passwordResetRoutes' => [ 'username' => true ],
|
|
'enableEmail' => true,
|
|
'allowsAuthenticationDataChange' => true,
|
|
'canEditPrivate' => true,
|
|
'block' => new SystemBlock( [ 'systemBlock' => 'unknown' ] ),
|
|
'globalBlock' => null,
|
|
'isAllowed' => false,
|
|
],
|
|
'blocked with multiple blocks, all allowing password reset' => [
|
|
'passwordResetRoutes' => [ 'username' => true ],
|
|
'enableEmail' => true,
|
|
'allowsAuthenticationDataChange' => true,
|
|
'canEditPrivate' => true,
|
|
'block' => new CompositeBlock( [
|
|
'originalBlocks' => [
|
|
new SystemBlock( [ 'systemBlock' => 'wgSoftBlockRanges', 'anonOnly' => true ] ),
|
|
new Block( [] ),
|
|
]
|
|
] ),
|
|
'globalBlock' => null,
|
|
'isAllowed' => true,
|
|
],
|
|
'blocked with multiple blocks, not all allowing password reset' => [
|
|
'passwordResetRoutes' => [ 'username' => true ],
|
|
'enableEmail' => true,
|
|
'allowsAuthenticationDataChange' => true,
|
|
'canEditPrivate' => true,
|
|
'block' => new CompositeBlock( [
|
|
'originalBlocks' => [
|
|
new SystemBlock( [ 'systemBlock' => 'wgSoftBlockRanges', 'anonOnly' => true ] ),
|
|
new SystemBlock( [ 'systemBlock' => 'proxy' ] ),
|
|
]
|
|
] ),
|
|
'globalBlock' => null,
|
|
'isAllowed' => false,
|
|
],
|
|
'all OK' => [
|
|
'passwordResetRoutes' => [ 'username' => true ],
|
|
'enableEmail' => true,
|
|
'allowsAuthenticationDataChange' => true,
|
|
'canEditPrivate' => true,
|
|
'block' => null,
|
|
'globalBlock' => null,
|
|
'isAllowed' => true,
|
|
],
|
|
];
|
|
}
|
|
|
|
public function testExecute_notAllowed() {
|
|
$user = $this->createMock( User::class );
|
|
/** @var User $user */
|
|
|
|
$passwordReset = $this->getMockBuilder( PasswordReset::class )
|
|
->disableOriginalConstructor()
|
|
->onlyMethods( [ 'isAllowed' ] )
|
|
->getMock();
|
|
$passwordReset->method( 'isAllowed' )
|
|
->with( $user )
|
|
->willReturn( Status::newFatal( 'somestatuscode' ) );
|
|
/** @var PasswordReset $passwordReset */
|
|
|
|
$this->expectException( \LogicException::class );
|
|
$passwordReset->execute( $user );
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideExecute
|
|
* @param string|bool $expectedError
|
|
* @param ServiceOptions $config
|
|
* @param User $performingUser
|
|
* @param PermissionManager $permissionManager
|
|
* @param AuthManager $authManager
|
|
* @param string|null $username
|
|
* @param string|null $email
|
|
* @param User[] $usersWithEmail
|
|
* @covers SendPasswordResetEmailUpdate
|
|
*/
|
|
public function testExecute(
|
|
$expectedError,
|
|
ServiceOptions $config,
|
|
User $performingUser,
|
|
PermissionManager $permissionManager,
|
|
AuthManager $authManager,
|
|
$username = '',
|
|
$email = '',
|
|
array $usersWithEmail = []
|
|
) {
|
|
// Unregister the hooks for proper unit testing
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [
|
|
'User::mailPasswordInternal' => [],
|
|
'SpecialPasswordResetOnSubmit' => [],
|
|
] );
|
|
|
|
$loadBalancer = $this->createMock( ILoadBalancer::class );
|
|
|
|
$users = $this->makeUsers();
|
|
|
|
// Only User1 has `requireemail` true, everything else false
|
|
$userRequiresEmail = function ( $user, $option ) {
|
|
$this->assertSame( 'requireemail', $option );
|
|
return ( $user->getName() === 'User1' );
|
|
};
|
|
$userOptionsLookup = $this->getMockBuilder( UserOptionsLookup::class )
|
|
->onlyMethods( [ 'getBoolOption' ] )
|
|
->getMockForAbstractClass();
|
|
$userOptionsLookup->method( 'getBoolOption' )
|
|
->willReturnCallback( $userRequiresEmail );
|
|
|
|
// Similar to $lookupUser callback, but with null instead of false
|
|
$userFactory = $this->createMock( UserFactory::class );
|
|
$userFactory->method( 'newFromName' )
|
|
->willReturnCallback(
|
|
static function ( $username ) use ( $users ) {
|
|
return $users[ $username ] ?? null;
|
|
}
|
|
);
|
|
|
|
$lookupUser = static function ( $username ) use ( $users ) {
|
|
return $users[ $username ] ?? false;
|
|
};
|
|
|
|
$mwServices = MediaWikiServices::getInstance();
|
|
$passwordReset = $this->getMockBuilder( PasswordReset::class )
|
|
->onlyMethods( [ 'getUsersByEmail', 'isAllowed' ] )
|
|
->setConstructorArgs( [
|
|
$config,
|
|
new NullLogger(),
|
|
$authManager,
|
|
$mwServices->getHookContainer(),
|
|
$loadBalancer,
|
|
$permissionManager,
|
|
$userFactory,
|
|
$mwServices->getUserNameUtils(),
|
|
$userOptionsLookup
|
|
] )
|
|
->getMock();
|
|
$passwordReset->method( 'getUsersByEmail' )->with( $email )
|
|
->willReturn( array_map( $lookupUser, $usersWithEmail ) );
|
|
$passwordReset->method( 'isAllowed' )
|
|
->willReturn( Status::newGood() );
|
|
|
|
/** @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 [
|
|
'Throttled, pretend everything is ok' => [
|
|
'expectedError' => false,
|
|
'config' => $defaultConfig,
|
|
'performingUser' => $throttledUser,
|
|
'permissionManager' => $permissionManager,
|
|
'authManager' => $this->makeAuthManager(),
|
|
'username' => 'User1',
|
|
'email' => '',
|
|
'usersWithEmail' => [],
|
|
],
|
|
'Throttled, email required for resets, is invalid, pretend everything is ok' => [
|
|
'expectedError' => false,
|
|
'config' => $emailRequiredConfig,
|
|
'performingUser' => $throttledUser,
|
|
'permissionManager' => $permissionManager,
|
|
'authManager' => $this->makeAuthManager(),
|
|
'username' => 'User1',
|
|
'email' => '[invalid email]',
|
|
'usersWithEmail' => [],
|
|
],
|
|
'Invalid email, pretend everything is OK' => [
|
|
'expectedError' => false,
|
|
'config' => $defaultConfig,
|
|
'performingUser' => $performingUser,
|
|
'permissionManager' => $permissionManager,
|
|
'authManager' => $this->makeAuthManager(),
|
|
'username' => '',
|
|
'email' => '[invalid email]',
|
|
'usersWithEmail' => [],
|
|
],
|
|
'No username, no email' => [
|
|
'expectedError' => 'passwordreset-nodata',
|
|
'config' => $defaultConfig,
|
|
'performingUser' => $performingUser,
|
|
'permissionManager' => $permissionManager,
|
|
'authManager' => $this->makeAuthManager(),
|
|
'username' => '',
|
|
'email' => '',
|
|
'usersWithEmail' => [],
|
|
],
|
|
'Email route not enabled' => [
|
|
'expectedError' => 'passwordreset-nodata',
|
|
'config' => $this->makeConfig( true, [ 'username' => true ], false ),
|
|
'performingUser' => $performingUser,
|
|
'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' => $performingUser,
|
|
'permissionManager' => $permissionManager,
|
|
'authManager' => $this->makeAuthManager(),
|
|
'username' => 'User1',
|
|
'email' => '',
|
|
'usersWithEmail' => [],
|
|
],
|
|
'No routes enabled' => [
|
|
'expectedError' => 'passwordreset-nodata',
|
|
'config' => $this->makeConfig( true, [], false ),
|
|
'performingUser' => $performingUser,
|
|
'permissionManager' => $permissionManager,
|
|
'authManager' => $this->makeAuthManager(),
|
|
'username' => 'User1',
|
|
'email' => self::VALID_EMAIL,
|
|
'usersWithEmail' => [],
|
|
],
|
|
'Email required for resets but is empty, pretend everything is OK' => [
|
|
'expectedError' => false,
|
|
'config' => $emailRequiredConfig,
|
|
'performingUser' => $performingUser,
|
|
'permissionManager' => $permissionManager,
|
|
'authManager' => $this->makeAuthManager(),
|
|
'username' => 'User1',
|
|
'email' => '',
|
|
'usersWithEmail' => [],
|
|
],
|
|
'Email required for resets but is invalid, pretend everything is OK' => [
|
|
'expectedError' => false,
|
|
'config' => $emailRequiredConfig,
|
|
'performingUser' => $performingUser,
|
|
'permissionManager' => $permissionManager,
|
|
'authManager' => $this->makeAuthManager(),
|
|
'username' => 'User1',
|
|
'email' => '[invalid email]',
|
|
'usersWithEmail' => [],
|
|
],
|
|
'Password email already sent within 24 hours, pretend everything is ok' => [
|
|
'expectedError' => false,
|
|
'config' => $defaultConfig,
|
|
'performingUser' => $performingUser,
|
|
'permissionManager' => $permissionManager,
|
|
'authManager' => $this->makeAuthManager( [ 'User1' ], 0, [], [ 'User1' ] ),
|
|
'username' => 'User1',
|
|
'email' => '',
|
|
'usersWithEmail' => [ 'User1' ],
|
|
],
|
|
'No user by this username, pretend everything is OK' => [
|
|
'expectedError' => false,
|
|
'config' => $defaultConfig,
|
|
'performingUser' => $performingUser,
|
|
'permissionManager' => $permissionManager,
|
|
'authManager' => $this->makeAuthManager(),
|
|
'username' => 'Nonexistent user',
|
|
'email' => '',
|
|
'usersWithEmail' => [],
|
|
],
|
|
'Username is not valid' => [
|
|
'expectedError' => 'noname',
|
|
'config' => $defaultConfig,
|
|
'performingUser' => $performingUser,
|
|
'permissionManager' => $permissionManager,
|
|
'authManager' => $this->makeAuthManager(),
|
|
'username' => 'Invalid|username',
|
|
'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, pretend everything is OK' => [
|
|
'expectedError' => false,
|
|
'config' => $defaultConfig,
|
|
'performingUser' => $performingUser,
|
|
'permissionManager' => $permissionManager,
|
|
'authManager' => $this->makeAuthManager(),
|
|
'username' => 'BadUser',
|
|
'email' => '',
|
|
'usersWithEmail' => [],
|
|
],
|
|
'Email required 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' ],
|
|
],
|
|
'Reset three users via email that did not opt in, multiple users with same email' => [
|
|
'expectedError' => false,
|
|
'config' => $emailRequiredConfig,
|
|
'performingUser' => $performingUser,
|
|
'permissionManager' => $permissionManager,
|
|
'authManager' => $this->makeAuthManager( [ 'User2', 'User3', 'User4' ], 3, [ 'User1' ] ),
|
|
'username' => '',
|
|
'email' => self::VALID_EMAIL,
|
|
'usersWithEmail' => [ 'User1', 'User2', 'User3', 'User4' ],
|
|
],
|
|
];
|
|
}
|
|
|
|
private function assertStatus( StatusValue $status, $error = false ) {
|
|
if ( $error === false ) {
|
|
$this->assertTrue(
|
|
$status->isGood(),
|
|
'Expected status to be good, result was: ' . $status->__toString()
|
|
);
|
|
} 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::CONSTRUCTOR_OPTIONS, $hash );
|
|
}
|
|
|
|
/**
|
|
* @param string|null $ip
|
|
* @param bool $pingLimited
|
|
* @return User
|
|
*/
|
|
private function makePerformingUser( $ip, $pingLimited ) : User {
|
|
$request = $this->getMockBuilder( WebRequest::class )
|
|
->getMock();
|
|
$request->method( 'getIP' )
|
|
->willReturn( $ip );
|
|
/** @var WebRequest $request */
|
|
|
|
$user = $this->getMockBuilder( User::class )
|
|
->onlyMethods( [ 'getName', 'pingLimiter', 'getRequest' ] )
|
|
->getMock();
|
|
|
|
$user->method( 'getName' )
|
|
->willReturn( 'SomeUser' );
|
|
$user->method( 'pingLimiter' )
|
|
->with( 'mailpassword' )
|
|
->willReturn( $pingLimited );
|
|
$user->method( 'getRequest' )
|
|
->willReturn( $request );
|
|
|
|
/** @var User $user */
|
|
return $user;
|
|
}
|
|
|
|
private function makePermissionManager( User $performingUser, $isAllowed ) : PermissionManager {
|
|
$permissionManager = $this->getMockBuilder( PermissionManager::class )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
$permissionManager->method( 'userHasRight' )
|
|
->with( $performingUser, 'editmyprivateinfo' )
|
|
->willReturn( $isAllowed );
|
|
|
|
/** @var PermissionManager $permissionManager */
|
|
return $permissionManager;
|
|
}
|
|
|
|
/**
|
|
* @param string[] $allowed Usernames that are allowed to send password reset email
|
|
* by AuthManager's allowsAuthenticationDataChange method.
|
|
* @param int $numUsersToAuth Number of users that will receive email
|
|
* @param string[] $ignored Usernames that are allowed but ignored by AuthManager's
|
|
* allowsAuthenticationDataChange method and will not receive password reset email.
|
|
* @param string[] $mailThrottledLimited Usernames that have already
|
|
* received the password reset email within a given time, and AuthManager
|
|
* changeAuthenticationData method will mark them as 'throttled-mailpassword.'
|
|
* @return AuthManager
|
|
*/
|
|
private function makeAuthManager(
|
|
array $allowed = [],
|
|
$numUsersToAuth = 0,
|
|
array $ignored = [],
|
|
array $mailThrottledLimited = []
|
|
) : AuthManager {
|
|
$authManager = $this->getMockBuilder( AuthManager::class )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
$authManager->method( 'allowsAuthenticationDataChange' )
|
|
->willReturnCallback(
|
|
static function ( TemporaryPasswordAuthenticationRequest $req )
|
|
use ( $allowed, $ignored, $mailThrottledLimited ) {
|
|
if ( in_array( $req->username, $mailThrottledLimited, true ) ) {
|
|
return Status::newGood( 'throttled-mailpassword' );
|
|
}
|
|
|
|
$value = in_array( $req->username, $ignored, true )
|
|
? 'ignored'
|
|
: 'okie dokie';
|
|
|
|
return in_array( $req->username, $allowed, true )
|
|
? Status::newGood( $value )
|
|
: Status::newFatal( 'rejected by test mock' );
|
|
} );
|
|
// changeAuthenticationData is executed in the deferred update class
|
|
// SendPasswordResetEmailUpdate
|
|
$authManager->expects( $this->exactly( $numUsersToAuth ) )
|
|
->method( 'changeAuthenticationData' );
|
|
|
|
/** @var AuthManager $authManager */
|
|
return $authManager;
|
|
}
|
|
|
|
/**
|
|
* @return User[]
|
|
*/
|
|
private function makeUsers() {
|
|
$user1 = $this->getMockBuilder( User::class )->getMock();
|
|
$user2 = $this->getMockBuilder( User::class )->getMock();
|
|
$user3 = $this->getMockBuilder( User::class )->getMock();
|
|
$user4 = $this->getMockBuilder( User::class )->getMock();
|
|
$user1->method( 'getName' )->willReturn( 'User1' );
|
|
$user2->method( 'getName' )->willReturn( 'User2' );
|
|
$user3->method( 'getName' )->willReturn( 'User3' );
|
|
$user4->method( 'getName' )->willReturn( 'User4' );
|
|
$user1->method( 'getId' )->willReturn( 1 );
|
|
$user2->method( 'getId' )->willReturn( 2 );
|
|
$user3->method( 'getId' )->willReturn( 3 );
|
|
$user4->method( 'getId' )->willReturn( 4 );
|
|
$user1->method( 'getEmail' )->willReturn( self::VALID_EMAIL );
|
|
$user2->method( 'getEmail' )->willReturn( self::VALID_EMAIL );
|
|
$user3->method( 'getEmail' )->willReturn( self::VALID_EMAIL );
|
|
$user4->method( 'getEmail' )->willReturn( self::VALID_EMAIL );
|
|
|
|
$badUser = $this->getMockBuilder( User::class )->getMock();
|
|
$badUser->method( 'getName' )->willReturn( 'BadUser' );
|
|
$badUser->method( 'getId' )->willReturn( 5 );
|
|
$badUser->method( 'getEmail' )->willReturn( '' );
|
|
|
|
return [
|
|
'User1' => $user1,
|
|
'User2' => $user2,
|
|
'User3' => $user3,
|
|
'User4' => $user4,
|
|
'BadUser' => $badUser,
|
|
];
|
|
}
|
|
}
|