}
/**
- * Bug 37963
+ * T39963
* Make sure defaults are loaded when setOption is called.
* @covers User::loadOptions
*/
$this->setMwGlobals( [
'wgCookieSetOnAutoblock' => true,
'wgCookiePrefix' => 'wmsitetitle',
+ 'wgSecretKey' => MWCryptRand::generateHex( 64, true ),
] );
// 1. Log in a test user, and block them.
// Test for the desired cookie name, value, and expiry.
$cookies = $request1->response()->getCookies();
$this->assertArrayHasKey( 'wmsitetitleBlockID', $cookies );
- $this->assertEquals( $block->getId(), $cookies['wmsitetitleBlockID']['value'] );
$this->assertEquals( $expiryFiveHours, $cookies['wmsitetitleBlockID']['expire'] );
+ $cookieValue = Block::getIdFromCookieValue( $cookies['wmsitetitleBlockID']['value'] );
+ $this->assertEquals( $block->getId(), $cookieValue );
// 2. Create a new request, set the cookies, and see if the (anon) user is blocked.
$request2 = new FauxRequest();
- $request2->setCookie( 'BlockID', $block->getId() );
+ $request2->setCookie( 'BlockID', $block->getCookieValue() );
$user2 = User::newFromSession( $request2 );
$user2->load();
$this->assertNotEquals( $user1->getId(), $user2->getId() );
$this->setMwGlobals( [
'wgCookieSetOnAutoblock' => false,
'wgCookiePrefix' => 'wm_no_cookies',
+ 'wgSecretKey' => MWCryptRand::generateHex( 64, true ),
] );
// 1. Log in a test user, and block them.
$this->setMwGlobals( [
'wgCookieSetOnAutoblock' => true,
'wgCookiePrefix' => 'wm_infinite_block',
+ 'wgSecretKey' => MWCryptRand::generateHex( 64, true ),
] );
// 1. Log in a test user, and block them indefinitely.
$user1Tmp = $this->getTestUser()->getUser();
$this->assertNull( $wgUser->getBlock() );
}
+ /**
+ * Test that a modified BlockID cookie doesn't actually load the relevant block (T152951).
+ */
+ public function testAutoblockCookieInauthentic() {
+ // Set up the bits of global configuration that we use.
+ $this->setMwGlobals( [
+ 'wgCookieSetOnAutoblock' => true,
+ 'wgCookiePrefix' => 'wmsitetitle',
+ 'wgSecretKey' => MWCryptRand::generateHex( 64, true ),
+ ] );
+
+ // 1. Log in a blocked test user.
+ $user1tmp = $this->getTestUser()->getUser();
+ $request1 = new FauxRequest();
+ $request1->getSession()->setUser( $user1tmp );
+ $block = new Block( [ 'enableAutoblock' => true ] );
+ $block->setTarget( $user1tmp );
+ $block->insert();
+ $user1 = User::newFromSession( $request1 );
+ $user1->mBlock = $block;
+ $user1->load();
+
+ // 2. Create a new request, set the cookie to an invalid value, and make sure the (anon)
+ // user not blocked.
+ $request2 = new FauxRequest();
+ $request2->setCookie( 'BlockID', $block->getId() . '!zzzzzzz' );
+ $user2 = User::newFromSession( $request2 );
+ $user2->load();
+ $this->assertTrue( $user2->isAnon() );
+ $this->assertFalse( $user2->isLoggedIn() );
+ $this->assertFalse( $user2->isBlocked() );
+
+ // Clean up.
+ $block->delete();
+ }
+
+ /**
+ * The BlockID cookie is normally verified with a HMAC, but not if wgSecretKey is not set.
+ * This checks that a non-authenticated cookie still works.
+ */
+ public function testAutoblockCookieNoSecretKey() {
+ // Set up the bits of global configuration that we use.
+ $this->setMwGlobals( [
+ 'wgCookieSetOnAutoblock' => true,
+ 'wgCookiePrefix' => 'wmsitetitle',
+ 'wgSecretKey' => null,
+ ] );
+
+ // 1. Log in a blocked test user.
+ $user1tmp = $this->getTestUser()->getUser();
+ $request1 = new FauxRequest();
+ $request1->getSession()->setUser( $user1tmp );
+ $block = new Block( [ 'enableAutoblock' => true ] );
+ $block->setTarget( $user1tmp );
+ $block->insert();
+ $user1 = User::newFromSession( $request1 );
+ $user1->mBlock = $block;
+ $user1->load();
+ $this->assertTrue( $user1->isBlocked() );
+
+ // 2. Create a new request, set the cookie to just the block ID, and the user should
+ // still get blocked when they log in again.
+ $request2 = new FauxRequest();
+ $request2->setCookie( 'BlockID', $block->getId() );
+ $user2 = User::newFromSession( $request2 );
+ $user2->load();
+ $this->assertNotEquals( $user1->getId(), $user2->getId() );
+ $this->assertNotEquals( $user1->getToken(), $user2->getToken() );
+ $this->assertTrue( $user2->isAnon() );
+ $this->assertFalse( $user2->isLoggedIn() );
+ $this->assertTrue( $user2->isBlocked() );
+ $this->assertEquals( true, $user2->getBlock()->isAutoblocking() ); // Non-strict type-check.
+
+ // Clean up.
+ $block->delete();
+ }
+
+ public function testIsPingLimitable() {
+ $request = new FauxRequest();
+ $request->setIP( '1.2.3.4' );
+ $user = User::newFromSession( $request );
+
+ $this->setMwGlobals( 'wgRateLimitsExcludedIPs', [] );
+ $this->assertTrue( $user->isPingLimitable() );
+
+ $this->setMwGlobals( 'wgRateLimitsExcludedIPs', [ '1.2.3.4' ] );
+ $this->assertFalse( $user->isPingLimitable() );
+
+ $this->setMwGlobals( 'wgRateLimitsExcludedIPs', [ '1.2.3.0/8' ] );
+ $this->assertFalse( $user->isPingLimitable() );
+
+ $this->setMwGlobals( 'wgRateLimitsExcludedIPs', [] );
+ $noRateLimitUser = $this->getMockBuilder( User::class )->disableOriginalConstructor()
+ ->setMethods( [ 'getIP', 'getRights' ] )->getMock();
+ $noRateLimitUser->expects( $this->any() )->method( 'getIP' )->willReturn( '1.2.3.4' );
+ $noRateLimitUser->expects( $this->any() )->method( 'getRights' )->willReturn( [ 'noratelimit' ] );
+ $this->assertFalse( $noRateLimitUser->isPingLimitable() );
+ }
+
+ public function provideExperienceLevel() {
+ return [
+ [ 2, 2, 'newcomer' ],
+ [ 12, 3, 'newcomer' ],
+ [ 8, 5, 'newcomer' ],
+ [ 15, 10, 'learner' ],
+ [ 450, 20, 'learner' ],
+ [ 460, 33, 'learner' ],
+ [ 525, 28, 'learner' ],
+ [ 538, 33, 'experienced' ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideExperienceLevel
+ */
+ public function testExperienceLevel( $editCount, $memberSince, $expLevel ) {
+ $this->setMwGlobals( [
+ 'wgLearnerEdits' => 10,
+ 'wgLearnerMemberSince' => 4,
+ 'wgExperiencedUserEdits' => 500,
+ 'wgExperiencedUserMemberSince' => 30,
+ ] );
+
+ $db = wfGetDB( DB_MASTER );
+
+ $data = new stdClass();
+ $data->user_id = 1;
+ $data->user_name = 'name';
+ $data->user_real_name = 'Real Name';
+ $data->user_touched = 1;
+ $data->user_token = 'token';
+ $data->user_email = 'a@a.a';
+ $data->user_email_authenticated = null;
+ $data->user_email_token = 'token';
+ $data->user_email_token_expires = null;
+ $data->user_editcount = $editCount;
+ $data->user_registration = $db->timestamp( time() - $memberSince * 86400 );
+ $user = User::newFromRow( $data );
+
+ $this->assertEquals( $expLevel, $user->getExperienceLevel() );
+ }
+
+ public function testExperienceLevelAnon() {
+ $user = User::newFromName( '10.11.12.13', false );
+
+ $this->assertFalse( $user->getExperienceLevel() );
+ }
}