Block::prevents plays several different roles: * acts as get/setter for Boolean properties that correspond to ipb_create_account, ipb_block_email and ipb_allow_usertalk * calculates whether a block blocks a given right, based on Block properties, global configs, white/blacklists and anonymous user rights * decides whether a block prevents editing of the target's own user talk page (listed separately because 'editownusertalk' is not a right) This patch: * renames mDisableUsertalk to allowEditUsertalk (and reverses the value), to match the field ipb_allow_usertalk and make this logic easier to follow * renames mCreateAccount to blockCreateAccount, to make it clear that the flag blocks account creation when true, and make this logic easier to follow * decouples the block that is stored in the database (which now reflects the form that the admin submitted) and the behaviour of the block on enforcement (since the properties set by the admin can be overridden by global configs) - so if the global configs change, the block behaviour could too * creates get/setters for blockCreateAccount, mBlockEmail and allowEditUsertalk properties * creates appliesToRight, exclusively for checking whether the block blocks a given right, taking into account the block properties, global configs and anonymous user rights * creates appliesToUsertalk, for checking whether the block blocks a user from editing their own talk page. The block is unaware of the user trying to make the edit, and this user is not always the same as the block target, e.g. if the block target is an IP range. Therefore the user's talk page is passed in to this method. appliesToUsertalk can be called from anywhere where the user is known * uses the get/setters wherever Block::prevents was being used as such * uses appliesToRight whenever Block::prevents was being used to determine if the block blocks a given right * uses appliesToUsertalk in User::isBlockedFrom Bug: T211578 Bug: T214508 Change-Id: I0e131696419211319082cb454f4f05297e55d22e
474 lines
14 KiB
PHP
474 lines
14 KiB
PHP
<?php
|
|
|
|
use MediaWiki\Block\BlockRestriction;
|
|
use MediaWiki\Block\Restriction\PageRestriction;
|
|
use MediaWiki\Block\Restriction\NamespaceRestriction;
|
|
use Wikimedia\TestingAccessWrapper;
|
|
|
|
/**
|
|
* @group Blocking
|
|
* @group Database
|
|
* @coversDefaultClass SpecialBlock
|
|
*/
|
|
class SpecialBlockTest extends SpecialPageTestBase {
|
|
/**
|
|
* {@inheritdoc}
|
|
*/
|
|
protected function newSpecialPage() {
|
|
return new SpecialBlock();
|
|
}
|
|
|
|
public function tearDown() {
|
|
parent::tearDown();
|
|
$this->resetTables();
|
|
}
|
|
|
|
/**
|
|
* @covers ::getFormFields()
|
|
*/
|
|
public function testGetFormFields() {
|
|
$this->setMwGlobals( [
|
|
'wgEnablePartialBlocks' => false,
|
|
'wgBlockAllowsUTEdit' => true,
|
|
] );
|
|
$page = $this->newSpecialPage();
|
|
$wrappedPage = TestingAccessWrapper::newFromObject( $page );
|
|
$fields = $wrappedPage->getFormFields();
|
|
$this->assertInternalType( 'array', $fields );
|
|
$this->assertArrayHasKey( 'Target', $fields );
|
|
$this->assertArrayHasKey( 'Expiry', $fields );
|
|
$this->assertArrayHasKey( 'Reason', $fields );
|
|
$this->assertArrayHasKey( 'CreateAccount', $fields );
|
|
$this->assertArrayHasKey( 'DisableUTEdit', $fields );
|
|
$this->assertArrayHasKey( 'AutoBlock', $fields );
|
|
$this->assertArrayHasKey( 'HardBlock', $fields );
|
|
$this->assertArrayHasKey( 'PreviousTarget', $fields );
|
|
$this->assertArrayHasKey( 'Confirm', $fields );
|
|
|
|
$this->assertArrayNotHasKey( 'EditingRestriction', $fields );
|
|
$this->assertArrayNotHasKey( 'PageRestrictions', $fields );
|
|
$this->assertArrayNotHasKey( 'NamespaceRestrictions', $fields );
|
|
}
|
|
|
|
/**
|
|
* @covers ::getFormFields()
|
|
*/
|
|
public function testGetFormFieldsPartialBlocks() {
|
|
$this->setMwGlobals( [
|
|
'wgEnablePartialBlocks' => true,
|
|
] );
|
|
$page = $this->newSpecialPage();
|
|
$wrappedPage = TestingAccessWrapper::newFromObject( $page );
|
|
$fields = $wrappedPage->getFormFields();
|
|
|
|
$this->assertArrayHasKey( 'EditingRestriction', $fields );
|
|
$this->assertArrayHasKey( 'PageRestrictions', $fields );
|
|
$this->assertArrayHasKey( 'NamespaceRestrictions', $fields );
|
|
}
|
|
|
|
/**
|
|
* @covers ::maybeAlterFormDefaults()
|
|
*/
|
|
public function testMaybeAlterFormDefaults() {
|
|
$this->setMwGlobals( [
|
|
'wgEnablePartialBlocks' => false,
|
|
'wgBlockAllowsUTEdit' => true,
|
|
] );
|
|
|
|
$block = $this->insertBlock();
|
|
|
|
// Refresh the block from the database.
|
|
$block = Block::newFromTarget( $block->getTarget() );
|
|
|
|
$page = $this->newSpecialPage();
|
|
|
|
$wrappedPage = TestingAccessWrapper::newFromObject( $page );
|
|
$wrappedPage->target = $block->getTarget();
|
|
$fields = $wrappedPage->getFormFields();
|
|
|
|
$this->assertSame( (string)$block->getTarget(), $fields['Target']['default'] );
|
|
$this->assertSame( $block->isHardblock(), $fields['HardBlock']['default'] );
|
|
$this->assertSame( $block->isCreateAccountBlocked(), $fields['CreateAccount']['default'] );
|
|
$this->assertSame( $block->isAutoblocking(), $fields['AutoBlock']['default'] );
|
|
$this->assertSame( !$block->isUsertalkEditAllowed(), $fields['DisableUTEdit']['default'] );
|
|
$this->assertSame( $block->mReason, $fields['Reason']['default'] );
|
|
$this->assertSame( 'infinite', $fields['Expiry']['default'] );
|
|
}
|
|
|
|
/**
|
|
* @covers ::maybeAlterFormDefaults()
|
|
*/
|
|
public function testMaybeAlterFormDefaultsPartial() {
|
|
$this->setMwGlobals( [
|
|
'wgEnablePartialBlocks' => true,
|
|
] );
|
|
|
|
$badActor = $this->getTestUser()->getUser();
|
|
$sysop = $this->getTestSysop()->getUser();
|
|
$pageSaturn = $this->getExistingTestPage( 'Saturn' );
|
|
$pageMars = $this->getExistingTestPage( 'Mars' );
|
|
|
|
$block = new \Block( [
|
|
'address' => $badActor->getName(),
|
|
'user' => $badActor->getId(),
|
|
'by' => $sysop->getId(),
|
|
'expiry' => 'infinity',
|
|
'sitewide' => 0,
|
|
'enableAutoblock' => true,
|
|
] );
|
|
|
|
$block->setRestrictions( [
|
|
new PageRestriction( 0, $pageSaturn->getId() ),
|
|
new PageRestriction( 0, $pageMars->getId() ),
|
|
new NamespaceRestriction( 0, NS_TALK ),
|
|
// Deleted page.
|
|
new PageRestriction( 0, 999999 ),
|
|
] );
|
|
|
|
$block->insert();
|
|
|
|
// Refresh the block from the database.
|
|
$block = Block::newFromTarget( $block->getTarget() );
|
|
|
|
$page = $this->newSpecialPage();
|
|
|
|
$wrappedPage = TestingAccessWrapper::newFromObject( $page );
|
|
$wrappedPage->target = $block->getTarget();
|
|
$fields = $wrappedPage->getFormFields();
|
|
|
|
$titles = [
|
|
$pageMars->getTitle()->getPrefixedText(),
|
|
$pageSaturn->getTitle()->getPrefixedText(),
|
|
];
|
|
|
|
$this->assertSame( (string)$block->getTarget(), $fields['Target']['default'] );
|
|
$this->assertSame( 'partial', $fields['EditingRestriction']['default'] );
|
|
$this->assertSame( implode( "\n", $titles ), $fields['PageRestrictions']['default'] );
|
|
}
|
|
|
|
/**
|
|
* @covers ::processForm()
|
|
*/
|
|
public function testProcessForm() {
|
|
$this->setMwGlobals( [
|
|
'wgEnablePartialBlocks' => false,
|
|
] );
|
|
$badActor = $this->getTestUser()->getUser();
|
|
$context = RequestContext::getMain();
|
|
|
|
$page = $this->newSpecialPage();
|
|
$reason = 'test';
|
|
$expiry = 'infinity';
|
|
$data = [
|
|
'Target' => (string)$badActor,
|
|
'Expiry' => 'infinity',
|
|
'Reason' => [
|
|
$reason,
|
|
],
|
|
'Confirm' => '1',
|
|
'CreateAccount' => '0',
|
|
'DisableUTEdit' => '0',
|
|
'DisableEmail' => '0',
|
|
'HardBlock' => '0',
|
|
'AutoBlock' => '1',
|
|
'HideUser' => '0',
|
|
'Watch' => '0',
|
|
];
|
|
$result = $page->processForm( $data, $context );
|
|
|
|
$this->assertTrue( $result );
|
|
|
|
$block = Block::newFromTarget( $badActor );
|
|
$this->assertSame( $reason, $block->mReason );
|
|
$this->assertSame( $expiry, $block->getExpiry() );
|
|
}
|
|
|
|
/**
|
|
* @covers ::processForm()
|
|
*/
|
|
public function testProcessFormExisting() {
|
|
$this->setMwGlobals( [
|
|
'wgEnablePartialBlocks' => false,
|
|
] );
|
|
$badActor = $this->getTestUser()->getUser();
|
|
$sysop = $this->getTestSysop()->getUser();
|
|
$context = RequestContext::getMain();
|
|
|
|
// Create a block that will be updated.
|
|
$block = new \Block( [
|
|
'address' => $badActor->getName(),
|
|
'user' => $badActor->getId(),
|
|
'by' => $sysop->getId(),
|
|
'expiry' => 'infinity',
|
|
'sitewide' => 0,
|
|
'enableAutoblock' => false,
|
|
] );
|
|
$block->insert();
|
|
|
|
$page = $this->newSpecialPage();
|
|
$reason = 'test';
|
|
$expiry = 'infinity';
|
|
$data = [
|
|
'Target' => (string)$badActor,
|
|
'Expiry' => 'infinity',
|
|
'Reason' => [
|
|
$reason,
|
|
],
|
|
'Confirm' => '1',
|
|
'CreateAccount' => '0',
|
|
'DisableUTEdit' => '0',
|
|
'DisableEmail' => '0',
|
|
'HardBlock' => '0',
|
|
'AutoBlock' => '1',
|
|
'HideUser' => '0',
|
|
'Watch' => '0',
|
|
];
|
|
$result = $page->processForm( $data, $context );
|
|
|
|
$this->assertTrue( $result );
|
|
|
|
$block = Block::newFromTarget( $badActor );
|
|
$this->assertSame( $reason, $block->mReason );
|
|
$this->assertSame( $expiry, $block->getExpiry() );
|
|
$this->assertSame( '1', $block->isAutoblocking() );
|
|
}
|
|
|
|
/**
|
|
* @covers ::processForm()
|
|
*/
|
|
public function testProcessFormRestrictions() {
|
|
$this->setMwGlobals( [
|
|
'wgEnablePartialBlocks' => true,
|
|
] );
|
|
$badActor = $this->getTestUser()->getUser();
|
|
$context = RequestContext::getMain();
|
|
|
|
$pageSaturn = $this->getExistingTestPage( 'Saturn' );
|
|
$pageMars = $this->getExistingTestPage( 'Mars' );
|
|
|
|
$titles = [
|
|
$pageSaturn->getTitle()->getText(),
|
|
$pageMars->getTitle()->getText(),
|
|
];
|
|
|
|
$page = $this->newSpecialPage();
|
|
$reason = 'test';
|
|
$expiry = 'infinity';
|
|
$data = [
|
|
'Target' => (string)$badActor,
|
|
'Expiry' => 'infinity',
|
|
'Reason' => [
|
|
$reason,
|
|
],
|
|
'Confirm' => '1',
|
|
'CreateAccount' => '0',
|
|
'DisableUTEdit' => '0',
|
|
'DisableEmail' => '0',
|
|
'HardBlock' => '0',
|
|
'AutoBlock' => '1',
|
|
'HideUser' => '0',
|
|
'Watch' => '0',
|
|
'EditingRestriction' => 'partial',
|
|
'PageRestrictions' => implode( "\n", $titles ),
|
|
'NamespaceRestrictions' => '',
|
|
];
|
|
$result = $page->processForm( $data, $context );
|
|
|
|
$this->assertTrue( $result );
|
|
|
|
$block = Block::newFromTarget( $badActor );
|
|
$this->assertSame( $reason, $block->mReason );
|
|
$this->assertSame( $expiry, $block->getExpiry() );
|
|
$this->assertCount( 2, $block->getRestrictions() );
|
|
$this->assertTrue( BlockRestriction::equals( $block->getRestrictions(), [
|
|
new PageRestriction( $block->getId(), $pageMars->getId() ),
|
|
new PageRestriction( $block->getId(), $pageSaturn->getId() ),
|
|
] ) );
|
|
}
|
|
|
|
/**
|
|
* @covers ::processForm()
|
|
*/
|
|
public function testProcessFormRestrictionsChange() {
|
|
$this->setMwGlobals( [
|
|
'wgEnablePartialBlocks' => true,
|
|
] );
|
|
$badActor = $this->getTestUser()->getUser();
|
|
$context = RequestContext::getMain();
|
|
|
|
$pageSaturn = $this->getExistingTestPage( 'Saturn' );
|
|
$pageMars = $this->getExistingTestPage( 'Mars' );
|
|
|
|
$titles = [
|
|
$pageSaturn->getTitle()->getText(),
|
|
$pageMars->getTitle()->getText(),
|
|
];
|
|
|
|
// Create a partial block.
|
|
$page = $this->newSpecialPage();
|
|
$reason = 'test';
|
|
$expiry = 'infinity';
|
|
$data = [
|
|
'Target' => (string)$badActor,
|
|
'Expiry' => 'infinity',
|
|
'Reason' => [
|
|
$reason,
|
|
],
|
|
'Confirm' => '1',
|
|
'CreateAccount' => '0',
|
|
'DisableUTEdit' => '0',
|
|
'DisableEmail' => '0',
|
|
'HardBlock' => '0',
|
|
'AutoBlock' => '1',
|
|
'HideUser' => '0',
|
|
'Watch' => '0',
|
|
'EditingRestriction' => 'partial',
|
|
'PageRestrictions' => implode( "\n", $titles ),
|
|
'NamespaceRestrictions' => '',
|
|
];
|
|
$result = $page->processForm( $data, $context );
|
|
|
|
$this->assertTrue( $result );
|
|
|
|
$block = Block::newFromTarget( $badActor );
|
|
$this->assertSame( $reason, $block->mReason );
|
|
$this->assertSame( $expiry, $block->getExpiry() );
|
|
$this->assertFalse( $block->isSitewide() );
|
|
$this->assertCount( 2, $block->getRestrictions() );
|
|
$this->assertTrue( BlockRestriction::equals( $block->getRestrictions(), [
|
|
new PageRestriction( $block->getId(), $pageMars->getId() ),
|
|
new PageRestriction( $block->getId(), $pageSaturn->getId() ),
|
|
] ) );
|
|
|
|
// Remove a page from the partial block.
|
|
$data['PageRestrictions'] = $pageMars->getTitle()->getText();
|
|
$result = $page->processForm( $data, $context );
|
|
|
|
$this->assertTrue( $result );
|
|
|
|
$block = Block::newFromTarget( $badActor );
|
|
$this->assertSame( $reason, $block->mReason );
|
|
$this->assertSame( $expiry, $block->getExpiry() );
|
|
$this->assertFalse( $block->isSitewide() );
|
|
$this->assertCount( 1, $block->getRestrictions() );
|
|
$this->assertTrue( BlockRestriction::equals( $block->getRestrictions(), [
|
|
new PageRestriction( $block->getId(), $pageMars->getId() ),
|
|
] ) );
|
|
|
|
// Remove the last page from the block.
|
|
$data['PageRestrictions'] = '';
|
|
$result = $page->processForm( $data, $context );
|
|
|
|
$this->assertTrue( $result );
|
|
|
|
$block = Block::newFromTarget( $badActor );
|
|
$this->assertSame( $reason, $block->mReason );
|
|
$this->assertSame( $expiry, $block->getExpiry() );
|
|
$this->assertFalse( $block->isSitewide() );
|
|
$this->assertCount( 0, $block->getRestrictions() );
|
|
|
|
// Change to sitewide.
|
|
$data['EditingRestriction'] = 'sitewide';
|
|
$result = $page->processForm( $data, $context );
|
|
|
|
$this->assertTrue( $result );
|
|
|
|
$block = Block::newFromTarget( $badActor );
|
|
$this->assertSame( $reason, $block->mReason );
|
|
$this->assertSame( $expiry, $block->getExpiry() );
|
|
$this->assertTrue( $block->isSitewide() );
|
|
$this->assertCount( 0, $block->getRestrictions() );
|
|
|
|
// Ensure that there are no restrictions where the blockId is 0.
|
|
$count = $this->db->selectRowCount(
|
|
'ipblocks_restrictions',
|
|
'*',
|
|
[ 'ir_ipb_id' => 0 ],
|
|
__METHOD__
|
|
);
|
|
$this->assertSame( 0, $count );
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideCheckUnblockSelf
|
|
* @covers ::checkUnblockSelf
|
|
*/
|
|
public function testCheckUnblockSelf(
|
|
$blockedUser,
|
|
$blockPerformer,
|
|
$adjustPerformer,
|
|
$adjustTarget,
|
|
$expectedResult,
|
|
$reason
|
|
) {
|
|
$this->setMwGlobals( [
|
|
'wgBlockDisablesLogin' => false,
|
|
] );
|
|
$this->setGroupPermissions( 'sysop', 'unblockself', true );
|
|
$this->setGroupPermissions( 'user', 'block', true );
|
|
// Getting errors about creating users in db in provider.
|
|
// Need to use mutable to ensure different named testusers.
|
|
$users = [
|
|
'u1' => TestUserRegistry::getMutableTestUser( __CLASS__, 'sysop' )->getUser(),
|
|
'u2' => TestUserRegistry::getMutableTestUser( __CLASS__, 'sysop' )->getUser(),
|
|
'u3' => TestUserRegistry::getMutableTestUser( __CLASS__, 'sysop' )->getUser(),
|
|
'u4' => TestUserRegistry::getMutableTestUser( __CLASS__, 'sysop' )->getUser(),
|
|
'nonsysop' => $this->getTestUser()->getUser()
|
|
];
|
|
foreach ( [ 'blockedUser', 'blockPerformer', 'adjustPerformer', 'adjustTarget' ] as $var ) {
|
|
$$var = $users[$$var];
|
|
}
|
|
|
|
$block = new \Block( [
|
|
'address' => $blockedUser->getName(),
|
|
'user' => $blockedUser->getId(),
|
|
'by' => $blockPerformer->getId(),
|
|
'expiry' => 'infinity',
|
|
'sitewide' => 1,
|
|
'enableAutoblock' => true,
|
|
] );
|
|
|
|
$block->insert();
|
|
|
|
$this->assertSame(
|
|
SpecialBlock::checkUnblockSelf( $adjustTarget, $adjustPerformer ),
|
|
$expectedResult,
|
|
$reason
|
|
);
|
|
}
|
|
|
|
public function provideCheckUnblockSelf() {
|
|
// 'blockedUser', 'blockPerformer', 'adjustPerformer', 'adjustTarget'
|
|
return [
|
|
[ 'u1', 'u2', 'u3', 'u4', true, 'Unrelated users' ],
|
|
[ 'u1', 'u2', 'u1', 'u4', 'ipbblocked', 'Block unrelated while blocked' ],
|
|
[ 'u1', 'u2', 'u1', 'u1', true, 'Has unblockself' ],
|
|
[ 'nonsysop', 'u2', 'nonsysop', 'nonsysop', 'ipbnounblockself', 'no unblockself' ],
|
|
[ 'nonsysop', 'nonsysop', 'nonsysop', 'nonsysop', true, 'no unblockself but can de-selfblock' ],
|
|
[ 'u1', 'u2', 'u1', 'u2', true, 'Can block user who blocked' ],
|
|
];
|
|
}
|
|
|
|
protected function insertBlock() {
|
|
$badActor = $this->getTestUser()->getUser();
|
|
$sysop = $this->getTestSysop()->getUser();
|
|
|
|
$block = new \Block( [
|
|
'address' => $badActor->getName(),
|
|
'user' => $badActor->getId(),
|
|
'by' => $sysop->getId(),
|
|
'expiry' => 'infinity',
|
|
'sitewide' => 1,
|
|
'enableAutoblock' => true,
|
|
] );
|
|
|
|
$block->insert();
|
|
|
|
return $block;
|
|
}
|
|
|
|
protected function resetTables() {
|
|
$this->db->delete( 'ipblocks', '*', __METHOD__ );
|
|
$this->db->delete( 'ipblocks_restrictions', '*', __METHOD__ );
|
|
}
|
|
}
|