* shared cache (memcached). Any operation which changes the
* corresponding database fields must call a cache-clearing function.
* @showinitializer
+ * @var string[]
*/
protected static $mCacheVars = [
// user table
];
/**
- * String Cached results of getAllRights()
+ * Array of Strings Core rights.
+ * Each of these should have a corresponding message of the form
+ * "right-$right".
+ * @showinitializer
+ * @var string[]
+ */
+ protected static $mCoreRights = [
+ 'apihighlimits',
+ 'applychangetags',
+ 'autoconfirmed',
+ 'autocreateaccount',
+ 'autopatrol',
+ 'bigdelete',
+ 'block',
+ 'blockemail',
+ 'bot',
+ 'browsearchive',
+ 'changetags',
+ 'createaccount',
+ 'createpage',
+ 'createtalk',
+ 'delete',
+ 'deletechangetags',
+ 'deletedhistory',
+ 'deletedtext',
+ 'deletelogentry',
+ 'deleterevision',
+ 'edit',
+ 'editcontentmodel',
+ 'editinterface',
+ 'editprotected',
+ 'editmyoptions',
+ 'editmyprivateinfo',
+ 'editmyusercss',
+ 'editmyuserjson',
+ 'editmyuserjs',
+ 'editmywatchlist',
+ 'editsemiprotected',
+ 'editsitecss',
+ 'editsitejson',
+ 'editsitejs',
+ 'editusercss',
+ 'edituserjson',
+ 'edituserjs',
+ 'hideuser',
+ 'import',
+ 'importupload',
+ 'ipblock-exempt',
+ 'managechangetags',
+ 'markbotedits',
+ 'mergehistory',
+ 'minoredit',
+ 'move',
+ 'movefile',
+ 'move-categorypages',
+ 'move-rootuserpages',
+ 'move-subpages',
+ 'nominornewtalk',
+ 'noratelimit',
+ 'override-export-depth',
+ 'pagelang',
+ 'patrol',
+ 'patrolmarks',
+ 'protect',
+ 'purge',
+ 'read',
+ 'reupload',
+ 'reupload-own',
+ 'reupload-shared',
+ 'rollback',
+ 'sendemail',
+ 'siteadmin',
+ 'suppressionlog',
+ 'suppressredirect',
+ 'suppressrevision',
+ 'unblockself',
+ 'undelete',
+ 'unwatchedpages',
+ 'upload',
+ 'upload_by_url',
+ 'userrights',
+ 'userrights-interwiki',
+ 'viewmyprivateinfo',
+ 'viewmywatchlist',
+ 'viewsuppressed',
+ 'writeapi',
+ ];
+
+ /**
+ * @var string[] Cached results of getAllRights()
*/
protected static $mAllRights = false;
protected $mOptionOverrides;
// @}
+ // @{
/**
- * Bool Whether the cache variables have been loaded.
+ * @var bool Whether the cache variables have been loaded.
*/
- // @{
public $mOptionsLoaded;
/**
- * Array with already loaded items or true if all items have been loaded.
+ * @var array|bool Array with already loaded items or true if all items have been loaded.
*/
protected $mLoadedItems = [];
// @}
/**
- * String Initialization data source if mLoadedItems!==true. May be one of:
+ * @var string Initialization data source if mLoadedItems!==true. May be one of:
* - 'defaults' anonymous user initialised from class defaults
* - 'name' initialise from mName
* - 'id' initialise from mId
/**
* Lazy-initialized variables, invalidated with clearInstanceCache
*/
+ /** @var int|bool */
protected $mNewtalk;
/** @var string */
protected $mDatePreference;
public $mBlockedby;
/** @var string */
protected $mHash;
+ /** @var array */
+ public $mRights;
/** @var string */
protected $mBlockreason;
/** @var array */
/** @var bool */
protected $mAllowUsertalk;
- /** @var AbstractBlock */
+ /** @var AbstractBlock|bool */
private $mBlockedFromCreateAccount = false;
/** @var int User::READ_* constant bitfield used to load data */
protected $queryFlagsUsed = self::READ_NORMAL;
+ /** @var int[] */
public static $idCacheByName = [];
/**
return (string)$this->getName();
}
- public function __get( $name ) {
- // A shortcut for $mRights deprecation phase
- if ( $name === 'mRights' ) {
- return $this->getRights();
- }
- }
-
- public function __set( $name, $value ) {
- // A shortcut for $mRights deprecation phase, only known legitimate use was for
- // testing purposes, other uses seem bad in principle
- if ( $name === 'mRights' ) {
- MediaWikiServices::getInstance()->getPermissionManager()->overrideUserRightsForTesting(
- $this,
- is_null( $value ) ? [] : $value
- );
- }
- }
-
/**
* Test if it's safe to load this User object.
*
// If this user is autoblocked, set a cookie to track the block. This has to be done on
// every session load, because an autoblocked editor might not edit again from the same
// IP address after being blocked.
- $this->trackBlockWithCookie();
+ MediaWikiServices::getInstance()->getBlockManager()->trackBlockWithCookie( $this );
}
// Other code expects these to be set in the session, so set them.
/**
* Set the 'BlockID' cookie depending on block type and user authentication status.
+ *
+ * @deprecated since 1.34 Use BlockManager::trackBlockWithCookie instead
*/
public function trackBlockWithCookie() {
- $block = $this->getBlock();
-
- if ( $block && $this->getRequest()->getCookie( 'BlockID' ) === null
- && $block->shouldTrackWithCookie( $this->isAnon() )
- ) {
- $block->setCookie( $this->getRequest()->response() );
- }
+ MediaWikiServices::getInstance()->getBlockManager()->trackBlockWithCookie( $this );
}
/**
* given source. May be "name", "id", "actor", "defaults", "session", or false for no reload.
*/
public function clearInstanceCache( $reloadFrom = false ) {
- global $wgFullyInitialised;
-
$this->mNewtalk = -1;
$this->mDatePreference = null;
$this->mBlockedby = -1; # Unset
$this->mHash = false;
+ $this->mRights = null;
$this->mEffectiveGroups = null;
$this->mImplicitGroups = null;
$this->mGroupMemberships = null;
$this->mOptionsLoaded = false;
$this->mEditCount = null;
- // Replacement of former `$this->mRights = null` line
- if ( $wgFullyInitialised && $this->mFrom ) {
- MediaWikiServices::getInstance()->getPermissionManager()->invalidateUsersRightsCache(
- $this
- );
- }
-
if ( $reloadFrom ) {
$this->mLoadedItems = [];
$this->mFrom = $reloadFrom;
* @param Title $title Title to check
* @param bool $fromReplica Whether to check the replica DB instead of the master
* @return bool
+ * @throws MWException
*
* @deprecated since 1.33,
* use MediaWikiServices::getInstance()->getPermissionManager()->isBlockedFrom(..)
$dbw->insert( 'user_newtalk',
[ $field => $id, 'user_last_timestamp' => $dbw->timestampOrNull( $ts ) ],
__METHOD__,
- 'IGNORE' );
+ [ 'IGNORE' ] );
if ( $dbw->affectedRows() ) {
wfDebug( __METHOD__ . ": set on ($field, $id)\n" );
return true;
/**
* Get the permissions this user has.
* @return string[] permission names
- *
- * @deprecated since 1.34, use MediaWikiServices::getInstance()->getPermissionManager()
- * ->getUserPermissions(..) instead
- *
*/
public function getRights() {
- return MediaWikiServices::getInstance()->getPermissionManager()->getUserPermissions( $this );
+ if ( is_null( $this->mRights ) ) {
+ $this->mRights = self::getGroupPermissions( $this->getEffectiveGroups() );
+ Hooks::run( 'UserGetRights', [ $this, &$this->mRights ] );
+
+ // Deny any rights denied by the user's session, unless this
+ // endpoint has no sessions.
+ if ( !defined( 'MW_NO_SESSION' ) ) {
+ $allowedRights = $this->getRequest()->getSession()->getAllowedUserRights();
+ if ( $allowedRights !== null ) {
+ $this->mRights = array_intersect( $this->mRights, $allowedRights );
+ }
+ }
+
+ Hooks::run( 'UserGetRightsRemove', [ $this, &$this->mRights ] );
+ // Force reindexation of rights when a hook has unset one of them
+ $this->mRights = array_values( array_unique( $this->mRights ) );
+
+ // If block disables login, we should also remove any
+ // extra rights blocked users might have, in case the
+ // blocked user has a pre-existing session (T129738).
+ // This is checked here for cases where people only call
+ // $user->isAllowed(). It is also checked in Title::checkUserBlock()
+ // to give a better error message in the common case.
+ $config = RequestContext::getMain()->getConfig();
+ // @TODO Partial blocks should not prevent the user from logging in.
+ // see: https://phabricator.wikimedia.org/T208895
+ if (
+ $this->isLoggedIn() &&
+ $config->get( 'BlockDisablesLogin' ) &&
+ $this->getBlock()
+ ) {
+ $anon = new User;
+ $this->mRights = array_intersect( $this->mRights, $anon->getRights() );
+ }
+ }
+ return $this->mRights;
}
/**
// Refresh the groups caches, and clear the rights cache so it will be
// refreshed on the next call to $this->getRights().
$this->getEffectiveGroups( true );
- MediaWikiServices::getInstance()->getPermissionManager()->invalidateUsersRightsCache( $this );
+ $this->mRights = null;
+
$this->invalidateCache();
return true;
// Refresh the groups caches, and clear the rights cache so it will be
// refreshed on the next call to $this->getRights().
$this->getEffectiveGroups( true );
- MediaWikiServices::getInstance()->getPermissionManager()->invalidateUsersRightsCache( $this );
+ $this->mRights = null;
+
$this->invalidateCache();
return true;
/**
* Internal mechanics of testing a permission
- *
- * @deprecated since 1.34, use MediaWikiServices::getInstance()
- * ->getPermissionManager()->userHasRight(...) instead
- *
* @param string $action
- *
* @return bool
*/
public function isAllowed( $action = '' ) {
- return MediaWikiServices::getInstance()->getPermissionManager()
- ->userHasRight( $this, $action );
+ if ( $action === '' ) {
+ return true; // In the spirit of DWIM
+ }
+ // Use strict parameter to avoid matching numeric 0 accidentally inserted
+ // by misconfiguration: 0 == 'foo'
+ return in_array( $action, $this->getRights(), true );
}
/**
/**
* Get the permissions associated with a given list of groups
*
- * @deprecated since 1.34, use MediaWikiServices::getInstance()->getPermissionManager()
- * ->getGroupPermissions() instead
- *
* @param array $groups Array of Strings List of internal group names
* @return array Array of Strings List of permission key names for given groups combined
*/
public static function getGroupPermissions( $groups ) {
- return MediaWikiServices::getInstance()->getPermissionManager()->getGroupPermissions( $groups );
+ global $wgGroupPermissions, $wgRevokePermissions;
+ $rights = [];
+ // grant every granted permission first
+ foreach ( $groups as $group ) {
+ if ( isset( $wgGroupPermissions[$group] ) ) {
+ $rights = array_merge( $rights,
+ // array_filter removes empty items
+ array_keys( array_filter( $wgGroupPermissions[$group] ) ) );
+ }
+ }
+ // now revoke the revoked permissions
+ foreach ( $groups as $group ) {
+ if ( isset( $wgRevokePermissions[$group] ) ) {
+ $rights = array_diff( $rights,
+ array_keys( array_filter( $wgRevokePermissions[$group] ) ) );
+ }
+ }
+ return array_unique( $rights );
}
/**
* Get all the groups who have a given permission
*
- * @deprecated since 1.34, use MediaWikiServices::getInstance()->getPermissionManager()
- * ->getGroupsWithPermission() instead
- *
* @param string $role Role to check
* @return array Array of Strings List of internal group names with the given permission
*/
public static function getGroupsWithPermission( $role ) {
- return MediaWikiServices::getInstance()->getPermissionManager()->getGroupsWithPermission( $role );
+ global $wgGroupPermissions;
+ $allowedGroups = [];
+ foreach ( array_keys( $wgGroupPermissions ) as $group ) {
+ if ( self::groupHasPermission( $group, $role ) ) {
+ $allowedGroups[] = $group;
+ }
+ }
+ return $allowedGroups;
}
/**
* User::isEveryoneAllowed() instead. That properly checks if it's revoked
* from anyone.
*
- * @deprecated since 1.34, use MediaWikiServices::getInstance()->getPermissionManager()
- * ->groupHasPermission(..) instead
- *
* @since 1.21
* @param string $group Group to check
* @param string $role Role to check
* @return bool
*/
public static function groupHasPermission( $group, $role ) {
- return MediaWikiServices::getInstance()->getPermissionManager()
- ->groupHasPermission( $group, $role );
+ global $wgGroupPermissions, $wgRevokePermissions;
+ return isset( $wgGroupPermissions[$group][$role] ) && $wgGroupPermissions[$group][$role]
+ && !( isset( $wgRevokePermissions[$group][$role] ) && $wgRevokePermissions[$group][$role] );
}
/**
* Specifically, session-based rights restrictions (such as OAuth or bot
* passwords) are applied based on the current session.
*
- * @deprecated since 1.34, use MediaWikiServices::getInstance()->getPermissionManager()
- * ->isEveryoneAllowed() instead
- *
+ * @since 1.22
* @param string $right Right to check
- *
* @return bool
- * @since 1.22
*/
public static function isEveryoneAllowed( $right ) {
- return MediaWikiServices::getInstance()->getPermissionManager()->isEveryoneAllowed( $right );
+ global $wgGroupPermissions, $wgRevokePermissions;
+ static $cache = [];
+
+ // Use the cached results, except in unit tests which rely on
+ // being able change the permission mid-request
+ if ( isset( $cache[$right] ) && !defined( 'MW_PHPUNIT_TEST' ) ) {
+ return $cache[$right];
+ }
+
+ if ( !isset( $wgGroupPermissions['*'][$right] ) || !$wgGroupPermissions['*'][$right] ) {
+ $cache[$right] = false;
+ return false;
+ }
+
+ // If it's revoked anywhere, then everyone doesn't have it
+ foreach ( $wgRevokePermissions as $rights ) {
+ if ( isset( $rights[$right] ) && $rights[$right] ) {
+ $cache[$right] = false;
+ return false;
+ }
+ }
+
+ // Remove any rights that aren't allowed to the global-session user,
+ // unless there are no sessions for this endpoint.
+ if ( !defined( 'MW_NO_SESSION' ) ) {
+ $allowedRights = SessionManager::getGlobalSession()->getAllowedUserRights();
+ if ( $allowedRights !== null && !in_array( $right, $allowedRights, true ) ) {
+ $cache[$right] = false;
+ return false;
+ }
+ }
+
+ // Allow extensions to say false
+ if ( !Hooks::run( 'UserIsEveryoneAllowed', [ $right ] ) ) {
+ $cache[$right] = false;
+ return false;
+ }
+
+ $cache[$right] = true;
+ return true;
}
/**
/**
* Get a list of all available permissions.
- *
- * @deprecated since 1.34, use MediaWikiServices::getInstance()->getPermissionManager()
- * ->getAllPermissions() instead
- *
* @return string[] Array of permission names
*/
public static function getAllRights() {
- return MediaWikiServices::getInstance()->getPermissionManager()->getAllPermissions();
+ if ( self::$mAllRights === false ) {
+ global $wgAvailableRights;
+ if ( count( $wgAvailableRights ) ) {
+ self::$mAllRights = array_unique( array_merge( self::$mCoreRights, $wgAvailableRights ) );
+ } else {
+ self::$mAllRights = self::$mCoreRights;
+ }
+ Hooks::run( 'UserGetAllRights', [ &self::$mAllRights ] );
+ }
+ return self::$mAllRights;
}
/**