This commit splits the existing Block class into AbstractBlock, Block and SystemBlock. Before this patch, the Block class represents several types of blocks, which can be separated into blocks stored in the database, and temporary blocks created by the system. These are now represented by Block and SystemBlock, which inherit from AbstractBlock. This lays the foundations for: * enforcing block parameters from multiple blocks that apply to a user/IP address * improvements to the Block API, including the addition of services Breaking changes: functions expecting a Block object should still expect a Block object if it came from the database, but other functions may now need to expect an AbstractBlock or SystemBlock object. (Note that an alternative naming scheme, in which the abstract class is called Block and the subclasses are DatabaseBlock and SystemBlock, avoids this breakage. However, it introduces more breakages to calls to static Block methods and new Block instantiations.) Changes to tests: system blocks don't set the $blockCreateAccount or $mExipry block properties, so remove/change any tests that assume they do. Bug: T222737 Change-Id: I83bceb5e5049e254c90ace060f8f8fad44696c67
201 lines
7 KiB
PHP
201 lines
7 KiB
PHP
<?php
|
|
|
|
use MediaWiki\Auth\AuthManager;
|
|
use MediaWiki\Block\SystemBlock;
|
|
|
|
/**
|
|
* @covers PasswordReset
|
|
* @group Database
|
|
*/
|
|
class PasswordResetTest extends MediaWikiTestCase {
|
|
/**
|
|
* @dataProvider provideIsAllowed
|
|
*/
|
|
public function testIsAllowed( $passwordResetRoutes, $enableEmail,
|
|
$allowsAuthenticationDataChange, $canEditPrivate, $block, $globalBlock, $isAllowed
|
|
) {
|
|
$config = new HashConfig( [
|
|
'PasswordResetRoutes' => $passwordResetRoutes,
|
|
'EnableEmail' => $enableEmail,
|
|
] );
|
|
|
|
$authManager = $this->getMockBuilder( AuthManager::class )->disableOriginalConstructor()
|
|
->getMock();
|
|
$authManager->expects( $this->any() )->method( 'allowsAuthenticationDataChange' )
|
|
->willReturn( $allowsAuthenticationDataChange ? Status::newGood() : Status::newFatal( 'foo' ) );
|
|
|
|
$user = $this->getMockBuilder( User::class )->getMock();
|
|
$user->expects( $this->any() )->method( 'getName' )->willReturn( 'Foo' );
|
|
$user->expects( $this->any() )->method( 'getBlock' )->willReturn( $block );
|
|
$user->expects( $this->any() )->method( 'getGlobalBlock' )->willReturn( $globalBlock );
|
|
$user->expects( $this->any() )->method( 'isAllowed' )
|
|
->will( $this->returnCallback( function ( $perm ) use ( $canEditPrivate ) {
|
|
if ( $perm === 'editmyprivateinfo' ) {
|
|
return $canEditPrivate;
|
|
} else {
|
|
$this->fail( 'Unexpected permission check' );
|
|
}
|
|
} ) );
|
|
|
|
$passwordReset = new PasswordReset( $config, $authManager );
|
|
|
|
$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 Block( [ 'createAccount' => true ] ),
|
|
'globalBlock' => null,
|
|
'isAllowed' => false,
|
|
],
|
|
'blocked w/o account creation disabled' => [
|
|
'passwordResetRoutes' => [ 'username' => true ],
|
|
'enableEmail' => true,
|
|
'allowsAuthenticationDataChange' => true,
|
|
'canEditPrivate' => true,
|
|
'block' => new Block( [] ),
|
|
'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,
|
|
],
|
|
'all OK' => [
|
|
'passwordResetRoutes' => [ 'username' => true ],
|
|
'enableEmail' => true,
|
|
'allowsAuthenticationDataChange' => true,
|
|
'canEditPrivate' => true,
|
|
'block' => null,
|
|
'globalBlock' => null,
|
|
'isAllowed' => true,
|
|
],
|
|
];
|
|
}
|
|
|
|
public function testExecute_email() {
|
|
$config = new HashConfig( [
|
|
'PasswordResetRoutes' => [ 'username' => true, 'email' => true ],
|
|
'EnableEmail' => true,
|
|
] );
|
|
|
|
// Unregister the hooks for proper unit testing
|
|
$this->mergeMwGlobalArrayValue( 'wgHooks', [
|
|
'User::mailPasswordInternal' => [],
|
|
'SpecialPasswordResetOnSubmit' => [],
|
|
] );
|
|
|
|
$authManager = $this->getMockBuilder( AuthManager::class )->disableOriginalConstructor()
|
|
->getMock();
|
|
$authManager->expects( $this->any() )->method( 'allowsAuthenticationDataChange' )
|
|
->willReturn( Status::newGood() );
|
|
$authManager->expects( $this->exactly( 2 ) )->method( 'changeAuthenticationData' );
|
|
|
|
$request = new FauxRequest();
|
|
$request->setIP( '1.2.3.4' );
|
|
$performingUser = $this->getMockBuilder( User::class )->getMock();
|
|
$performingUser->expects( $this->any() )->method( 'getRequest' )->willReturn( $request );
|
|
$performingUser->expects( $this->any() )->method( 'isAllowed' )->willReturn( true );
|
|
$performingUser->expects( $this->any() )->method( 'getName' )->willReturn( 'Performer' );
|
|
|
|
$targetUser1 = $this->getMockBuilder( User::class )->getMock();
|
|
$targetUser2 = $this->getMockBuilder( User::class )->getMock();
|
|
$targetUser1->expects( $this->any() )->method( 'getName' )->willReturn( 'User1' );
|
|
$targetUser2->expects( $this->any() )->method( 'getName' )->willReturn( 'User2' );
|
|
$targetUser1->expects( $this->any() )->method( 'getId' )->willReturn( 1 );
|
|
$targetUser2->expects( $this->any() )->method( 'getId' )->willReturn( 2 );
|
|
$targetUser1->expects( $this->any() )->method( 'getEmail' )->willReturn( 'foo@bar.baz' );
|
|
$targetUser2->expects( $this->any() )->method( 'getEmail' )->willReturn( 'foo@bar.baz' );
|
|
|
|
$passwordReset = $this->getMockBuilder( PasswordReset::class )
|
|
->setMethods( [ 'getUsersByEmail' ] )->setConstructorArgs( [ $config, $authManager ] )
|
|
->getMock();
|
|
$passwordReset->expects( $this->any() )->method( 'getUsersByEmail' )->with( 'foo@bar.baz' )
|
|
->willReturn( [ $targetUser1, $targetUser2 ] );
|
|
|
|
$status = $passwordReset->isAllowed( $performingUser );
|
|
$this->assertTrue( $status->isGood() );
|
|
|
|
$status = $passwordReset->execute( $performingUser, null, 'foo@bar.baz' );
|
|
$this->assertTrue( $status->isGood() );
|
|
}
|
|
}
|