All GetUserBlock hook handlers face a common problem: when there are multiple blocks that match, or there is another block passed down by core or previous extensions, they need to create a composite block since they can only pass forward a single Block object. Even though the logic for doing so is not complicated, it's better to centralize it so it's one less thing the extension author needs to think about. Unlike the original code snippet, the target is not passed in explicitly but taken from the first composing block. Since CompositeBlock is created after target filtering already happend, this shouldn't make much difference; and the target property of a composite block which contains blocks with different targets isn't particularly meaningful. Change-Id: I14f754e3c479ce61d18d7d2ebd1656940088d67d
310 lines
8.7 KiB
PHP
310 lines
8.7 KiB
PHP
<?php
|
|
|
|
use MediaWiki\Block\BlockRestrictionStore;
|
|
use MediaWiki\Block\CompositeBlock;
|
|
use MediaWiki\Block\DatabaseBlock;
|
|
use MediaWiki\Block\Restriction\NamespaceRestriction;
|
|
use MediaWiki\Block\Restriction\PageRestriction;
|
|
use MediaWiki\Block\SystemBlock;
|
|
use MediaWiki\MainConfigNames;
|
|
|
|
/**
|
|
* @group Database
|
|
* @group Blocking
|
|
* @covers \MediaWiki\Block\CompositeBlock
|
|
*/
|
|
class CompositeBlockTest extends MediaWikiLangTestCase {
|
|
private function getPartialBlocks() {
|
|
$sysopUser = $this->getTestSysop()->getUser();
|
|
|
|
$userBlock = new DatabaseBlock( [
|
|
'address' => $this->getTestUser()->getUser(),
|
|
'by' => $sysopUser,
|
|
'sitewide' => false,
|
|
] );
|
|
$ipBlock = new DatabaseBlock( [
|
|
'address' => '127.0.0.1',
|
|
'by' => $sysopUser,
|
|
'sitewide' => false,
|
|
] );
|
|
|
|
$blockStore = $this->getServiceContainer()->getDatabaseBlockStore();
|
|
$blockStore->insertBlock( $userBlock );
|
|
$blockStore->insertBlock( $ipBlock );
|
|
|
|
return [
|
|
'user' => $userBlock,
|
|
'ip' => $ipBlock,
|
|
];
|
|
}
|
|
|
|
private function deleteBlocks( $blocks ) {
|
|
$blockStore = $this->getServiceContainer()->getDatabaseBlockStore();
|
|
foreach ( $blocks as $block ) {
|
|
$blockStore->deleteBlock( $block );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideTestStrictestParametersApplied
|
|
*/
|
|
public function testStrictestParametersApplied( $blocks, $expected ) {
|
|
$this->overrideConfigValues( [
|
|
MainConfigNames::BlockDisablesLogin => false,
|
|
MainConfigNames::BlockAllowsUTEdit => true,
|
|
] );
|
|
|
|
$block = new CompositeBlock( [
|
|
'originalBlocks' => $blocks,
|
|
] );
|
|
|
|
$this->assertSame( $expected[ 'hideName' ], $block->getHideName() );
|
|
$this->assertSame( $expected[ 'sitewide' ], $block->isSitewide() );
|
|
$this->assertSame( $expected[ 'blockEmail' ], $block->isEmailBlocked() );
|
|
$this->assertSame( $expected[ 'allowUsertalk' ], $block->isUsertalkEditAllowed() );
|
|
}
|
|
|
|
public static function provideTestStrictestParametersApplied() {
|
|
return [
|
|
'Sitewide block and partial block' => [
|
|
[
|
|
new DatabaseBlock( [
|
|
'sitewide' => false,
|
|
'blockEmail' => true,
|
|
'allowUsertalk' => true,
|
|
] ),
|
|
new DatabaseBlock( [
|
|
'sitewide' => true,
|
|
'blockEmail' => false,
|
|
'allowUsertalk' => false,
|
|
] ),
|
|
],
|
|
[
|
|
'hideName' => false,
|
|
'sitewide' => true,
|
|
'blockEmail' => true,
|
|
'allowUsertalk' => false,
|
|
],
|
|
],
|
|
'Partial block and system block' => [
|
|
[
|
|
new DatabaseBlock( [
|
|
'sitewide' => false,
|
|
'blockEmail' => true,
|
|
'allowUsertalk' => false,
|
|
] ),
|
|
new SystemBlock( [
|
|
'systemBlock' => 'proxy',
|
|
] ),
|
|
],
|
|
[
|
|
'hideName' => false,
|
|
'sitewide' => true,
|
|
'blockEmail' => true,
|
|
'allowUsertalk' => false,
|
|
],
|
|
],
|
|
'System block and user name hiding block' => [
|
|
[
|
|
new DatabaseBlock( [
|
|
'hideName' => true,
|
|
'sitewide' => true,
|
|
'blockEmail' => true,
|
|
'allowUsertalk' => false,
|
|
] ),
|
|
new SystemBlock( [
|
|
'systemBlock' => 'proxy',
|
|
] ),
|
|
],
|
|
[
|
|
'hideName' => true,
|
|
'sitewide' => true,
|
|
'blockEmail' => true,
|
|
'allowUsertalk' => false,
|
|
],
|
|
],
|
|
'Two lenient partial blocks' => [
|
|
[
|
|
new DatabaseBlock( [
|
|
'sitewide' => false,
|
|
'blockEmail' => false,
|
|
'allowUsertalk' => true,
|
|
] ),
|
|
new DatabaseBlock( [
|
|
'sitewide' => false,
|
|
'blockEmail' => false,
|
|
'allowUsertalk' => true,
|
|
] ),
|
|
],
|
|
[
|
|
'hideName' => false,
|
|
'sitewide' => false,
|
|
'blockEmail' => false,
|
|
'allowUsertalk' => true,
|
|
],
|
|
],
|
|
];
|
|
}
|
|
|
|
public function testBlockAppliesToTitle() {
|
|
$this->overrideConfigValue( MainConfigNames::BlockDisablesLogin, false );
|
|
|
|
$blocks = $this->getPartialBlocks();
|
|
|
|
$block = new CompositeBlock( [
|
|
'originalBlocks' => $blocks,
|
|
] );
|
|
|
|
$pageFoo = $this->getExistingTestPage( 'Foo' );
|
|
$pageBar = $this->getExistingTestPage( 'User:Bar' );
|
|
|
|
$this->getBlockRestrictionStore()->insert( [
|
|
new PageRestriction( $blocks[ 'user' ]->getId(), $pageFoo->getId() ),
|
|
new NamespaceRestriction( $blocks[ 'ip' ]->getId(), NS_USER ),
|
|
] );
|
|
|
|
$this->assertTrue( $block->appliesToTitle( $pageFoo->getTitle() ) );
|
|
$this->assertTrue( $block->appliesToTitle( $pageBar->getTitle() ) );
|
|
|
|
$this->deleteBlocks( $blocks );
|
|
}
|
|
|
|
public function testBlockAppliesToUsertalk() {
|
|
$this->overrideConfigValues( [
|
|
MainConfigNames::BlockAllowsUTEdit => true,
|
|
MainConfigNames::BlockDisablesLogin => false,
|
|
] );
|
|
|
|
$blocks = $this->getPartialBlocks();
|
|
|
|
$block = new CompositeBlock( [
|
|
'originalBlocks' => $blocks,
|
|
] );
|
|
|
|
$userFactory = $this->getServiceContainer()->getUserFactory();
|
|
$targetIdentity = $userFactory->newFromUserIdentity( $blocks[ 'user' ]->getTargetUserIdentity() );
|
|
$title = $targetIdentity->getTalkPage();
|
|
$page = $this->getExistingTestPage( 'User talk:' . $title->getText() );
|
|
|
|
$this->getBlockRestrictionStore()->insert( [
|
|
new PageRestriction( $blocks[ 'user' ]->getId(), $page->getId() ),
|
|
new NamespaceRestriction( $blocks[ 'ip' ]->getId(), NS_USER ),
|
|
] );
|
|
|
|
$this->assertTrue( $block->appliesToUsertalk( $title ) );
|
|
|
|
$this->deleteBlocks( $blocks );
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideTestBlockAppliesToRight
|
|
*/
|
|
public function testBlockAppliesToRight( $applies, $expected ) {
|
|
$this->overrideConfigValue( MainConfigNames::BlockDisablesLogin, false );
|
|
|
|
$block = new CompositeBlock( [
|
|
'originalBlocks' => [
|
|
$this->getMockBlockForTestAppliesToRight( $applies[ 0 ] ),
|
|
$this->getMockBlockForTestAppliesToRight( $applies[ 1 ] ),
|
|
],
|
|
] );
|
|
|
|
$this->assertSame( $expected, $block->appliesToRight( 'right' ) );
|
|
}
|
|
|
|
private function getMockBlockForTestAppliesToRight( $applies ) {
|
|
$mockBlock = $this->getMockBuilder( DatabaseBlock::class )
|
|
->onlyMethods( [ 'appliesToRight' ] )
|
|
->getMock();
|
|
$mockBlock->method( 'appliesToRight' )
|
|
->willReturn( $applies );
|
|
return $mockBlock;
|
|
}
|
|
|
|
public static function provideTestBlockAppliesToRight() {
|
|
return [
|
|
'Block does not apply if no original blocks apply' => [
|
|
[ false, false ],
|
|
false,
|
|
],
|
|
'Block applies if any original block applies (second block doesn\'t apply)' => [
|
|
[ true, false ],
|
|
true,
|
|
],
|
|
'Block applies if any original block applies (second block unsure)' => [
|
|
[ true, null ],
|
|
true,
|
|
],
|
|
'Block is unsure if all original blocks are unsure' => [
|
|
[ null, null ],
|
|
null,
|
|
],
|
|
'Block is unsure if any original block is unsure, and no others apply' => [
|
|
[ null, false ],
|
|
null,
|
|
],
|
|
];
|
|
}
|
|
|
|
public function testTimestamp() {
|
|
$timestamp = 20000101000000;
|
|
|
|
$firstBlock = $this->createMock( DatabaseBlock::class );
|
|
$firstBlock->method( 'getTimestamp' )
|
|
->willReturn( (string)$timestamp );
|
|
|
|
$secondBlock = $this->createMock( DatabaseBlock::class );
|
|
$secondBlock->method( 'getTimestamp' )
|
|
->willReturn( (string)( $timestamp + 10 ) );
|
|
|
|
$thirdBlock = $this->createMock( DatabaseBlock::class );
|
|
$thirdBlock->method( 'getTimestamp' )
|
|
->willReturn( (string)( $timestamp + 100 ) );
|
|
|
|
$block = new CompositeBlock( [
|
|
'originalBlocks' => [ $thirdBlock, $firstBlock, $secondBlock ],
|
|
] );
|
|
$this->assertSame( (string)$timestamp, $block->getTimestamp() );
|
|
}
|
|
|
|
public function testCreateFromBlocks() {
|
|
$block1 = new SystemBlock( [
|
|
'address' => '127.0.0.1',
|
|
'systemBlock' => 'test1',
|
|
] );
|
|
$block2 = new SystemBlock( [
|
|
'address' => '127.0.0.1',
|
|
'systemBlock' => 'test2',
|
|
] );
|
|
$block3 = new SystemBlock( [
|
|
'address' => '127.0.0.1',
|
|
'systemBlock' => 'test3',
|
|
] );
|
|
|
|
$compositeBlock = CompositeBlock::createFromBlocks( $block1, $block2 );
|
|
$this->assertInstanceOf( CompositeBlock::class, $compositeBlock );
|
|
$this->assertCount( 2, $compositeBlock->getOriginalBlocks() );
|
|
[ $actualBlock1, $actualBlock2 ] = $compositeBlock->getOriginalBlocks();
|
|
$this->assertSame( $block1->getSystemBlockType(), $actualBlock1->getSystemBlockType() );
|
|
$this->assertSame( $block2->getSystemBlockType(), $actualBlock2->getSystemBlockType() );
|
|
$this->assertSame( 'blockedtext-composite-reason',
|
|
$compositeBlock->getReasonComment()->message->getKey() );
|
|
$this->assertSame( '127.0.0.1', $compositeBlock->getTargetName() );
|
|
|
|
$compositeBlock2 = CompositeBlock::createFromBlocks( $compositeBlock, $block3 );
|
|
$this->assertCount( 3, $compositeBlock2->getOriginalBlocks() );
|
|
[ $actualBlock1, $actualBlock2, $actualBlock3 ] = $compositeBlock2->getOriginalBlocks();
|
|
$this->assertSame( $block1->getSystemBlockType(), $actualBlock1->getSystemBlockType() );
|
|
$this->assertSame( $block2->getSystemBlockType(), $actualBlock2->getSystemBlockType() );
|
|
$this->assertSame( $block3->getSystemBlockType(), $actualBlock3->getSystemBlockType() );
|
|
}
|
|
|
|
/**
|
|
* Get an instance of BlockRestrictionStore
|
|
*
|
|
* @return BlockRestrictionStore
|
|
*/
|
|
protected function getBlockRestrictionStore(): BlockRestrictionStore {
|
|
return $this->getServiceContainer()->getBlockRestrictionStore();
|
|
}
|
|
}
|