Use Authority and GroupPermissionLookup in Action API

Replaces calls directly to PermissionManager with calls to
the Authority object available from Context or the
GroupPermissionLookup service.

This patch does not address use of PermissionManager for
blocks.

Deprecations:
- ApiBase::checkUserRightsAny deprecated passing optional
User parameter
- ApiBase::checkTitleUserPermissions deprecated passing
LinkTarget as first parameter, takes PageIdentity instead

Bug: T271462
Bug: T271854
Change-Id: I5d7cac1c28a37e074750c46cda03283980a07fca
This commit is contained in:
Cindy Cicalese 2021-01-11 10:26:02 -05:00 committed by Cicalese
parent 9cd7d35500
commit f6e1891c6f
37 changed files with 141 additions and 182 deletions

View file

@ -27,8 +27,11 @@ use MediaWiki\Block\DatabaseBlock;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\MediaWikiServices;
use MediaWiki\Page\PageIdentity;
use MediaWiki\ParamValidator\TypeDef\NamespaceDef;
use MediaWiki\Permissions\GroupPermissionsLookup;
use MediaWiki\Permissions\PermissionManager;
use MediaWiki\Permissions\PermissionStatus;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\TypeDef\EnumDef;
use Wikimedia\ParamValidator\TypeDef\IntegerDef;
@ -628,6 +631,17 @@ abstract class ApiBase extends ContextSource {
return MediaWikiServices::getInstance()->getPermissionManager();
}
/**
* Obtain a GroupPermissionsLookup instance that subclasses may use to access group permissions.
*
* @since 1.36
* @return GroupPermissionsLookup
* @internal
*/
protected function getGroupPermissionsLookup(): GroupPermissionsLookup {
return MediaWikiServices::getInstance()->getGroupPermissionsLookup();
}
/**
* Get a HookContainer, for running extension hooks or for hook metadata.
*
@ -1465,18 +1479,18 @@ abstract class ApiBase extends ContextSource {
* Helper function for permission-denied errors
* @since 1.29
* @param string|string[] $rights
* @param User|null $user
* @param User|null $user deprecated since 1.36
* @throws ApiUsageException if the user doesn't have any of the rights.
* The error message is based on $rights[0].
*/
public function checkUserRightsAny( $rights, $user = null ) {
if ( !$user ) {
$user = $this->getUser();
$authority = $this->getAuthority();
if ( $user !== null ) {
wfDeprecatedMsg( __METHOD__ . ': $user parameter is deprecated', '1.36' );
$authority = $user;
}
$rights = (array)$rights;
if ( !$this->getPermissionManager()
->userHasAnyRight( $user, ...$rights )
) {
if ( !$authority->isAllowedAny( ...$rights ) ) {
$this->dieWithError( [ 'apierror-permissiondenied', $this->msg( "action-{$rights[0]}" ) ] );
}
}
@ -1484,7 +1498,7 @@ abstract class ApiBase extends ContextSource {
/**
* Helper function for permission-denied errors
*
* @param LinkTarget $linkTarget
* @param PageIdentity|LinkTarget $pageIdentity deprecated passing LinkTarget since 1.36
* @param string|string[] $actions
* @param array $options Additional options
* - user: (User) User to use rather than $this->getUser()
@ -1493,28 +1507,31 @@ abstract class ApiBase extends ContextSource {
*
* @since 1.29
* @since 1.33 Changed the third parameter from $user to $options.
* @since 1.36 deprecated passing LinkTarget as first parameter
*/
public function checkTitleUserPermissions(
LinkTarget $linkTarget,
$pageIdentity,
$actions,
array $options = []
) {
$user = $options['user'] ?? $this->getUser();
$errors = [];
foreach ( (array)$actions as $action ) {
$errors = array_merge(
$errors,
$this->getPermissionManager()->getPermissionErrors( $action, $user, $linkTarget )
);
if ( !$pageIdentity instanceof PageIdentity ) {
wfDeprecatedMsg( __METHOD__ . ': passing LinkTarget as $pageIdentity parameter is deprecated',
'1.36' );
$pageIdentity = Title::castFromLinkTarget( $pageIdentity );
}
if ( $errors ) {
if ( !empty( $options['autoblock'] ) ) {
$user->spreadAnyEditBlock();
$status = new PermissionStatus();
foreach ( (array)$actions as $action ) {
if ( $this->isWriteMode() ) {
$this->getAuthority()->authorizeWrite( $action, $pageIdentity, $status );
} else {
$this->getAuthority()->authorizeRead( $action, $pageIdentity, $status );
}
$this->dieStatus( $this->errorArrayToStatus( $errors, $user ) );
}
if ( !$status->isGood() ) {
if ( !empty( $options['autoblock'] ) ) {
$this->getUser()->spreadAnyEditBlock();
}
$this->dieStatus( $status );
}
}

View file

@ -241,9 +241,7 @@ class ApiComparePages extends ApiBase {
*/
private function getRevisionById( $id ) {
$rev = $this->revisionStore->getRevisionById( $id );
if ( !$rev && $this->getPermissionManager()
->userHasAnyRight( $this->getUser(), 'deletedtext', 'undelete' )
) {
if ( !$rev && $this->getAuthority()->isAllowedAny( 'deletedtext', 'undelete' ) ) {
// Try the 'archive' table
$arQuery = $this->revisionStore->getArchiveQueryInfo();
$row = $this->getDB()->selectRow(

View file

@ -154,9 +154,8 @@ class ApiFeedContributions extends ApiBase {
// Hook completed and did not return a valid feed item
$title = Title::makeTitle( (int)$row->page_namespace, $row->page_title );
$user = $this->getUser();
if ( $title && $this->getPermissionManager()->userCan( 'read', $user, $title ) ) {
if ( $title && $this->getAuthority()->authorizeRead( 'read', $title ) ) {
$date = $row->rev_timestamp;
$comments = $title->getTalkPage()->getFullURL();
$revision = $this->revisionStore->newRevisionFromRow( $row, 0, $title );

View file

@ -36,7 +36,7 @@ class ApiImport extends ApiBase {
$isUpload = false;
if ( isset( $params['interwikisource'] ) ) {
if ( !$this->getPermissionManager()->userHasRight( $user, 'import' ) ) {
if ( !$this->getAuthority()->isAllowed( 'import' ) ) {
$this->dieWithError( 'apierror-cantimport' );
}
if ( !isset( $params['interwikipage'] ) ) {
@ -51,7 +51,7 @@ class ApiImport extends ApiBase {
$usernamePrefix = $params['interwikisource'];
} else {
$isUpload = true;
if ( !$this->getPermissionManager()->userHasRight( $user, 'importupload' ) ) {
if ( !$this->getAuthority()->isAllowed( 'importupload' ) ) {
$this->dieWithError( 'apierror-cantimport-upload' );
}
$source = ImportStreamSource::newFromUpload( 'xml' );

View file

@ -139,7 +139,6 @@ class ApiMain extends ApiBase {
'services' => [
'BlockPermissionCheckerFactory',
'UnblockUserFactory',
'PermissionManager',
'UserCache',
]
],
@ -1420,7 +1419,7 @@ class ApiMain extends ApiBase {
protected function checkExecutePermissions( $module ) {
$user = $this->getUser();
if ( $module->isReadMode() && !$this->getPermissionManager()->isEveryoneAllowed( 'read' ) &&
!$this->getPermissionManager()->userHasRight( $user, 'read' )
!$this->getAuthority()->isAllowed( 'read' )
) {
$this->dieWithError( 'apierror-readapidenied' );
}
@ -1428,7 +1427,7 @@ class ApiMain extends ApiBase {
if ( $module->isWriteMode() ) {
if ( !$this->mEnableWrite ) {
$this->dieWithError( 'apierror-noapiwrite' );
} elseif ( !$this->getPermissionManager()->userHasRight( $user, 'writeapi' ) ) {
} elseif ( !$this->getAuthority()->isAllowed( 'writeapi' ) ) {
$this->dieWithError( 'apierror-writeapidenied' );
} elseif ( $this->getRequest()->getHeader( 'Promise-Non-Write-API-Action' ) ) {
$this->dieWithError( 'apierror-promised-nonwrite-api' );
@ -1438,6 +1437,7 @@ class ApiMain extends ApiBase {
}
// Allow extensions to stop execution for arbitrary reasons.
// TODO: change hook to accept Authority
$message = 'hookaborted';
if ( !$this->getHookRunner()->onApiCheckCanExecute( $module, $user, $message ) ) {
$this->dieWithError( $message );
@ -1518,7 +1518,7 @@ class ApiMain extends ApiBase {
}
break;
case 'bot':
if ( !$this->getPermissionManager()->userHasRight( $user, 'bot' ) ) {
if ( !$this->getAuthority()->isAllowed( 'bot' ) ) {
$this->dieWithError( 'apierror-assertbotfailed' );
}
break;
@ -1973,7 +1973,7 @@ class ApiMain extends ApiBase {
$groups = array_map( static function ( $group ) {
return $group == '*' ? 'all' : $group;
}, $this->getPermissionManager()->getGroupsWithPermission( $right ) );
}, $this->getGroupPermissionsLookup()->getGroupsWithPermission( $right ) );
$help['permissions'] .= Html::rawElement( 'dd', null,
$this->msg( 'api-help-permissions-granted-to' )
@ -2104,8 +2104,7 @@ class ApiMain extends ApiBase {
*/
public function canApiHighLimits() {
if ( !isset( $this->mCanApiHighLimits ) ) {
$this->mCanApiHighLimits = $this->getPermissionManager()
->userHasRight( $this->getUser(), 'apihighlimits' );
$this->mCanApiHighLimits = $this->getAuthority()->isAllowed( 'apihighlimits' );
}
return $this->mCanApiHighLimits;

View file

@ -31,15 +31,16 @@ class ApiManageTags extends ApiBase {
// make sure the user is allowed
if ( $params['operation'] !== 'delete'
&& !$this->getPermissionManager()->userHasRight( $user, 'managechangetags' )
&& !$this->getAuthority()->isAllowed( 'managechangetags' )
) {
$this->dieWithError( 'tags-manage-no-permission', 'permissiondenied' );
} elseif ( !$this->getPermissionManager()->userHasRight( $user, 'deletechangetags' ) ) {
} elseif ( !$this->getAuthority()->isAllowed( 'deletechangetags' ) ) {
$this->dieWithError( 'tags-delete-no-permission', 'permissiondenied' );
}
// Check if user can add the log entry tags which were requested
if ( $params['tags'] ) {
// TODO: change to accept Authority
$ableToTag = ChangeTags::canAddTagsAccompanyingChange( $params['tags'], $user );
if ( !$ableToTag->isOK() ) {
$this->dieStatus( $ableToTag );

View file

@ -84,9 +84,9 @@ class ApiMove extends ApiBase {
&& $repoGroup->findFile( $toTitle )
) {
if ( !$params['ignorewarnings'] &&
$this->getPermissionManager()->userHasRight( $user, 'reupload-shared' ) ) {
$this->getAuthority()->isAllowed( 'reupload-shared' ) ) {
$this->dieWithError( 'apierror-fileexists-sharedrepo-perm' );
} elseif ( !$this->getPermissionManager()->userHasRight( $user, 'reupload-shared' ) ) {
} elseif ( !$this->getAuthority()->isAllowed( 'reupload-shared' ) ) {
$this->dieWithError( 'apierror-cantoverwrite-sharedfile' );
}
}
@ -201,13 +201,14 @@ class ApiMove extends ApiBase {
}
$user = $this->getUser();
// TODO: change to accept Authority
$permStatus = $mp->checkPermissions( $user, $reason );
if ( !$permStatus->isOK() ) {
return $permStatus;
}
// Check suppressredirect permission
if ( !$this->getPermissionManager()->userHasRight( $user, 'suppressredirect' ) ) {
if ( !$this->getAuthority()->isAllowed( 'suppressredirect' ) ) {
$createRedirect = true;
}

View file

@ -971,7 +971,7 @@ class ApiPageSet extends ApiBase {
// titles from the archive table and include them too. We ignore
// ar_page_id because deleted revisions are tied by title, not page_id.
if ( $goodRemaining &&
$this->getPermissionManager()->userHasRight( $this->getUser(), 'deletedhistory' ) ) {
$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
$tables = [ 'archive' ];
$fields = [ 'ar_rev_id', 'ar_namespace', 'ar_title' ];
$where = [ 'ar_rev_id' => array_keys( $goodRemaining ) ];

View file

@ -156,8 +156,7 @@ class ApiParse extends ApiBase {
$this->dieWithError( [ 'apierror-nosuchrevid', $oldid ] );
}
$revLinkTarget = $rev->getPageAsLinkTarget();
$this->checkTitleUserPermissions( $revLinkTarget, 'read' );
$this->checkTitleUserPermissions( $rev->getPage(), 'read' );
if ( !$rev->audienceCan(
RevisionRecord::DELETED_TEXT,
@ -169,6 +168,7 @@ class ApiParse extends ApiBase {
);
}
$revLinkTarget = $rev->getPageAsLinkTarget();
$titleObj = Title::newFromLinkTarget( $revLinkTarget );
$wgTitle = $titleObj;
$pageObj = WikiPage::factory( $titleObj );

View file

@ -459,7 +459,7 @@ class ApiQuery extends ApiBase {
if ( count( $titles ) ) {
/** @var Title $title */
foreach ( $titles as $title ) {
if ( $this->getPermissionManager()->userCan( 'read', $this->getUser(), $title ) ) {
if ( $this->getAuthority()->authorizeRead( 'read', $title ) ) {
$exportTitles[] = $title;
}
}

View file

@ -147,12 +147,12 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
// This means stricter restrictions
if ( ( $this->fld_comment || $this->fld_parsedcomment ) &&
!$this->getPermissionManager()->userHasRight( $user, 'deletedhistory' )
!$this->getAuthority()->isAllowed( 'deletedhistory' )
) {
$this->dieWithError( 'apierror-cantview-deleted-comment', 'permissiondenied' );
}
if ( $this->fetchContent &&
!$this->getPermissionManager()->userHasAnyRight( $user, 'deletedtext', 'undelete' )
!$this->getAuthority()->isAllowedAny( 'deletedtext', 'undelete' )
) {
$this->dieWithError( 'apierror-cantview-deleted-revision-content', 'permissiondenied' );
}
@ -244,11 +244,9 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
if ( $params['user'] !== null || $params['excludeuser'] !== null ) {
// Paranoia: avoid brute force searches (T19342)
if ( !$this->getPermissionManager()->userHasRight( $user, 'deletedhistory' ) ) {
if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
$bitmask = RevisionRecord::DELETED_USER;
} elseif ( !$this->getPermissionManager()
->userHasAnyRight( $user, 'suppressrevision', 'viewsuppressed' )
) {
} elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
$bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
} else {
$bitmask = 0;

View file

@ -215,7 +215,7 @@ class ApiQueryAllImages extends ApiQueryGeneratorBase {
$this->addJoinConds( [ 'user_groups' => [
'LEFT JOIN',
[
'ug_group' => $this->getPermissionManager()->getGroupsWithPermission( 'bot' ),
'ug_group' => $this->getGroupPermissionsLookup()->getGroupsWithPermission( 'bot' ),
'ug_user = ' . $actorQuery['fields']['img_user'],
'ug_expiry IS NULL OR ug_expiry >= ' . $db->addQuotes( $db->timestamp() )
]

View file

@ -155,10 +155,9 @@ class ApiQueryAllRevisions extends ApiQueryRevisionsBase {
if ( $params['user'] !== null || $params['excludeuser'] !== null ) {
// Paranoia: avoid brute force searches (T19342)
if ( !$this->getPermissionManager()->userHasRight( $this->getUser(), 'deletedhistory' ) ) {
if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
$bitmask = RevisionRecord::DELETED_USER;
} elseif ( !$this->getPermissionManager()
->userHasAnyRight( $this->getUser(), 'suppressrevision', 'viewsuppressed' )
} elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' )
) {
$bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
} else {

View file

@ -91,7 +91,7 @@ class ApiQueryAllUsers extends ApiQueryBase {
if ( $params['rights'] !== null && count( $params['rights'] ) ) {
$groups = [];
foreach ( $params['rights'] as $r ) {
$groups = array_merge( $groups, $this->getPermissionManager()
$groups = array_merge( $groups, $this->getGroupPermissionsLookup()
->getGroupsWithPermission( $r ) );
}
@ -304,7 +304,7 @@ class ApiQueryAllUsers extends ApiQueryBase {
}
if ( $fld_rights ) {
$data['rights'] = $this->getPermissionManager()->getGroupPermissions( $groups );
$data['rights'] = $this->getGroupPermissionsLookup()->getGroupPermissions( $groups );
ApiResult::setIndexedTagName( $data['rights'], 'r' );
ApiResult::setArrayType( $data['rights'], 'array' );
}

View file

@ -605,8 +605,7 @@ abstract class ApiQueryBase extends ApiBase {
* @return bool
*/
public function userCanSeeRevDel() {
return $this->getPermissionManager()->userHasAnyRight(
$this->getUser(),
return $this->getAuthority()->isAllowedAny(
'deletedhistory',
'deletedtext',
'suppressrevision',

View file

@ -19,7 +19,7 @@
*/
use MediaWiki\Block\DatabaseBlock;
use MediaWiki\Permissions\PermissionManager;
use MediaWiki\Permissions\Authority;
use Wikimedia\Rdbms\IDatabase;
/**
@ -59,7 +59,7 @@ trait ApiQueryBlockInfoTrait {
] );
// Don't show hidden names
if ( !$this->getPermissionManager()->userHasRight( $this->getUser(), 'hideuser' ) ) {
if ( !$this->getAuthority()->isAllowed( 'hideuser' ) ) {
$this->addWhere( 'ipb_deleted = 0 OR ipb_deleted IS NULL' );
}
}
@ -75,16 +75,10 @@ trait ApiQueryBlockInfoTrait {
abstract protected function getDB();
/**
* @see ApiBase::getPermissionManager
* @return PermissionManager
* @see IContextSource::getAuthority
* @return Authority
*/
abstract protected function getPermissionManager(): PermissionManager;
/**
* @see IContextSource::getUser
* @return User
*/
abstract public function getUser();
abstract public function getAuthority();
/**
* @see ApiQueryBase::addTables

View file

@ -178,7 +178,7 @@ class ApiQueryBlocks extends ApiQueryBase {
$this->addWhereIf( 'ipb_range_end > ipb_range_start', isset( $show['range'] ) );
}
if ( !$this->getPermissionManager()->userHasRight( $this->getUser(), 'hideuser' ) ) {
if ( !$this->getAuthority()->isAllowed( 'hideuser' ) ) {
$this->addWhereFld( 'ipb_deleted', 0 );
}

View file

@ -146,7 +146,7 @@ class ApiQueryContributors extends ApiQueryBase {
} elseif ( $params['rights'] ) {
$excludeGroups = false;
foreach ( $params['rights'] as $r ) {
$limitGroups = array_merge( $limitGroups, $this->getPermissionManager()
$limitGroups = array_merge( $limitGroups, $this->getGroupPermissionsLookup()
->getGroupsWithPermission( $r ) );
}
@ -163,7 +163,7 @@ class ApiQueryContributors extends ApiQueryBase {
} elseif ( $params['excluderights'] ) {
$excludeGroups = true;
foreach ( $params['excluderights'] as $r ) {
$limitGroups = array_merge( $limitGroups, $this->getPermissionManager()
$limitGroups = array_merge( $limitGroups, $this->getGroupPermissionsLookup()
->getGroupsWithPermission( $r ) );
}
}

View file

@ -99,13 +99,11 @@ class ApiQueryDeletedRevisions extends ApiQueryRevisionsBase {
// This means stricter restrictions
if ( ( $this->fld_comment || $this->fld_parsedcomment ) &&
!$this->getPermissionManager()->userHasRight( $user, 'deletedhistory' )
!$this->getAuthority()->isAllowed( 'deletedhistory' )
) {
$this->dieWithError( 'apierror-cantview-deleted-comment', 'permissiondenied' );
}
if ( $this->fetchContent &&
!$this->getPermissionManager()->userHasAnyRight( $user, 'deletedtext', 'undelete' )
) {
if ( $this->fetchContent && !$this->getAuthority()->isAllowedAny( 'deletedtext', 'undelete' ) ) {
$this->dieWithError( 'apierror-cantview-deleted-revision-content', 'permissiondenied' );
}
@ -141,11 +139,9 @@ class ApiQueryDeletedRevisions extends ApiQueryRevisionsBase {
if ( $params['user'] !== null || $params['excludeuser'] !== null ) {
// Paranoia: avoid brute force searches (T19342)
if ( !$this->getPermissionManager()->userHasRight( $user, 'deletedhistory' ) ) {
if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
$bitmask = RevisionRecord::DELETED_USER;
} elseif ( !$this->getPermissionManager()
->userHasAnyRight( $this->getUser(), 'suppressrevision', 'viewsuppressed' )
) {
} elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
$bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
} else {
$bitmask = 0;

View file

@ -71,7 +71,7 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
}
// If user can't undelete, no tokens
if ( !$this->getPermissionManager()->userHasRight( $user, 'undelete' ) ) {
if ( !$this->getAuthority()->isAllowed( 'undelete' ) ) {
$fld_token = false;
}
@ -210,11 +210,9 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
// Paranoia: avoid brute force searches (T19342)
// (shouldn't be able to get here without 'deletedhistory', but
// check it again just in case)
if ( !$this->getPermissionManager()->userHasRight( $user, 'deletedhistory' ) ) {
if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
$bitmask = RevisionRecord::DELETED_USER;
} elseif ( !$this->getPermissionManager()
->userHasAnyRight( $user, 'suppressrevision', 'viewsuppressed' )
) {
} elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
$bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
} else {
$bitmask = 0;

View file

@ -57,14 +57,10 @@ class ApiQueryFilearchive extends ApiQueryBase {
$fld_bitdepth = isset( $prop['bitdepth'] );
$fld_archivename = isset( $prop['archivename'] );
if ( $fld_description &&
!$this->getPermissionManager()->userHasRight( $user, 'deletedhistory' )
) {
if ( $fld_description && !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
$this->dieWithError( 'apierror-cantview-deleted-description', 'permissiondenied' );
}
if ( $fld_metadata &&
!$this->getPermissionManager()->userHasAnyRight( $user, 'deletedtext', 'undelete' )
) {
if ( $fld_metadata && !$this->getAuthority()->isAllowedAny( 'deletedtext', 'undelete' ) ) {
$this->dieWithError( 'apierror-cantview-deleted-metadata', 'permissiondenied' );
}
@ -119,11 +115,9 @@ class ApiQueryFilearchive extends ApiQueryBase {
if ( $sha1 ) {
$this->addWhereFld( 'fa_sha1', $sha1 );
// Paranoia: avoid brute force searches (T19342)
if ( !$this->getPermissionManager()->userHasRight( $user, 'deletedtext' ) ) {
if ( !$this->getAuthority()->isAllowed( 'deletedtext' ) ) {
$bitmask = File::DELETED_FILE;
} elseif ( !$this->getPermissionManager()
->userHasAnyRight( $user, 'suppressrevision', 'viewsuppressed' )
) {
} elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
$bitmask = File::DELETED_FILE | File::DELETED_RESTRICTED;
} else {
$bitmask = 0;

View file

@ -21,9 +21,8 @@
*/
use MediaWiki\Cache\LinkBatchFactory;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\MediaWikiServices;
use MediaWiki\ParamValidator\TypeDef\TitleDef;
use MediaWiki\Permissions\PermissionManager;
use MediaWiki\Permissions\PermissionStatus;
/**
* A query module to show basic page information.
@ -190,10 +189,7 @@ class ApiQueryInfo extends ApiQueryBase {
* @return string|false
*/
private static function getUserToken( User $user, string $right ) {
if ( !MediaWikiServices::getInstance()
->getPermissionManager()
->userHasRight( $user, $right )
) {
if ( !$user->isAllowed( $right ) ) {
return false;
}
@ -284,9 +280,7 @@ class ApiQueryInfo extends ApiQueryBase {
* @param User $user
*/
public static function getImportToken( User $user ) {
if ( !MediaWikiServices::getInstance()
->getPermissionManager()
->userHasAnyRight( $user, 'import', 'importupload' ) ) {
if ( !$user->isAllowedAny( 'import', 'importupload' ) ) {
return false;
}
@ -457,8 +451,6 @@ class ApiQueryInfo extends ApiQueryBase {
$pageInfo['pagelanguagehtmlcode'] = $pageLanguage->getHtmlCode();
$pageInfo['pagelanguagedir'] = $pageLanguage->getDir();
$user = $this->getUser();
if ( $titleExists ) {
$pageInfo['touched'] = wfTimestamp( TS_ISO_8601, $this->pageTouched[$pageid] );
$pageInfo['lastrevid'] = (int)$this->pageLatest[$pageid];
@ -551,9 +543,7 @@ class ApiQueryInfo extends ApiQueryBase {
$pageInfo['canonicalurl'] = wfExpandUrl( $title->getFullURL(), PROTO_CANONICAL );
}
if ( $this->fld_readable ) {
$pageInfo['readable'] = $this->getPermissionManager()->userCan(
'read', $user, $title
);
$pageInfo['readable'] = $this->getAuthority()->definitelyCan( 'read', $title );
}
if ( $this->fld_preload ) {
@ -590,32 +580,27 @@ class ApiQueryInfo extends ApiQueryBase {
}
$detailLevel = $this->params['testactionsdetail'];
$rigor = $detailLevel === 'quick'
? PermissionManager::RIGOR_QUICK
// Not using RIGOR_SECURE here, because that results in master connection
: PermissionManager::RIGOR_FULL;
$errorFormatter = $this->getErrorFormatter();
if ( $errorFormatter->getFormat() === 'bc' ) {
// Eew, no. Use a more modern format here.
$errorFormatter = $errorFormatter->newWithFormat( 'plaintext' );
}
$user = $this->getUser();
$pageInfo['actions'] = [];
foreach ( $this->params['testactions'] as $action ) {
$this->countTestedActions++;
if ( $detailLevel === 'boolean' ) {
$pageInfo['actions'][$action] = $this->getPermissionManager()->userCan(
$action, $user, $title
);
$pageInfo['actions'][$action] = $this->getAuthority()->authorizeRead( $action, $title );
} else {
$pageInfo['actions'][$action] = $errorFormatter->arrayFromStatus( $this->errorArrayToStatus(
$this->getPermissionManager()->getPermissionErrors(
$action, $user, $title, $rigor
),
$user
) );
$status = new PermissionStatus();
if ( $detailLevel === 'quick' ) {
$this->getAuthority()->probablyCan( $action, $title, $status );
} else {
$this->getAuthority()->definitelyCan( $action, $title, $status );
}
$this->addBlockInfoToStatus( $status );
$pageInfo['actions'][$action] = $errorFormatter->arrayFromStatus( $status );
}
}
}
@ -916,7 +901,7 @@ class ApiQueryInfo extends ApiQueryBase {
$user = $this->getUser();
if ( $user->isAnon() || count( $this->everything ) == 0
|| !$this->getPermissionManager()->userHasRight( $user, 'viewmywatchlist' )
|| !$this->getAuthority()->isAllowed( 'viewmywatchlist' )
) {
return;
}
@ -955,8 +940,7 @@ class ApiQueryInfo extends ApiQueryBase {
return;
}
$user = $this->getUser();
$canUnwatchedpages = $this->getPermissionManager()->userHasRight( $user, 'unwatchedpages' );
$canUnwatchedpages = $this->getAuthority()->isAllowed( 'unwatchedpages' );
$unwatchedPageThreshold = $this->getConfig()->get( 'UnwatchedPageThreshold' );
if ( !$canUnwatchedpages && !is_int( $unwatchedPageThreshold ) ) {
return;
@ -983,11 +967,10 @@ class ApiQueryInfo extends ApiQueryBase {
*/
private function getVisitingWatcherInfo() {
$config = $this->getConfig();
$user = $this->getUser();
$db = $this->getDB();
$canUnwatchedpages = $this->getPermissionManager()->userHasRight( $user, 'unwatchedpages' );
$unwatchedPageThreshold = $this->getConfig()->get( 'UnwatchedPageThreshold' );
$canUnwatchedpages = $this->getAuthority()->isAllowed( 'unwatchedpages' );
$unwatchedPageThreshold = $config->get( 'UnwatchedPageThreshold' );
if ( !$canUnwatchedpages && !is_int( $unwatchedPageThreshold ) ) {
return;
}

View file

@ -223,12 +223,10 @@ class ApiQueryLogEvents extends ApiQueryBase {
// Paranoia: avoid brute force searches (T19342)
if ( $params['namespace'] !== null || $title !== null || $user !== null ) {
if ( !$this->getPermissionManager()->userHasRight( $this->getUser(), 'deletedhistory' ) ) {
if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
$titleBits = LogPage::DELETED_ACTION;
$userBits = LogPage::DELETED_USER;
} elseif ( !$this->getPermissionManager()
->userHasAnyRight( $this->getUser(), 'suppressrevision', 'viewsuppressed' )
) {
} elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
$titleBits = LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED;
$userBits = LogPage::DELETED_USER | LogPage::DELETED_RESTRICTED;
} else {

View file

@ -363,11 +363,9 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
// Paranoia: avoid brute force searches (T19342)
if ( $params['user'] !== null || $params['excludeuser'] !== null ) {
if ( !$this->getPermissionManager()->userHasRight( $user, 'deletedhistory' ) ) {
if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
$bitmask = RevisionRecord::DELETED_USER;
} elseif ( !$this->getPermissionManager()
->userHasAnyRight( $user, 'suppressrevision', 'viewsuppressed' )
) {
} elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
$bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
} else {
$bitmask = 0;
@ -378,11 +376,9 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
}
if ( $this->getRequest()->getCheck( 'namespace' ) ) {
// LogPage::DELETED_ACTION hides the affected page, too.
if ( !$this->getPermissionManager()->userHasRight( $user, 'deletedhistory' ) ) {
if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
$bitmask = LogPage::DELETED_ACTION;
} elseif ( !$this->getPermissionManager()
->userHasAnyRight( $user, 'suppressrevision', 'viewsuppressed' )
) {
} elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
$bitmask = LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED;
} else {
$bitmask = 0;

View file

@ -77,8 +77,7 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
* @return string|false
*/
public static function getRollbackToken( User $user ) {
if ( !MediaWikiServices::getInstance()->getPermissionManager()
->userHasRight( $user, 'rollback' ) ) {
if ( !$user->isAllowed( 'rollback' ) ) {
return false;
}
@ -206,11 +205,10 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
if ( $resultPageSet === null && $this->fetchContent ) {
// For each page we will request, the user must have read rights for that page
$status = Status::newGood();
$user = $this->getUser();
/** @var Title $title */
foreach ( $pageSet->getGoodTitles() as $title ) {
if ( !$this->getPermissionManager()->userCan( 'read', $user, $title ) ) {
if ( !$this->getAuthority()->authorizeRead( 'read', $title ) ) {
$status->fatal( ApiMessage::create(
[ 'apierror-cannotviewtitle', wfEscapeWikiText( $title->getPrefixedText() ) ],
'accessdenied'
@ -343,10 +341,9 @@ class ApiQueryRevisions extends ApiQueryRevisionsBase {
if ( $params['user'] !== null || $params['excludeuser'] !== null ) {
// Paranoia: avoid brute force searches (T19342)
if ( !$this->getPermissionManager()->userHasRight( $this->getUser(), 'deletedhistory' ) ) {
if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
$bitmask = RevisionRecord::DELETED_USER;
} elseif ( !$this->getPermissionManager()
->userHasAnyRight( $this->getUser(), 'suppressrevision', 'viewsuppressed' )
} elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' )
) {
$bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
} else {

View file

@ -340,12 +340,9 @@ class ApiQueryUserContribs extends ApiQueryBase {
// Don't include any revisions where we're not supposed to be able to
// see the username.
$user = $this->getUser();
if ( !$this->getPermissionManager()->userHasRight( $user, 'deletedhistory' ) ) {
if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
$bitmask = RevisionRecord::DELETED_USER;
} elseif ( !$this->getPermissionManager()
->userHasAnyRight( $user, 'suppressrevision', 'viewsuppressed' )
) {
} elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
$bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
} else {
$bitmask = 0;
@ -413,6 +410,7 @@ class ApiQueryUserContribs extends ApiQueryBase {
if ( isset( $show['patrolled'] ) || isset( $show['!patrolled'] ) ||
isset( $show['autopatrolled'] ) || isset( $show['!autopatrolled'] ) || $this->fld_patrolled
) {
$user = $this->getUser();
if ( !$user->useRCPatrol() && !$user->useNPPatrol() ) {
$this->dieWithError( 'apierror-permissiondenied-patrolflag', 'permissiondenied' );
}

View file

@ -186,7 +186,7 @@ class ApiQueryUserInfo extends ApiQueryBase {
if ( isset( $this->prop['preferencestoken'] ) &&
!$this->lacksSameOriginSecurity() &&
$this->getPermissionManager()->userHasRight( $user, 'editmyoptions' )
$this->getAuthority()->isAllowed( 'editmyoptions' )
) {
$vals['preferencestoken'] = $user->getEditToken( '', $this->getMain()->getRequest() );
}
@ -212,8 +212,7 @@ class ApiQueryUserInfo extends ApiQueryBase {
$vals['realname'] = $user->getRealName();
}
if ( $this->getPermissionManager()->userHasRight( $user, 'viewmyprivateinfo' ) &&
isset( $this->prop['email'] ) ) {
if ( $this->getAuthority()->isAllowed( 'viewmyprivateinfo' ) && isset( $this->prop['email'] ) ) {
$vals['email'] = $user->getEmail();
$auth = $user->getEmailAuthenticationTimestamp();
if ( $auth !== null ) {

View file

@ -91,6 +91,7 @@ class ApiRevisionDelete extends ApiBase {
$this->dieWithError( [ 'apierror-revdel-needtarget' ], 'needtarget' );
}
// TODO: replace use of PermissionManager
if ( $this->getPermissionManager()->isBlockedFrom( $user, $targetObj ) ) {
$this->dieBlocked( $user->getBlock() );
}

View file

@ -94,6 +94,7 @@ class ApiTag extends ApiBase {
switch ( $type ) {
case 'rcid':
$valid = RecentChange::newFromId( $id );
// TODO: replace use of PermissionManager
if ( $valid && $this->getPermissionManager()->isBlockedFrom( $user, $valid->getTitle() ) ) {
$idResult['status'] = 'error';
// @phan-suppress-next-line PhanTypeMismatchArgument
@ -107,6 +108,7 @@ class ApiTag extends ApiBase {
break;
case 'revid':
$valid = $this->revisionStore->getRevisionById( $id );
// TODO: replace use of PermissionManager
if (
$valid &&
$this->getPermissionManager()->isBlockedFrom( $user, $valid->getPageAsLinkTarget() )

View file

@ -24,7 +24,6 @@ use MediaWiki\Block\BlockPermissionCheckerFactory;
use MediaWiki\Block\DatabaseBlock;
use MediaWiki\Block\UnblockUserFactory;
use MediaWiki\ParamValidator\TypeDef\UserDef;
use MediaWiki\Permissions\PermissionManager;
/**
* API module that facilitates the unblocking of users. Requires API write mode
@ -42,9 +41,6 @@ class ApiUnblock extends ApiBase {
/** @var UnblockUserFactory */
private $unblockUserFactory;
/** @var PermissionManager */
private $permissionManager;
/** @var UserCache */
private $userCache;
@ -53,14 +49,12 @@ class ApiUnblock extends ApiBase {
$action,
BlockPermissionCheckerFactory $permissionCheckerFactory,
UnblockUserFactory $unblockUserFactory,
PermissionManager $permissionManager,
UserCache $userCache
) {
parent::__construct( $main, $action );
$this->permissionCheckerFactory = $permissionCheckerFactory;
$this->unblockUserFactory = $unblockUserFactory;
$this->permissionManager = $permissionManager;
$this->userCache = $userCache;
}
@ -73,7 +67,7 @@ class ApiUnblock extends ApiBase {
$this->requireOnlyOneParameter( $params, 'id', 'user', 'userid' );
if ( !$this->permissionManager->userHasRight( $performer, 'block' ) ) {
if ( !$this->getAuthority()->isAllowed( 'block' ) ) {
$this->dieWithError( 'apierror-permissiondenied-unblock', 'permissiondenied' );
}

View file

@ -50,12 +50,13 @@ class ApiUndelete extends ApiBase {
$this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['title'] ) ] );
}
if ( !$this->getPermissionManager()->userCan( 'undelete', $this->getUser(), $titleObj ) ) {
if ( !$this->getAuthority()->authorizeWrite( 'undelete', $titleObj ) ) {
$this->dieWithError( 'permdenied-undelete' );
}
// Check if user can add tags
if ( $params['tags'] !== null ) {
// TODO: change to accept Authority
$ableToTag = ChangeTags::canAddTagsAccompanyingChange( $params['tags'], $user );
if ( !$ableToTag->isOK() ) {
$this->dieStatus( $ableToTag );

View file

@ -53,7 +53,7 @@ class ApiUserrights extends ApiBase {
// Deny if the user is blocked and doesn't have the full 'userrights' permission.
// This matches what Special:UserRights does for the web UI.
if ( !$this->getPermissionManager()->userHasRight( $pUser, 'userrights' ) ) {
if ( !$this->getAuthority()->isAllowed( 'userrights' ) ) {
$block = $pUser->getBlock();
if ( $block && $block->isSitewide() ) {
$this->dieBlocked( $block );

View file

@ -44,6 +44,9 @@ class ApiOptionsTest extends MediaWikiLangTestCase {
$this->mUserMock->method( 'getOptions' )
->willReturn( [] );
$this->mUserMock->expects( $this->any() )
->method( 'isAllowedAny' )->willReturn( true );
// DefaultPreferencesFactory calls a ton of user methods, but we still want to list all of
// them in case bugs are caused by unexpected things returning null that shouldn't.
$this->mUserMock->expects( $this->never() )->method( $this->anythingBut(
@ -51,7 +54,8 @@ class ApiOptionsTest extends MediaWikiLangTestCase {
'isAnon', 'getRequest', 'isLoggedIn', 'getName', 'getGroupMemberships', 'getEditCount',
'getRegistration', 'isAllowed', 'getRealName', 'getOption', 'getStubThreshold',
'getBoolOption', 'getEmail', 'getDatePreference', 'useRCPatrol', 'useNPPatrol',
'setOption', 'saveSettings', 'resetOptions', 'isRegistered', 'getTitleKey'
'setOption', 'saveSettings', 'resetOptions', 'isRegistered', 'getTitleKey',
'isAllowedAny'
) );
// Create a new context

View file

@ -95,6 +95,7 @@ abstract class ApiTestCase extends MediaWikiLangTestCase {
$contextUser = $wgUser;
}
$sessionObj->setUser( $contextUser );
if ( $tokenType !== null ) {
if ( $tokenType === 'auto' ) {
$tokenType = ( new ApiMain() )->getModuleManager()

View file

@ -1,7 +1,6 @@
<?php
use MediaWiki\Block\DatabaseBlock;
use MediaWiki\MediaWikiServices;
use Wikimedia\TestingAccessWrapper;
use Wikimedia\Timestamp\ConvertibleTimestamp;
@ -26,9 +25,7 @@ class ApiQueryBlockInfoTraitTest extends MediaWikiIntegrationTestCase {
$mock = $this->getMockForTrait( ApiQueryBlockInfoTrait::class );
$mock->method( 'getDB' )->willReturn( wfGetDB( DB_REPLICA ) );
$mock->method( 'getPermissionManager' )
->willReturn( MediaWikiServices::getInstance()->getPermissionManager() );
$mock->method( 'getUser' )
$mock->method( 'getAuthority' )
->willReturn( $this->getMutableTestUser()->getUser() );
$mock->method( 'addTables' )->willReturnCallback( static function ( $v ) use ( &$data ) {
$data['tables'] = array_merge( $data['tables'] ?? [], (array)$v );

View file

@ -38,6 +38,7 @@ class ApiQueryInfoTest extends ApiTestCase {
$page = $this->getExistingTestPage( 'Pluto' );
$title = $page->getTitle();
$user = $this->getTestUser()->getUser();
RequestContext::getMain()->setUser( $user );
WatchAction::doWatch(
$title,
$user,

View file

@ -11,7 +11,6 @@ use MediaWiki\Block\BlockPermissionCheckerFactory;
use MediaWiki\Block\DatabaseBlock;
use MediaWiki\Block\UnblockUser;
use MediaWiki\Block\UnblockUserFactory;
use MediaWiki\Permissions\PermissionManager;
use MediaWikiUnitTestCase;
use RequestContext;
use Status;
@ -41,7 +40,6 @@ class ApiUnblockTest extends MediaWikiUnitTestCase {
$action = 'unblock';
$blockPermissionCheckerFactory = $this->createMock( BlockPermissionCheckerFactory::class );
$unblockUserFactory = $this->createMock( UnblockUserFactory::class );
$permissionManager = $this->createMock( PermissionManager::class );
$userCache = $this->createMock( UserCache::class );
// Expose requestContext and user so that they can be further modified in the test
@ -50,7 +48,6 @@ class ApiUnblockTest extends MediaWikiUnitTestCase {
'action' => 'unblock',
'blockPermissionCheckerFactory' => $blockPermissionCheckerFactory,
'unblockUserFactory' => $unblockUserFactory,
'permissionManager' => $permissionManager,
'userCache' => $userCache,
'requestContext' => $requestContext,
'performer' => $performer,
@ -69,7 +66,6 @@ class ApiUnblockTest extends MediaWikiUnitTestCase {
$args['action'],
$args['blockPermissionCheckerFactory'],
$args['unblockUserFactory'],
$args['permissionManager'],
$args['userCache']
] )
->getMock();
@ -128,7 +124,6 @@ class ApiUnblockTest extends MediaWikiUnitTestCase {
$args['action'],
$args['blockPermissionCheckerFactory'],
$args['unblockUserFactory'],
$args['permissionManager'],
$args['userCache']
);
// Ensure everything was created right
@ -171,10 +166,9 @@ class ApiUnblockTest extends MediaWikiUnitTestCase {
// Since the actual internals of that are complicated, mock it
$args = $this->getConstructorArgs();
$args['permissionManager']->expects( $this->once() )
->method( 'userHasRight' )
$args['performer']->expects( $this->once() )
->method( 'isAllowed' )
->with(
$this->equalTo( $args['performer'] ),
$this->equalTo( 'block' )
)
->willReturn( false );
@ -205,7 +199,7 @@ class ApiUnblockTest extends MediaWikiUnitTestCase {
// If $params['userid'] is set and the usercache call returns false, there is an error
$args = $this->getConstructorArgs();
$args['permissionManager']->method( 'userHasRight' )->willReturn( true );
$args['performer']->method( 'isAllowed' )->willReturn( true );
$args['userCache']->expects( $this->once() )
->method( 'getProp' )
@ -246,7 +240,7 @@ class ApiUnblockTest extends MediaWikiUnitTestCase {
// Next potential failure is the actual unblock call failing
$args = $this->getConstructorArgs();
$args['permissionManager']->method( 'userHasRight' )->willReturn( true );
$args['performer']->method( 'isAllowed' )->willReturn( true );
// Return true
$args['blockPermissionCheckerFactory'] = $this->getBlockPermissionCheckerFactory(
@ -318,7 +312,7 @@ class ApiUnblockTest extends MediaWikiUnitTestCase {
->willReturn( true );
$args['apiMain']->method( 'getResult' )->willReturn( $apiResult );
$args['permissionManager']->method( 'userHasRight' )->willReturn( true );
$args['performer']->method( 'isAllowed' )->willReturn( true );
$args['blockPermissionCheckerFactory'] = $this->getBlockPermissionCheckerFactory(
'targetNameGoesHere',
$args['performer'],