MediaWiki  master
ApiUserrightsTest.php
Go to the documentation of this file.
1 <?php
2 
4 
13 
14  protected function setUp() {
15  parent::setUp();
16  $this->tablesUsed = array_merge(
17  $this->tablesUsed,
18  [ 'change_tag', 'change_tag_def', 'logging' ]
19  );
20  }
21 
30  protected function setPermissions( $add = [], $remove = [] ) {
31  $this->setGroupPermissions( 'bureaucrat', 'userrights', false );
32 
33  if ( $add ) {
34  $this->mergeMwGlobalArrayValue( 'wgAddGroups', [ 'bureaucrat' => $add ] );
35  }
36  if ( $remove ) {
37  $this->mergeMwGlobalArrayValue( 'wgRemoveGroups', [ 'bureaucrat' => $remove ] );
38  }
39  }
40 
55  protected function doSuccessfulRightsChange(
56  $expectedGroups = 'sysop', array $params = [], User $user = null
57  ) {
58  $expectedGroups = (array)$expectedGroups;
59  $params['action'] = 'userrights';
60 
61  if ( !$user ) {
62  $user = $this->getMutableTestUser()->getUser();
63  }
64 
65  $this->assertTrue( TestUserRegistry::isMutable( $user ),
66  'Immutable user passed to doSuccessfulRightsChange!' );
67 
68  if ( !isset( $params['user'] ) && !isset( $params['userid'] ) ) {
69  $params['user'] = $user->getName();
70  }
71  if ( !isset( $params['add'] ) && !isset( $params['remove'] ) ) {
72  $params['add'] = 'sysop';
73  }
74 
76 
77  $user->clearInstanceCache();
78  $this->assertSame( $expectedGroups, $user->getGroups() );
79 
80  $this->assertArrayNotHasKey( 'warnings', $res[0] );
81  }
82 
92  protected function doFailedRightsChange(
93  $expectedException, array $params = [], User $user = null
94  ) {
95  $params['action'] = 'userrights';
96 
97  $this->setExpectedException( ApiUsageException::class, $expectedException );
98 
99  if ( !$user ) {
100  // If 'user' or 'userid' is specified and $user was not specified,
101  // the user we're creating now will have nothing to do with the API
102  // request, but that's okay, since we're just testing that it has
103  // no groups.
104  $user = $this->getMutableTestUser()->getUser();
105  }
106 
107  $this->assertTrue( TestUserRegistry::isMutable( $user ),
108  'Immutable user passed to doFailedRightsChange!' );
109 
110  if ( !isset( $params['user'] ) && !isset( $params['userid'] ) ) {
111  $params['user'] = $user->getName();
112  }
113  if ( !isset( $params['add'] ) && !isset( $params['remove'] ) ) {
114  $params['add'] = 'sysop';
115  }
116  $expectedGroups = $user->getGroups();
117 
118  try {
119  $this->doApiRequestWithToken( $params );
120  } finally {
121  $user->clearInstanceCache();
122  $this->assertSame( $expectedGroups, $user->getGroups() );
123  }
124  }
125 
126  public function testAdd() {
127  $this->doSuccessfulRightsChange();
128  }
129 
130  public function testBlockedWithUserrights() {
131  global $wgUser;
132 
133  $block = new DatabaseBlock( [ 'address' => $wgUser, 'by' => $wgUser->getId(), ] );
134  $block->insert();
135 
136  try {
137  $this->doSuccessfulRightsChange();
138  } finally {
139  $block->delete();
140  $wgUser->clearInstanceCache();
141  }
142  }
143 
144  public function testBlockedWithoutUserrights() {
145  $user = $this->getTestSysop()->getUser();
146 
147  $this->setPermissions( true, true );
148 
149  $block = new DatabaseBlock( [ 'address' => $user, 'by' => $user->getId() ] );
150  $block->insert();
151 
152  try {
153  $this->doFailedRightsChange( 'You have been blocked from editing.' );
154  } finally {
155  $block->delete();
156  $user->clearInstanceCache();
157  }
158  }
159 
160  public function testAddMultiple() {
162  [ 'bureaucrat', 'sysop' ],
163  [ 'add' => 'bureaucrat|sysop' ]
164  );
165  }
166 
167  public function testTooFewExpiries() {
168  $this->doFailedRightsChange(
169  '2 expiry timestamps were provided where 3 were needed.',
170  [ 'add' => 'sysop|bureaucrat|bot', 'expiry' => 'infinity|tomorrow' ]
171  );
172  }
173 
174  public function testTooManyExpiries() {
175  $this->doFailedRightsChange(
176  '3 expiry timestamps were provided where 2 were needed.',
177  [ 'add' => 'sysop|bureaucrat', 'expiry' => 'infinity|tomorrow|never' ]
178  );
179  }
180 
181  public function testInvalidExpiry() {
182  $this->doFailedRightsChange( 'Invalid expiry time', [ 'expiry' => 'yummy lollipops!' ] );
183  }
184 
185  public function testMultipleInvalidExpiries() {
186  $this->doFailedRightsChange(
187  'Invalid expiry time "foo".',
188  [ 'add' => 'sysop|bureaucrat', 'expiry' => 'foo|bar' ]
189  );
190  }
191 
192  public function testWithTag() {
193  ChangeTags::defineTag( 'custom tag' );
194 
195  $user = $this->getMutableTestUser()->getUser();
196 
197  $this->doSuccessfulRightsChange( 'sysop', [ 'tags' => 'custom tag' ], $user );
198 
199  $dbr = wfGetDB( DB_REPLICA );
200  $this->assertSame(
201  'custom tag',
202  $dbr->selectField(
203  [ 'change_tag', 'logging', 'change_tag_def' ],
204  'ctd_name',
205  [
206  'ct_log_id = log_id',
207  'log_namespace' => NS_USER,
208  'log_title' => strtr( $user->getName(), ' ', '_' )
209  ],
210  __METHOD__,
211  [ 'change_tag_def' => [ 'JOIN', 'ctd_id = ct_tag_id' ] ]
212  )
213  );
214  }
215 
216  public function testWithoutTagPermission() {
217  ChangeTags::defineTag( 'custom tag' );
218 
219  $this->setGroupPermissions( 'user', 'applychangetags', false );
220 
221  $this->doFailedRightsChange(
222  'You do not have permission to apply change tags along with your changes.',
223  [ 'tags' => 'custom tag' ]
224  );
225  }
226 
227  public function testNonexistentUser() {
228  $this->doFailedRightsChange(
229  'There is no user by the name "Nonexistent user". Check your spelling.',
230  [ 'user' => 'Nonexistent user' ]
231  );
232  }
233 
234  public function testWebToken() {
235  $sysop = $this->getTestSysop()->getUser();
236  $user = $this->getMutableTestUser()->getUser();
237 
238  $token = $sysop->getEditToken( $user->getName() );
239 
240  $res = $this->doApiRequest( [
241  'action' => 'userrights',
242  'user' => $user->getName(),
243  'add' => 'sysop',
244  'token' => $token,
245  ] );
246 
247  $user->clearInstanceCache();
248  $this->assertSame( [ 'sysop' ], $user->getGroups() );
249 
250  $this->assertArrayNotHasKey( 'warnings', $res[0] );
251  }
252 
261  private function getMockForProcessingExpiries( $canProcessExpiries ) {
262  $sysop = $this->getTestSysop()->getUser();
263  $user = $this->getMutableTestUser()->getUser();
264 
265  $token = $sysop->getEditToken( 'userrights' );
266 
267  $main = new ApiMain( new FauxRequest( [
268  'action' => 'userrights',
269  'user' => $user->getName(),
270  'add' => 'sysop',
271  'token' => $token,
272  ] ) );
273 
274  $mockUserRightsPage = $this->getMockBuilder( UserrightsPage::class )
275  ->setMethods( [ 'canProcessExpiries' ] )
276  ->getMock();
277  $mockUserRightsPage->method( 'canProcessExpiries' )->willReturn( $canProcessExpiries );
278 
279  $mockApi = $this->getMockBuilder( ApiUserrights::class )
280  ->setConstructorArgs( [ $main, 'userrights' ] )
281  ->setMethods( [ 'getUserRightsPage' ] )
282  ->getMock();
283  $mockApi->method( 'getUserRightsPage' )->willReturn( $mockUserRightsPage );
284 
285  return $mockApi;
286  }
287 
288  public function testCanProcessExpiries() {
289  $mock1 = $this->getMockForProcessingExpiries( true );
290  $this->assertArrayHasKey( 'expiry', $mock1->getAllowedParams() );
291 
292  $mock2 = $this->getMockForProcessingExpiries( false );
293  $this->assertArrayNotHasKey( 'expiry', $mock2->getAllowedParams() );
294  }
295 
305  public function testAddAndRemoveGroups(
306  array $permissions = null, array $groupsToChange, array $expectedGroups
307  ) {
308  if ( $permissions !== null ) {
309  $this->setPermissions( $permissions[0], $permissions[1] );
310  }
311 
312  $params = [
313  'add' => implode( '|', $groupsToChange[0] ),
314  'remove' => implode( '|', $groupsToChange[1] ),
315  ];
316 
317  // We'll take a bot so we have a group to remove
318  $user = $this->getMutableTestUser( [ 'bot' ] )->getUser();
319 
320  $this->doSuccessfulRightsChange( $expectedGroups, $params, $user );
321  }
322 
323  public function addAndRemoveGroupsProvider() {
324  return [
325  'Simple add' => [
326  [ [ 'sysop' ], [] ],
327  [ [ 'sysop' ], [] ],
328  [ 'bot', 'sysop' ]
329  ], 'Add with only remove permission' => [
330  [ [], [ 'sysop' ] ],
331  [ [ 'sysop' ], [] ],
332  [ 'bot' ],
333  ], 'Add with global remove permission' => [
334  [ [], true ],
335  [ [ 'sysop' ], [] ],
336  [ 'bot' ],
337  ], 'Simple remove' => [
338  [ [], [ 'bot' ] ],
339  [ [], [ 'bot' ] ],
340  [],
341  ], 'Remove with only add permission' => [
342  [ [ 'bot' ], [] ],
343  [ [], [ 'bot' ] ],
344  [ 'bot' ],
345  ], 'Remove with global add permission' => [
346  [ true, [] ],
347  [ [], [ 'bot' ] ],
348  [ 'bot' ],
349  ], 'Add and remove same new group' => [
350  null,
351  [ [ 'sysop' ], [ 'sysop' ] ],
352  // The userrights code does removals before adds, so it doesn't remove the sysop
353  // group here and only adds it.
354  [ 'bot', 'sysop' ],
355  ], 'Add and remove same existing group' => [
356  null,
357  [ [ 'bot' ], [ 'bot' ] ],
358  // But here it first removes the existing group and then re-adds it.
359  [ 'bot' ],
360  ],
361  ];
362  }
363 }
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
testAddAndRemoveGroups(array $permissions=null, array $groupsToChange, array $expectedGroups)
Tests adding and removing various groups with various permissions.
getMockForProcessingExpiries( $canProcessExpiries)
Helper for testCanProcessExpiries that returns a mock ApiUserrights that either can or cannot process...
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
doApiRequest(array $params, array $session=null, $appendModule=false, User $user=null, $tokenType=null)
Does the API request and returns the result.
Definition: ApiTestCase.php:62
API Database medium.
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
mergeMwGlobalArrayValue( $name, $values)
Merges the given values into a MW global array variable.
doApiRequestWithToken(array $params, array $session=null, User $user=null, $tokenType='auto')
Convenience function to access the token parameter of doApiRequest() more succinctly.
The User object encapsulates all of the user-specific settings (user_id, name, rights, email address, options, last login time).
Definition: User.php:51
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:1982
insert( $dbw=null)
Insert a block into the block table.
static getTestSysop()
Convenience method for getting an immutable admin test user.
setGroupPermissions( $newPerms, $newKey=null, $newValue=null)
Alters $wgGroupPermissions for the duration of the test.
static getMutableTestUser( $groups=[])
Convenience method for getting a mutable test user.
$res
Definition: database.txt:21
$params
This is the main API class, used for both external and internal processing.
Definition: ApiMain.php:41
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
setPermissions( $add=[], $remove=[])
Unsets $wgGroupPermissions[&#39;bureaucrat&#39;][&#39;userrights&#39;], and sets $wgAddGroups[&#39;bureaucrat&#39;] and $wgRe...
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
static defineTag( $tag)
Set ctd_user_defined = 1 in change_tag_def without checking that the tag name is valid.
Definition: ChangeTags.php:881
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
static isMutable(User $user)
doFailedRightsChange( $expectedException, array $params=[], User $user=null)
Perform an API userrights request that&#39;s expected to fail.
const DB_REPLICA
Definition: defines.php:25
return true to allow those checks to and false if checking is done & $user
Definition: hooks.txt:1473
doSuccessfulRightsChange( $expectedGroups='sysop', array $params=[], User $user=null)
Perform an API userrights request that&#39;s expected to be successful.