2019-03-07 20:02:07 +00:00
|
|
|
<?php
|
|
|
|
|
|
2020-10-18 01:02:40 +00:00
|
|
|
namespace MediaWiki\Tests\Integration\Permissions;
|
2019-03-07 20:02:07 +00:00
|
|
|
|
|
|
|
|
use Action;
|
2018-11-01 23:29:22 +00:00
|
|
|
use ContentHandler;
|
2019-05-13 14:18:07 +00:00
|
|
|
use MediaWiki\Block\DatabaseBlock;
|
2021-06-02 12:45:38 +00:00
|
|
|
use MediaWiki\Block\Restriction\ActionRestriction;
|
2019-03-07 20:02:07 +00:00
|
|
|
use MediaWiki\Block\Restriction\NamespaceRestriction;
|
|
|
|
|
use MediaWiki\Block\Restriction\PageRestriction;
|
Separate Block into AbstractBlock, Block and SystemBlock
This commit splits the existing Block class into AbstractBlock, Block
and SystemBlock.
Before this patch, the Block class represents several types of
blocks, which can be separated into blocks stored in the database,
and temporary blocks created by the system. These are now
represented by Block and SystemBlock, which inherit from
AbstractBlock.
This lays the foundations for:
* enforcing block parameters from multiple blocks that apply to a
user/IP address
* improvements to the Block API, including the addition of services
Breaking changes: functions expecting a Block object should still
expect a Block object if it came from the database, but other
functions may now need to expect an AbstractBlock or SystemBlock
object. (Note that an alternative naming scheme, in which the
abstract class is called Block and the subclasses are DatabaseBlock
and SystemBlock, avoids this breakage. However, it introduces more
breakages to calls to static Block methods and new Block
instantiations.)
Changes to tests: system blocks don't set the $blockCreateAccount or
$mExipry block properties, so remove/change any tests that assume
they do.
Bug: T222737
Change-Id: I83bceb5e5049e254c90ace060f8f8fad44696c67
2019-03-18 22:09:49 +00:00
|
|
|
use MediaWiki\Block\SystemBlock;
|
2021-07-26 13:24:22 +00:00
|
|
|
use MediaWiki\Cache\CacheKeyHelper;
|
2022-07-07 22:54:32 +00:00
|
|
|
use MediaWiki\MainConfigNames;
|
2022-04-11 01:39:33 +00:00
|
|
|
use MediaWiki\Permissions\PermissionManager;
|
2022-10-28 10:04:25 +00:00
|
|
|
use MediaWiki\Request\FauxRequest;
|
2018-11-01 23:29:22 +00:00
|
|
|
use MediaWiki\Revision\MutableRevisionRecord;
|
2023-01-03 15:24:42 +00:00
|
|
|
use MediaWiki\Revision\SlotRecord;
|
2018-11-01 23:29:22 +00:00
|
|
|
use MediaWiki\Session\SessionId;
|
|
|
|
|
use MediaWiki\Session\TestUtils;
|
2021-06-02 09:44:38 +00:00
|
|
|
use MediaWiki\User\UserIdentityValue;
|
2018-11-01 23:29:22 +00:00
|
|
|
use MediaWikiLangTestCase;
|
2021-11-30 11:26:39 +00:00
|
|
|
use Message;
|
2018-11-01 23:29:22 +00:00
|
|
|
use RequestContext;
|
2020-03-02 13:38:29 +00:00
|
|
|
use stdClass;
|
2020-01-10 00:00:51 +00:00
|
|
|
use TestAllServiceOptionsUsed;
|
2018-11-01 23:29:22 +00:00
|
|
|
use Title;
|
|
|
|
|
use User;
|
2020-01-10 00:00:51 +00:00
|
|
|
use Wikimedia\ScopedCallback;
|
2019-04-09 06:58:04 +00:00
|
|
|
use Wikimedia\TestingAccessWrapper;
|
2019-03-07 20:02:07 +00:00
|
|
|
|
|
|
|
|
/**
|
2022-06-01 23:38:22 +00:00
|
|
|
* For the pure unit tests, see \MediaWiki\Tests\Unit\Permissions\PermissionManagerTest.
|
2020-10-18 01:02:40 +00:00
|
|
|
*
|
2022-06-01 23:38:22 +00:00
|
|
|
* @group Database
|
2019-03-07 20:02:07 +00:00
|
|
|
* @covers \MediaWiki\Permissions\PermissionManager
|
|
|
|
|
*/
|
|
|
|
|
class PermissionManagerTest extends MediaWikiLangTestCase {
|
2019-08-21 05:28:47 +00:00
|
|
|
use TestAllServiceOptionsUsed;
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2022-06-01 23:38:22 +00:00
|
|
|
/** @var string */
|
|
|
|
|
protected $userName;
|
|
|
|
|
/** @var Title */
|
2019-03-07 20:02:07 +00:00
|
|
|
protected $title;
|
2022-06-01 23:38:22 +00:00
|
|
|
/** @var User */
|
|
|
|
|
protected $user;
|
|
|
|
|
/** @var User */
|
|
|
|
|
protected $anonUser;
|
|
|
|
|
/** @var User */
|
|
|
|
|
protected $userUser;
|
|
|
|
|
/** @var User */
|
|
|
|
|
protected $altUser;
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2020-05-16 00:27:13 +00:00
|
|
|
private const USER_TALK_PAGE = '<user talk page>';
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2021-07-22 03:11:47 +00:00
|
|
|
protected function setUp(): void {
|
2019-03-07 20:02:07 +00:00
|
|
|
parent::setUp();
|
|
|
|
|
|
|
|
|
|
$localZone = 'UTC';
|
|
|
|
|
$localOffset = date( 'Z' ) / 60;
|
|
|
|
|
|
2022-07-07 22:54:32 +00:00
|
|
|
$this->overrideConfigValues( [
|
|
|
|
|
MainConfigNames::Localtimezone => $localZone,
|
|
|
|
|
MainConfigNames::LocalTZoffset => $localOffset,
|
|
|
|
|
MainConfigNames::RevokePermissions => [
|
2019-04-09 06:58:04 +00:00
|
|
|
'formertesters' => [
|
|
|
|
|
'runtest' => true
|
|
|
|
|
]
|
|
|
|
|
],
|
2022-07-07 22:54:32 +00:00
|
|
|
MainConfigNames::AvailableRights => [
|
2019-04-09 06:58:04 +00:00
|
|
|
'test',
|
|
|
|
|
'runtest',
|
|
|
|
|
'writetest',
|
|
|
|
|
'nukeworld',
|
|
|
|
|
'modifytest',
|
2020-10-15 21:37:09 +00:00
|
|
|
'editmyoptions',
|
|
|
|
|
'editinterface',
|
|
|
|
|
|
|
|
|
|
// Interface admin
|
|
|
|
|
'editsitejs',
|
|
|
|
|
'edituserjs',
|
|
|
|
|
|
|
|
|
|
// Admin
|
|
|
|
|
'delete',
|
|
|
|
|
'undelete',
|
|
|
|
|
'deletedhistory',
|
|
|
|
|
'deletedtext',
|
2019-04-09 06:58:04 +00:00
|
|
|
]
|
2019-03-07 20:02:07 +00:00
|
|
|
] );
|
2019-04-09 06:58:04 +00:00
|
|
|
|
|
|
|
|
$this->setGroupPermissions( 'unittesters', 'test', true );
|
|
|
|
|
$this->setGroupPermissions( 'unittesters', 'runtest', true );
|
|
|
|
|
$this->setGroupPermissions( 'unittesters', 'writetest', false );
|
|
|
|
|
$this->setGroupPermissions( 'unittesters', 'nukeworld', false );
|
|
|
|
|
|
|
|
|
|
$this->setGroupPermissions( 'testwriters', 'test', true );
|
|
|
|
|
$this->setGroupPermissions( 'testwriters', 'writetest', true );
|
|
|
|
|
$this->setGroupPermissions( 'testwriters', 'modifytest', true );
|
|
|
|
|
|
|
|
|
|
$this->setGroupPermissions( '*', 'editmyoptions', true );
|
|
|
|
|
|
2020-10-15 21:37:09 +00:00
|
|
|
$this->setGroupPermissions( 'interface-admin', 'editinterface', true );
|
|
|
|
|
$this->setGroupPermissions( 'interface-admin', 'editsitejs', true );
|
|
|
|
|
$this->setGroupPermissions( 'interface-admin', 'edituserjs', true );
|
|
|
|
|
$this->setGroupPermissions( 'sysop', 'editinterface', true );
|
|
|
|
|
$this->setGroupPermissions( 'sysop', 'delete', true );
|
|
|
|
|
$this->setGroupPermissions( 'sysop', 'undelete', true );
|
|
|
|
|
$this->setGroupPermissions( 'sysop', 'deletedhistory', true );
|
|
|
|
|
$this->setGroupPermissions( 'sysop', 'deletedtext', true );
|
|
|
|
|
|
2019-03-07 20:02:07 +00:00
|
|
|
// Without this testUserBlock will use a non-English context on non-English MediaWiki
|
|
|
|
|
// installations (because of how Title::checkUserBlock is implemented) and fail.
|
|
|
|
|
RequestContext::resetMain();
|
|
|
|
|
|
|
|
|
|
$this->userName = 'Useruser';
|
2022-06-01 23:38:22 +00:00
|
|
|
$altUserName = 'Altuseruser';
|
2019-03-07 20:02:07 +00:00
|
|
|
date_default_timezone_set( $localZone );
|
|
|
|
|
|
2021-11-17 15:20:38 +00:00
|
|
|
/**
|
|
|
|
|
* TODO: We should provision title object(s) via providers not in here
|
|
|
|
|
* in order for us to avoid setting mInterwiki via reflection property.
|
|
|
|
|
*/
|
2019-03-07 20:02:07 +00:00
|
|
|
$this->title = Title::makeTitle( NS_MAIN, "Main Page" );
|
|
|
|
|
if ( !isset( $this->userUser ) || !( $this->userUser instanceof User ) ) {
|
|
|
|
|
$this->userUser = User::newFromName( $this->userName );
|
|
|
|
|
|
|
|
|
|
if ( !$this->userUser->getId() ) {
|
|
|
|
|
$this->userUser = User::createNew( $this->userName, [
|
|
|
|
|
"email" => "test@example.com",
|
|
|
|
|
"real_name" => "Test User" ] );
|
|
|
|
|
$this->userUser->load();
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-01 23:38:22 +00:00
|
|
|
$this->altUser = User::newFromName( $altUserName );
|
2019-03-07 20:02:07 +00:00
|
|
|
if ( !$this->altUser->getId() ) {
|
2022-06-01 23:38:22 +00:00
|
|
|
$this->altUser = User::createNew( $altUserName, [
|
2019-03-07 20:02:07 +00:00
|
|
|
"email" => "alttest@example.com",
|
|
|
|
|
"real_name" => "Test User Alt" ] );
|
|
|
|
|
$this->altUser->load();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->anonUser = User::newFromId( 0 );
|
|
|
|
|
|
|
|
|
|
$this->user = $this->userUser;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function setTitle( $ns, $title = "Main_Page" ) {
|
|
|
|
|
$this->title = Title::makeTitle( $ns, $title );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function setUser( $userName = null ) {
|
|
|
|
|
if ( $userName === 'anon' ) {
|
|
|
|
|
$this->user = $this->anonUser;
|
|
|
|
|
} elseif ( $userName === null || $userName === $this->userName ) {
|
|
|
|
|
$this->user = $this->userUser;
|
|
|
|
|
} else {
|
|
|
|
|
$this->user = $this->altUser;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2021-03-29 18:35:33 +00:00
|
|
|
* @dataProvider provideSpecialsAndNSPermissions
|
2019-03-07 20:02:07 +00:00
|
|
|
*/
|
2021-03-29 18:35:33 +00:00
|
|
|
public function testSpecialsAndNSPermissions(
|
|
|
|
|
$namespace,
|
|
|
|
|
$userPerms,
|
|
|
|
|
$namespaceProtection,
|
|
|
|
|
$expectedPermErrors,
|
|
|
|
|
$expectedUserCan
|
|
|
|
|
) {
|
2019-03-07 20:02:07 +00:00
|
|
|
$this->setUser( $this->userName );
|
2021-03-29 18:35:33 +00:00
|
|
|
$this->setTitle( $namespace );
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2021-03-29 18:35:33 +00:00
|
|
|
$this->mergeMwGlobalArrayValue( 'wgNamespaceProtection', $namespaceProtection );
|
2022-07-07 22:54:32 +00:00
|
|
|
$this->overrideConfigValue(
|
2022-07-12 17:34:54 +00:00
|
|
|
MainConfigNames::NamespaceProtection,
|
2022-07-07 22:54:32 +00:00
|
|
|
$namespaceProtection + [ NS_MEDIAWIKI => 'editinterface' ]
|
|
|
|
|
);
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2022-01-12 20:13:39 +00:00
|
|
|
$permissionManager = $this->getServiceContainer()->getPermissionManager();
|
2020-10-16 00:04:08 +00:00
|
|
|
|
2021-03-29 18:35:33 +00:00
|
|
|
$this->overrideUserPermissions( $this->user, $userPerms );
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2020-10-16 00:04:08 +00:00
|
|
|
$this->assertEquals(
|
2021-03-29 18:35:33 +00:00
|
|
|
$expectedPermErrors,
|
2020-10-16 00:04:08 +00:00
|
|
|
$permissionManager->getPermissionErrors( 'bogus', $this->user, $this->title )
|
|
|
|
|
);
|
2021-03-29 18:35:33 +00:00
|
|
|
$this->assertSame(
|
|
|
|
|
$expectedUserCan,
|
|
|
|
|
$permissionManager->userCan( 'bogus', $this->user, $this->title )
|
2020-10-16 00:04:08 +00:00
|
|
|
);
|
2021-03-29 18:35:33 +00:00
|
|
|
}
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2021-03-29 18:35:33 +00:00
|
|
|
public function provideSpecialsAndNSPermissions() {
|
|
|
|
|
yield [
|
|
|
|
|
'namespace' => NS_SPECIAL,
|
|
|
|
|
'user permissions' => [],
|
|
|
|
|
'namespace protection' => [],
|
|
|
|
|
'expected permission errors' => [ [ 'badaccess-group0' ], [ 'ns-specialprotected' ] ],
|
|
|
|
|
'user can' => false,
|
|
|
|
|
];
|
|
|
|
|
yield [
|
|
|
|
|
'namespace' => NS_MAIN,
|
|
|
|
|
'user permissions' => [ 'bogus' ],
|
|
|
|
|
'namespace protection' => [],
|
|
|
|
|
'expected permission errors' => [],
|
|
|
|
|
'user can' => true,
|
|
|
|
|
];
|
|
|
|
|
yield [
|
|
|
|
|
'namespace' => NS_MAIN,
|
|
|
|
|
'user permissions' => [],
|
|
|
|
|
'namespace protection' => [],
|
|
|
|
|
'expected permission errors' => [ [ 'badaccess-group0' ] ],
|
|
|
|
|
'user can' => false,
|
|
|
|
|
];
|
|
|
|
|
yield [
|
|
|
|
|
'namespace' => NS_USER,
|
|
|
|
|
'user permissions' => [],
|
|
|
|
|
'namespace protection' => [ NS_USER => [ 'bogus' ] ],
|
|
|
|
|
'expected permission errors' => [ [ 'badaccess-group0' ], [ 'namespaceprotected', 'User', 'bogus' ] ],
|
|
|
|
|
'user can' => false,
|
|
|
|
|
];
|
|
|
|
|
yield [
|
|
|
|
|
'namespace' => NS_MEDIAWIKI,
|
|
|
|
|
'user permissions' => [ 'bogus' ],
|
|
|
|
|
'namespace protection' => [],
|
|
|
|
|
'expected permission errors' => [ [ 'protectedinterface', 'bogus' ] ],
|
|
|
|
|
'user can' => false,
|
|
|
|
|
];
|
|
|
|
|
yield [
|
|
|
|
|
'namespace' => NS_MAIN,
|
|
|
|
|
'user permissions' => [ 'bogus' ],
|
|
|
|
|
'namespace protection' => [],
|
|
|
|
|
'expected permission errors' => [],
|
|
|
|
|
'user can' => true,
|
|
|
|
|
];
|
2019-03-07 20:02:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testCascadingSourcesRestrictions() {
|
|
|
|
|
$this->setTitle( NS_MAIN, "test page" );
|
2021-06-09 17:52:49 +00:00
|
|
|
$this->overrideUserPermissions( $this->user, [ "edit", "bogus", 'createpage' ] );
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2022-01-12 20:13:39 +00:00
|
|
|
$rs = $this->getServiceContainer()->getRestrictionStore();
|
2021-07-26 13:24:22 +00:00
|
|
|
$wrapper = TestingAccessWrapper::newFromObject( $rs );
|
|
|
|
|
$wrapper->cache = [ CacheKeyHelper::getKeyForPage( $this->title ) => [
|
|
|
|
|
'cascade_sources' => [
|
|
|
|
|
[
|
|
|
|
|
Title::makeTitle( NS_MAIN, "Bogus" ),
|
|
|
|
|
Title::makeTitle( NS_MAIN, "UnBogus" )
|
|
|
|
|
], [
|
|
|
|
|
"bogus" => [ 'bogus', "sysop", "protect", "" ],
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
] ];
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2022-01-12 20:13:39 +00:00
|
|
|
$permissionManager = $this->getServiceContainer()->getPermissionManager();
|
2020-10-16 00:04:08 +00:00
|
|
|
|
|
|
|
|
$this->assertFalse( $permissionManager->userCan( 'bogus', $this->user, $this->title ) );
|
2019-03-07 20:02:07 +00:00
|
|
|
$this->assertEquals( [
|
|
|
|
|
[ "cascadeprotected", 2, "* [[:Bogus]]\n* [[:UnBogus]]\n", 'bogus' ] ],
|
2020-10-16 00:04:08 +00:00
|
|
|
$permissionManager->getPermissionErrors(
|
2019-04-09 06:58:04 +00:00
|
|
|
'bogus', $this->user, $this->title ) );
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2020-10-16 00:04:08 +00:00
|
|
|
$this->assertTrue( $permissionManager->userCan( 'edit', $this->user, $this->title ) );
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
[],
|
|
|
|
|
$permissionManager->getPermissionErrors( 'edit', $this->user, $this->title )
|
|
|
|
|
);
|
2019-03-07 20:02:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2021-03-29 18:35:33 +00:00
|
|
|
* @dataProvider provideActionPermissions
|
2019-03-07 20:02:07 +00:00
|
|
|
*/
|
2021-03-29 18:35:33 +00:00
|
|
|
public function testActionPermissions(
|
|
|
|
|
$namespace,
|
|
|
|
|
$titleOverrides,
|
|
|
|
|
$action,
|
|
|
|
|
$userPerms,
|
|
|
|
|
$expectedPermErrors,
|
|
|
|
|
$expectedUserCan
|
|
|
|
|
) {
|
2021-07-26 13:24:22 +00:00
|
|
|
$this->setTitle( $namespace, "Test page" );
|
|
|
|
|
|
|
|
|
|
$this->overrideUserPermissions( $this->user, $userPerms );
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2022-01-12 20:13:39 +00:00
|
|
|
$permissionManager = $this->getServiceContainer()->getPermissionManager();
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2022-01-12 20:13:39 +00:00
|
|
|
$rs = $this->getServiceContainer()->getRestrictionStore();
|
2021-07-26 13:24:22 +00:00
|
|
|
$wrapper = TestingAccessWrapper::newFromObject( $rs );
|
|
|
|
|
$wrapper->cache = [ CacheKeyHelper::getKeyForPage( $this->title ) => [
|
|
|
|
|
'create_protection' => [
|
|
|
|
|
'permission' => $titleOverrides['protectedPermission'] ?? '',
|
|
|
|
|
'user' => $this->user->getId(),
|
|
|
|
|
'expiry' => 'infinity',
|
|
|
|
|
'reason' => 'test',
|
|
|
|
|
],
|
|
|
|
|
'has_cascading' => false,
|
|
|
|
|
// XXX This is bogus, restrictions won't be empty if there's create protection
|
|
|
|
|
'restrictions' => [],
|
|
|
|
|
] ];
|
|
|
|
|
|
|
|
|
|
if ( isset( $titleOverrides['interwiki'] ) ) {
|
2021-11-17 15:20:38 +00:00
|
|
|
$reflectedTitle = TestingAccessWrapper::newFromObject( $this->title );
|
|
|
|
|
$reflectedTitle->mInterwiki = $titleOverrides['interwiki'];
|
2021-07-26 13:24:22 +00:00
|
|
|
}
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2020-10-16 00:04:08 +00:00
|
|
|
$this->assertEquals(
|
2021-03-29 18:35:33 +00:00
|
|
|
$expectedPermErrors,
|
|
|
|
|
$permissionManager->getPermissionErrors( $action, $this->user, $this->title )
|
2020-10-16 00:04:08 +00:00
|
|
|
);
|
2021-03-29 18:35:33 +00:00
|
|
|
$this->assertSame(
|
|
|
|
|
$expectedUserCan,
|
|
|
|
|
$permissionManager->userCan( $action, $this->user, $this->title )
|
2020-10-16 00:04:08 +00:00
|
|
|
);
|
2021-03-29 18:35:33 +00:00
|
|
|
}
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2021-03-29 18:35:33 +00:00
|
|
|
public function provideActionPermissions() {
|
|
|
|
|
// title overrides can include "protectedPermission" to override
|
|
|
|
|
// $title->mTitleProtection['permission'], and "interwiki" to override
|
|
|
|
|
// $title->mInterwiki, for the few cases those are needed
|
|
|
|
|
yield [
|
|
|
|
|
'namespace' => NS_MAIN,
|
|
|
|
|
'title overrides' => [],
|
|
|
|
|
'action' => 'create',
|
|
|
|
|
'user permissions' => [ 'createpage' ],
|
|
|
|
|
'expected permission errors' => [ [ 'titleprotected', 'Useruser', 'test' ] ],
|
|
|
|
|
'user can' => false,
|
|
|
|
|
];
|
|
|
|
|
yield [
|
|
|
|
|
'namespace' => NS_MAIN,
|
|
|
|
|
'title overrides' => [ 'protectedPermission' => 'editprotected' ],
|
|
|
|
|
'action' => 'create',
|
|
|
|
|
'user permissions' => [ 'createpage', 'protect' ],
|
|
|
|
|
'expected permission errors' => [ [ 'titleprotected', 'Useruser', 'test' ] ],
|
|
|
|
|
'user can' => false,
|
|
|
|
|
];
|
|
|
|
|
yield [
|
|
|
|
|
'namespace' => NS_MAIN,
|
|
|
|
|
'title overrides' => [ 'protectedPermission' => 'editprotected' ],
|
|
|
|
|
'action' => 'create',
|
|
|
|
|
'user permissions' => [ 'createpage', 'editprotected' ],
|
|
|
|
|
'expected permission errors' => [],
|
|
|
|
|
'user can' => true,
|
|
|
|
|
];
|
|
|
|
|
yield [
|
|
|
|
|
'namespace' => NS_MEDIA,
|
|
|
|
|
'title overrides' => [],
|
|
|
|
|
'action' => 'move',
|
|
|
|
|
'user permissions' => [ 'move' ],
|
|
|
|
|
'expected permission errors' => [ [ 'immobile-source-namespace', 'Media' ] ],
|
|
|
|
|
'user can' => false,
|
|
|
|
|
];
|
|
|
|
|
yield [
|
|
|
|
|
'namespace' => NS_HELP,
|
|
|
|
|
'title overrides' => [],
|
|
|
|
|
'action' => 'move',
|
|
|
|
|
'user permissions' => [ 'move' ],
|
|
|
|
|
'expected permission errors' => [],
|
|
|
|
|
'user can' => true,
|
|
|
|
|
];
|
|
|
|
|
yield [
|
|
|
|
|
'namespace' => NS_HELP,
|
|
|
|
|
'title overrides' => [ 'interwiki' => 'no' ],
|
|
|
|
|
'action' => 'move',
|
|
|
|
|
'user permissions' => [ 'move' ],
|
|
|
|
|
'expected permission errors' => [ [ 'immobile-source-page' ] ],
|
|
|
|
|
'user can' => false,
|
|
|
|
|
];
|
|
|
|
|
yield [
|
|
|
|
|
'namespace' => NS_MEDIA,
|
|
|
|
|
'title overrides' => [],
|
|
|
|
|
'action' => 'move-target',
|
|
|
|
|
'user permissions' => [ 'move' ],
|
|
|
|
|
'expected permission errors' => [ [ 'immobile-target-namespace', 'Media' ] ],
|
|
|
|
|
'user can' => false,
|
|
|
|
|
];
|
|
|
|
|
yield [
|
|
|
|
|
'namespace' => NS_HELP,
|
|
|
|
|
'title overrides' => [],
|
|
|
|
|
'action' => 'move-target',
|
|
|
|
|
'user permissions' => [ 'move' ],
|
|
|
|
|
'expected permission errors' => [],
|
|
|
|
|
'user can' => true,
|
|
|
|
|
];
|
|
|
|
|
yield [
|
|
|
|
|
'namespace' => NS_HELP,
|
|
|
|
|
'title overrides' => [ 'interwiki' => 'no' ],
|
|
|
|
|
'action' => 'move-target',
|
|
|
|
|
'user permissions' => [ 'move' ],
|
|
|
|
|
'expected permission errors' => [ [ 'immobile-target-page' ] ],
|
|
|
|
|
'user can' => false,
|
|
|
|
|
];
|
2021-06-09 17:52:49 +00:00
|
|
|
yield [
|
|
|
|
|
'namespace' => NS_MAIN,
|
|
|
|
|
'title overrides' => [],
|
|
|
|
|
'action' => 'edit',
|
|
|
|
|
'user permissions' => [ 'createpage', 'edit' ],
|
|
|
|
|
'expected permission errors' => [ [ 'titleprotected', 'Useruser', 'test' ] ],
|
|
|
|
|
'user can' => false,
|
|
|
|
|
];
|
|
|
|
|
yield [
|
|
|
|
|
'namespace' => NS_MAIN,
|
|
|
|
|
'title overrides' => [],
|
|
|
|
|
'action' => 'edit',
|
|
|
|
|
'user permissions' => [ 'edit' ],
|
|
|
|
|
'expected permission errors' => [ [ 'nocreate-loggedin' ] ],
|
|
|
|
|
'user can' => false,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testEditActionPermissionWithExistingPage() {
|
|
|
|
|
$title = $this->getExistingTestPage( 'test page' )->getTitle();
|
|
|
|
|
|
2022-01-12 20:13:39 +00:00
|
|
|
$permissionManager = $this->getServiceContainer()->getPermissionManager();
|
2021-06-09 17:52:49 +00:00
|
|
|
|
|
|
|
|
$this->overrideUserPermissions( $this->user, [ 'edit' ] );
|
|
|
|
|
|
2022-11-24 22:22:36 +00:00
|
|
|
$this->assertSame( [], $permissionManager->getPermissionErrors( 'edit', $this->user, $title ) );
|
2021-06-09 17:52:49 +00:00
|
|
|
$this->assertTrue( $permissionManager->userCan( 'edit', $this->user, $title ) );
|
2019-03-07 20:02:07 +00:00
|
|
|
}
|
|
|
|
|
|
2022-04-11 01:39:33 +00:00
|
|
|
public function testAutocreatePermissionsHack() {
|
2022-07-07 22:54:32 +00:00
|
|
|
$this->overrideConfigValues( [
|
|
|
|
|
MainConfigNames::AutoCreateTempUser => [
|
2022-04-11 01:39:33 +00:00
|
|
|
'enabled' => true,
|
|
|
|
|
'actions' => [ 'edit' ],
|
|
|
|
|
'serialProvider' => [ 'type' => 'local' ],
|
|
|
|
|
'serialMapping' => [ 'type' => 'plain-numeric' ],
|
|
|
|
|
'matchPattern' => '*Unregistered $1',
|
|
|
|
|
'genPattern' => '*Unregistered $1'
|
|
|
|
|
],
|
2022-07-07 22:54:32 +00:00
|
|
|
MainConfigNames::GroupPermissions => [
|
2022-04-11 01:39:33 +00:00
|
|
|
'*' => [ 'edit' => false ],
|
|
|
|
|
'user' => [ 'edit' => true, 'createpage' => true ],
|
|
|
|
|
]
|
|
|
|
|
] );
|
|
|
|
|
$services = $this->getServiceContainer();
|
|
|
|
|
$permissionManager = $services->getPermissionManager();
|
|
|
|
|
$user = $services->getUserFactory()->newAnonymous();
|
|
|
|
|
$title = $this->getNonexistingTestPage()->getTitle();
|
|
|
|
|
$this->assertNotEmpty(
|
|
|
|
|
$permissionManager->getPermissionErrors(
|
|
|
|
|
'edit',
|
|
|
|
|
$user,
|
|
|
|
|
$title
|
|
|
|
|
)
|
|
|
|
|
);
|
2022-11-24 22:22:36 +00:00
|
|
|
$this->assertSame(
|
|
|
|
|
[],
|
2022-04-11 01:39:33 +00:00
|
|
|
$permissionManager->getPermissionErrors(
|
|
|
|
|
'edit',
|
|
|
|
|
$user,
|
|
|
|
|
$title,
|
|
|
|
|
PermissionManager::RIGOR_QUICK
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-07 20:02:07 +00:00
|
|
|
/**
|
2019-10-11 16:06:38 +00:00
|
|
|
* @dataProvider provideTestCheckUserBlockActions
|
2019-03-07 20:02:07 +00:00
|
|
|
*/
|
2019-10-11 16:06:38 +00:00
|
|
|
public function testCheckUserBlockActions( $block, $restriction, $expected ) {
|
2022-07-07 22:54:32 +00:00
|
|
|
$this->overrideConfigValues( [
|
|
|
|
|
MainConfigNames::EmailConfirmToEdit => false,
|
|
|
|
|
MainConfigNames::EnablePartialActionBlocks => true,
|
2019-03-07 20:02:07 +00:00
|
|
|
] );
|
|
|
|
|
|
2019-10-11 16:06:38 +00:00
|
|
|
if ( $restriction ) {
|
|
|
|
|
$pageRestriction = new PageRestriction( 0, $this->title->getArticleID() );
|
|
|
|
|
$pageRestriction->setTitle( $this->title );
|
|
|
|
|
$block->setRestrictions( [ $pageRestriction ] );
|
|
|
|
|
}
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2019-10-11 16:06:38 +00:00
|
|
|
$user = $this->getMockBuilder( User::class )
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods( [ 'getBlock' ] )
|
2019-10-11 16:06:38 +00:00
|
|
|
->getMock();
|
|
|
|
|
$user->method( 'getBlock' )
|
|
|
|
|
->willReturn( $block );
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2019-10-11 16:06:38 +00:00
|
|
|
$this->overrideUserPermissions( $user, [
|
2019-04-09 06:58:04 +00:00
|
|
|
'createpage',
|
|
|
|
|
'edit',
|
|
|
|
|
'move',
|
|
|
|
|
'rollback',
|
|
|
|
|
'patrol',
|
|
|
|
|
'upload',
|
|
|
|
|
'purge'
|
|
|
|
|
] );
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2022-01-12 20:13:39 +00:00
|
|
|
$permissionManager = $this->getServiceContainer()->getPermissionManager();
|
2019-10-11 16:06:38 +00:00
|
|
|
|
|
|
|
|
// Check that user is blocked or unblocked from specific actions
|
|
|
|
|
foreach ( $expected as $action => $blocked ) {
|
|
|
|
|
$expectedErrorCount = $blocked ? 1 : 0;
|
2020-02-28 15:45:22 +00:00
|
|
|
$this->assertCount(
|
2019-10-11 16:06:38 +00:00
|
|
|
$expectedErrorCount,
|
2020-02-28 15:45:22 +00:00
|
|
|
$permissionManager->getPermissionErrors(
|
2019-10-11 16:06:38 +00:00
|
|
|
$action,
|
|
|
|
|
$user,
|
|
|
|
|
$this->title
|
2020-02-28 15:45:22 +00:00
|
|
|
)
|
2019-10-11 16:06:38 +00:00
|
|
|
);
|
|
|
|
|
}
|
2019-03-07 20:02:07 +00:00
|
|
|
|
|
|
|
|
// quickUserCan should ignore user blocks
|
2020-01-09 23:23:19 +00:00
|
|
|
$this->assertTrue(
|
2019-10-11 16:06:38 +00:00
|
|
|
$permissionManager->quickUserCan( 'move-target', $this->user, $this->title )
|
|
|
|
|
);
|
|
|
|
|
}
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2019-10-11 16:06:38 +00:00
|
|
|
public static function provideTestCheckUserBlockActions() {
|
|
|
|
|
return [
|
|
|
|
|
'Sitewide autoblock' => [
|
|
|
|
|
new DatabaseBlock( [
|
|
|
|
|
'address' => '127.0.8.1',
|
2021-06-02 09:44:38 +00:00
|
|
|
'by' => new UserIdentityValue( 100, 'TestUser' ),
|
2019-10-11 16:06:38 +00:00
|
|
|
'auto' => true,
|
|
|
|
|
] ),
|
|
|
|
|
false,
|
|
|
|
|
[
|
|
|
|
|
'edit' => true,
|
|
|
|
|
'move-target' => true,
|
|
|
|
|
'rollback' => true,
|
|
|
|
|
'patrol' => true,
|
|
|
|
|
'upload' => true,
|
|
|
|
|
'purge' => false,
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'Sitewide block' => [
|
|
|
|
|
new DatabaseBlock( [
|
|
|
|
|
'address' => '127.0.8.1',
|
2021-06-02 09:44:38 +00:00
|
|
|
'by' => new UserIdentityValue( 100, 'TestUser' ),
|
2019-10-11 16:06:38 +00:00
|
|
|
] ),
|
|
|
|
|
false,
|
|
|
|
|
[
|
|
|
|
|
'edit' => true,
|
|
|
|
|
'move-target' => true,
|
|
|
|
|
'rollback' => true,
|
|
|
|
|
'patrol' => true,
|
|
|
|
|
'upload' => true,
|
|
|
|
|
'purge' => false,
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'Partial block without restriction against this page' => [
|
|
|
|
|
new DatabaseBlock( [
|
|
|
|
|
'address' => '127.0.8.1',
|
2021-06-02 09:44:38 +00:00
|
|
|
'by' => new UserIdentityValue( 100, 'TestUser' ),
|
2019-10-11 16:06:38 +00:00
|
|
|
'sitewide' => false,
|
|
|
|
|
] ),
|
|
|
|
|
false,
|
|
|
|
|
[
|
|
|
|
|
'edit' => false,
|
|
|
|
|
'move-target' => false,
|
|
|
|
|
'rollback' => false,
|
|
|
|
|
'patrol' => false,
|
|
|
|
|
'upload' => false,
|
|
|
|
|
'purge' => false,
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'Partial block with restriction against this page' => [
|
|
|
|
|
new DatabaseBlock( [
|
|
|
|
|
'address' => '127.0.8.1',
|
2021-06-02 09:44:38 +00:00
|
|
|
'by' => new UserIdentityValue( 100, 'TestUser' ),
|
2019-10-11 16:06:38 +00:00
|
|
|
'sitewide' => false,
|
|
|
|
|
] ),
|
|
|
|
|
true,
|
|
|
|
|
[
|
|
|
|
|
'edit' => true,
|
|
|
|
|
'move-target' => true,
|
|
|
|
|
'rollback' => true,
|
|
|
|
|
'patrol' => true,
|
|
|
|
|
'upload' => false,
|
|
|
|
|
'purge' => false,
|
|
|
|
|
]
|
|
|
|
|
],
|
2021-06-02 12:45:38 +00:00
|
|
|
'Partial block with action restriction against uploading' => [
|
|
|
|
|
( new DatabaseBlock( [
|
|
|
|
|
'address' => '127.0.8.1',
|
2021-06-05 14:31:28 +00:00
|
|
|
'by' => UserIdentityValue::newRegistered( 100, 'Test' ),
|
2021-06-02 12:45:38 +00:00
|
|
|
'sitewide' => false,
|
|
|
|
|
] ) )->setRestrictions( [
|
|
|
|
|
new ActionRestriction( 0, 1 )
|
|
|
|
|
] ),
|
|
|
|
|
false,
|
|
|
|
|
[
|
|
|
|
|
'edit' => false,
|
|
|
|
|
'move-target' => false,
|
|
|
|
|
'rollback' => false,
|
|
|
|
|
'patrol' => false,
|
|
|
|
|
'upload' => true,
|
|
|
|
|
'purge' => false,
|
|
|
|
|
]
|
|
|
|
|
],
|
2019-10-11 16:06:38 +00:00
|
|
|
'System block' => [
|
|
|
|
|
new SystemBlock( [
|
|
|
|
|
'address' => '127.0.8.1',
|
|
|
|
|
'by' => 100,
|
|
|
|
|
'systemBlock' => 'test',
|
|
|
|
|
] ),
|
|
|
|
|
false,
|
|
|
|
|
[
|
|
|
|
|
'edit' => true,
|
|
|
|
|
'move-target' => true,
|
|
|
|
|
'rollback' => true,
|
|
|
|
|
'patrol' => true,
|
|
|
|
|
'upload' => true,
|
|
|
|
|
'purge' => false,
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'No block' => [
|
|
|
|
|
null,
|
|
|
|
|
false,
|
|
|
|
|
[
|
|
|
|
|
'edit' => false,
|
|
|
|
|
'move-target' => false,
|
|
|
|
|
'rollback' => false,
|
|
|
|
|
'patrol' => false,
|
|
|
|
|
'upload' => false,
|
|
|
|
|
'purge' => false,
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestCheckUserBlockMessage
|
|
|
|
|
*/
|
|
|
|
|
public function testCheckUserBlockMessage( $blockType, $blockParams, $restriction, $expected ) {
|
2022-07-07 22:54:32 +00:00
|
|
|
$this->overrideConfigValue(
|
|
|
|
|
MainConfigNames::EmailConfirmToEdit, false
|
|
|
|
|
);
|
2019-10-11 16:06:38 +00:00
|
|
|
$block = new $blockType( array_merge( [
|
2019-03-07 20:02:07 +00:00
|
|
|
'address' => '127.0.8.1',
|
2021-06-04 09:05:47 +00:00
|
|
|
'by' => $this->user,
|
2019-10-11 16:06:38 +00:00
|
|
|
'reason' => 'Test reason',
|
|
|
|
|
'timestamp' => '20000101000000',
|
|
|
|
|
'expiry' => 0,
|
|
|
|
|
], $blockParams ) );
|
|
|
|
|
|
|
|
|
|
if ( $restriction ) {
|
|
|
|
|
$pageRestriction = new PageRestriction( 0, $this->title->getArticleID() );
|
|
|
|
|
$pageRestriction->setTitle( $this->title );
|
|
|
|
|
$block->setRestrictions( [ $pageRestriction ] );
|
|
|
|
|
}
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2019-10-11 16:06:38 +00:00
|
|
|
$user = $this->getMockBuilder( User::class )
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods( [ 'getBlock' ] )
|
2019-10-11 16:06:38 +00:00
|
|
|
->getMock();
|
|
|
|
|
$user->method( 'getBlock' )
|
|
|
|
|
->willReturn( $block );
|
|
|
|
|
|
2021-06-09 17:52:49 +00:00
|
|
|
$this->overrideUserPermissions( $user, [ 'edit', 'createpage' ] );
|
2019-10-11 16:06:38 +00:00
|
|
|
|
2022-01-12 20:13:39 +00:00
|
|
|
$permissionManager = $this->getServiceContainer()->getPermissionManager();
|
2019-09-05 21:25:33 +00:00
|
|
|
$errors = $permissionManager->getPermissionErrors(
|
|
|
|
|
'edit',
|
|
|
|
|
$user,
|
|
|
|
|
$this->title
|
|
|
|
|
);
|
2019-10-11 16:06:38 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals(
|
2019-09-05 21:25:33 +00:00
|
|
|
$expected['message'],
|
|
|
|
|
$errors[0][0]
|
2019-10-11 16:06:38 +00:00
|
|
|
);
|
|
|
|
|
}
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2019-10-11 16:06:38 +00:00
|
|
|
public static function provideTestCheckUserBlockMessage() {
|
|
|
|
|
return [
|
|
|
|
|
'Sitewide autoblock' => [
|
|
|
|
|
DatabaseBlock::class,
|
|
|
|
|
[ 'auto' => true ],
|
|
|
|
|
false,
|
|
|
|
|
[
|
|
|
|
|
'message' => 'autoblockedtext',
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
'Sitewide block' => [
|
|
|
|
|
DatabaseBlock::class,
|
|
|
|
|
[],
|
|
|
|
|
false,
|
|
|
|
|
[
|
|
|
|
|
'message' => 'blockedtext',
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
'Partial block with restriction against this page' => [
|
|
|
|
|
DatabaseBlock::class,
|
|
|
|
|
[ 'sitewide' => false ],
|
|
|
|
|
true,
|
|
|
|
|
[
|
|
|
|
|
'message' => 'blockedtext-partial',
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
'System block' => [
|
|
|
|
|
SystemBlock::class,
|
|
|
|
|
[ 'systemBlock' => 'test' ],
|
|
|
|
|
false,
|
|
|
|
|
[
|
|
|
|
|
'message' => 'systemblockedtext',
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
}
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2019-10-11 16:06:38 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestCheckUserBlockEmailConfirmToEdit
|
|
|
|
|
*/
|
|
|
|
|
public function testCheckUserBlockEmailConfirmToEdit( $emailConfirmToEdit, $assertion ) {
|
2022-07-07 22:54:32 +00:00
|
|
|
$this->overrideConfigValues( [
|
|
|
|
|
MainConfigNames::EmailConfirmToEdit => $emailConfirmToEdit,
|
|
|
|
|
MainConfigNames::EmailAuthentication => true,
|
2019-03-07 20:02:07 +00:00
|
|
|
] );
|
|
|
|
|
|
2019-10-11 16:06:38 +00:00
|
|
|
$this->overrideUserPermissions( $this->user, [
|
|
|
|
|
'edit',
|
|
|
|
|
'move',
|
2019-03-07 20:02:07 +00:00
|
|
|
] );
|
|
|
|
|
|
2022-01-12 20:13:39 +00:00
|
|
|
$permissionManager = $this->getServiceContainer()->getPermissionManager();
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2019-10-11 16:06:38 +00:00
|
|
|
$this->$assertion( [ 'confirmedittext' ],
|
|
|
|
|
$permissionManager->getPermissionErrors( 'edit', $this->user, $this->title ) );
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2019-10-11 16:06:38 +00:00
|
|
|
// $wgEmailConfirmToEdit only applies to 'edit' action
|
2019-03-07 20:02:07 +00:00
|
|
|
$this->assertEquals( [],
|
2019-10-11 16:06:38 +00:00
|
|
|
$permissionManager->getPermissionErrors( 'move-target', $this->user, $this->title ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static function provideTestCheckUserBlockEmailConfirmToEdit() {
|
|
|
|
|
return [
|
|
|
|
|
'User must confirm email to edit' => [
|
|
|
|
|
true,
|
|
|
|
|
'assertContains',
|
|
|
|
|
],
|
|
|
|
|
'User may edit without confirming email' => [
|
|
|
|
|
false,
|
|
|
|
|
'assertNotContains',
|
|
|
|
|
],
|
|
|
|
|
];
|
2019-03-07 20:02:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-06-01 23:38:22 +00:00
|
|
|
* Determine that the passed-in permission does not get mixed up with
|
2019-03-07 20:02:07 +00:00
|
|
|
* an action of the same name.
|
|
|
|
|
*/
|
2019-10-11 16:06:38 +00:00
|
|
|
public function testCheckUserBlockActionPermission() {
|
2022-07-14 12:42:07 +00:00
|
|
|
$tester = $this->createMock( Action::class );
|
2019-03-07 20:02:07 +00:00
|
|
|
$tester->method( 'getName' )
|
2021-09-03 22:52:31 +00:00
|
|
|
->willReturn( 'tester' );
|
2019-03-07 20:02:07 +00:00
|
|
|
$tester->method( 'getRestriction' )
|
2021-09-03 22:52:31 +00:00
|
|
|
->willReturn( 'test' );
|
2019-03-07 20:02:07 +00:00
|
|
|
$tester->method( 'requiresUnblock' )
|
2021-09-03 22:52:31 +00:00
|
|
|
->willReturn( false );
|
2019-03-07 20:02:07 +00:00
|
|
|
|
2022-07-07 22:54:32 +00:00
|
|
|
$this->overrideConfigValues( [
|
|
|
|
|
MainConfigNames::Actions => [
|
2019-03-07 20:02:07 +00:00
|
|
|
'tester' => $tester,
|
|
|
|
|
],
|
2022-07-07 22:54:32 +00:00
|
|
|
MainConfigNames::GroupPermissions => [
|
2019-03-07 20:02:07 +00:00
|
|
|
'*' => [
|
|
|
|
|
'tester' => true,
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
] );
|
|
|
|
|
|
2019-10-11 16:06:38 +00:00
|
|
|
$user = $this->getMockBuilder( User::class )
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods( [ 'getBlock' ] )
|
2019-10-11 16:06:38 +00:00
|
|
|
->getMock();
|
|
|
|
|
$user->method( 'getBlock' )
|
|
|
|
|
->willReturn( new DatabaseBlock( [
|
|
|
|
|
'address' => '127.0.8.1',
|
2021-06-02 09:44:38 +00:00
|
|
|
'by' => $this->user,
|
2019-10-11 16:06:38 +00:00
|
|
|
] ) );
|
|
|
|
|
|
2022-01-12 20:13:39 +00:00
|
|
|
$this->assertCount( 1, $this->getServiceContainer()->getPermissionManager()
|
2020-02-28 15:45:22 +00:00
|
|
|
->getPermissionErrors( 'tester', $user, $this->title )
|
2019-10-11 16:06:38 +00:00
|
|
|
);
|
2019-03-07 20:02:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testBlockInstanceCache() {
|
|
|
|
|
// First, check the user isn't blocked
|
|
|
|
|
$user = $this->getMutableTestUser()->getUser();
|
|
|
|
|
$ut = Title::makeTitle( NS_USER_TALK, $user->getName() );
|
2021-11-21 19:13:24 +00:00
|
|
|
$this->assertNull( $user->getBlock( false ) );
|
2022-01-12 20:13:39 +00:00
|
|
|
$this->assertFalse( $this->getServiceContainer()->getPermissionManager()
|
2021-11-21 19:13:24 +00:00
|
|
|
->isBlockedFrom( $user, $ut ) );
|
2019-03-07 20:02:07 +00:00
|
|
|
|
|
|
|
|
// Block the user
|
|
|
|
|
$blocker = $this->getTestSysop()->getUser();
|
2019-05-13 14:18:07 +00:00
|
|
|
$block = new DatabaseBlock( [
|
2019-03-07 20:02:07 +00:00
|
|
|
'hideName' => true,
|
|
|
|
|
'allowUsertalk' => false,
|
|
|
|
|
'reason' => 'Because',
|
|
|
|
|
] );
|
|
|
|
|
$block->setTarget( $user );
|
|
|
|
|
$block->setBlocker( $blocker );
|
2022-01-12 20:13:39 +00:00
|
|
|
$blockStore = $this->getServiceContainer()->getDatabaseBlockStore();
|
2020-08-27 09:27:10 +00:00
|
|
|
$res = $blockStore->insertBlock( $block );
|
2021-11-21 19:13:24 +00:00
|
|
|
$this->assertTrue( (bool)$res['id'], 'Failed to insert block' );
|
2019-03-07 20:02:07 +00:00
|
|
|
|
|
|
|
|
// Clear cache and confirm it loaded the block properly
|
|
|
|
|
$user->clearInstanceCache();
|
2019-05-13 14:18:07 +00:00
|
|
|
$this->assertInstanceOf( DatabaseBlock::class, $user->getBlock( false ) );
|
2022-01-12 20:13:39 +00:00
|
|
|
$this->assertTrue( $this->getServiceContainer()->getPermissionManager()
|
2019-04-09 06:58:04 +00:00
|
|
|
->isBlockedFrom( $user, $ut ) );
|
2019-03-07 20:02:07 +00:00
|
|
|
|
|
|
|
|
// Unblock
|
2020-08-27 09:27:10 +00:00
|
|
|
$blockStore->deleteBlock( $block );
|
2019-03-07 20:02:07 +00:00
|
|
|
|
|
|
|
|
// Clear cache and confirm it loaded the not-blocked properly
|
|
|
|
|
$user->clearInstanceCache();
|
|
|
|
|
$this->assertNull( $user->getBlock( false ) );
|
2022-01-12 20:13:39 +00:00
|
|
|
$this->assertFalse( $this->getServiceContainer()->getPermissionManager()
|
2019-04-09 06:58:04 +00:00
|
|
|
->isBlockedFrom( $user, $ut ) );
|
2019-03-07 20:02:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideIsBlockedFrom
|
|
|
|
|
* @param string|null $title Title to test.
|
|
|
|
|
* @param bool $expect Expected result from User::isBlockedFrom()
|
|
|
|
|
* @param array $options Additional test options:
|
|
|
|
|
* - 'blockAllowsUTEdit': (bool, default true) Value for $wgBlockAllowsUTEdit
|
2019-05-13 14:18:07 +00:00
|
|
|
* - 'allowUsertalk': (bool, default false) Passed to DatabaseBlock::__construct()
|
2019-03-07 20:02:07 +00:00
|
|
|
* - 'pageRestrictions': (array|null) If non-empty, page restriction titles for the block.
|
|
|
|
|
*/
|
|
|
|
|
public function testIsBlockedFrom( $title, $expect, array $options = [] ) {
|
2022-07-07 22:54:32 +00:00
|
|
|
$this->overrideConfigValue(
|
|
|
|
|
MainConfigNames::BlockAllowsUTEdit,
|
|
|
|
|
$options['blockAllowsUTEdit'] ?? true
|
|
|
|
|
);
|
2019-03-07 20:02:07 +00:00
|
|
|
|
|
|
|
|
$user = $this->getTestUser()->getUser();
|
|
|
|
|
|
|
|
|
|
if ( $title === self::USER_TALK_PAGE ) {
|
|
|
|
|
$title = $user->getTalkPage();
|
|
|
|
|
} else {
|
|
|
|
|
$title = Title::newFromText( $title );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$restrictions = [];
|
|
|
|
|
foreach ( $options['pageRestrictions'] ?? [] as $pagestr ) {
|
|
|
|
|
$page = $this->getExistingTestPage(
|
|
|
|
|
$pagestr === self::USER_TALK_PAGE ? $user->getTalkPage() : $pagestr
|
|
|
|
|
);
|
|
|
|
|
$restrictions[] = new PageRestriction( 0, $page->getId() );
|
|
|
|
|
}
|
|
|
|
|
foreach ( $options['namespaceRestrictions'] ?? [] as $ns ) {
|
|
|
|
|
$restrictions[] = new NamespaceRestriction( 0, $ns );
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-13 14:18:07 +00:00
|
|
|
$block = new DatabaseBlock( [
|
2019-03-07 20:02:07 +00:00
|
|
|
'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
|
|
|
|
|
'allowUsertalk' => $options['allowUsertalk'] ?? false,
|
|
|
|
|
'sitewide' => !$restrictions,
|
|
|
|
|
] );
|
|
|
|
|
$block->setTarget( $user );
|
|
|
|
|
$block->setBlocker( $this->getTestSysop()->getUser() );
|
|
|
|
|
if ( $restrictions ) {
|
|
|
|
|
$block->setRestrictions( $restrictions );
|
|
|
|
|
}
|
2022-01-12 20:13:39 +00:00
|
|
|
$blockStore = $this->getServiceContainer()->getDatabaseBlockStore();
|
2020-08-27 09:27:10 +00:00
|
|
|
$blockStore->insertBlock( $block );
|
2019-03-07 20:02:07 +00:00
|
|
|
|
|
|
|
|
try {
|
2022-01-12 20:13:39 +00:00
|
|
|
$this->assertSame( $expect, $this->getServiceContainer()->getPermissionManager()
|
2019-04-09 06:58:04 +00:00
|
|
|
->isBlockedFrom( $user, $title ) );
|
2019-03-07 20:02:07 +00:00
|
|
|
} finally {
|
2020-08-27 09:27:10 +00:00
|
|
|
$blockStore->deleteBlock( $block );
|
2019-03-07 20:02:07 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static function provideIsBlockedFrom() {
|
|
|
|
|
return [
|
|
|
|
|
'Sitewide block, basic operation' => [ 'Test page', true ],
|
|
|
|
|
'Sitewide block, not allowing user talk' => [
|
|
|
|
|
self::USER_TALK_PAGE, true, [
|
|
|
|
|
'allowUsertalk' => false,
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'Sitewide block, allowing user talk' => [
|
|
|
|
|
self::USER_TALK_PAGE, false, [
|
|
|
|
|
'allowUsertalk' => true,
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'Sitewide block, allowing user talk but $wgBlockAllowsUTEdit is false' => [
|
|
|
|
|
self::USER_TALK_PAGE, true, [
|
|
|
|
|
'allowUsertalk' => true,
|
|
|
|
|
'blockAllowsUTEdit' => false,
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'Partial block, blocking the page' => [
|
|
|
|
|
'Test page', true, [
|
|
|
|
|
'pageRestrictions' => [ 'Test page' ],
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'Partial block, not blocking the page' => [
|
|
|
|
|
'Test page 2', false, [
|
|
|
|
|
'pageRestrictions' => [ 'Test page' ],
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'Partial block, not allowing user talk but user talk page is not blocked' => [
|
|
|
|
|
self::USER_TALK_PAGE, false, [
|
|
|
|
|
'allowUsertalk' => false,
|
|
|
|
|
'pageRestrictions' => [ 'Test page' ],
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'Partial block, allowing user talk but user talk page is blocked' => [
|
|
|
|
|
self::USER_TALK_PAGE, true, [
|
|
|
|
|
'allowUsertalk' => true,
|
|
|
|
|
'pageRestrictions' => [ self::USER_TALK_PAGE ],
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'Partial block, user talk page is not blocked but $wgBlockAllowsUTEdit is false' => [
|
|
|
|
|
self::USER_TALK_PAGE, false, [
|
|
|
|
|
'allowUsertalk' => false,
|
|
|
|
|
'pageRestrictions' => [ 'Test page' ],
|
|
|
|
|
'blockAllowsUTEdit' => false,
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'Partial block, user talk page is blocked and $wgBlockAllowsUTEdit is false' => [
|
|
|
|
|
self::USER_TALK_PAGE, true, [
|
|
|
|
|
'allowUsertalk' => true,
|
|
|
|
|
'pageRestrictions' => [ self::USER_TALK_PAGE ],
|
|
|
|
|
'blockAllowsUTEdit' => false,
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'Partial user talk namespace block, not allowing user talk' => [
|
|
|
|
|
self::USER_TALK_PAGE, true, [
|
|
|
|
|
'allowUsertalk' => false,
|
|
|
|
|
'namespaceRestrictions' => [ NS_USER_TALK ],
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'Partial user talk namespace block, allowing user talk' => [
|
|
|
|
|
self::USER_TALK_PAGE, false, [
|
|
|
|
|
'allowUsertalk' => true,
|
|
|
|
|
'namespaceRestrictions' => [ NS_USER_TALK ],
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'Partial user talk namespace block, where $wgBlockAllowsUTEdit is false' => [
|
|
|
|
|
self::USER_TALK_PAGE, true, [
|
|
|
|
|
'allowUsertalk' => true,
|
|
|
|
|
'namespaceRestrictions' => [ NS_USER_TALK ],
|
|
|
|
|
'blockAllowsUTEdit' => false,
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-09 06:58:04 +00:00
|
|
|
public function testGetUserPermissions() {
|
|
|
|
|
$user = $this->getTestUser( [ 'unittesters' ] )->getUser();
|
2022-01-12 20:13:39 +00:00
|
|
|
$rights = $this->getServiceContainer()->getPermissionManager()
|
2019-04-09 06:58:04 +00:00
|
|
|
->getUserPermissions( $user );
|
|
|
|
|
$this->assertContains( 'runtest', $rights );
|
|
|
|
|
$this->assertNotContains( 'writetest', $rights );
|
|
|
|
|
$this->assertNotContains( 'modifytest', $rights );
|
|
|
|
|
$this->assertNotContains( 'nukeworld', $rights );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetUserPermissionsHooks() {
|
|
|
|
|
$user = $this->getTestUser( [ 'unittesters', 'testwriters' ] )->getUser();
|
|
|
|
|
$userWrapper = TestingAccessWrapper::newFromObject( $user );
|
|
|
|
|
|
2022-01-12 20:13:39 +00:00
|
|
|
$permissionManager = $this->getServiceContainer()->getPermissionManager();
|
2020-04-15 19:30:38 +00:00
|
|
|
$rights = $permissionManager->getUserPermissions( $user );
|
2021-11-21 19:13:24 +00:00
|
|
|
$this->assertContains( 'test', $rights );
|
|
|
|
|
$this->assertContains( 'runtest', $rights );
|
|
|
|
|
$this->assertContains( 'writetest', $rights );
|
|
|
|
|
$this->assertNotContains( 'nukeworld', $rights );
|
2019-04-09 06:58:04 +00:00
|
|
|
|
|
|
|
|
// Add a hook manipluating the rights
|
2021-02-07 13:10:36 +00:00
|
|
|
$this->setTemporaryHook( 'UserGetRights', static function ( $user, &$rights ) {
|
2019-04-09 06:58:04 +00:00
|
|
|
$rights[] = 'nukeworld';
|
|
|
|
|
$rights = array_diff( $rights, [ 'writetest' ] );
|
2019-08-21 02:21:13 +00:00
|
|
|
} );
|
2019-04-09 06:58:04 +00:00
|
|
|
|
2020-04-15 19:30:38 +00:00
|
|
|
$permissionManager->invalidateUsersRightsCache( $user );
|
|
|
|
|
$rights = $permissionManager->getUserPermissions( $user );
|
2019-04-09 06:58:04 +00:00
|
|
|
$this->assertContains( 'test', $rights );
|
|
|
|
|
$this->assertContains( 'runtest', $rights );
|
|
|
|
|
$this->assertNotContains( 'writetest', $rights );
|
|
|
|
|
$this->assertContains( 'nukeworld', $rights );
|
|
|
|
|
|
2020-03-02 13:38:29 +00:00
|
|
|
// Add a Session that limits rights. We're mocking a stdClass because the Session
|
|
|
|
|
// class is final, and thus not mockable.
|
|
|
|
|
$mock = $this->getMockBuilder( stdClass::class )
|
2021-03-20 15:18:58 +00:00
|
|
|
->addMethods( [ 'getAllowedUserRights', 'deregisterSession', 'getSessionId' ] )
|
2019-04-09 06:58:04 +00:00
|
|
|
->getMock();
|
|
|
|
|
$mock->method( 'getAllowedUserRights' )->willReturn( [ 'test', 'writetest' ] );
|
|
|
|
|
$mock->method( 'getSessionId' )->willReturn(
|
|
|
|
|
new SessionId( str_repeat( 'X', 32 ) )
|
|
|
|
|
);
|
|
|
|
|
$session = TestUtils::getDummySession( $mock );
|
|
|
|
|
$mockRequest = $this->getMockBuilder( FauxRequest::class )
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods( [ 'getSession' ] )
|
2019-04-09 06:58:04 +00:00
|
|
|
->getMock();
|
|
|
|
|
$mockRequest->method( 'getSession' )->willReturn( $session );
|
|
|
|
|
$userWrapper->mRequest = $mockRequest;
|
|
|
|
|
|
|
|
|
|
$this->resetServices();
|
2022-01-12 20:13:39 +00:00
|
|
|
$rights = $this->getServiceContainer()
|
2019-08-21 02:21:13 +00:00
|
|
|
->getPermissionManager()
|
2019-04-09 06:58:04 +00:00
|
|
|
->getUserPermissions( $user );
|
|
|
|
|
$this->assertContains( 'test', $rights );
|
|
|
|
|
$this->assertNotContains( 'runtest', $rights );
|
|
|
|
|
$this->assertNotContains( 'writetest', $rights );
|
|
|
|
|
$this->assertNotContains( 'nukeworld', $rights );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGroupPermissions() {
|
2022-12-21 17:44:26 +00:00
|
|
|
$this->hideDeprecated( 'MediaWiki\\Permissions\\PermissionManager::getGroupPermissions' );
|
2022-01-12 20:13:39 +00:00
|
|
|
$rights = $this->getServiceContainer()->getPermissionManager()
|
2019-04-09 06:58:04 +00:00
|
|
|
->getGroupPermissions( [ 'unittesters' ] );
|
|
|
|
|
$this->assertContains( 'runtest', $rights );
|
|
|
|
|
$this->assertNotContains( 'writetest', $rights );
|
|
|
|
|
$this->assertNotContains( 'modifytest', $rights );
|
|
|
|
|
$this->assertNotContains( 'nukeworld', $rights );
|
|
|
|
|
|
2022-01-12 20:13:39 +00:00
|
|
|
$rights = $this->getServiceContainer()->getPermissionManager()
|
2019-04-09 06:58:04 +00:00
|
|
|
->getGroupPermissions( [ 'unittesters', 'testwriters' ] );
|
|
|
|
|
$this->assertContains( 'runtest', $rights );
|
|
|
|
|
$this->assertContains( 'writetest', $rights );
|
|
|
|
|
$this->assertContains( 'modifytest', $rights );
|
|
|
|
|
$this->assertNotContains( 'nukeworld', $rights );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testRevokePermissions() {
|
2022-12-21 17:44:26 +00:00
|
|
|
$this->hideDeprecated( 'MediaWiki\\Permissions\\PermissionManager::getGroupPermissions' );
|
2022-01-12 20:13:39 +00:00
|
|
|
$rights = $this->getServiceContainer()->getPermissionManager()
|
2019-04-09 06:58:04 +00:00
|
|
|
->getGroupPermissions( [ 'unittesters', 'formertesters' ] );
|
|
|
|
|
$this->assertNotContains( 'runtest', $rights );
|
|
|
|
|
$this->assertNotContains( 'writetest', $rights );
|
|
|
|
|
$this->assertNotContains( 'modifytest', $rights );
|
|
|
|
|
$this->assertNotContains( 'nukeworld', $rights );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideGetGroupsWithPermission
|
|
|
|
|
*/
|
|
|
|
|
public function testGetGroupsWithPermission( $expected, $right ) {
|
2022-12-21 17:44:26 +00:00
|
|
|
$this->hideDeprecated( 'MediaWiki\\Permissions\\PermissionManager::getGroupsWithPermission' );
|
2022-01-12 20:13:39 +00:00
|
|
|
$result = $this->getServiceContainer()->getPermissionManager()
|
2019-04-09 06:58:04 +00:00
|
|
|
->getGroupsWithPermission( $right );
|
|
|
|
|
sort( $result );
|
|
|
|
|
sort( $expected );
|
|
|
|
|
|
|
|
|
|
$this->assertEquals( $expected, $result, "Groups with permission $right" );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static function provideGetGroupsWithPermission() {
|
|
|
|
|
return [
|
|
|
|
|
[
|
|
|
|
|
[ 'unittesters', 'testwriters' ],
|
|
|
|
|
'test'
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'unittesters' ],
|
|
|
|
|
'runtest'
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'testwriters' ],
|
|
|
|
|
'writetest'
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'testwriters' ],
|
|
|
|
|
'modifytest'
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testUserHasRight() {
|
2022-01-12 20:13:39 +00:00
|
|
|
$permissionManager = $this->getServiceContainer()->getPermissionManager();
|
2020-10-16 00:04:08 +00:00
|
|
|
|
|
|
|
|
$result = $permissionManager->userHasRight(
|
2019-04-09 06:58:04 +00:00
|
|
|
$this->getTestUser( 'unittesters' )->getUser(),
|
|
|
|
|
'test'
|
|
|
|
|
);
|
|
|
|
|
$this->assertTrue( $result );
|
|
|
|
|
|
2020-10-16 00:04:08 +00:00
|
|
|
$result = $permissionManager->userHasRight(
|
2019-04-09 06:58:04 +00:00
|
|
|
$this->getTestUser( 'formertesters' )->getUser(),
|
|
|
|
|
'runtest'
|
|
|
|
|
);
|
|
|
|
|
$this->assertFalse( $result );
|
|
|
|
|
|
2020-10-16 00:04:08 +00:00
|
|
|
$result = $permissionManager->userHasRight(
|
2019-04-09 06:58:04 +00:00
|
|
|
$this->getTestUser( 'formertesters' )->getUser(),
|
|
|
|
|
''
|
|
|
|
|
);
|
|
|
|
|
$this->assertTrue( $result );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGroupHasPermission() {
|
2022-12-21 17:44:26 +00:00
|
|
|
$this->hideDeprecated( 'MediaWiki\\Permissions\\PermissionManager::groupHasPermission' );
|
2022-01-12 20:13:39 +00:00
|
|
|
$permissionManager = $this->getServiceContainer()->getPermissionManager();
|
2020-10-16 00:04:08 +00:00
|
|
|
|
|
|
|
|
$result = $permissionManager->groupHasPermission(
|
2019-04-09 06:58:04 +00:00
|
|
|
'unittesters',
|
|
|
|
|
'test'
|
|
|
|
|
);
|
|
|
|
|
$this->assertTrue( $result );
|
|
|
|
|
|
2020-10-16 00:04:08 +00:00
|
|
|
$result = $permissionManager->groupHasPermission(
|
2019-04-09 06:58:04 +00:00
|
|
|
'formertesters',
|
|
|
|
|
'runtest'
|
|
|
|
|
);
|
|
|
|
|
$this->assertFalse( $result );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testIsEveryoneAllowed() {
|
2022-01-12 20:13:39 +00:00
|
|
|
$permissionManager = $this->getServiceContainer()->getPermissionManager();
|
2020-10-16 00:04:08 +00:00
|
|
|
|
|
|
|
|
$result = $permissionManager->isEveryoneAllowed( 'editmyoptions' );
|
2019-04-09 06:58:04 +00:00
|
|
|
$this->assertTrue( $result );
|
|
|
|
|
|
2020-10-16 00:04:08 +00:00
|
|
|
$result = $permissionManager->isEveryoneAllowed( 'test' );
|
2019-04-09 06:58:04 +00:00
|
|
|
$this->assertFalse( $result );
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-17 12:00:21 +00:00
|
|
|
public function testAddTemporaryUserRights() {
|
2022-01-12 20:13:39 +00:00
|
|
|
$permissionManager = $this->getServiceContainer()->getPermissionManager();
|
2019-07-11 17:22:20 +00:00
|
|
|
$this->overrideUserPermissions( $this->user, [ 'read', 'edit' ] );
|
2021-11-21 16:23:11 +00:00
|
|
|
|
2019-07-11 17:22:20 +00:00
|
|
|
$this->assertEquals( [ 'read', 'edit' ], $permissionManager->getUserPermissions( $this->user ) );
|
|
|
|
|
$this->assertFalse( $permissionManager->userHasRight( $this->user, 'move' ) );
|
|
|
|
|
|
|
|
|
|
$scope = $permissionManager->addTemporaryUserRights( $this->user, [ 'move', 'delete' ] );
|
|
|
|
|
$this->assertEquals( [ 'read', 'edit', 'move', 'delete' ],
|
|
|
|
|
$permissionManager->getUserPermissions( $this->user ) );
|
|
|
|
|
$this->assertTrue( $permissionManager->userHasRight( $this->user, 'move' ) );
|
|
|
|
|
|
|
|
|
|
$scope2 = $permissionManager->addTemporaryUserRights( $this->user, [ 'delete', 'upload' ] );
|
|
|
|
|
$this->assertEquals( [ 'read', 'edit', 'move', 'delete', 'upload' ],
|
|
|
|
|
$permissionManager->getUserPermissions( $this->user ) );
|
|
|
|
|
|
|
|
|
|
ScopedCallback::consume( $scope );
|
|
|
|
|
$this->assertEquals( [ 'read', 'edit', 'delete', 'upload' ],
|
|
|
|
|
$permissionManager->getUserPermissions( $this->user ) );
|
|
|
|
|
ScopedCallback::consume( $scope2 );
|
|
|
|
|
$this->assertEquals( [ 'read', 'edit' ],
|
|
|
|
|
$permissionManager->getUserPermissions( $this->user ) );
|
|
|
|
|
$this->assertFalse( $permissionManager->userHasRight( $this->user, 'move' ) );
|
|
|
|
|
|
|
|
|
|
( function () use ( $permissionManager ) {
|
|
|
|
|
$scope = $permissionManager->addTemporaryUserRights( $this->user, 'move' );
|
|
|
|
|
$this->assertTrue( $permissionManager->userHasRight( $this->user, 'move' ) );
|
|
|
|
|
} )();
|
|
|
|
|
$this->assertFalse( $permissionManager->userHasRight( $this->user, 'move' ) );
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-01 23:29:22 +00:00
|
|
|
/**
|
|
|
|
|
* Create a RevisionRecord with a single Javascript main slot.
|
|
|
|
|
* @param Title $title
|
|
|
|
|
* @param User $user
|
|
|
|
|
* @param string $text
|
|
|
|
|
* @return MutableRevisionRecord
|
|
|
|
|
*/
|
|
|
|
|
private function getJavascriptRevision( Title $title, User $user, $text ) {
|
|
|
|
|
$content = ContentHandler::makeContent( $text, $title, CONTENT_MODEL_JAVASCRIPT );
|
|
|
|
|
$revision = new MutableRevisionRecord( $title );
|
2023-01-03 15:24:42 +00:00
|
|
|
$revision->setContent( SlotRecord::MAIN, $content );
|
2018-11-01 23:29:22 +00:00
|
|
|
return $revision;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a RevisionRecord with a single Javascript redirect main slot.
|
|
|
|
|
* @param Title $title
|
|
|
|
|
* @param Title $redirectTargetTitle
|
|
|
|
|
* @param User $user
|
|
|
|
|
* @return MutableRevisionRecord
|
|
|
|
|
*/
|
|
|
|
|
private function getJavascriptRedirectRevision(
|
|
|
|
|
Title $title, Title $redirectTargetTitle, User $user
|
|
|
|
|
) {
|
2022-01-12 20:13:39 +00:00
|
|
|
$content = $this->getServiceContainer()->getContentHandlerFactory()
|
2020-01-18 20:25:04 +00:00
|
|
|
->getContentHandler( CONTENT_MODEL_JAVASCRIPT )
|
2018-11-01 23:29:22 +00:00
|
|
|
->makeRedirectContent( $redirectTargetTitle );
|
|
|
|
|
$revision = new MutableRevisionRecord( $title );
|
2023-01-03 15:24:42 +00:00
|
|
|
$revision->setContent( SlotRecord::MAIN, $content );
|
2018-11-01 23:29:22 +00:00
|
|
|
return $revision;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-21 19:49:59 +00:00
|
|
|
public function provideGetRestrictionLevels() {
|
|
|
|
|
return [
|
|
|
|
|
'No namespace restriction' => [ [ '', 'autoconfirmed', 'sysop' ], NS_TALK ],
|
|
|
|
|
'Restricted to autoconfirmed' => [ [ '', 'sysop' ], NS_MAIN ],
|
|
|
|
|
'Restricted to sysop' => [ [ '' ], NS_USER ],
|
|
|
|
|
'Restricted to someone in two groups' => [ [ '', 'sysop' ], 101 ],
|
|
|
|
|
'No special permissions' => [
|
|
|
|
|
[ '' ],
|
|
|
|
|
NS_TALK,
|
|
|
|
|
[]
|
|
|
|
|
],
|
|
|
|
|
'autoconfirmed' => [
|
|
|
|
|
[ '', 'autoconfirmed' ],
|
|
|
|
|
NS_TALK,
|
|
|
|
|
[ 'autoconfirmed' ]
|
|
|
|
|
],
|
|
|
|
|
'autoconfirmed revoked' => [
|
|
|
|
|
[ '' ],
|
|
|
|
|
NS_TALK,
|
|
|
|
|
[ 'autoconfirmed', 'noeditsemiprotected' ]
|
|
|
|
|
],
|
|
|
|
|
'sysop' => [
|
|
|
|
|
[ '', 'autoconfirmed', 'sysop' ],
|
|
|
|
|
NS_TALK,
|
|
|
|
|
[ 'sysop' ]
|
|
|
|
|
],
|
|
|
|
|
'sysop with autoconfirmed revoked (a bit silly)' => [
|
|
|
|
|
[ '', 'sysop' ],
|
|
|
|
|
NS_TALK,
|
|
|
|
|
[ 'sysop', 'noeditsemiprotected' ]
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideGetRestrictionLevels
|
|
|
|
|
*/
|
|
|
|
|
public function testGetRestrictionLevels( array $expected, $ns, array $userGroups = null ) {
|
2022-07-07 22:54:32 +00:00
|
|
|
$this->overrideConfigValues( [
|
|
|
|
|
MainConfigNames::GroupPermissions => [
|
2019-08-21 19:49:59 +00:00
|
|
|
'*' => [ 'edit' => true ],
|
|
|
|
|
'autoconfirmed' => [ 'editsemiprotected' => true ],
|
|
|
|
|
'sysop' => [
|
|
|
|
|
'editsemiprotected' => true,
|
|
|
|
|
'editprotected' => true,
|
|
|
|
|
],
|
|
|
|
|
'privileged' => [ 'privileged' => true ],
|
|
|
|
|
],
|
2022-07-07 22:54:32 +00:00
|
|
|
MainConfigNames::RevokePermissions => [
|
2019-08-21 19:49:59 +00:00
|
|
|
'noeditsemiprotected' => [ 'editsemiprotected' => true ],
|
|
|
|
|
],
|
2022-07-07 22:54:32 +00:00
|
|
|
MainConfigNames::NamespaceProtection => [
|
2019-08-21 19:49:59 +00:00
|
|
|
NS_MAIN => 'autoconfirmed',
|
|
|
|
|
NS_USER => 'sysop',
|
|
|
|
|
101 => [ 'editsemiprotected', 'privileged' ],
|
|
|
|
|
],
|
2022-07-07 22:54:32 +00:00
|
|
|
MainConfigNames::RestrictionLevels => [ '', 'autoconfirmed', 'sysop' ],
|
|
|
|
|
MainConfigNames::Autopromote => []
|
2019-08-21 19:49:59 +00:00
|
|
|
] );
|
2020-01-09 23:48:34 +00:00
|
|
|
$user = $userGroups === null ? null : $this->getTestUser( $userGroups )->getUser();
|
2022-01-12 20:13:39 +00:00
|
|
|
$this->assertSame( $expected, $this->getServiceContainer()
|
2019-08-21 19:49:59 +00:00
|
|
|
->getPermissionManager()
|
|
|
|
|
->getNamespaceRestrictionLevels( $ns, $user ) );
|
|
|
|
|
}
|
2019-08-20 20:59:49 +00:00
|
|
|
|
2019-08-28 17:31:34 +00:00
|
|
|
public function testGetAllPermissions() {
|
2022-07-07 22:54:32 +00:00
|
|
|
$this->overrideConfigValue( MainConfigNames::AvailableRights, [ 'test_right' ] );
|
2019-08-28 17:31:34 +00:00
|
|
|
$this->assertContains(
|
|
|
|
|
'test_right',
|
2022-01-12 20:13:39 +00:00
|
|
|
$this->getServiceContainer()
|
2019-08-28 17:31:34 +00:00
|
|
|
->getPermissionManager()
|
|
|
|
|
->getAllPermissions()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-20 20:59:49 +00:00
|
|
|
public function testAnonPermissionsNotClash() {
|
|
|
|
|
$user1 = User::newFromName( 'User1' );
|
|
|
|
|
$user2 = User::newFromName( 'User2' );
|
2022-01-12 20:13:39 +00:00
|
|
|
$pm = $this->getServiceContainer()->getPermissionManager();
|
2019-08-20 20:59:49 +00:00
|
|
|
$pm->overrideUserRightsForTesting( $user2, [] );
|
|
|
|
|
$this->assertNotSame( $pm->getUserPermissions( $user1 ), $pm->getUserPermissions( $user2 ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testAnonPermissionsNotClashOneRegistered() {
|
|
|
|
|
$user1 = User::newFromName( 'User1' );
|
|
|
|
|
$user2 = $this->getTestSysop()->getUser();
|
2022-01-12 20:13:39 +00:00
|
|
|
$pm = $this->getServiceContainer()->getPermissionManager();
|
2019-08-20 20:59:49 +00:00
|
|
|
$this->assertNotSame( $pm->getUserPermissions( $user1 ), $pm->getUserPermissions( $user2 ) );
|
|
|
|
|
}
|
Add `delete-redirect` for deleting single-rev redirects during moves
A new user right, `delete-redirect`, is added (not given to anyone
by default). At Special:MovePage, if attempting to move to a single
revision redirect that would otherwise be an invalid target (i.e.
doesn't point to the source page), the user is able to delete the
target.
Deletions are logged as `delete/delete_redir2`, and the move is
then logged normally as `move/move`, mirroring current delete and
move logging.
To allow for separate handling by Special:MovePage,
MovePage::isValidMove now returns a fatal status `redirectexists` if
the target isn't valid but passes Title::isSingleRevRedirect.
Otherwise, `articleexists` is returned (as previously). Other callers
that don't intend to treat single revision redirects differently
should treat `redirectexists` the same as `articleexists`.
Currently, this deletion (like normal delete and move) cannot be
done through the move api. Since the deletion is only valid when
moving a page, unlike for normal deletion, deleting redirects with
this right cannot be done via the delete api either.
Bug: T239277
Change-Id: I36c8df0a12d326ae07018046541bd00103936144
2019-12-19 23:13:31 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Test delete-redirect checks for Special:MovePage
|
|
|
|
|
*/
|
|
|
|
|
public function testDeleteRedirect() {
|
|
|
|
|
$this->editPage( 'ExistentRedirect3', '#REDIRECT [[Existent]]' );
|
2022-07-05 22:21:30 +00:00
|
|
|
$page = Title::makeTitle( NS_MAIN, 'ExistentRedirect3' );
|
2022-01-12 20:13:39 +00:00
|
|
|
$pm = $this->getServiceContainer()->getPermissionManager();
|
Add `delete-redirect` for deleting single-rev redirects during moves
A new user right, `delete-redirect`, is added (not given to anyone
by default). At Special:MovePage, if attempting to move to a single
revision redirect that would otherwise be an invalid target (i.e.
doesn't point to the source page), the user is able to delete the
target.
Deletions are logged as `delete/delete_redir2`, and the move is
then logged normally as `move/move`, mirroring current delete and
move logging.
To allow for separate handling by Special:MovePage,
MovePage::isValidMove now returns a fatal status `redirectexists` if
the target isn't valid but passes Title::isSingleRevRedirect.
Otherwise, `articleexists` is returned (as previously). Other callers
that don't intend to treat single revision redirects differently
should treat `redirectexists` the same as `articleexists`.
Currently, this deletion (like normal delete and move) cannot be
done through the move api. Since the deletion is only valid when
moving a page, unlike for normal deletion, deleting redirects with
this right cannot be done via the delete api either.
Bug: T239277
Change-Id: I36c8df0a12d326ae07018046541bd00103936144
2019-12-19 23:13:31 +00:00
|
|
|
|
2021-07-01 10:32:24 +00:00
|
|
|
$user = $this->getTestUser()->getUser();
|
Add `delete-redirect` for deleting single-rev redirects during moves
A new user right, `delete-redirect`, is added (not given to anyone
by default). At Special:MovePage, if attempting to move to a single
revision redirect that would otherwise be an invalid target (i.e.
doesn't point to the source page), the user is able to delete the
target.
Deletions are logged as `delete/delete_redir2`, and the move is
then logged normally as `move/move`, mirroring current delete and
move logging.
To allow for separate handling by Special:MovePage,
MovePage::isValidMove now returns a fatal status `redirectexists` if
the target isn't valid but passes Title::isSingleRevRedirect.
Otherwise, `articleexists` is returned (as previously). Other callers
that don't intend to treat single revision redirects differently
should treat `redirectexists` the same as `articleexists`.
Currently, this deletion (like normal delete and move) cannot be
done through the move api. Since the deletion is only valid when
moving a page, unlike for normal deletion, deleting redirects with
this right cannot be done via the delete api either.
Bug: T239277
Change-Id: I36c8df0a12d326ae07018046541bd00103936144
2019-12-19 23:13:31 +00:00
|
|
|
|
|
|
|
|
$this->assertFalse( $pm->quickUserCan( 'delete-redirect', $user, $page ) );
|
|
|
|
|
|
|
|
|
|
$pm->overrideUserRightsForTesting( $user, 'delete-redirect' );
|
|
|
|
|
|
|
|
|
|
$this->assertTrue( $pm->quickUserCan( 'delete-redirect', $user, $page ) );
|
|
|
|
|
$this->assertArrayEquals( [], $pm->getPermissionErrors( 'delete-redirect', $user, $page ) );
|
|
|
|
|
}
|
2020-10-15 21:37:09 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Enuser normal admins can view deleted javascript, but not restore it
|
|
|
|
|
* See T202989
|
|
|
|
|
*/
|
|
|
|
|
public function testSysopInterfaceAdminRights() {
|
|
|
|
|
$interfaceAdmin = $this->getTestUser( [ 'interface-admin', 'sysop' ] )->getUser();
|
|
|
|
|
$admin = $this->getTestSysop()->getUser();
|
|
|
|
|
|
2022-01-12 20:13:39 +00:00
|
|
|
$permManager = $this->getServiceContainer()->getPermissionManager();
|
2022-07-05 22:21:30 +00:00
|
|
|
$userJs = Title::makeTitle( NS_USER, 'Example/common.js' );
|
2020-10-15 21:37:09 +00:00
|
|
|
|
|
|
|
|
$this->assertTrue( $permManager->userCan( 'delete', $admin, $userJs ) );
|
|
|
|
|
$this->assertTrue( $permManager->userCan( 'delete', $interfaceAdmin, $userJs ) );
|
|
|
|
|
$this->assertTrue( $permManager->userCan( 'deletedhistory', $admin, $userJs ) );
|
|
|
|
|
$this->assertTrue( $permManager->userCan( 'deletedhistory', $interfaceAdmin, $userJs ) );
|
|
|
|
|
$this->assertTrue( $permManager->userCan( 'deletedtext', $admin, $userJs ) );
|
|
|
|
|
$this->assertTrue( $permManager->userCan( 'deletedtext', $interfaceAdmin, $userJs ) );
|
|
|
|
|
$this->assertFalse( $permManager->userCan( 'undelete', $admin, $userJs ) );
|
|
|
|
|
$this->assertTrue( $permManager->userCan( 'undelete', $interfaceAdmin, $userJs ) );
|
|
|
|
|
}
|
2022-04-28 23:34:43 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Regression test for T306358 -- proper page assertion when checking
|
|
|
|
|
* blocked status on a special page
|
|
|
|
|
*/
|
|
|
|
|
public function testBlockedFromNonProperPage() {
|
2022-07-05 22:21:30 +00:00
|
|
|
$page = Title::makeTitle( NS_SPECIAL, 'Blankpage' );
|
2022-04-28 23:34:43 +00:00
|
|
|
$pm = $this->getServiceContainer()->getPermissionManager();
|
|
|
|
|
$user = $this->getMockBuilder( User::class )
|
|
|
|
|
->onlyMethods( [ 'getBlock' ] )
|
|
|
|
|
->getMock();
|
|
|
|
|
$user->method( 'getBlock' )
|
|
|
|
|
->willReturn( new DatabaseBlock( [
|
|
|
|
|
'address' => '127.0.8.1',
|
|
|
|
|
'by' => $this->user,
|
|
|
|
|
] ) );
|
|
|
|
|
$errors = $pm->getPermissionErrors( 'test', $user, $page );
|
|
|
|
|
$this->assertNotEmpty( $errors );
|
|
|
|
|
}
|
2022-05-08 12:50:07 +00:00
|
|
|
|
|
|
|
|
/**
|
2022-06-01 23:38:22 +00:00
|
|
|
* Test interaction with $wgWhitelistRead.
|
2022-05-08 12:50:07 +00:00
|
|
|
*
|
|
|
|
|
* @dataProvider provideWhitelistRead
|
|
|
|
|
*/
|
|
|
|
|
public function testWhitelistRead( array $whitelist, string $title, bool $shouldAllow ) {
|
2022-07-07 22:54:32 +00:00
|
|
|
$this->overrideConfigValue( MainConfigNames::WhitelistRead, $whitelist );
|
2022-05-08 12:50:07 +00:00
|
|
|
$this->setGroupPermissions( '*', 'read', false );
|
|
|
|
|
|
2022-07-07 22:54:32 +00:00
|
|
|
$this->overrideConfigValue( MainConfigNames::LanguageCode, 'es' );
|
2022-05-08 12:50:07 +00:00
|
|
|
|
|
|
|
|
$title = Title::newFromText( $title );
|
|
|
|
|
$pm = $this->getServiceContainer()->getPermissionManager();
|
|
|
|
|
$errors = $pm->getPermissionErrors( 'read', new User, $title );
|
|
|
|
|
if ( $shouldAllow ) {
|
|
|
|
|
$this->assertSame( [], $errors );
|
|
|
|
|
} else {
|
|
|
|
|
$this->assertNotEmpty( $errors );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function provideWhitelistRead() {
|
2022-06-01 23:38:22 +00:00
|
|
|
yield 'no match' => [ [ 'Bar', 'Baz' ], 'Foo', false ];
|
|
|
|
|
yield 'match' => [ [ 'Bar', 'Foo', 'Baz' ], 'Foo', true ];
|
|
|
|
|
yield 'text form' => [ [ 'Foo bar' ], 'Foo_bar', true ];
|
|
|
|
|
yield 'dbkey form' => [ [ 'Foo_bar' ], 'Foo bar', true ];
|
|
|
|
|
yield 'local namespace' => [ [ 'Usuario:Foo' ], 'User:Foo', true ];
|
|
|
|
|
yield 'legacy mainspace' => [ [ ':Foo' ], 'Foo', true ];
|
|
|
|
|
yield 'local special' => [ [ 'Especial:Todas' ], 'Special:Allpages', true ];
|
2022-05-08 12:50:07 +00:00
|
|
|
}
|
2021-11-30 11:26:39 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers \MediaWiki\Permissions\PermissionManager::getPermissionErrors
|
|
|
|
|
*/
|
|
|
|
|
public function testGetPermissionErrors_ignoreErrors() {
|
|
|
|
|
$hookCallback = static function ( $title, $user, $action, &$result ) {
|
|
|
|
|
$result = [
|
|
|
|
|
[ 'ignore', 'param' ],
|
|
|
|
|
[ 'noignore', 'param' ],
|
|
|
|
|
'ignore',
|
|
|
|
|
'noignore',
|
|
|
|
|
new Message( 'ignore' ),
|
|
|
|
|
];
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
$this->setTemporaryHook( 'getUserPermissionsErrors', $hookCallback );
|
|
|
|
|
|
|
|
|
|
$pm = $this->getServiceContainer()->getPermissionManager();
|
|
|
|
|
$errors = $pm->getPermissionErrors(
|
|
|
|
|
'read',
|
|
|
|
|
$this->user,
|
|
|
|
|
$this->title,
|
|
|
|
|
$pm::RIGOR_QUICK,
|
|
|
|
|
[ 'ignore' ]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->assertSame( [
|
|
|
|
|
[ 'noignore', 'param' ],
|
|
|
|
|
'noignore',
|
|
|
|
|
], $errors );
|
|
|
|
|
}
|
2019-03-07 20:02:07 +00:00
|
|
|
}
|