3 use MediaWiki\Block\Restriction\PageRestriction
;
9 class BlockTest
extends MediaWikiLangTestCase
{
14 private function getUserForBlocking() {
15 $testUser = $this->getMutableTestUser();
16 $user = $testUser->getUser();
17 $user->addToDatabase();
18 TestUser
::setPasswordForUser( $user, 'UTBlockeePassword' );
19 $user->saveSettings();
29 private function addBlockForUser( User
$user ) {
30 // Delete the last round's block if it's still there
31 $oldBlock = Block
::newFromTarget( $user->getName() );
33 // An old block will prevent our new one from saving.
38 'address' => $user->getName(),
39 'user' => $user->getId(),
40 'by' => $this->getTestSysop()->getUser()->getId(),
41 'reason' => 'Parce que',
42 'expiry' => time() +
100500,
44 $block = new Block( $blockOptions );
47 // save up ID for use in assertion. Since ID is an autoincrement,
48 // its value might change depending on the order the tests are run.
49 // ApiBlockTest insert its own blocks!
50 if ( !$block->getId() ) {
51 throw new MWException( "Failed to insert block for BlockTest; old leftover block remaining?" );
54 $this->addXffBlocks();
60 * @covers Block::newFromTarget
62 public function testINewFromTargetReturnsCorrectBlock() {
63 $user = $this->getUserForBlocking();
64 $block = $this->addBlockForUser( $user );
67 $block->equals( Block
::newFromTarget( $user->getName() ) ),
68 "newFromTarget() returns the same block as the one that was made"
73 * @covers Block::newFromID
75 public function testINewFromIDReturnsCorrectBlock() {
76 $user = $this->getUserForBlocking();
77 $block = $this->addBlockForUser( $user );
80 $block->equals( Block
::newFromID( $block->getId() ) ),
81 "newFromID() returns the same block as the one that was made"
87 * @covers Block::__construct
89 public function testT28425BlockTimestampDefaultsToTime() {
90 $user = $this->getUserForBlocking();
91 $block = $this->addBlockForUser( $user );
92 $madeAt = wfTimestamp( TS_MW
);
94 // delta to stop one-off errors when things happen to go over a second mark.
95 $delta = abs( $madeAt - $block->mTimestamp
);
96 $this->assertLessThan(
99 "If no timestamp is specified, the block is recorded as time()"
104 * CheckUser since being changed to use Block::newFromTarget started failing
105 * because the new function didn't accept empty strings like Block::load()
106 * had. Regression T31116.
108 * @dataProvider provideT31116Data
109 * @covers Block::newFromTarget
111 public function testT31116NewFromTargetWithEmptyIp( $vagueTarget ) {
112 $user = $this->getUserForBlocking();
113 $initialBlock = $this->addBlockForUser( $user );
114 $block = Block
::newFromTarget( $user->getName(), $vagueTarget );
117 $initialBlock->equals( $block ),
118 "newFromTarget() returns the same block as the one that was made when "
119 . "given empty vagueTarget param " . var_export( $vagueTarget, true )
123 public static function provideT31116Data() {
132 * @covers Block::prevents
134 public function testBlockedUserCanNotCreateAccount() {
135 $username = 'BlockedUserToCreateAccountWith';
136 $u = User
::newFromName( $username );
138 $userId = $u->getId();
139 $this->assertNotEquals( 0, $userId, 'sanity' );
140 TestUser
::setPasswordForUser( $u, 'NotRandomPass' );
145 Block
::newFromTarget( $username ),
146 "$username should not be blocked"
150 $u = User
::newFromName( $username );
152 $u->isBlockedFromCreateAccount(),
153 "Our sandbox user should be able to create account before being blocked"
156 // Foreign perspective (blockee not on current wiki)...
158 'address' => $username,
160 'reason' => 'crosswiki block...',
161 'timestamp' => wfTimestampNow(),
162 'expiry' => $this->db
->getInfinity(),
163 'createAccount' => true,
164 'enableAutoblock' => true,
166 'blockEmail' => true,
167 'byText' => 'm>MetaWikiUser',
169 $block = new Block( $blockOptions );
172 // Reload block from DB
173 $userBlock = Block
::newFromTarget( $username );
175 (bool)$block->prevents( 'createaccount' ),
176 "Block object in DB should prevents 'createaccount'"
179 $this->assertInstanceOf(
182 "'$username' block block object should be existent"
186 $u = User
::newFromName( $username );
188 (bool)$u->isBlockedFromCreateAccount(),
189 "Our sandbox user '$username' should NOT be able to create account"
194 * @covers Block::insert
196 public function testCrappyCrossWikiBlocks() {
197 // Delete the last round's block if it's still there
198 $oldBlock = Block
::newFromTarget( 'UserOnForeignWiki' );
200 // An old block will prevent our new one from saving.
204 // Local perspective (blockee on current wiki)...
205 $user = User
::newFromName( 'UserOnForeignWiki' );
206 $user->addToDatabase();
207 $userId = $user->getId();
208 $this->assertNotEquals( 0, $userId, 'sanity' );
210 // Foreign perspective (blockee not on current wiki)...
212 'address' => 'UserOnForeignWiki',
213 'user' => $user->getId(),
214 'reason' => 'crosswiki block...',
215 'timestamp' => wfTimestampNow(),
216 'expiry' => $this->db
->getInfinity(),
217 'createAccount' => true,
218 'enableAutoblock' => true,
220 'blockEmail' => true,
221 'byText' => 'Meta>MetaWikiUser',
223 $block = new Block( $blockOptions );
225 $res = $block->insert( $this->db
);
226 $this->assertTrue( (bool)$res['id'], 'Block succeeded' );
228 $user = null; // clear
230 $block = Block
::newFromID( $res['id'] );
233 $block->getTarget()->getName(),
234 'Correct blockee name'
236 $this->assertEquals( $userId, $block->getTarget()->getId(), 'Correct blockee id' );
237 $this->assertEquals( 'Meta>MetaWikiUser', $block->getBlocker()->getName(),
238 'Correct blocker name' );
239 $this->assertEquals( 'Meta>MetaWikiUser', $block->getByName(), 'Correct blocker name' );
240 $this->assertEquals( 0, $block->getBy(), 'Correct blocker id' );
243 protected function addXffBlocks() {
244 static $inited = false;
253 [ 'target' => '70.2.0.0/16',
254 'type' => Block
::TYPE_RANGE
,
255 'desc' => 'Range Hardblock',
256 'ACDisable' => false,
257 'isHardblock' => true,
258 'isAutoBlocking' => false,
260 [ 'target' => '2001:4860:4001::/48',
261 'type' => Block
::TYPE_RANGE
,
262 'desc' => 'Range6 Hardblock',
263 'ACDisable' => false,
264 'isHardblock' => true,
265 'isAutoBlocking' => false,
267 [ 'target' => '60.2.0.0/16',
268 'type' => Block
::TYPE_RANGE
,
269 'desc' => 'Range Softblock with AC Disabled',
271 'isHardblock' => false,
272 'isAutoBlocking' => false,
274 [ 'target' => '50.2.0.0/16',
275 'type' => Block
::TYPE_RANGE
,
276 'desc' => 'Range Softblock',
277 'ACDisable' => false,
278 'isHardblock' => false,
279 'isAutoBlocking' => false,
281 [ 'target' => '50.1.1.1',
282 'type' => Block
::TYPE_IP
,
283 'desc' => 'Exact Softblock',
284 'ACDisable' => false,
285 'isHardblock' => false,
286 'isAutoBlocking' => false,
290 $blocker = $this->getTestUser()->getUser();
291 foreach ( $blockList as $insBlock ) {
292 $target = $insBlock['target'];
294 if ( $insBlock['type'] === Block
::TYPE_IP
) {
295 $target = User
::newFromName( IP
::sanitizeIP( $target ), false )->getName();
296 } elseif ( $insBlock['type'] === Block
::TYPE_RANGE
) {
297 $target = IP
::sanitizeRange( $target );
300 $block = new Block();
301 $block->setTarget( $target );
302 $block->setBlocker( $blocker );
303 $block->mReason
= $insBlock['desc'];
304 $block->mExpiry
= 'infinity';
305 $block->prevents( 'createaccount', $insBlock['ACDisable'] );
306 $block->isHardblock( $insBlock['isHardblock'] );
307 $block->isAutoblocking( $insBlock['isAutoBlocking'] );
312 public static function providerXff() {
314 [ 'xff' => '1.2.3.4, 70.2.1.1, 60.2.1.1, 2.3.4.5',
316 'result' => 'Range Hardblock'
318 [ 'xff' => '1.2.3.4, 50.2.1.1, 60.2.1.1, 2.3.4.5',
320 'result' => 'Range Softblock with AC Disabled'
322 [ 'xff' => '1.2.3.4, 70.2.1.1, 50.1.1.1, 2.3.4.5',
324 'result' => 'Exact Softblock'
326 [ 'xff' => '1.2.3.4, 70.2.1.1, 50.2.1.1, 50.1.1.1, 2.3.4.5',
328 'result' => 'Exact Softblock'
330 [ 'xff' => '1.2.3.4, 70.2.1.1, 50.2.1.1, 2.3.4.5',
332 'result' => 'Range Hardblock'
334 [ 'xff' => '1.2.3.4, 70.2.1.1, 60.2.1.1, 2.3.4.5',
336 'result' => 'Range Hardblock'
338 [ 'xff' => '50.2.1.1, 60.2.1.1, 2.3.4.5',
340 'result' => 'Range Softblock with AC Disabled'
342 [ 'xff' => '1.2.3.4, 50.1.1.1, 60.2.1.1, 2.3.4.5',
344 'result' => 'Exact Softblock'
346 [ 'xff' => '1.2.3.4, <$A_BUNCH-OF{INVALID}TEXT\>, 60.2.1.1, 2.3.4.5',
348 'result' => 'Range Softblock with AC Disabled'
350 [ 'xff' => '1.2.3.4, 50.2.1.1, 2001:4860:4001:802::1003, 2.3.4.5',
352 'result' => 'Range6 Hardblock'
358 * @dataProvider providerXff
359 * @covers Block::getBlocksForIPList
360 * @covers Block::chooseBlock
362 public function testBlocksOnXff( $xff, $exCount, $exResult ) {
363 $user = $this->getUserForBlocking();
364 $this->addBlockForUser( $user );
366 $list = array_map( 'trim', explode( ',', $xff ) );
367 $xffblocks = Block
::getBlocksForIPList( $list, true );
368 $this->assertEquals( $exCount, count( $xffblocks ), 'Number of blocks for ' . $xff );
369 $block = Block
::chooseBlock( $xffblocks, $list );
370 $this->assertEquals( $exResult, $block->mReason
, 'Correct block type for XFF header ' . $xff );
374 * @covers Block::__construct
376 public function testDeprecatedConstructor() {
377 $this->hideDeprecated( 'Block::__construct with multiple arguments' );
378 $username = 'UnthinkablySecretRandomUsername';
379 $reason = 'being irrational';
382 $u = User
::newFromName( $username );
383 if ( $u->getId() == 0 ) {
385 TestUser
::setPasswordForUser( $u, 'TotallyObvious' );
389 # Make sure the user isn't blocked
391 Block
::newFromTarget( $username ),
392 "$username should not be blocked"
397 /* address */ $username,
399 /* by */ $this->getTestSysop()->getUser()->getId(),
400 /* reason */ $reason,
409 $block->getTarget()->getName(),
411 "Target should be set properly"
414 # Check supplied parameter
418 "Reason should be non-default"
421 # Check default parameter
423 (bool)$block->prevents( 'createaccount' ),
424 "Account creation should not be blocked by default"
429 * @covers Block::getSystemBlockType
430 * @covers Block::insert
431 * @covers Block::doAutoblock
433 public function testSystemBlocks() {
434 $user = $this->getUserForBlocking();
435 $this->addBlockForUser( $user );
438 'address' => $user->getName(),
439 'reason' => 'test system block',
440 'timestamp' => wfTimestampNow(),
441 'expiry' => $this->db
->getInfinity(),
442 'byText' => 'MediaWiki default',
443 'systemBlock' => 'test',
444 'enableAutoblock' => true,
446 $block = new Block( $blockOptions );
448 $this->assertSame( 'test', $block->getSystemBlockType() );
452 $this->fail( 'Expected exception not thrown' );
453 } catch ( MWException
$ex ) {
454 $this->assertSame( 'Cannot insert a system block into the database', $ex->getMessage() );
458 $block->doAutoblock( '192.0.2.2' );
459 $this->fail( 'Expected exception not thrown' );
460 } catch ( MWException
$ex ) {
461 $this->assertSame( 'Cannot autoblock from a system block', $ex->getMessage() );
466 * @covers Block::newFromRow
468 public function testNewFromRow() {
469 $badActor = $this->getTestUser()->getUser();
470 $sysop = $this->getTestSysop()->getUser();
472 $block = new Block( [
473 'address' => $badActor->getName(),
474 'user' => $badActor->getId(),
475 'by' => $sysop->getId(),
476 'expiry' => 'infinity',
480 $blockQuery = Block
::getQueryInfo();
481 $row = $this->db
->select(
482 $blockQuery['tables'],
483 $blockQuery['fields'],
485 'ipb_id' => $block->getId(),
492 $block = Block
::newFromRow( $row );
493 $this->assertInstanceOf( Block
::class, $block );
494 $this->assertEquals( $block->getBy(), $sysop->getId() );
495 $this->assertEquals( $block->getTarget()->getName(), $badActor->getName() );
500 * @covers Block::equals
502 public function testEquals() {
503 $block = new Block();
505 $this->assertTrue( $block->equals( $block ) );
507 $partial = new Block( [
510 $this->assertFalse( $block->equals( $partial ) );
514 * @covers Block::isSitewide
516 public function testIsSitewide() {
517 $block = new Block();
518 $this->assertTrue( $block->isSitewide() );
520 $block = new Block( [
523 $this->assertTrue( $block->isSitewide() );
525 $block = new Block( [
528 $this->assertFalse( $block->isSitewide() );
530 $block = new Block( [
533 $block->isSitewide( true );
534 $this->assertTrue( $block->isSitewide() );
538 * @covers Block::getRestrictions
539 * @covers Block::setRestrictions
541 public function testRestrictions() {
542 $block = new Block();
544 new PageRestriction( 0, 1 )
546 $block->setRestrictions( $restrictions );
548 $this->assertSame( $restrictions, $block->getRestrictions() );
552 * @covers Block::getRestrictions
553 * @covers Block::insert
555 public function testRestrictionsFromDatabase() {
556 $badActor = $this->getTestUser()->getUser();
557 $sysop = $this->getTestSysop()->getUser();
559 $block = new Block( [
560 'address' => $badActor->getName(),
561 'user' => $badActor->getId(),
562 'by' => $sysop->getId(),
563 'expiry' => 'infinity',
565 $page = $this->getExistingTestPage( 'Foo' );
566 $restriction = new PageRestriction( 0, $page->getId() );
567 $block->setRestrictions( [ $restriction ] );
570 // Refresh the block from the database.
571 $block = Block
::newFromID( $block->getId() );
572 $restrictions = $block->getRestrictions();
573 $this->assertCount( 1, $restrictions );
574 $this->assertTrue( $restriction->equals( $restrictions[0] ) );
579 * @covers Block::insert
581 public function testInsertExistingBlock() {
582 $badActor = $this->getTestUser()->getUser();
583 $sysop = $this->getTestSysop()->getUser();
585 $block = new Block( [
586 'address' => $badActor->getName(),
587 'user' => $badActor->getId(),
588 'by' => $sysop->getId(),
589 'expiry' => 'infinity',
591 $page = $this->getExistingTestPage( 'Foo' );
592 $restriction = new PageRestriction( 0, $page->getId() );
593 $block->setRestrictions( [ $restriction ] );
596 // Insert the block again, which should result in a failur
597 $result = $block->insert();
599 $this->assertFalse( $result );
601 // Ensure that there are no restrictions where the blockId is 0.
602 $count = $this->db
->selectRowCount(
603 'ipblocks_restrictions',
605 [ 'ir_ipb_id' => 0 ],
608 $this->assertSame( 0, $count );