wiki.techinc.nl/tests/phpunit/includes/block/DatabaseBlockTest.php

798 lines
23 KiB
PHP
Raw Normal View History

2010-12-28 19:12:27 +00:00
<?php
use MediaWiki\Block\BlockRestrictionStore;
use MediaWiki\Block\DatabaseBlock;
use MediaWiki\Block\Restriction\NamespaceRestriction;
use MediaWiki\Block\Restriction\PageRestriction;
use MediaWiki\MediaWikiServices;
use MediaWiki\User\UserIdentity;
use MediaWiki\User\UserIdentityValue;
use Wikimedia\IPUtils;
/**
* @group Database
* @group Blocking
* @coversDefaultClass \MediaWiki\Block\DatabaseBlock
*/
class DatabaseBlockTest extends MediaWikiLangTestCase {
/**
* @return UserIdentity
*/
private function getUserForBlocking() {
$testUser = $this->getMutableTestUser();
$user = $testUser->getUser();
$user->addToDatabase();
TestUser::setPasswordForUser( $user, 'UTBlockeePassword' );
$user->saveSettings();
return $testUser->getUserIdentity();
}
/**
* @param UserIdentity $user
*
* @return DatabaseBlock
* @throws MWException
*/
private function addBlockForUser( UserIdentity $user ) {
$blockStore = MediaWikiServices::getInstance()->getDatabaseBlockStore();
// Delete the last round's block if it's still there
$oldBlock = DatabaseBlock::newFromTarget( $user );
if ( $oldBlock ) {
// An old block will prevent our new one from saving.
$blockStore->deleteBlock( $oldBlock );
}
$blockOptions = [
'address' => $user->getName(),
'user' => $user->getId(),
'by' => $this->getTestSysop()->getUser(),
'reason' => 'Parce que',
'expiry' => time() + 100500,
];
$block = new DatabaseBlock( $blockOptions );
2010-12-28 19:12:27 +00:00
$blockStore->insertBlock( $block );
// save up ID for use in assertion. Since ID is an autoincrement,
// its value might change depending on the order the tests are run.
// ApiBlockTest insert its own blocks!
if ( !$block->getId() ) {
throw new MWException( "Failed to insert block for BlockTest; old leftover block remaining?" );
}
$this->addXffBlocks();
return $block;
}
/**
* @covers ::newFromTarget
*/
public function testINewFromTargetReturnsCorrectBlock() {
$user = $this->getUserForBlocking();
$block = $this->addBlockForUser( $user );
$this->assertTrue(
$block->equals( DatabaseBlock::newFromTarget( $user->getName() ) ),
"newFromTarget() returns the same block as the one that was made"
);
}
/**
* @covers ::newFromID
*/
public function testINewFromIDReturnsCorrectBlock() {
$user = $this->getUserForBlocking();
$block = $this->addBlockForUser( $user );
$this->assertTrue(
$block->equals( DatabaseBlock::newFromID( $block->getId() ) ),
"newFromID() returns the same block as the one that was made"
);
2010-12-28 19:12:27 +00:00
}
2011-06-26 20:04:38 +00:00
/**
* per T28425
* @covers ::__construct
*/
public function testT28425BlockTimestampDefaultsToTime() {
$user = $this->getUserForBlocking();
$block = $this->addBlockForUser( $user );
$madeAt = wfTimestamp( TS_MW );
// delta to stop one-off errors when things happen to go over a second mark.
$delta = abs( $madeAt - $block->getTimestamp() );
$this->assertLessThan(
2,
$delta,
"If no timestamp is specified, the block is recorded as time()"
);
}
2010-12-28 19:12:27 +00:00
/**
* CheckUser since being changed to use DatabaseBlock::newFromTarget started failing
* because the new function didn't accept empty strings like DatabaseBlock::load()
* had. Regression T31116.
*
* @dataProvider provideT31116Data
* @covers ::newFromTarget
*/
public function testT31116NewFromTargetWithEmptyIp( $vagueTarget ) {
$user = $this->getUserForBlocking();
$initialBlock = $this->addBlockForUser( $user );
$block = DatabaseBlock::newFromTarget( $user->getName(), $vagueTarget );
$this->assertTrue(
$initialBlock->equals( $block ),
"newFromTarget() returns the same block as the one that was made when "
. "given empty vagueTarget param " . var_export( $vagueTarget, true )
);
}
public static function provideT31116Data() {
return [
[ null ],
[ '' ],
[ false ]
];
}
/**
* @dataProvider provideNewFromTargetRangeBlocks
* @covers ::newFromTarget
*/
public function testNewFromTargetRangeBlocks( $targets, $ip, $expectedTarget ) {
$blockStore = MediaWikiServices::getInstance()->getDatabaseBlockStore();
$blocker = $this->getTestSysop()->getUser();
foreach ( $targets as $target ) {
$block = new DatabaseBlock();
$block->setTarget( $target );
$block->setBlocker( $blocker );
$blockStore->insertBlock( $block );
}
// Should find the block with the narrowest range
$blockTarget = DatabaseBlock::newFromTarget( $this->getTestUser()->getUserIdentity(), $ip )->getTarget();
$this->assertSame(
$blockTarget instanceof User ? $blockTarget->getName() : $blockTarget,
$expectedTarget
);
foreach ( $targets as $target ) {
$block = DatabaseBlock::newFromTarget( $target );
$blockStore->deleteBlock( $block );
}
}
public function provideNewFromTargetRangeBlocks() {
return [
'Blocks to IPv4 ranges' => [
[ '0.0.0.0/20', '0.0.0.0/30', '0.0.0.0/25' ],
'0.0.0.0',
'0.0.0.0/30'
],
'Blocks to IPv6 ranges' => [
[ '0:0:0:0:0:0:0:0/20', '0:0:0:0:0:0:0:0/30', '0:0:0:0:0:0:0:0/25' ],
'0:0:0:0:0:0:0:0',
'0:0:0:0:0:0:0:0/30'
],
'Blocks to wide IPv4 range and IP' => [
[ '0.0.0.0/16', '0.0.0.0' ],
'0.0.0.0',
'0.0.0.0'
],
'Blocks to narrow IPv4 range and IP' => [
[ '0.0.0.0/31', '0.0.0.0' ],
'0.0.0.0',
'0.0.0.0'
],
'Blocks to wide IPv6 range and IP' => [
[ '0:0:0:0:0:0:0:0/19', '0:0:0:0:0:0:0:0' ],
'0:0:0:0:0:0:0:0',
'0:0:0:0:0:0:0:0'
],
'Blocks to narrow IPv6 range and IP' => [
[ '0:0:0:0:0:0:0:0/127', '0:0:0:0:0:0:0:0' ],
'0:0:0:0:0:0:0:0',
'0:0:0:0:0:0:0:0'
],
'Blocks to wide IPv6 range and IP, large numbers' => [
[ '2000:DEAD:BEEF:A:0:0:0:0/19', '2000:DEAD:BEEF:A:0:0:0:0' ],
'2000:DEAD:BEEF:A:0:0:0:0',
'2000:DEAD:BEEF:A:0:0:0:0'
],
'Blocks to narrow IPv6 range and IP, large numbers' => [
[ '2000:DEAD:BEEF:A:0:0:0:0/127', '2000:DEAD:BEEF:A:0:0:0:0' ],
'2000:DEAD:BEEF:A:0:0:0:0',
'2000:DEAD:BEEF:A:0:0:0:0'
],
];
}
/**
* @covers ::appliesToRight
*/
public function testBlockedUserCanNotCreateAccount() {
$username = 'BlockedUserToCreateAccountWith';
$u = User::newFromName( $username );
$u->addToDatabase();
$userId = $u->getId();
$this->assertNotEquals( 0, $userId, 'sanity' );
TestUser::setPasswordForUser( $u, 'NotRandomPass' );
unset( $u );
// Sanity check
$this->assertNull(
DatabaseBlock::newFromTarget( $username ),
"$username should not be blocked"
);
// Reload user
$u = User::newFromName( $username );
$this->assertFalse(
$u->isBlockedFromCreateAccount(),
"Our sandbox user should be able to create account before being blocked"
);
// Foreign perspective (blockee not on current wiki)...
$blockOptions = [
'address' => $username,
'user' => $userId,
'reason' => 'crosswiki block...',
'timestamp' => wfTimestampNow(),
'expiry' => $this->db->getInfinity(),
'createAccount' => true,
'enableAutoblock' => true,
'hideName' => true,
'blockEmail' => true,
'by' => UserIdentityValue::newExternal( 'm', 'MetaWikiUser' ),
];
$block = new DatabaseBlock( $blockOptions );
MediaWikiServices::getInstance()->getDatabaseBlockStore()->insertBlock( $block );
// Reload block from DB
$userBlock = DatabaseBlock::newFromTarget( $username );
$this->assertTrue(
Separate out different functionalities of Block::prevents 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
2019-02-09 12:17:54 +00:00
(bool)$block->appliesToRight( 'createaccount' ),
"Block object in DB should block right 'createaccount'"
);
$this->assertInstanceOf(
DatabaseBlock::class,
$userBlock,
"'$username' block block object should be existent"
);
// Reload user
$u = User::newFromName( $username );
$this->assertTrue(
(bool)$u->isBlockedFromCreateAccount(),
"Our sandbox user '$username' should NOT be able to create account"
);
}
/**
* TODO: Move to DatabaseBlockStoreTest
*
* @covers ::insert
*/
public function testCrappyCrossWikiBlocks() {
$blockStore = MediaWikiServices::getInstance()->getDatabaseBlockStore();
// Delete the last round's block if it's still there
$oldBlock = DatabaseBlock::newFromTarget( 'UserOnForeignWiki' );
if ( $oldBlock ) {
// An old block will prevent our new one from saving.
$blockStore->deleteBlock( $oldBlock );
}
// Local perspective (blockee on current wiki)...
$user = User::newFromName( 'UserOnForeignWiki' );
$user->addToDatabase();
$userId = $user->getId();
$this->assertNotEquals( 0, $userId, 'sanity' );
// Foreign perspective (blockee not on current wiki)...
$blockOptions = [
'address' => 'UserOnForeignWiki',
'user' => $user->getId(),
'reason' => 'crosswiki block...',
'timestamp' => wfTimestampNow(),
'expiry' => $this->db->getInfinity(),
'createAccount' => true,
'enableAutoblock' => true,
'hideName' => true,
'blockEmail' => true,
'by' => UserIdentityValue::newExternal( 'm', 'MetaWikiUser' ),
];
$block = new DatabaseBlock( $blockOptions );
$res = $blockStore->insertBlock( $block, $this->db );
$this->assertTrue( (bool)$res['id'], 'Block succeeded' );
$user = null; // clear
$block = DatabaseBlock::newFromID( $res['id'] );
$this->assertEquals(
'UserOnForeignWiki',
$block->getTarget()->getName(),
'Correct blockee name'
);
$this->assertEquals(
'UserOnForeignWiki',
$block->getTargetUserIdentity()->getName(),
'Correct blockee name'
);
$this->assertEquals( $userId, $block->getTarget()->getId(), 'Correct blockee id' );
$this->assertEquals( $userId, $block->getTargetUserIdentity()->getId(), 'Correct blockee id' );
$this->assertEquals( 'UserOnForeignWiki', $block->getTargetName(), 'Correct blockee name' );
$this->assertTrue( $block->isBlocking( 'UserOnForeignWiki' ), 'Is blocking blockee' );
$this->assertEquals( 'm>MetaWikiUser', $block->getBlocker()->getName(),
Avoid DB rows with usable names but ID = 0 by introducing "interwiki" usernames Importing revisions in MediaWiki has long been weird: if the username on the imported revision exists locally it's automatically attributed to the local user, while if the name does not exist locally we wind up with revision table rows with rev_user = 0 and rev_user_text being a valid name that someone might later create. "Global" blocks too create rows with ipb_by = 0 an ipb_by_text being a valid name. The upcoming actor table change, as things currently stand, would regularize that a bit by automatically attributing those imported revisions to the newly-created user. But that's not necessarily what we actually want to happen. And it would certainly confuse CentralAuth's attempt to detect its own global blocks. Thus, this patch introduces "interwiki" usernames that aren't valid for local use, of the format "iw>Example".[1] Linker will interpret these names and generate an appropriate interwiki link in history pages and the like, as if from wikitext like `[[iw:User:Example]]`. Imports for non-existant local users (and optionally for existing local users too) will credit the edit to such an interwiki name. There is also a new hook, 'ImportHandleUnknownUser', to allow extension such as CentralAuth to create local users as their edits are imported. Block will no longer accept usable-but-nonexistent names for 'byText' or ->setBlocker(). CentralAuth's global blocks will be submitted with an interwiki username (see Ieae5d24f9). Wikis that have imported edits or CentralAuth global blocks should run the new maintenance/cleanupUsersWithNoId.php maintenance script. This isn't done by update.php because (1) it needs an interwiki prefix to use and (2) the updater can't know whether to pass the `--assign` flag. [1]: '>' was used instead of the more usual ':' because WMF wikis have many existing usernames containing colons. Bug: T9240 Bug: T20209 Bug: T111605 Change-Id: I5401941c06102e8faa813910519d55482dff36cb Depends-On: Ieae5d24f9098c1977447c50a8d4e2cab58a24d9f
2017-10-25 19:26:53 +00:00
'Correct blocker name' );
$this->assertEquals( 'm>MetaWikiUser', $block->getByName(), 'Correct blocker name' );
$this->assertSame( 0, $block->getBy(), 'Correct blocker id' );
}
protected function addXffBlocks() {
static $inited = false;
if ( $inited ) {
return;
}
$inited = true;
$blockList = [
[ 'target' => '70.2.0.0/16',
'type' => DatabaseBlock::TYPE_RANGE,
'desc' => 'Range Hardblock',
'ACDisable' => false,
'isHardblock' => true,
'isAutoBlocking' => false,
],
[ 'target' => '2001:4860:4001::/48',
'type' => DatabaseBlock::TYPE_RANGE,
'desc' => 'Range6 Hardblock',
'ACDisable' => false,
'isHardblock' => true,
'isAutoBlocking' => false,
],
[ 'target' => '60.2.0.0/16',
'type' => DatabaseBlock::TYPE_RANGE,
'desc' => 'Range Softblock with AC Disabled',
'ACDisable' => true,
'isHardblock' => false,
'isAutoBlocking' => false,
],
[ 'target' => '50.2.0.0/16',
'type' => DatabaseBlock::TYPE_RANGE,
'desc' => 'Range Softblock',
'ACDisable' => false,
'isHardblock' => false,
'isAutoBlocking' => false,
],
[ 'target' => '50.1.1.1',
'type' => DatabaseBlock::TYPE_IP,
'desc' => 'Exact Softblock',
'ACDisable' => false,
'isHardblock' => false,
'isAutoBlocking' => false,
],
];
$blockStore = MediaWikiServices::getInstance()->getDatabaseBlockStore();
Avoid DB rows with usable names but ID = 0 by introducing "interwiki" usernames Importing revisions in MediaWiki has long been weird: if the username on the imported revision exists locally it's automatically attributed to the local user, while if the name does not exist locally we wind up with revision table rows with rev_user = 0 and rev_user_text being a valid name that someone might later create. "Global" blocks too create rows with ipb_by = 0 an ipb_by_text being a valid name. The upcoming actor table change, as things currently stand, would regularize that a bit by automatically attributing those imported revisions to the newly-created user. But that's not necessarily what we actually want to happen. And it would certainly confuse CentralAuth's attempt to detect its own global blocks. Thus, this patch introduces "interwiki" usernames that aren't valid for local use, of the format "iw>Example".[1] Linker will interpret these names and generate an appropriate interwiki link in history pages and the like, as if from wikitext like `[[iw:User:Example]]`. Imports for non-existant local users (and optionally for existing local users too) will credit the edit to such an interwiki name. There is also a new hook, 'ImportHandleUnknownUser', to allow extension such as CentralAuth to create local users as their edits are imported. Block will no longer accept usable-but-nonexistent names for 'byText' or ->setBlocker(). CentralAuth's global blocks will be submitted with an interwiki username (see Ieae5d24f9). Wikis that have imported edits or CentralAuth global blocks should run the new maintenance/cleanupUsersWithNoId.php maintenance script. This isn't done by update.php because (1) it needs an interwiki prefix to use and (2) the updater can't know whether to pass the `--assign` flag. [1]: '>' was used instead of the more usual ':' because WMF wikis have many existing usernames containing colons. Bug: T9240 Bug: T20209 Bug: T111605 Change-Id: I5401941c06102e8faa813910519d55482dff36cb Depends-On: Ieae5d24f9098c1977447c50a8d4e2cab58a24d9f
2017-10-25 19:26:53 +00:00
$blocker = $this->getTestUser()->getUser();
foreach ( $blockList as $insBlock ) {
$target = $insBlock['target'];
if ( $insBlock['type'] === DatabaseBlock::TYPE_IP ) {
$target = User::newFromName( IPUtils::sanitizeIP( $target ), false )->getName();
} elseif ( $insBlock['type'] === DatabaseBlock::TYPE_RANGE ) {
$target = IPUtils::sanitizeRange( $target );
}
$block = new DatabaseBlock();
$block->setTarget( $target );
Avoid DB rows with usable names but ID = 0 by introducing "interwiki" usernames Importing revisions in MediaWiki has long been weird: if the username on the imported revision exists locally it's automatically attributed to the local user, while if the name does not exist locally we wind up with revision table rows with rev_user = 0 and rev_user_text being a valid name that someone might later create. "Global" blocks too create rows with ipb_by = 0 an ipb_by_text being a valid name. The upcoming actor table change, as things currently stand, would regularize that a bit by automatically attributing those imported revisions to the newly-created user. But that's not necessarily what we actually want to happen. And it would certainly confuse CentralAuth's attempt to detect its own global blocks. Thus, this patch introduces "interwiki" usernames that aren't valid for local use, of the format "iw>Example".[1] Linker will interpret these names and generate an appropriate interwiki link in history pages and the like, as if from wikitext like `[[iw:User:Example]]`. Imports for non-existant local users (and optionally for existing local users too) will credit the edit to such an interwiki name. There is also a new hook, 'ImportHandleUnknownUser', to allow extension such as CentralAuth to create local users as their edits are imported. Block will no longer accept usable-but-nonexistent names for 'byText' or ->setBlocker(). CentralAuth's global blocks will be submitted with an interwiki username (see Ieae5d24f9). Wikis that have imported edits or CentralAuth global blocks should run the new maintenance/cleanupUsersWithNoId.php maintenance script. This isn't done by update.php because (1) it needs an interwiki prefix to use and (2) the updater can't know whether to pass the `--assign` flag. [1]: '>' was used instead of the more usual ':' because WMF wikis have many existing usernames containing colons. Bug: T9240 Bug: T20209 Bug: T111605 Change-Id: I5401941c06102e8faa813910519d55482dff36cb Depends-On: Ieae5d24f9098c1977447c50a8d4e2cab58a24d9f
2017-10-25 19:26:53 +00:00
$block->setBlocker( $blocker );
$block->setReason( $insBlock['desc'] );
$block->setExpiry( 'infinity' );
Separate out different functionalities of Block::prevents 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
2019-02-09 12:17:54 +00:00
$block->isCreateAccountBlocked( $insBlock['ACDisable'] );
$block->isHardblock( $insBlock['isHardblock'] );
$block->isAutoblocking( $insBlock['isAutoBlocking'] );
$blockStore->insertBlock( $block );
}
}
public static function providerXff() {
return [
[ 'xff' => '1.2.3.4, 70.2.1.1, 60.2.1.1, 2.3.4.5',
'count' => 2,
'result' => 'Range Hardblock'
],
[ 'xff' => '1.2.3.4, 50.2.1.1, 60.2.1.1, 2.3.4.5',
'count' => 2,
'result' => 'Range Softblock with AC Disabled'
],
[ 'xff' => '1.2.3.4, 70.2.1.1, 50.1.1.1, 2.3.4.5',
'count' => 2,
'result' => 'Exact Softblock'
],
[ 'xff' => '1.2.3.4, 70.2.1.1, 50.2.1.1, 50.1.1.1, 2.3.4.5',
'count' => 3,
'result' => 'Exact Softblock'
],
[ 'xff' => '1.2.3.4, 70.2.1.1, 50.2.1.1, 2.3.4.5',
'count' => 2,
'result' => 'Range Hardblock'
],
[ 'xff' => '1.2.3.4, 70.2.1.1, 60.2.1.1, 2.3.4.5',
'count' => 2,
'result' => 'Range Hardblock'
],
[ 'xff' => '50.2.1.1, 60.2.1.1, 2.3.4.5',
'count' => 2,
'result' => 'Range Softblock with AC Disabled'
],
[ 'xff' => '1.2.3.4, 50.1.1.1, 60.2.1.1, 2.3.4.5',
'count' => 2,
'result' => 'Exact Softblock'
],
[ 'xff' => '1.2.3.4, <$A_BUNCH-OF{INVALID}TEXT\>, 60.2.1.1, 2.3.4.5',
'count' => 1,
'result' => 'Range Softblock with AC Disabled'
],
[ 'xff' => '1.2.3.4, 50.2.1.1, 2001:4860:4001:802::1003, 2.3.4.5',
'count' => 2,
'result' => 'Range6 Hardblock'
],
];
}
/**
* @dataProvider providerXff
* @covers ::getBlocksForIPList
*/
public function testBlocksOnXff( $xff, $exCount, $exResult ) {
$user = $this->getUserForBlocking();
$this->addBlockForUser( $user );
$list = array_map( 'trim', explode( ',', $xff ) );
$xffblocks = DatabaseBlock::getBlocksForIPList( $list, true );
$this->assertCount( $exCount, $xffblocks, 'Number of blocks for ' . $xff );
}
/**
* @covers ::newFromRow
*/
public function testNewFromRow() {
$badActor = $this->getTestUser()->getUser();
$sysop = $this->getTestSysop()->getUser();
$block = new DatabaseBlock( [
'address' => $badActor->getName(),
'user' => $badActor->getId(),
'by' => $sysop,
'expiry' => 'infinity',
] );
$blockStore = MediaWikiServices::getInstance()->getDatabaseBlockStore();
$blockStore->insertBlock( $block );
$blockQuery = DatabaseBlock::getQueryInfo();
$row = $this->db->select(
$blockQuery['tables'],
$blockQuery['fields'],
[
'ipb_id' => $block->getId(),
],
__METHOD__,
[],
$blockQuery['joins']
)->fetchObject();
$block = DatabaseBlock::newFromRow( $row );
$this->assertInstanceOf( DatabaseBlock::class, $block );
$this->assertEquals( $block->getBy(), $sysop->getId() );
$this->assertEquals( $block->getTarget()->getName(), $badActor->getName() );
$this->assertEquals( $block->getTargetName(), $badActor->getName() );
$this->assertTrue( $block->isBlocking( $badActor ), 'Is blocking expected user' );
$this->assertEquals( $block->getTargetUserIdentity()->getId(), $badActor->getId() );
$blockStore->deleteBlock( $block );
}
/**
* @covers ::equals
*/
public function testEquals() {
$block = new DatabaseBlock();
$this->assertTrue( $block->equals( $block ) );
$partial = new DatabaseBlock( [
'sitewide' => false,
] );
$this->assertFalse( $block->equals( $partial ) );
}
/**
* @covers ::isSitewide
*/
public function testIsSitewide() {
$block = new DatabaseBlock();
$this->assertTrue( $block->isSitewide() );
$block = new DatabaseBlock( [
'sitewide' => true,
] );
$this->assertTrue( $block->isSitewide() );
$block = new DatabaseBlock( [
'sitewide' => false,
] );
$this->assertFalse( $block->isSitewide() );
$block = new DatabaseBlock( [
'sitewide' => false,
] );
$block->isSitewide( true );
$this->assertTrue( $block->isSitewide() );
}
/**
* @covers ::getRestrictions
* @covers ::setRestrictions
*/
public function testRestrictions() {
$block = new DatabaseBlock();
$restrictions = [
new PageRestriction( 0, 1 )
];
$block->setRestrictions( $restrictions );
$this->assertSame( $restrictions, $block->getRestrictions() );
}
/**
* @covers ::getRestrictions
* @covers ::insert
*/
public function testRestrictionsFromDatabase() {
$blockStore = MediaWikiServices::getInstance()->getDatabaseBlockStore();
$badActor = $this->getTestUser()->getUser();
$sysop = $this->getTestSysop()->getUser();
$block = new DatabaseBlock( [
'address' => $badActor->getName(),
'user' => $badActor->getId(),
'by' => $sysop,
'expiry' => 'infinity',
] );
$page = $this->getExistingTestPage( 'Foo' );
$restriction = new PageRestriction( 0, $page->getId() );
$block->setRestrictions( [ $restriction ] );
$blockStore->insertBlock( $block );
// Refresh the block from the database.
$block = DatabaseBlock::newFromID( $block->getId() );
$restrictions = $block->getRestrictions();
$this->assertCount( 1, $restrictions );
$this->assertTrue( $restriction->equals( $restrictions[0] ) );
$blockStore->deleteBlock( $block );
}
/**
* TODO: Move to DatabaseBlockStoreTest
*
* @covers ::insert
*/
public function testInsertExistingBlock() {
$blockStore = MediaWikiServices::getInstance()->getDatabaseBlockStore();
$badActor = $this->getTestUser()->getUser();
$sysop = $this->getTestSysop()->getUser();
$block = new DatabaseBlock( [
'address' => $badActor->getName(),
'user' => $badActor->getId(),
'by' => $sysop,
'expiry' => 'infinity',
] );
$page = $this->getExistingTestPage( 'Foo' );
$restriction = new PageRestriction( 0, $page->getId() );
$block->setRestrictions( [ $restriction ] );
$blockStore->insertBlock( $block );
// Insert the block again, which should result in a failure
$result = $block->insert();
$this->assertFalse( $result );
// 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 );
$blockStore->deleteBlock( $block );
}
/**
* @covers ::appliesToTitle
*/
public function testAppliesToTitleReturnsTrueOnSitewideBlock() {
$this->setMwGlobals( [
'wgBlockDisablesLogin' => false,
] );
$user = $this->getTestUser()->getUser();
$block = new DatabaseBlock( [
'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
'allowUsertalk' => true,
'sitewide' => true
] );
$block->setTarget( new UserIdentityValue( $user->getId(), $user->getName() ) );
$block->setBlocker( $this->getTestSysop()->getUser() );
$blockStore = MediaWikiServices::getInstance()->getDatabaseBlockStore();
$blockStore->insertBlock( $block );
$title = $this->getExistingTestPage( 'Foo' )->getTitle();
$this->assertTrue( $block->appliesToTitle( $title ) );
// appliesToTitle() ignores allowUsertalk
$title = $user->getTalkPage();
$this->assertTrue( $block->appliesToTitle( $title ) );
$blockStore->deleteBlock( $block );
}
/**
* @covers ::appliesToTitle
*/
public function testAppliesToTitleOnPartialBlock() {
$this->setMwGlobals( [
'wgBlockDisablesLogin' => false,
] );
$user = $this->getTestUser()->getUser();
$block = new DatabaseBlock( [
'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
'allowUsertalk' => true,
'sitewide' => false
] );
$block->setTarget( $user );
$block->setBlocker( $this->getTestSysop()->getUser() );
$blockStore = MediaWikiServices::getInstance()->getDatabaseBlockStore();
$blockStore->insertBlock( $block );
$pageFoo = $this->getExistingTestPage( 'Foo' );
$pageBar = $this->getExistingTestPage( 'Bar' );
$pageJohn = $this->getExistingTestPage( 'User:John' );
$pageRestriction = new PageRestriction( $block->getId(), $pageFoo->getId() );
$namespaceRestriction = new NamespaceRestriction( $block->getId(), NS_USER );
$this->getBlockRestrictionStore()->insert( [ $pageRestriction, $namespaceRestriction ] );
$this->assertTrue( $block->appliesToTitle( $pageFoo->getTitle() ) );
$this->assertFalse( $block->appliesToTitle( $pageBar->getTitle() ) );
$this->assertTrue( $block->appliesToTitle( $pageJohn->getTitle() ) );
$blockStore->deleteBlock( $block );
}
/**
* @covers ::appliesToNamespace
* @covers ::appliesToPage
*/
public function testAppliesToReturnsTrueOnSitewideBlock() {
$this->setMwGlobals( [
'wgBlockDisablesLogin' => false,
] );
$user = $this->getTestUser()->getUser();
$block = new DatabaseBlock( [
'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
'allowUsertalk' => true,
'sitewide' => true
] );
$block->setTarget( $user );
$block->setBlocker( $this->getTestSysop()->getUser() );
$blockStore = MediaWikiServices::getInstance()->getDatabaseBlockStore();
$blockStore->insertBlock( $block );
$title = $this->getExistingTestPage()->getTitle();
$this->assertTrue( $block->appliesToPage( $title->getArticleID() ) );
$this->assertTrue( $block->appliesToNamespace( NS_MAIN ) );
$this->assertTrue( $block->appliesToNamespace( NS_USER_TALK ) );
$blockStore->deleteBlock( $block );
}
/**
* @covers ::appliesToPage
*/
public function testAppliesToPageOnPartialPageBlock() {
$this->setMwGlobals( [
'wgBlockDisablesLogin' => false,
] );
$user = $this->getTestUser()->getUser();
$block = new DatabaseBlock( [
'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
'allowUsertalk' => true,
'sitewide' => false
] );
$block->setTarget( $user );
$block->setBlocker( $this->getTestSysop()->getUser() );
$blockStore = MediaWikiServices::getInstance()->getDatabaseBlockStore();
$blockStore->insertBlock( $block );
$title = $this->getExistingTestPage()->getTitle();
$pageRestriction = new PageRestriction(
$block->getId(),
$title->getArticleID()
);
$this->getBlockRestrictionStore()->insert( [ $pageRestriction ] );
$this->assertTrue( $block->appliesToPage( $title->getArticleID() ) );
$blockStore->deleteBlock( $block );
}
/**
* @covers ::appliesToNamespace
*/
public function testAppliesToNamespaceOnPartialNamespaceBlock() {
$this->setMwGlobals( [
'wgBlockDisablesLogin' => false,
] );
$user = $this->getTestUser()->getUser();
$block = new DatabaseBlock( [
'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
'allowUsertalk' => true,
'sitewide' => false
] );
$block->setTarget( $user );
$block->setBlocker( $this->getTestSysop()->getUser() );
$blockStore = MediaWikiServices::getInstance()->getDatabaseBlockStore();
$blockStore->insertBlock( $block );
$namespaceRestriction = new NamespaceRestriction( $block->getId(), NS_MAIN );
$this->getBlockRestrictionStore()->insert( [ $namespaceRestriction ] );
$this->assertTrue( $block->appliesToNamespace( NS_MAIN ) );
$this->assertFalse( $block->appliesToNamespace( NS_USER ) );
$blockStore->deleteBlock( $block );
}
/**
* @covers ::appliesToRight
*/
public function testBlockAllowsPurge() {
$this->setMwGlobals( [
'wgBlockDisablesLogin' => false,
] );
$block = new DatabaseBlock();
Separate out different functionalities of Block::prevents 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
2019-02-09 12:17:54 +00:00
$this->assertFalse( $block->appliesToRight( 'purge' ) );
}
/**
* Get an instance of BlockRestrictionStore
*
* @return BlockRestrictionStore
*/
protected function getBlockRestrictionStore() : BlockRestrictionStore {
return MediaWikiServices::getInstance()->getBlockRestrictionStore();
}
2010-12-28 19:12:27 +00:00
}