MediaWiki  1.33.0
UserTest.php
Go to the documentation of this file.
1 <?php
2 
3 define( 'NS_UNITTEST', 5600 );
4 define( 'NS_UNITTEST_TALK', 5601 );
5 
10 use Wikimedia\TestingAccessWrapper;
11 
15 class UserTest extends MediaWikiTestCase {
16 
18  const USER_TALK_PAGE = '<user talk page>';
19 
23  protected $user;
24 
25  protected function setUp() {
26  parent::setUp();
27 
28  $this->setMwGlobals( [
29  'wgGroupPermissions' => [],
30  'wgRevokePermissions' => [],
31  'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
32  ] );
33  $this->overrideMwServices();
34 
35  $this->setUpPermissionGlobals();
36 
37  $this->user = $this->getTestUser( [ 'unittesters' ] )->getUser();
38  }
39 
40  private function setUpPermissionGlobals() {
42 
43  # Data for regular $wgGroupPermissions test
44  $wgGroupPermissions['unittesters'] = [
45  'test' => true,
46  'runtest' => true,
47  'writetest' => false,
48  'nukeworld' => false,
49  ];
50  $wgGroupPermissions['testwriters'] = [
51  'test' => true,
52  'writetest' => true,
53  'modifytest' => true,
54  ];
55 
56  # Data for regular $wgRevokePermissions test
57  $wgRevokePermissions['formertesters'] = [
58  'runtest' => true,
59  ];
60 
61  # For the options test
62  $wgGroupPermissions['*'] = [
63  'editmyoptions' => true,
64  ];
65  }
66 
70  public function testGroupPermissions() {
71  $rights = User::getGroupPermissions( [ 'unittesters' ] );
72  $this->assertContains( 'runtest', $rights );
73  $this->assertNotContains( 'writetest', $rights );
74  $this->assertNotContains( 'modifytest', $rights );
75  $this->assertNotContains( 'nukeworld', $rights );
76 
77  $rights = User::getGroupPermissions( [ 'unittesters', 'testwriters' ] );
78  $this->assertContains( 'runtest', $rights );
79  $this->assertContains( 'writetest', $rights );
80  $this->assertContains( 'modifytest', $rights );
81  $this->assertNotContains( 'nukeworld', $rights );
82  }
83 
87  public function testRevokePermissions() {
88  $rights = User::getGroupPermissions( [ 'unittesters', 'formertesters' ] );
89  $this->assertNotContains( 'runtest', $rights );
90  $this->assertNotContains( 'writetest', $rights );
91  $this->assertNotContains( 'modifytest', $rights );
92  $this->assertNotContains( 'nukeworld', $rights );
93  }
94 
98  public function testUserPermissions() {
99  $rights = $this->user->getRights();
100  $this->assertContains( 'runtest', $rights );
101  $this->assertNotContains( 'writetest', $rights );
102  $this->assertNotContains( 'modifytest', $rights );
103  $this->assertNotContains( 'nukeworld', $rights );
104  }
105 
109  public function testUserGetRightsHooks() {
110  $user = $this->getTestUser( [ 'unittesters', 'testwriters' ] )->getUser();
111  $userWrapper = TestingAccessWrapper::newFromObject( $user );
112 
113  $rights = $user->getRights();
114  $this->assertContains( 'test', $rights, 'sanity check' );
115  $this->assertContains( 'runtest', $rights, 'sanity check' );
116  $this->assertContains( 'writetest', $rights, 'sanity check' );
117  $this->assertNotContains( 'nukeworld', $rights, 'sanity check' );
118 
119  // Add a hook manipluating the rights
120  $this->mergeMwGlobalArrayValue( 'wgHooks', [ 'UserGetRights' => [ function ( $user, &$rights ) {
121  $rights[] = 'nukeworld';
122  $rights = array_diff( $rights, [ 'writetest' ] );
123  } ] ] );
124 
125  $userWrapper->mRights = null;
126  $rights = $user->getRights();
127  $this->assertContains( 'test', $rights );
128  $this->assertContains( 'runtest', $rights );
129  $this->assertNotContains( 'writetest', $rights );
130  $this->assertContains( 'nukeworld', $rights );
131 
132  // Add a Session that limits rights
133  $mock = $this->getMockBuilder( stdClass::class )
134  ->setMethods( [ 'getAllowedUserRights', 'deregisterSession', 'getSessionId' ] )
135  ->getMock();
136  $mock->method( 'getAllowedUserRights' )->willReturn( [ 'test', 'writetest' ] );
137  $mock->method( 'getSessionId' )->willReturn(
138  new MediaWiki\Session\SessionId( str_repeat( 'X', 32 ) )
139  );
141  $mockRequest = $this->getMockBuilder( FauxRequest::class )
142  ->setMethods( [ 'getSession' ] )
143  ->getMock();
144  $mockRequest->method( 'getSession' )->willReturn( $session );
145  $userWrapper->mRequest = $mockRequest;
146 
147  $userWrapper->mRights = null;
148  $rights = $user->getRights();
149  $this->assertContains( 'test', $rights );
150  $this->assertNotContains( 'runtest', $rights );
151  $this->assertNotContains( 'writetest', $rights );
152  $this->assertNotContains( 'nukeworld', $rights );
153  }
154 
159  public function testGetGroupsWithPermission( $expected, $right ) {
161  sort( $result );
162  sort( $expected );
163 
164  $this->assertEquals( $expected, $result, "Groups with permission $right" );
165  }
166 
167  public static function provideGetGroupsWithPermission() {
168  return [
169  [
170  [ 'unittesters', 'testwriters' ],
171  'test'
172  ],
173  [
174  [ 'unittesters' ],
175  'runtest'
176  ],
177  [
178  [ 'testwriters' ],
179  'writetest'
180  ],
181  [
182  [ 'testwriters' ],
183  'modifytest'
184  ],
185  ];
186  }
187 
192  public function testIsIP( $value, $result, $message ) {
193  $this->assertEquals( $this->user->isIP( $value ), $result, $message );
194  }
195 
196  public static function provideIPs() {
197  return [
198  [ '', false, 'Empty string' ],
199  [ ' ', false, 'Blank space' ],
200  [ '10.0.0.0', true, 'IPv4 private 10/8' ],
201  [ '10.255.255.255', true, 'IPv4 private 10/8' ],
202  [ '192.168.1.1', true, 'IPv4 private 192.168/16' ],
203  [ '203.0.113.0', true, 'IPv4 example' ],
204  [ '2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff', true, 'IPv6 example' ],
205  // Not valid IPs but classified as such by MediaWiki for negated asserting
206  // of whether this might be the identifier of a logged-out user or whether
207  // to allow usernames like it.
208  [ '300.300.300.300', true, 'Looks too much like an IPv4 address' ],
209  [ '203.0.113.xxx', true, 'Assigned by UseMod to cloaked logged-out users' ],
210  ];
211  }
212 
217  public function testIsValidUserName( $username, $result, $message ) {
218  $this->assertEquals( $this->user->isValidUserName( $username ), $result, $message );
219  }
220 
221  public static function provideUserNames() {
222  return [
223  [ '', false, 'Empty string' ],
224  [ ' ', false, 'Blank space' ],
225  [ 'abcd', false, 'Starts with small letter' ],
226  [ 'Ab/cd', false, 'Contains slash' ],
227  [ 'Ab cd', true, 'Whitespace' ],
228  [ '192.168.1.1', false, 'IP' ],
229  [ '116.17.184.5/32', false, 'IP range' ],
230  [ '::e:f:2001/96', false, 'IPv6 range' ],
231  [ 'User:Abcd', false, 'Reserved Namespace' ],
232  [ '12abcd232', true, 'Starts with Numbers' ],
233  [ '?abcd', true, 'Start with ? mark' ],
234  [ '#abcd', false, 'Start with #' ],
235  [ 'Abcdകഖഗഘ', true, ' Mixed scripts' ],
236  [ 'ജോസ്‌തോമസ്', false, 'ZWNJ- Format control character' ],
237  [ 'Ab cd', false, ' Ideographic space' ],
238  [ '300.300.300.300', false, 'Looks too much like an IPv4 address' ],
239  [ '302.113.311.900', false, 'Looks too much like an IPv4 address' ],
240  [ '203.0.113.xxx', false, 'Reserved for usage by UseMod for cloaked logged-out users' ],
241  ];
242  }
243 
249  public function testGetEditCount() {
250  $user = $this->getMutableTestUser()->getUser();
251 
252  // let the user have a few (3) edits
253  $page = WikiPage::factory( Title::newFromText( 'Help:UserTest_EditCount' ) );
254  for ( $i = 0; $i < 3; $i++ ) {
255  $page->doEditContent(
256  ContentHandler::makeContent( (string)$i, $page->getTitle() ),
257  'test',
258  0,
259  false,
260  $user
261  );
262  }
263 
264  $this->assertEquals(
265  3,
266  $user->getEditCount(),
267  'After three edits, the user edit count should be 3'
268  );
269 
270  // increase the edit count
271  $user->incEditCount();
273 
274  $this->assertEquals(
275  4,
276  $user->getEditCount(),
277  'After increasing the edit count manually, the user edit count should be 4'
278  );
279  }
280 
286  public function testGetEditCountForAnons() {
287  $user = User::newFromName( 'Anonymous' );
288 
289  $this->assertNull(
290  $user->getEditCount(),
291  'Edit count starts null for anonymous users.'
292  );
293 
294  $user->incEditCount();
295 
296  $this->assertNull(
297  $user->getEditCount(),
298  'Edit count remains null for anonymous users despite calls to increase it.'
299  );
300  }
301 
307  public function testIncEditCount() {
308  $user = $this->getMutableTestUser()->getUser();
309  $user->incEditCount();
310 
311  $reloadedUser = User::newFromId( $user->getId() );
312  $reloadedUser->incEditCount();
313 
314  $this->assertEquals(
315  2,
316  $reloadedUser->getEditCount(),
317  'Increasing the edit count after a fresh load leaves the object up to date.'
318  );
319  }
320 
326  public function testOptions() {
327  $user = $this->getMutableTestUser()->getUser();
328 
329  $user->setOption( 'userjs-someoption', 'test' );
330  $user->setOption( 'rclimit', 200 );
331  $user->setOption( 'wpwatchlistdays', '0' );
332  $user->saveSettings();
333 
335  $user->load( User::READ_LATEST );
336  $this->assertEquals( 'test', $user->getOption( 'userjs-someoption' ) );
337  $this->assertEquals( 200, $user->getOption( 'rclimit' ) );
338 
340  MediaWikiServices::getInstance()->getMainWANObjectCache()->clearProcessCache();
341  $this->assertEquals( 'test', $user->getOption( 'userjs-someoption' ) );
342  $this->assertEquals( 200, $user->getOption( 'rclimit' ) );
343 
344  // Check that an option saved as a string '0' is returned as an integer.
346  $user->load( User::READ_LATEST );
347  $this->assertSame( 0, $user->getOption( 'wpwatchlistdays' ) );
348  }
349 
355  public function testAnonOptions() {
356  global $wgDefaultUserOptions;
357  $this->user->setOption( 'userjs-someoption', 'test' );
358  $this->assertEquals( $wgDefaultUserOptions['rclimit'], $this->user->getOption( 'rclimit' ) );
359  $this->assertEquals( 'test', $this->user->getOption( 'userjs-someoption' ) );
360  }
361 
371  public function testCheckPasswordValidity() {
372  $this->setMwGlobals( [
373  'wgPasswordPolicy' => [
374  'policies' => [
375  'sysop' => [
376  'MinimalPasswordLength' => 8,
377  'MinimumPasswordLengthToLogin' => 1,
378  'PasswordCannotMatchUsername' => 1,
379  ],
380  'default' => [
381  'MinimalPasswordLength' => 6,
382  'PasswordCannotMatchUsername' => true,
383  'PasswordCannotMatchBlacklist' => true,
384  'MaximalPasswordLength' => 40,
385  ],
386  ],
387  'checks' => [
388  'MinimalPasswordLength' => 'PasswordPolicyChecks::checkMinimalPasswordLength',
389  'MinimumPasswordLengthToLogin' => 'PasswordPolicyChecks::checkMinimumPasswordLengthToLogin',
390  'PasswordCannotMatchUsername' => 'PasswordPolicyChecks::checkPasswordCannotMatchUsername',
391  'PasswordCannotMatchBlacklist' => 'PasswordPolicyChecks::checkPasswordCannotMatchBlacklist',
392  'MaximalPasswordLength' => 'PasswordPolicyChecks::checkMaximalPasswordLength',
393  ],
394  ],
395  ] );
396  $this->hideDeprecated( 'User::getPasswordValidity' );
397 
398  $user = static::getTestUser()->getUser();
399 
400  // Sanity
401  $this->assertTrue( $user->isValidPassword( 'Password1234' ) );
402 
403  // Minimum length
404  $this->assertFalse( $user->isValidPassword( 'a' ) );
405  $this->assertFalse( $user->checkPasswordValidity( 'a' )->isGood() );
406  $this->assertTrue( $user->checkPasswordValidity( 'a' )->isOK() );
407  $this->assertEquals( 'passwordtooshort', $user->getPasswordValidity( 'a' ) );
408 
409  // Maximum length
410  $longPass = str_repeat( 'a', 41 );
411  $this->assertFalse( $user->isValidPassword( $longPass ) );
412  $this->assertFalse( $user->checkPasswordValidity( $longPass )->isGood() );
413  $this->assertFalse( $user->checkPasswordValidity( $longPass )->isOK() );
414  $this->assertEquals( 'passwordtoolong', $user->getPasswordValidity( $longPass ) );
415 
416  // Matches username
417  $this->assertFalse( $user->checkPasswordValidity( $user->getName() )->isGood() );
418  $this->assertTrue( $user->checkPasswordValidity( $user->getName() )->isOK() );
419  $this->assertEquals( 'password-name-match', $user->getPasswordValidity( $user->getName() ) );
420 
421  // On the forbidden list
422  $user = User::newFromName( 'Useruser' );
423  $this->assertFalse( $user->checkPasswordValidity( 'Passpass' )->isGood() );
424  $this->assertEquals( 'password-login-forbidden', $user->getPasswordValidity( 'Passpass' ) );
425  }
426 
431  public function testGetCanonicalName( $name, $expectedArray ) {
432  // fake interwiki map for the 'Interwiki prefix' testcase
433  $this->mergeMwGlobalArrayValue( 'wgHooks', [
434  'InterwikiLoadPrefix' => [
435  function ( $prefix, &$iwdata ) {
436  if ( $prefix === 'interwiki' ) {
437  $iwdata = [
438  'iw_url' => 'http://example.com/',
439  'iw_local' => 0,
440  'iw_trans' => 0,
441  ];
442  return false;
443  }
444  },
445  ],
446  ] );
447 
448  foreach ( $expectedArray as $validate => $expected ) {
449  $this->assertEquals(
450  $expected,
451  User::getCanonicalName( $name, $validate === 'false' ? false : $validate ), $validate );
452  }
453  }
454 
455  public static function provideGetCanonicalName() {
456  return [
457  'Leading space' => [ ' Leading space', [ 'creatable' => 'Leading space' ] ],
458  'Trailing space ' => [ 'Trailing space ', [ 'creatable' => 'Trailing space' ] ],
459  'Namespace prefix' => [ 'Talk:Username', [ 'creatable' => false, 'usable' => false,
460  'valid' => false, 'false' => 'Talk:Username' ] ],
461  'Interwiki prefix' => [ 'interwiki:Username', [ 'creatable' => false, 'usable' => false,
462  'valid' => false, 'false' => 'Interwiki:Username' ] ],
463  'With hash' => [ 'name with # hash', [ 'creatable' => false, 'usable' => false ] ],
464  'Multi spaces' => [ 'Multi spaces', [ 'creatable' => 'Multi spaces',
465  'usable' => 'Multi spaces' ] ],
466  'Lowercase' => [ 'lowercase', [ 'creatable' => 'Lowercase' ] ],
467  'Invalid character' => [ 'in[]valid', [ 'creatable' => false, 'usable' => false,
468  'valid' => false, 'false' => 'In[]valid' ] ],
469  'With slash' => [ 'with / slash', [ 'creatable' => false, 'usable' => false, 'valid' => false,
470  'false' => 'With / slash' ] ],
471  ];
472  }
473 
477  public function testEquals() {
478  $first = $this->getMutableTestUser()->getUser();
479  $second = User::newFromName( $first->getName() );
480 
481  $this->assertTrue( $first->equals( $first ) );
482  $this->assertTrue( $first->equals( $second ) );
483  $this->assertTrue( $second->equals( $first ) );
484 
485  $third = $this->getMutableTestUser()->getUser();
486  $fourth = $this->getMutableTestUser()->getUser();
487 
488  $this->assertFalse( $third->equals( $fourth ) );
489  $this->assertFalse( $fourth->equals( $third ) );
490 
491  // Test users loaded from db with id
492  $user = $this->getMutableTestUser()->getUser();
493  $fifth = User::newFromId( $user->getId() );
494  $sixth = User::newFromName( $user->getName() );
495  $this->assertTrue( $fifth->equals( $sixth ) );
496  }
497 
501  public function testGetId() {
502  $user = static::getTestUser()->getUser();
503  $this->assertTrue( $user->getId() > 0 );
504  }
505 
510  public function testLoggedIn() {
511  $user = $this->getMutableTestUser()->getUser();
512  $this->assertTrue( $user->isLoggedIn() );
513  $this->assertFalse( $user->isAnon() );
514 
515  // Non-existent users are perceived as anonymous
516  $user = User::newFromName( 'UTNonexistent' );
517  $this->assertFalse( $user->isLoggedIn() );
518  $this->assertTrue( $user->isAnon() );
519 
520  $user = new User;
521  $this->assertFalse( $user->isLoggedIn() );
522  $this->assertTrue( $user->isAnon() );
523  }
524 
528  public function testCheckAndSetTouched() {
529  $user = $this->getMutableTestUser()->getUser();
530  $user = TestingAccessWrapper::newFromObject( $user );
531  $this->assertTrue( $user->isLoggedIn() );
532 
533  $touched = $user->getDBTouched();
534  $this->assertTrue(
535  $user->checkAndSetTouched(), "checkAndSetTouched() succedeed" );
536  $this->assertGreaterThan(
537  $touched, $user->getDBTouched(), "user_touched increased with casOnTouched()" );
538 
539  $touched = $user->getDBTouched();
540  $this->assertTrue(
541  $user->checkAndSetTouched(), "checkAndSetTouched() succedeed #2" );
542  $this->assertGreaterThan(
543  $touched, $user->getDBTouched(), "user_touched increased with casOnTouched() #2" );
544  }
545 
549  public function testFindUsersByGroup() {
550  // FIXME: fails under postgres
551  $this->markTestSkippedIfDbType( 'postgres' );
552 
554  $this->assertEquals( 0, iterator_count( $users ) );
555 
556  $users = User::findUsersByGroup( 'foo' );
557  $this->assertEquals( 0, iterator_count( $users ) );
558 
559  $user = $this->getMutableTestUser( [ 'foo' ] )->getUser();
560  $users = User::findUsersByGroup( 'foo' );
561  $this->assertEquals( 1, iterator_count( $users ) );
562  $users->rewind();
563  $this->assertTrue( $user->equals( $users->current() ) );
564 
565  // arguments have OR relationship
566  $user2 = $this->getMutableTestUser( [ 'bar' ] )->getUser();
567  $users = User::findUsersByGroup( [ 'foo', 'bar' ] );
568  $this->assertEquals( 2, iterator_count( $users ) );
569  $users->rewind();
570  $this->assertTrue( $user->equals( $users->current() ) );
571  $users->next();
572  $this->assertTrue( $user2->equals( $users->current() ) );
573 
574  // users are not duplicated
575  $user = $this->getMutableTestUser( [ 'baz', 'boom' ] )->getUser();
576  $users = User::findUsersByGroup( [ 'baz', 'boom' ] );
577  $this->assertEquals( 1, iterator_count( $users ) );
578  $users->rewind();
579  $this->assertTrue( $user->equals( $users->current() ) );
580  }
581 
588  public function testAutoblockCookies() {
589  // Set up the bits of global configuration that we use.
590  $this->setMwGlobals( [
591  'wgCookieSetOnAutoblock' => true,
592  'wgCookiePrefix' => 'wmsitetitle',
593  'wgSecretKey' => MWCryptRand::generateHex( 64, true ),
594  ] );
595 
596  // Unregister the hooks for proper unit testing
597  $this->mergeMwGlobalArrayValue( 'wgHooks', [
598  'PerformRetroactiveAutoblock' => []
599  ] );
600 
601  // 1. Log in a test user, and block them.
602  $user1tmp = $this->getTestUser()->getUser();
603  $request1 = new FauxRequest();
604  $request1->getSession()->setUser( $user1tmp );
605  $expiryFiveHours = wfTimestamp() + ( 5 * 60 * 60 );
606  $block = new Block( [
607  'enableAutoblock' => true,
608  'expiry' => wfTimestamp( TS_MW, $expiryFiveHours ),
609  ] );
610  $block->setBlocker( $this->getTestSysop()->getUser() );
611  $block->setTarget( $user1tmp );
612  $res = $block->insert();
613  $this->assertTrue( (bool)$res['id'], 'Failed to insert block' );
614  $user1 = User::newFromSession( $request1 );
615  $user1->mBlock = $block;
616  $user1->load();
617 
618  // Confirm that the block has been applied as required.
619  $this->assertTrue( $user1->isLoggedIn() );
620  $this->assertTrue( $user1->isBlocked() );
621  $this->assertEquals( Block::TYPE_USER, $block->getType() );
622  $this->assertTrue( $block->isAutoblocking() );
623  $this->assertGreaterThanOrEqual( 1, $block->getId() );
624 
625  // Test for the desired cookie name, value, and expiry.
626  $cookies = $request1->response()->getCookies();
627  $this->assertArrayHasKey( 'wmsitetitleBlockID', $cookies );
628  $this->assertEquals( $expiryFiveHours, $cookies['wmsitetitleBlockID']['expire'] );
629  $cookieValue = Block::getIdFromCookieValue( $cookies['wmsitetitleBlockID']['value'] );
630  $this->assertEquals( $block->getId(), $cookieValue );
631 
632  // 2. Create a new request, set the cookies, and see if the (anon) user is blocked.
633  $request2 = new FauxRequest();
634  $request2->setCookie( 'BlockID', $block->getCookieValue() );
635  $user2 = User::newFromSession( $request2 );
636  $user2->load();
637  $this->assertNotEquals( $user1->getId(), $user2->getId() );
638  $this->assertNotEquals( $user1->getToken(), $user2->getToken() );
639  $this->assertTrue( $user2->isAnon() );
640  $this->assertFalse( $user2->isLoggedIn() );
641  $this->assertTrue( $user2->isBlocked() );
642  // Non-strict type-check.
643  $this->assertEquals( true, $user2->getBlock()->isAutoblocking(), 'Autoblock does not work' );
644  // Can't directly compare the objects because of member type differences.
645  // One day this will work: $this->assertEquals( $block, $user2->getBlock() );
646  $this->assertEquals( $block->getId(), $user2->getBlock()->getId() );
647  $this->assertEquals( $block->getExpiry(), $user2->getBlock()->getExpiry() );
648 
649  // 3. Finally, set up a request as a new user, and the block should still be applied.
650  $user3tmp = $this->getTestUser()->getUser();
651  $request3 = new FauxRequest();
652  $request3->getSession()->setUser( $user3tmp );
653  $request3->setCookie( 'BlockID', $block->getId() );
654  $user3 = User::newFromSession( $request3 );
655  $user3->load();
656  $this->assertTrue( $user3->isLoggedIn() );
657  $this->assertTrue( $user3->isBlocked() );
658  $this->assertEquals( true, $user3->getBlock()->isAutoblocking() ); // Non-strict type-check.
659 
660  // Clean up.
661  $block->delete();
662  }
663 
669  public function testAutoblockCookiesDisabled() {
670  // Set up the bits of global configuration that we use.
671  $this->setMwGlobals( [
672  'wgCookieSetOnAutoblock' => false,
673  'wgCookiePrefix' => 'wm_no_cookies',
674  'wgSecretKey' => MWCryptRand::generateHex( 64, true ),
675  ] );
676 
677  // Unregister the hooks for proper unit testing
678  $this->mergeMwGlobalArrayValue( 'wgHooks', [
679  'PerformRetroactiveAutoblock' => []
680  ] );
681 
682  // 1. Log in a test user, and block them.
683  $testUser = $this->getTestUser()->getUser();
684  $request1 = new FauxRequest();
685  $request1->getSession()->setUser( $testUser );
686  $block = new Block( [ 'enableAutoblock' => true ] );
687  $block->setBlocker( $this->getTestSysop()->getUser() );
688  $block->setTarget( $testUser );
689  $res = $block->insert();
690  $this->assertTrue( (bool)$res['id'], 'Failed to insert block' );
691  $user = User::newFromSession( $request1 );
692  $user->mBlock = $block;
693  $user->load();
694 
695  // 2. Test that the cookie IS NOT present.
696  $this->assertTrue( $user->isLoggedIn() );
697  $this->assertTrue( $user->isBlocked() );
698  $this->assertEquals( Block::TYPE_USER, $block->getType() );
699  $this->assertTrue( $block->isAutoblocking() );
700  $this->assertGreaterThanOrEqual( 1, $user->getBlockId() );
701  $this->assertGreaterThanOrEqual( $block->getId(), $user->getBlockId() );
702  $cookies = $request1->response()->getCookies();
703  $this->assertArrayNotHasKey( 'wm_no_cookiesBlockID', $cookies );
704 
705  // Clean up.
706  $block->delete();
707  }
708 
716  $this->setMwGlobals( [
717  'wgCookieSetOnAutoblock' => true,
718  'wgCookiePrefix' => 'wm_infinite_block',
719  'wgSecretKey' => MWCryptRand::generateHex( 64, true ),
720  ] );
721 
722  // Unregister the hooks for proper unit testing
723  $this->mergeMwGlobalArrayValue( 'wgHooks', [
724  'PerformRetroactiveAutoblock' => []
725  ] );
726 
727  // 1. Log in a test user, and block them indefinitely.
728  $user1Tmp = $this->getTestUser()->getUser();
729  $request1 = new FauxRequest();
730  $request1->getSession()->setUser( $user1Tmp );
731  $block = new Block( [ 'enableAutoblock' => true, 'expiry' => 'infinity' ] );
732  $block->setBlocker( $this->getTestSysop()->getUser() );
733  $block->setTarget( $user1Tmp );
734  $res = $block->insert();
735  $this->assertTrue( (bool)$res['id'], 'Failed to insert block' );
736  $user1 = User::newFromSession( $request1 );
737  $user1->mBlock = $block;
738  $user1->load();
739 
740  // 2. Test the cookie's expiry timestamp.
741  $this->assertTrue( $user1->isLoggedIn() );
742  $this->assertTrue( $user1->isBlocked() );
743  $this->assertEquals( Block::TYPE_USER, $block->getType() );
744  $this->assertTrue( $block->isAutoblocking() );
745  $this->assertGreaterThanOrEqual( 1, $user1->getBlockId() );
746  $cookies = $request1->response()->getCookies();
747  // Test the cookie's expiry to the nearest minute.
748  $this->assertArrayHasKey( 'wm_infinite_blockBlockID', $cookies );
749  $expOneDay = wfTimestamp() + ( 24 * 60 * 60 );
750  // Check for expiry dates in a 10-second window, to account for slow testing.
751  $this->assertEquals(
752  $expOneDay,
753  $cookies['wm_infinite_blockBlockID']['expire'],
754  'Expiry date',
755  5.0
756  );
757 
758  // 3. Change the block's expiry (to 2 hours), and the cookie's should be changed also.
759  $newExpiry = wfTimestamp() + 2 * 60 * 60;
760  $block->setExpiry( wfTimestamp( TS_MW, $newExpiry ) );
761  $block->update();
762  $user2tmp = $this->getTestUser()->getUser();
763  $request2 = new FauxRequest();
764  $request2->getSession()->setUser( $user2tmp );
765  $user2 = User::newFromSession( $request2 );
766  $user2->mBlock = $block;
767  $user2->load();
768  $cookies = $request2->response()->getCookies();
769  $this->assertEquals( wfTimestamp( TS_MW, $newExpiry ), $block->getExpiry() );
770  $this->assertEquals( $newExpiry, $cookies['wm_infinite_blockBlockID']['expire'] );
771 
772  // Clean up.
773  $block->delete();
774  }
775 
779  public function testSoftBlockRanges() {
780  $setSessionUser = function ( User $user, WebRequest $request ) {
781  $this->setMwGlobals( 'wgUser', $user );
782  RequestContext::getMain()->setUser( $user );
783  RequestContext::getMain()->setRequest( $request );
784  TestingAccessWrapper::newFromObject( $user )->mRequest = $request;
785  $request->getSession()->setUser( $user );
786  };
787  $this->setMwGlobals( 'wgSoftBlockRanges', [ '10.0.0.0/8' ] );
788 
789  // IP isn't in $wgSoftBlockRanges
790  $wgUser = new User();
791  $request = new FauxRequest();
792  $request->setIP( '192.168.0.1' );
793  $setSessionUser( $wgUser, $request );
794  $this->assertNull( $wgUser->getBlock() );
795 
796  // IP is in $wgSoftBlockRanges
797  $wgUser = new User();
798  $request = new FauxRequest();
799  $request->setIP( '10.20.30.40' );
800  $setSessionUser( $wgUser, $request );
801  $block = $wgUser->getBlock();
802  $this->assertInstanceOf( Block::class, $block );
803  $this->assertSame( 'wgSoftBlockRanges', $block->getSystemBlockType() );
804 
805  // Make sure the block is really soft
806  $wgUser = $this->getTestUser()->getUser();
807  $request = new FauxRequest();
808  $request->setIP( '10.20.30.40' );
809  $setSessionUser( $wgUser, $request );
810  $this->assertFalse( $wgUser->isAnon(), 'sanity check' );
811  $this->assertNull( $wgUser->getBlock() );
812  }
813 
818  public function testAutoblockCookieInauthentic() {
819  // Set up the bits of global configuration that we use.
820  $this->setMwGlobals( [
821  'wgCookieSetOnAutoblock' => true,
822  'wgCookiePrefix' => 'wmsitetitle',
823  'wgSecretKey' => MWCryptRand::generateHex( 64, true ),
824  ] );
825 
826  // Unregister the hooks for proper unit testing
827  $this->mergeMwGlobalArrayValue( 'wgHooks', [
828  'PerformRetroactiveAutoblock' => []
829  ] );
830 
831  // 1. Log in a blocked test user.
832  $user1tmp = $this->getTestUser()->getUser();
833  $request1 = new FauxRequest();
834  $request1->getSession()->setUser( $user1tmp );
835  $block = new Block( [ 'enableAutoblock' => true ] );
836  $block->setBlocker( $this->getTestSysop()->getUser() );
837  $block->setTarget( $user1tmp );
838  $res = $block->insert();
839  $this->assertTrue( (bool)$res['id'], 'Failed to insert block' );
840  $user1 = User::newFromSession( $request1 );
841  $user1->mBlock = $block;
842  $user1->load();
843 
844  // 2. Create a new request, set the cookie to an invalid value, and make sure the (anon)
845  // user not blocked.
846  $request2 = new FauxRequest();
847  $request2->setCookie( 'BlockID', $block->getId() . '!zzzzzzz' );
848  $user2 = User::newFromSession( $request2 );
849  $user2->load();
850  $this->assertTrue( $user2->isAnon() );
851  $this->assertFalse( $user2->isLoggedIn() );
852  $this->assertFalse( $user2->isBlocked() );
853 
854  // Clean up.
855  $block->delete();
856  }
857 
863  public function testAutoblockCookieNoSecretKey() {
864  // Set up the bits of global configuration that we use.
865  $this->setMwGlobals( [
866  'wgCookieSetOnAutoblock' => true,
867  'wgCookiePrefix' => 'wmsitetitle',
868  'wgSecretKey' => null,
869  ] );
870 
871  // Unregister the hooks for proper unit testing
872  $this->mergeMwGlobalArrayValue( 'wgHooks', [
873  'PerformRetroactiveAutoblock' => []
874  ] );
875 
876  // 1. Log in a blocked test user.
877  $user1tmp = $this->getTestUser()->getUser();
878  $request1 = new FauxRequest();
879  $request1->getSession()->setUser( $user1tmp );
880  $block = new Block( [ 'enableAutoblock' => true ] );
881  $block->setBlocker( $this->getTestSysop()->getUser() );
882  $block->setTarget( $user1tmp );
883  $res = $block->insert();
884  $this->assertTrue( (bool)$res['id'], 'Failed to insert block' );
885  $user1 = User::newFromSession( $request1 );
886  $user1->mBlock = $block;
887  $user1->load();
888  $this->assertTrue( $user1->isBlocked() );
889 
890  // 2. Create a new request, set the cookie to just the block ID, and the user should
891  // still get blocked when they log in again.
892  $request2 = new FauxRequest();
893  $request2->setCookie( 'BlockID', $block->getId() );
894  $user2 = User::newFromSession( $request2 );
895  $user2->load();
896  $this->assertNotEquals( $user1->getId(), $user2->getId() );
897  $this->assertNotEquals( $user1->getToken(), $user2->getToken() );
898  $this->assertTrue( $user2->isAnon() );
899  $this->assertFalse( $user2->isLoggedIn() );
900  $this->assertTrue( $user2->isBlocked() );
901  $this->assertEquals( true, $user2->getBlock()->isAutoblocking() ); // Non-strict type-check.
902 
903  // Clean up.
904  $block->delete();
905  }
906 
910  public function testIsPingLimitable() {
911  $request = new FauxRequest();
912  $request->setIP( '1.2.3.4' );
914 
915  $this->setMwGlobals( 'wgRateLimitsExcludedIPs', [] );
916  $this->assertTrue( $user->isPingLimitable() );
917 
918  $this->setMwGlobals( 'wgRateLimitsExcludedIPs', [ '1.2.3.4' ] );
919  $this->assertFalse( $user->isPingLimitable() );
920 
921  $this->setMwGlobals( 'wgRateLimitsExcludedIPs', [ '1.2.3.0/8' ] );
922  $this->assertFalse( $user->isPingLimitable() );
923 
924  $this->setMwGlobals( 'wgRateLimitsExcludedIPs', [] );
925  $noRateLimitUser = $this->getMockBuilder( User::class )->disableOriginalConstructor()
926  ->setMethods( [ 'getIP', 'getRights' ] )->getMock();
927  $noRateLimitUser->expects( $this->any() )->method( 'getIP' )->willReturn( '1.2.3.4' );
928  $noRateLimitUser->expects( $this->any() )->method( 'getRights' )->willReturn( [ 'noratelimit' ] );
929  $this->assertFalse( $noRateLimitUser->isPingLimitable() );
930  }
931 
932  public function provideExperienceLevel() {
933  return [
934  [ 2, 2, 'newcomer' ],
935  [ 12, 3, 'newcomer' ],
936  [ 8, 5, 'newcomer' ],
937  [ 15, 10, 'learner' ],
938  [ 450, 20, 'learner' ],
939  [ 460, 33, 'learner' ],
940  [ 525, 28, 'learner' ],
941  [ 538, 33, 'experienced' ],
942  ];
943  }
944 
949  public function testExperienceLevel( $editCount, $memberSince, $expLevel ) {
950  $this->setMwGlobals( [
951  'wgLearnerEdits' => 10,
952  'wgLearnerMemberSince' => 4,
953  'wgExperiencedUserEdits' => 500,
954  'wgExperiencedUserMemberSince' => 30,
955  ] );
956 
957  $db = wfGetDB( DB_MASTER );
958  $userQuery = User::getQueryInfo();
959  $row = $db->selectRow(
960  $userQuery['tables'],
961  $userQuery['fields'],
962  [ 'user_id' => $this->getTestUser()->getUser()->getId() ],
963  __METHOD__,
964  [],
965  $userQuery['joins']
966  );
967  $row->user_editcount = $editCount;
968  $row->user_registration = $db->timestamp( time() - $memberSince * 86400 );
969  $user = User::newFromRow( $row );
970 
971  $this->assertEquals( $expLevel, $user->getExperienceLevel() );
972  }
973 
977  public function testExperienceLevelAnon() {
978  $user = User::newFromName( '10.11.12.13', false );
979 
980  $this->assertFalse( $user->getExperienceLevel() );
981  }
982 
983  public static function provideIsLocallBlockedProxy() {
984  return [
985  [ '1.2.3.4', '1.2.3.4' ],
986  [ '1.2.3.4', '1.2.3.0/16' ],
987  ];
988  }
989 
994  public function testIsLocallyBlockedProxy( $ip, $blockListEntry ) {
995  $this->setMwGlobals(
996  'wgProxyList', []
997  );
998  $this->assertFalse( User::isLocallyBlockedProxy( $ip ) );
999 
1000  $this->setMwGlobals(
1001  'wgProxyList',
1002  [
1003  $blockListEntry
1004  ]
1005  );
1006  $this->assertTrue( User::isLocallyBlockedProxy( $ip ) );
1007 
1008  $this->setMwGlobals(
1009  'wgProxyList',
1010  [
1011  'test' => $blockListEntry
1012  ]
1013  );
1014  $this->assertTrue( User::isLocallyBlockedProxy( $ip ) );
1015 
1016  $this->hideDeprecated(
1017  'IP addresses in the keys of $wgProxyList (found the following IP ' .
1018  'addresses in keys: ' . $blockListEntry . ', please move them to values)'
1019  );
1020  $this->setMwGlobals(
1021  'wgProxyList',
1022  [
1023  $blockListEntry => 'test'
1024  ]
1025  );
1026  $this->assertTrue( User::isLocallyBlockedProxy( $ip ) );
1027  }
1028 
1032  public function testActorId() {
1033  $domain = MediaWikiServices::getInstance()->getDBLoadBalancer()->getLocalDomainID();
1034  $this->hideDeprecated( 'User::selectFields' );
1035 
1036  // Newly-created user has an actor ID
1037  $user = User::createNew( 'UserTestActorId1' );
1038  $id = $user->getId();
1039  $this->assertTrue( $user->getActorId() > 0, 'User::createNew sets an actor ID' );
1040 
1041  $user = User::newFromName( 'UserTestActorId2' );
1042  $user->addToDatabase();
1043  $this->assertTrue( $user->getActorId() > 0, 'User::addToDatabase sets an actor ID' );
1044 
1045  $user = User::newFromName( 'UserTestActorId1' );
1046  $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be retrieved for user loaded by name' );
1047 
1048  $user = User::newFromId( $id );
1049  $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be retrieved for user loaded by ID' );
1050 
1051  $user2 = User::newFromActorId( $user->getActorId() );
1052  $this->assertEquals( $user->getId(), $user2->getId(),
1053  'User::newFromActorId works for an existing user' );
1054 
1055  $row = $this->db->selectRow( 'user', User::selectFields(), [ 'user_id' => $id ], __METHOD__ );
1056  $user = User::newFromRow( $row );
1057  $this->assertTrue( $user->getActorId() > 0,
1058  'Actor ID can be retrieved for user loaded with User::selectFields()' );
1059 
1060  $user = User::newFromId( $id );
1061  $user->setName( 'UserTestActorId4-renamed' );
1062  $user->saveSettings();
1063  $this->assertEquals(
1064  $user->getName(),
1065  $this->db->selectField(
1066  'actor', 'actor_name', [ 'actor_id' => $user->getActorId() ], __METHOD__
1067  ),
1068  'User::saveSettings updates actor table for name change'
1069  );
1070 
1071  // For sanity
1072  $ip = '192.168.12.34';
1073  $this->db->delete( 'actor', [ 'actor_name' => $ip ], __METHOD__ );
1074 
1075  $user = User::newFromName( $ip, false );
1076  $this->assertFalse( $user->getActorId() > 0, 'Anonymous user has no actor ID by default' );
1077  $this->assertTrue( $user->getActorId( $this->db ) > 0,
1078  'Actor ID can be created for an anonymous user' );
1079 
1080  $user = User::newFromName( $ip, false );
1081  $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be loaded for an anonymous user' );
1082  $user2 = User::newFromActorId( $user->getActorId() );
1083  $this->assertEquals( $user->getName(), $user2->getName(),
1084  'User::newFromActorId works for an anonymous user' );
1085  }
1086 
1096  public function testActorId_old() {
1097  $this->setMwGlobals( [
1098  'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
1099  ] );
1100  $this->overrideMwServices();
1101 
1102  $domain = MediaWikiServices::getInstance()->getDBLoadBalancer()->getLocalDomainID();
1103  $this->hideDeprecated( 'User::selectFields' );
1104 
1105  // Newly-created user has an actor ID
1106  $user = User::createNew( 'UserTestActorIdOld1' );
1107  $id = $user->getId();
1108  $this->assertTrue( $user->getActorId() > 0, 'User::createNew sets an actor ID' );
1109 
1110  $user = User::newFromName( 'UserTestActorIdOld2' );
1111  $user->addToDatabase();
1112  $this->assertTrue( $user->getActorId() > 0, 'User::addToDatabase sets an actor ID' );
1113 
1114  $user = User::newFromName( 'UserTestActorIdOld1' );
1115  $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be retrieved for user loaded by name' );
1116 
1117  $user = User::newFromId( $id );
1118  $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be retrieved for user loaded by ID' );
1119 
1120  $user2 = User::newFromActorId( $user->getActorId() );
1121  $this->assertEquals( $user->getId(), $user2->getId(),
1122  'User::newFromActorId works for an existing user' );
1123 
1124  $row = $this->db->selectRow( 'user', User::selectFields(), [ 'user_id' => $id ], __METHOD__ );
1125  $user = User::newFromRow( $row );
1126  $this->assertTrue( $user->getActorId() > 0,
1127  'Actor ID can be retrieved for user loaded with User::selectFields()' );
1128 
1129  $this->db->delete( 'actor', [ 'actor_user' => $id ], __METHOD__ );
1130  User::purge( $domain, $id );
1131  // Because WANObjectCache->delete() stupidly doesn't delete from the process cache.
1132  ObjectCache::getMainWANInstance()->clearProcessCache();
1133 
1134  $user = User::newFromId( $id );
1135  $this->assertFalse( $user->getActorId() > 0, 'No Actor ID by default if none in database' );
1136  $this->assertTrue( $user->getActorId( $this->db ) > 0, 'Actor ID can be created if none in db' );
1137 
1138  $user->setName( 'UserTestActorIdOld4-renamed' );
1139  $user->saveSettings();
1140  $this->assertEquals(
1141  $user->getName(),
1142  $this->db->selectField(
1143  'actor', 'actor_name', [ 'actor_id' => $user->getActorId() ], __METHOD__
1144  ),
1145  'User::saveSettings updates actor table for name change'
1146  );
1147 
1148  // For sanity
1149  $ip = '192.168.12.34';
1150  $this->db->delete( 'actor', [ 'actor_name' => $ip ], __METHOD__ );
1151 
1152  $user = User::newFromName( $ip, false );
1153  $this->assertFalse( $user->getActorId() > 0, 'Anonymous user has no actor ID by default' );
1154  $this->assertTrue( $user->getActorId( $this->db ) > 0,
1155  'Actor ID can be created for an anonymous user' );
1156 
1157  $user = User::newFromName( $ip, false );
1158  $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be loaded for an anonymous user' );
1159  $user2 = User::newFromActorId( $user->getActorId() );
1160  $this->assertEquals( $user->getName(), $user2->getName(),
1161  'User::newFromActorId works for an anonymous user' );
1162  }
1163 
1167  public function testNewFromAnyId() {
1168  // Registered user
1169  $user = $this->getTestUser()->getUser();
1170  for ( $i = 1; $i <= 7; $i++ ) {
1171  $test = User::newFromAnyId(
1172  ( $i & 1 ) ? $user->getId() : null,
1173  ( $i & 2 ) ? $user->getName() : null,
1174  ( $i & 4 ) ? $user->getActorId() : null
1175  );
1176  $this->assertSame( $user->getId(), $test->getId() );
1177  $this->assertSame( $user->getName(), $test->getName() );
1178  $this->assertSame( $user->getActorId(), $test->getActorId() );
1179  }
1180 
1181  // Anon user. Can't load by only user ID when that's 0.
1182  $user = User::newFromName( '192.168.12.34', false );
1183  $user->getActorId( $this->db ); // Make sure an actor ID exists
1184 
1185  $test = User::newFromAnyId( null, '192.168.12.34', null );
1186  $this->assertSame( $user->getId(), $test->getId() );
1187  $this->assertSame( $user->getName(), $test->getName() );
1188  $this->assertSame( $user->getActorId(), $test->getActorId() );
1189  $test = User::newFromAnyId( null, null, $user->getActorId() );
1190  $this->assertSame( $user->getId(), $test->getId() );
1191  $this->assertSame( $user->getName(), $test->getName() );
1192  $this->assertSame( $user->getActorId(), $test->getActorId() );
1193 
1194  // Bogus data should still "work" as long as nothing triggers a ->load(),
1195  // and accessing the specified data shouldn't do that.
1196  $test = User::newFromAnyId( 123456, 'Bogus', 654321 );
1197  $this->assertSame( 123456, $test->getId() );
1198  $this->assertSame( 'Bogus', $test->getName() );
1199  $this->assertSame( 654321, $test->getActorId() );
1200 
1201  // Exceptional cases
1202  try {
1203  User::newFromAnyId( null, null, null );
1204  $this->fail( 'Expected exception not thrown' );
1205  } catch ( InvalidArgumentException $ex ) {
1206  }
1207  try {
1208  User::newFromAnyId( 0, null, 0 );
1209  $this->fail( 'Expected exception not thrown' );
1210  } catch ( InvalidArgumentException $ex ) {
1211  }
1212  }
1213 
1217  public function testNewFromIdentity() {
1218  // Registered user
1219  $user = $this->getTestUser()->getUser();
1220 
1221  $this->assertSame( $user, User::newFromIdentity( $user ) );
1222 
1223  // ID only
1224  $identity = new UserIdentityValue( $user->getId(), '', 0 );
1225  $result = User::newFromIdentity( $identity );
1226  $this->assertInstanceOf( User::class, $result );
1227  $this->assertSame( $user->getId(), $result->getId(), 'ID' );
1228  $this->assertSame( $user->getName(), $result->getName(), 'Name' );
1229  $this->assertSame( $user->getActorId(), $result->getActorId(), 'Actor' );
1230 
1231  // Name only
1232  $identity = new UserIdentityValue( 0, $user->getName(), 0 );
1233  $result = User::newFromIdentity( $identity );
1234  $this->assertInstanceOf( User::class, $result );
1235  $this->assertSame( $user->getId(), $result->getId(), 'ID' );
1236  $this->assertSame( $user->getName(), $result->getName(), 'Name' );
1237  $this->assertSame( $user->getActorId(), $result->getActorId(), 'Actor' );
1238 
1239  // Actor only
1240  $identity = new UserIdentityValue( 0, '', $user->getActorId() );
1241  $result = User::newFromIdentity( $identity );
1242  $this->assertInstanceOf( User::class, $result );
1243  $this->assertSame( $user->getId(), $result->getId(), 'ID' );
1244  $this->assertSame( $user->getName(), $result->getName(), 'Name' );
1245  $this->assertSame( $user->getActorId(), $result->getActorId(), 'Actor' );
1246  }
1247 
1256  public function testBlockInstanceCache() {
1257  // First, check the user isn't blocked
1258  $user = $this->getMutableTestUser()->getUser();
1260  $this->assertNull( $user->getBlock( false ), 'sanity check' );
1261  $this->assertSame( '', $user->blockedBy(), 'sanity check' );
1262  $this->assertSame( '', $user->blockedFor(), 'sanity check' );
1263  $this->assertFalse( (bool)$user->isHidden(), 'sanity check' );
1264  $this->assertFalse( $user->isBlockedFrom( $ut ), 'sanity check' );
1265 
1266  // Block the user
1267  $blocker = $this->getTestSysop()->getUser();
1268  $block = new Block( [
1269  'hideName' => true,
1270  'allowUsertalk' => false,
1271  'reason' => 'Because',
1272  ] );
1273  $block->setTarget( $user );
1274  $block->setBlocker( $blocker );
1275  $res = $block->insert();
1276  $this->assertTrue( (bool)$res['id'], 'sanity check: Failed to insert block' );
1277 
1278  // Clear cache and confirm it loaded the block properly
1280  $this->assertInstanceOf( Block::class, $user->getBlock( false ) );
1281  $this->assertSame( $blocker->getName(), $user->blockedBy() );
1282  $this->assertSame( 'Because', $user->blockedFor() );
1283  $this->assertTrue( (bool)$user->isHidden() );
1284  $this->assertTrue( $user->isBlockedFrom( $ut ) );
1285 
1286  // Unblock
1287  $block->delete();
1288 
1289  // Clear cache and confirm it loaded the not-blocked properly
1291  $this->assertNull( $user->getBlock( false ) );
1292  $this->assertSame( '', $user->blockedBy() );
1293  $this->assertSame( '', $user->blockedFor() );
1294  $this->assertFalse( (bool)$user->isHidden() );
1295  $this->assertFalse( $user->isBlockedFrom( $ut ) );
1296  }
1297 
1308  public function testIsBlockedFrom( $title, $expect, array $options = [] ) {
1309  $this->setMwGlobals( [
1310  'wgBlockAllowsUTEdit' => $options['blockAllowsUTEdit'] ?? true,
1311  ] );
1312 
1313  $user = $this->getTestUser()->getUser();
1314 
1315  if ( $title === self::USER_TALK_PAGE ) {
1316  $title = $user->getTalkPage();
1317  } else {
1319  }
1320 
1321  $restrictions = [];
1322  foreach ( $options['pageRestrictions'] ?? [] as $pagestr ) {
1323  $page = $this->getExistingTestPage(
1324  $pagestr === self::USER_TALK_PAGE ? $user->getTalkPage() : $pagestr
1325  );
1326  $restrictions[] = new PageRestriction( 0, $page->getId() );
1327  }
1328  foreach ( $options['namespaceRestrictions'] ?? [] as $ns ) {
1329  $restrictions[] = new NamespaceRestriction( 0, $ns );
1330  }
1331 
1332  $block = new Block( [
1333  'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
1334  'allowUsertalk' => $options['allowUsertalk'] ?? false,
1335  'sitewide' => !$restrictions,
1336  ] );
1337  $block->setTarget( $user );
1338  $block->setBlocker( $this->getTestSysop()->getUser() );
1339  if ( $restrictions ) {
1340  $block->setRestrictions( $restrictions );
1341  }
1342  $block->insert();
1343 
1344  try {
1345  $this->assertSame( $expect, $user->isBlockedFrom( $title ) );
1346  } finally {
1347  $block->delete();
1348  }
1349  }
1350 
1351  public static function provideIsBlockedFrom() {
1352  return [
1353  'Sitewide block, basic operation' => [ 'Test page', true ],
1354  'Sitewide block, not allowing user talk' => [
1355  self::USER_TALK_PAGE, true, [
1356  'allowUsertalk' => false,
1357  ]
1358  ],
1359  'Sitewide block, allowing user talk' => [
1360  self::USER_TALK_PAGE, false, [
1361  'allowUsertalk' => true,
1362  ]
1363  ],
1364  'Sitewide block, allowing user talk but $wgBlockAllowsUTEdit is false' => [
1365  self::USER_TALK_PAGE, true, [
1366  'allowUsertalk' => true,
1367  'blockAllowsUTEdit' => false,
1368  ]
1369  ],
1370  'Partial block, blocking the page' => [
1371  'Test page', true, [
1372  'pageRestrictions' => [ 'Test page' ],
1373  ]
1374  ],
1375  'Partial block, not blocking the page' => [
1376  'Test page 2', false, [
1377  'pageRestrictions' => [ 'Test page' ],
1378  ]
1379  ],
1380  'Partial block, not allowing user talk but user talk page is not blocked' => [
1381  self::USER_TALK_PAGE, false, [
1382  'allowUsertalk' => false,
1383  'pageRestrictions' => [ 'Test page' ],
1384  ]
1385  ],
1386  'Partial block, allowing user talk but user talk page is blocked' => [
1387  self::USER_TALK_PAGE, true, [
1388  'allowUsertalk' => true,
1389  'pageRestrictions' => [ self::USER_TALK_PAGE ],
1390  ]
1391  ],
1392  'Partial block, user talk page is not blocked but $wgBlockAllowsUTEdit is false' => [
1393  self::USER_TALK_PAGE, false, [
1394  'allowUsertalk' => false,
1395  'pageRestrictions' => [ 'Test page' ],
1396  'blockAllowsUTEdit' => false,
1397  ]
1398  ],
1399  'Partial block, user talk page is blocked and $wgBlockAllowsUTEdit is false' => [
1400  self::USER_TALK_PAGE, true, [
1401  'allowUsertalk' => true,
1402  'pageRestrictions' => [ self::USER_TALK_PAGE ],
1403  'blockAllowsUTEdit' => false,
1404  ]
1405  ],
1406  'Partial user talk namespace block, not allowing user talk' => [
1407  self::USER_TALK_PAGE, true, [
1408  'allowUsertalk' => false,
1409  'namespaceRestrictions' => [ NS_USER_TALK ],
1410  ]
1411  ],
1412  'Partial user talk namespace block, allowing user talk' => [
1413  self::USER_TALK_PAGE, false, [
1414  'allowUsertalk' => true,
1415  'namespaceRestrictions' => [ NS_USER_TALK ],
1416  ]
1417  ],
1418  'Partial user talk namespace block, where $wgBlockAllowsUTEdit is false' => [
1419  self::USER_TALK_PAGE, true, [
1420  'allowUsertalk' => true,
1421  'namespaceRestrictions' => [ NS_USER_TALK ],
1422  'blockAllowsUTEdit' => false,
1423  ]
1424  ],
1425  ];
1426  }
1427 
1433  public function testIpBlockCookieSet() {
1434  $this->setMwGlobals( [
1435  'wgCookieSetOnIpBlock' => true,
1436  'wgCookiePrefix' => 'wiki',
1437  'wgSecretKey' => MWCryptRand::generateHex( 64, true ),
1438  ] );
1439 
1440  // setup block
1441  $block = new Block( [
1442  'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 5 * 60 * 60 ) ),
1443  ] );
1444  $block->setTarget( '1.2.3.4' );
1445  $block->setBlocker( $this->getTestSysop()->getUser() );
1446  $block->insert();
1447 
1448  // setup request
1449  $request = new FauxRequest();
1450  $request->setIP( '1.2.3.4' );
1451 
1452  // get user
1455 
1456  // test cookie was set
1457  $cookies = $request->response()->getCookies();
1458  $this->assertArrayHasKey( 'wikiBlockID', $cookies );
1459 
1460  // clean up
1461  $block->delete();
1462  }
1463 
1469  public function testIpBlockCookieNotSet() {
1470  $this->setMwGlobals( [
1471  'wgCookieSetOnIpBlock' => false,
1472  'wgCookiePrefix' => 'wiki',
1473  'wgSecretKey' => MWCryptRand::generateHex( 64, true ),
1474  ] );
1475 
1476  // setup block
1477  $block = new Block( [
1478  'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 5 * 60 * 60 ) ),
1479  ] );
1480  $block->setTarget( '1.2.3.4' );
1481  $block->setBlocker( $this->getTestSysop()->getUser() );
1482  $block->insert();
1483 
1484  // setup request
1485  $request = new FauxRequest();
1486  $request->setIP( '1.2.3.4' );
1487 
1488  // get user
1491 
1492  // test cookie was not set
1493  $cookies = $request->response()->getCookies();
1494  $this->assertArrayNotHasKey( 'wikiBlockID', $cookies );
1495 
1496  // clean up
1497  $block->delete();
1498  }
1499 
1506  $this->setMwGlobals( [
1507  'wgAutoblockExpiry' => 8000,
1508  'wgCookieSetOnIpBlock' => true,
1509  'wgCookiePrefix' => 'wiki',
1510  'wgSecretKey' => MWCryptRand::generateHex( 64, true ),
1511  ] );
1512 
1513  // setup block
1514  $block = new Block( [
1515  'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
1516  ] );
1517  $block->setTarget( '1.2.3.4' );
1518  $block->setBlocker( $this->getTestSysop()->getUser() );
1519  $block->insert();
1520 
1521  // setup request
1522  $request = new FauxRequest();
1523  $request->setIP( '1.2.3.4' );
1524  $request->getSession()->setUser( $this->getTestUser()->getUser() );
1525  $request->setCookie( 'BlockID', $block->getCookieValue() );
1526 
1527  // setup user
1529 
1530  // logged in users should be inmune to cookie block of type ip/range
1531  $this->assertFalse( $user->isBlocked() );
1532 
1533  // cookie is being cleared
1534  $cookies = $request->response()->getCookies();
1535  $this->assertEquals( '', $cookies['wikiBlockID']['value'] );
1536 
1537  // clean up
1538  $block->delete();
1539  }
1540 
1546  $clock = MWTimestamp::convert( TS_UNIX, '20100101000000' );
1547  MWTimestamp::setFakeTime( function () use ( &$clock ) {
1548  return $clock += 1000;
1549  } );
1550  try {
1551  $user = $this->getTestUser()->getUser();
1552  $firstRevision = self::makeEdit( $user, 'Help:UserTest_GetEditTimestamp', 'one', 'test' );
1553  $secondRevision = self::makeEdit( $user, 'Help:UserTest_GetEditTimestamp', 'two', 'test' );
1554  // Sanity check: revisions timestamp are different
1555  $this->assertNotEquals( $firstRevision->getTimestamp(), $secondRevision->getTimestamp() );
1556 
1557  $this->assertEquals( $firstRevision->getTimestamp(), $user->getFirstEditTimestamp() );
1558  $this->assertEquals( $secondRevision->getTimestamp(), $user->getLatestEditTimestamp() );
1559  } finally {
1560  MWTimestamp::setFakeTime( false );
1561  }
1562  }
1563 
1571  private static function makeEdit( User $user, $title, $content, $comment ) {
1573  $content = ContentHandler::makeContent( $content, $page->getTitle() );
1574  $updater = $page->newPageUpdater( $user );
1575  $updater->setContent( 'main', $content );
1576  return $updater->saveRevision( CommentStoreComment::newUnsavedComment( $comment ) );
1577  }
1578 }
MediaWiki\User\UserIdentityValue
Value object representing a user's identity.
Definition: UserIdentityValue.php:32
User\load
load( $flags=self::READ_NORMAL)
Load the user table data for this object from the source given by mFrom.
Definition: User.php:358
CommentStoreComment\newUnsavedComment
static newUnsavedComment( $comment, array $data=null)
Create a new, unsaved CommentStoreComment.
Definition: CommentStoreComment.php:66
UserTest\testGetCanonicalName
testGetCanonicalName( $name, $expectedArray)
User::getCanonicalName() provideGetCanonicalName.
Definition: UserTest.php:431
UserTest\provideGetGroupsWithPermission
static provideGetGroupsWithPermission()
Definition: UserTest.php:167
FauxRequest
WebRequest clone which takes values from a provided array.
Definition: FauxRequest.php:33
User\newFromId
static newFromId( $id)
Static factory method for creation from a given user ID.
Definition: User.php:609
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:306
UserTest\testGetFirstLatestEditTimestamp
testGetFirstLatestEditTimestamp()
User::getFirstEditTimestamp User::getLatestEditTimestamp.
Definition: UserTest.php:1545
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
UserTest\testSoftBlockRanges
testSoftBlockRanges()
User::getBlockedStatus.
Definition: UserTest.php:779
User\isValidPassword
isValidPassword( $password)
Is the input a valid password for this user?
Definition: User.php:1147
User\getId
getId()
Get the user's ID.
Definition: User.php:2425
UserTest\testCheckAndSetTouched
testCheckAndSetTouched()
User::checkAndSetTouched.
Definition: UserTest.php:528
User\isAnon
isAnon()
Get whether the user is anonymous.
Definition: User.php:3804
MediaWikiTestCase\mergeMwGlobalArrayValue
mergeMwGlobalArrayValue( $name, $values)
Merges the given values into a MW global array variable.
Definition: MediaWikiTestCase.php:904
UserTest\testIsIP
testIsIP( $value, $result, $message)
provideIPs User::isIP
Definition: UserTest.php:192
MediaWikiTestCase\getTestUser
static getTestUser( $groups=[])
Convenience method for getting an immutable test user.
Definition: MediaWikiTestCase.php:180
User\getActorId
getActorId(IDatabase $dbw=null)
Get the user's actor ID.
Definition: User.php:2491
User\isLocallyBlockedProxy
static isLocallyBlockedProxy( $ip)
Check if an IP address is in the local proxy list.
Definition: User.php:2061
UserTest\testIpBlockCookieIgnoredWhenUserLoggedIn
testIpBlockCookieIgnoredWhenUserLoggedIn()
When an ip user is blocked and then they log in, cookie block should be invalid and the cookie remove...
Definition: UserTest.php:1505
$wgRevokePermissions
$wgRevokePermissions
Permission keys revoked from users in each group.
Definition: DefaultSettings.php:5245
User\isBlockedFrom
isBlockedFrom( $title, $fromReplica=false)
Check if user is blocked from editing a particular article.
Definition: User.php:2306
User\getEditCount
getEditCount()
Get the user's edit count.
Definition: User.php:3695
User\incEditCount
incEditCount()
Schedule a deferred update to update the user's edit count.
Definition: User.php:5353
User\newFromSession
static newFromSession(WebRequest $request=null)
Create a new user object using data from session.
Definition: User.php:750
UserTest\testFindUsersByGroup
testFindUsersByGroup()
User::findUsersByGroup.
Definition: UserTest.php:549
UserTest\testActorId_old
testActorId_old()
Actor tests with SCHEMA_COMPAT_READ_OLD.
Definition: UserTest.php:1096
User\getBlockId
getBlockId()
If user is blocked, return the ID for the block.
Definition: User.php:2333
UserTest\testUserGetRightsHooks
testUserGetRightsHooks()
User::getRights.
Definition: UserTest.php:109
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUnknownUser':When a user doesn 't exist locally, this hook is called to give extensions an opportunity to auto-create it. If the auto-creation is successful, return false. $name:User name 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. & $title:Title object for the current page & $request:WebRequest & $ignoreRedirect:boolean to skip redirect check & $target:Title/string of redirect target & $article:Article object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) & $article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Array with elements of the form "language:title" in the order that they will be output. & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED since 1.28! Use HtmlPageLinkRendererBegin instead. Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1983
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1912
$wgDefaultUserOptions
$wgDefaultUserOptions
Settings added to this array will override the default globals for the user preferences used by anony...
Definition: DefaultSettings.php:4825
UserTest\testLoggedIn
testLoggedIn()
User::isLoggedIn User::isAnon.
Definition: UserTest.php:510
UserTest\makeEdit
static makeEdit(User $user, $title, $content, $comment)
Definition: UserTest.php:1571
UserTest\testAutoblockCookieInauthentic
testAutoblockCookieInauthentic()
Test that a modified BlockID cookie doesn't actually load the relevant block (T152951).
Definition: UserTest.php:818
UserTest\USER_TALK_PAGE
const USER_TALK_PAGE
Constant for self::testIsBlockedFrom.
Definition: UserTest.php:18
UserTest\$user
User $user
Definition: UserTest.php:23
UserTest\testOptions
testOptions()
Test changing user options.
Definition: UserTest.php:326
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:585
UserTest\provideIsLocallBlockedProxy
static provideIsLocallBlockedProxy()
Definition: UserTest.php:983
User\newFromAnyId
static newFromAnyId( $userId, $userName, $actorId)
Static factory method for creation from an ID, name, and/or actor ID.
Definition: User.php:676
User\newFromIdentity
static newFromIdentity(UserIdentity $identity)
Returns a User object corresponding to the given UserIdentity.
Definition: User.php:652
$res
$res
Definition: database.txt:21
User
User
Definition: All_system_messages.txt:425
User\newFromRow
static newFromRow( $row, $data=null)
Create a new user object from a user row.
Definition: User.php:772
UserTest\testGetId
testGetId()
User::getId.
Definition: UserTest.php:501
UserTest\testExperienceLevel
testExperienceLevel( $editCount, $memberSince, $expLevel)
User::getExperienceLevel provideExperienceLevel.
Definition: UserTest.php:949
UserTest\testIsBlockedFrom
testIsBlockedFrom( $title, $expect, array $options=[])
User::isBlockedFrom provideIsBlockedFrom.
Definition: UserTest.php:1308
UserTest\testIsLocallyBlockedProxy
testIsLocallyBlockedProxy( $ip, $blockListEntry)
provideIsLocallBlockedProxy User::isLocallyBlockedProxy
Definition: UserTest.php:994
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
User\getRights
getRights()
Get the permissions this user has.
Definition: User.php:3550
User\createNew
static createNew( $name, $params=[])
Add a user to the database, return the user object.
Definition: User.php:4303
UserTest\testGroupPermissions
testGroupPermissions()
User::getGroupPermissions.
Definition: UserTest.php:70
MediaWikiTestCase\overrideMwServices
overrideMwServices(Config $configOverrides=null, array $services=[])
Stashes the global instance of MediaWikiServices, and installs a new one, allowing test cases to over...
Definition: MediaWikiTestCase.php:937
UserTest\testBlockInstanceCache
testBlockInstanceCache()
User::getBlockedStatus User::getBlock User::blockedBy User::blockedFor User::isHidden User::isBlocked...
Definition: UserTest.php:1256
User\getTalkPage
getTalkPage()
Get this user's talk page title.
Definition: User.php:4559
User\addToDatabase
addToDatabase()
Add this existing user object to the database.
Definition: User.php:4379
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:925
WikiPage\factory
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:138
user
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such and we might be restricted by PHP settings such as safe mode or open_basedir We cannot assume that the software even has read access anywhere useful Many shared hosts run all users web applications under the same user
Definition: distributors.txt:9
User\checkPasswordValidity
checkPasswordValidity( $password)
Check if this is a valid password for this user.
Definition: User.php:1202
User\blockedFor
blockedFor()
If user is blocked, return the specified reason for the block.
Definition: User.php:2324
UserTest\testAnonOptions
testAnonOptions()
T39963 Make sure defaults are loaded when setOption is called.
Definition: UserTest.php:355
UserTest\provideIsBlockedFrom
static provideIsBlockedFrom()
Definition: UserTest.php:1351
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2636
MediaWikiTestCase\setMwGlobals
setMwGlobals( $pairs, $value=null)
Sets a global, maintaining a stashed version of the previous global to be restored in tearDown.
Definition: MediaWikiTestCase.php:709
UserTest\testGetEditCountForAnons
testGetEditCountForAnons()
Test User::editCount medium User::getEditCount.
Definition: UserTest.php:286
MediaWikiTestCase
Definition: MediaWikiTestCase.php:17
UserTest\testAutoblockCookieNoSecretKey
testAutoblockCookieNoSecretKey()
The BlockID cookie is normally verified with a HMAC, but not if wgSecretKey is not set.
Definition: UserTest.php:863
UserTest\testIpBlockCookieNotSet
testIpBlockCookieNotSet()
Block cookie should NOT be set when wgCookieSetOnIpBlock is disabled User::trackBlockWithCookie.
Definition: UserTest.php:1469
MediaWikiTestCase\hideDeprecated
hideDeprecated( $function)
Don't throw a warning if $function is deprecated and called later.
Definition: MediaWikiTestCase.php:1974
User\isHidden
isHidden()
Check if user account is hidden.
Definition: User.php:2408
UserTest\testCheckPasswordValidity
testCheckPasswordValidity()
Test password validity checks.
Definition: UserTest.php:371
MediaWiki
A helper class for throttling authentication attempts.
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
UserTest\setUpPermissionGlobals
setUpPermissionGlobals()
Definition: UserTest.php:40
User\isPingLimitable
isPingLimitable()
Is this user subject to rate limiting?
Definition: User.php:2107
MediaWikiTestCase\$users
static TestUser[] $users
Definition: MediaWikiTestCase.php:53
UserTest
Database.
Definition: UserTest.php:15
User\clearInstanceCache
clearInstanceCache( $reloadFrom=false)
Clear various cached data stored in this object.
Definition: User.php:1734
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:576
UserTest\testUserPermissions
testUserPermissions()
User::getRights.
Definition: UserTest.php:98
UserTest\testIpBlockCookieSet
testIpBlockCookieSet()
Block cookie should be set for IP Blocks if wgCookieSetOnIpBlock is set to true User::trackBlockWithC...
Definition: UserTest.php:1433
User\getBlock
getBlock( $fromReplica=true)
Get the block affecting the user, or null if the user is not blocked.
Definition: User.php:2289
User\setName
setName( $str)
Set the user name.
Definition: User.php:2480
DB_MASTER
const DB_MASTER
Definition: defines.php:26
array
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
User\saveSettings
saveSettings()
Save this user's settings into the database.
Definition: User.php:4190
ContentHandler\makeContent
static makeContent( $text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
Definition: ContentHandler.php:133
UserTest\testIsPingLimitable
testIsPingLimitable()
User::isPingLimitable.
Definition: UserTest.php:910
User\getFirstEditTimestamp
getFirstEditTimestamp()
Get the timestamp of the first edit.
Definition: User.php:4964
null
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not null
Definition: hooks.txt:780
$request
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on $request
Definition: hooks.txt:2636
UserTest\provideIPs
static provideIPs()
Definition: UserTest.php:196
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:271
any
they could even be mouse clicks or menu items whatever suits your program You should also get your if any
Definition: COPYING.txt:326
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:67
Block\getIdFromCookieValue
static getIdFromCookieValue( $cookieValue)
Get the stored ID from the 'BlockID' cookie.
Definition: Block.php:1851
$value
$value
Definition: styleTest.css.php:49
User\getOption
getOption( $oname, $defaultOverride=null, $ignoreHidden=false)
Get the user's current setting for a given option.
Definition: User.php:3172
MediaWikiTestCase\getMutableTestUser
static getMutableTestUser( $groups=[])
Convenience method for getting a mutable test user.
Definition: MediaWikiTestCase.php:192
MediaWiki\Session\TestUtils\getDummySession
static getDummySession( $backend=null, $index=-1, $logger=null)
If you need a Session for testing but don't want to create a backend to construct one,...
Definition: TestUtils.php:86
UserTest\testAutoblockCookiesDisabled
testAutoblockCookiesDisabled()
Make sure that no cookie is set to track autoblocked users when $wgCookieSetOnAutoblock is false.
Definition: UserTest.php:669
UserTest\provideGetCanonicalName
static provideGetCanonicalName()
Definition: UserTest.php:455
User\checkAndSetTouched
checkAndSetTouched()
Bump user_touched if it didn't change since this object was loaded.
Definition: User.php:1696
UserTest\provideExperienceLevel
provideExperienceLevel()
Definition: UserTest.php:932
User\getGroupPermissions
static getGroupPermissions( $groups)
Get the permissions associated with a given list of groups.
Definition: User.php:5015
MediaWikiTestCase\getTestSysop
static getTestSysop()
Convenience method for getting an immutable admin test user.
Definition: MediaWikiTestCase.php:204
UserTest\testIncEditCount
testIncEditCount()
Test User::editCount medium User::incEditCount.
Definition: UserTest.php:307
UserTest\testAutoblockCookieInfiniteExpiry
testAutoblockCookieInfiniteExpiry()
When a user is autoblocked and a cookie is set to track them, the expiry time of the cookie should ma...
Definition: UserTest.php:715
UserTest\setUp
setUp()
Definition: UserTest.php:25
MWCryptRand\generateHex
static generateHex( $chars)
Generate a run of cryptographically random data and return it in hexadecimal string format.
Definition: MWCryptRand.php:74
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:430
User\getQueryInfo
static getQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new user object.
Definition: User.php:5646
User\blockedBy
blockedBy()
If user is blocked, return the name of the user who placed the block.
Definition: User.php:2315
User\getDBTouched
getDBTouched()
Get the user_touched timestamp field (time of last DB updates)
Definition: User.php:2871
UserTest\testGetGroupsWithPermission
testGetGroupsWithPermission( $expected, $right)
provideGetGroupsWithPermission User::getGroupsWithPermission
Definition: UserTest.php:159
WebRequest
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form stripping il...
Definition: WebRequest.php:41
MediaWikiTestCase\getExistingTestPage
getExistingTestPage( $title=null)
Returns a WikiPage representing an existing page.
Definition: MediaWikiTestCase.php:220
MediaWiki\Block\Restriction\NamespaceRestriction
Definition: NamespaceRestriction.php:25
Block\TYPE_USER
const TYPE_USER
Definition: Block.php:96
User\getExperienceLevel
getExperienceLevel()
Compute experienced level based on edit count and registration date.
Definition: User.php:4068
UserTest\testNewFromAnyId
testNewFromAnyId()
User::newFromAnyId.
Definition: UserTest.php:1167
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1985
ObjectCache\getMainWANInstance
static getMainWANInstance()
Get the main WAN cache object.
Definition: ObjectCache.php:369
User\isLoggedIn
isLoggedIn()
Get whether the user is logged in.
Definition: User.php:3796
MediaWiki\Block\Restriction\PageRestriction
Definition: PageRestriction.php:25
SCHEMA_COMPAT_NEW
const SCHEMA_COMPAT_NEW
Definition: Defines.php:291
User\findUsersByGroup
static findUsersByGroup( $groups, $limit=5000, $after=null)
Return the users who are members of the given group(s).
Definition: User.php:1077
User\getCanonicalName
static getCanonicalName( $name, $validate='valid')
Given unvalidated user input, return a canonical username, or false if the username is invalid.
Definition: User.php:1244
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
Block
Definition: Block.php:31
SCHEMA_COMPAT_WRITE_BOTH
const SCHEMA_COMPAT_WRITE_BOTH
Definition: Defines.php:288
User\isBlocked
isBlocked( $fromReplica=true)
Check if user is blocked.
Definition: User.php:2278
User\newFromActorId
static newFromActorId( $id)
Static factory method for creation from a given actor ID.
Definition: User.php:624
UserTest\testExperienceLevelAnon
testExperienceLevelAnon()
User::getExperienceLevel.
Definition: UserTest.php:977
$updater
$page->newPageUpdater($user) $updater
Definition: pageupdater.txt:63
true
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
Definition: hooks.txt:1985
$content
$content
Definition: pageupdater.txt:72
User\equals
equals(UserIdentity $user)
Checks if two user objects point to the same user.
Definition: User.php:5732
User\selectFields
static selectFields()
Return the list of user fields that should be selected to create a new user object.
Definition: User.php:5620
$wgGroupPermissions
$wgGroupPermissions['sysop']['replacetext']
Definition: ReplaceText.php:56
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
MediaWikiServices
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency MediaWikiServices
Definition: injection.txt:23
User\purge
static purge( $wikiId, $userId)
Definition: User.php:488
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:48
User\setOption
setOption( $oname, $val)
Set the given option for a user.
Definition: User.php:3259
$username
this hook is for auditing only or null if authentication failed before getting that far $username
Definition: hooks.txt:780
UserTest\provideUserNames
static provideUserNames()
Definition: UserTest.php:221
MediaWikiTestCase\markTestSkippedIfDbType
markTestSkippedIfDbType( $type)
Skip the test if using the specified database type.
Definition: MediaWikiTestCase.php:2303
User\getPasswordValidity
getPasswordValidity( $password)
Given unvalidated password input, return error message on failure.
Definition: User.php:1159
UserTest\testRevokePermissions
testRevokePermissions()
User::getGroupPermissions.
Definition: UserTest.php:87
User\getName
getName()
Get the user name, or the IP of an anonymous user.
Definition: User.php:2452
UserTest\testGetEditCount
testGetEditCount()
Test User::editCount medium User::getEditCount.
Definition: UserTest.php:249
UserTest\testEquals
testEquals()
User::equals.
Definition: UserTest.php:477
UserTest\testActorId
testActorId()
User::newFromActorId.
Definition: UserTest.php:1032
SCHEMA_COMPAT_READ_OLD
const SCHEMA_COMPAT_READ_OLD
Definition: Defines.php:285
User\getLatestEditTimestamp
getLatestEditTimestamp()
Get the timestamp of the latest edit.
Definition: User.php:4975
MediaWikiTestCase\$db
Database $db
Primary database.
Definition: MediaWikiTestCase.php:61
User\getGroupsWithPermission
static getGroupsWithPermission( $role)
Get all the groups who have a given permission.
Definition: User.php:5042
UserTest\testIsValidUserName
testIsValidUserName( $username, $result, $message)
provideUserNames User::isValidUserName
Definition: UserTest.php:217
UserTest\testAutoblockCookies
testAutoblockCookies()
When a user is autoblocked a cookie is set with which to track them in case they log out and change I...
Definition: UserTest.php:588
UserTest\testNewFromIdentity
testNewFromIdentity()
User::newFromIdentity.
Definition: UserTest.php:1217
User\trackBlockWithCookie
trackBlockWithCookie()
Set the 'BlockID' cookie depending on block type and user authentication status.
Definition: User.php:1405