in JavaScript, use mw.log.deprecate() instead.
* The 'user.groups' module, deprecated in 1.28, was removed.
Use the 'user' module instead.
-* The ability to override User::$mRights has been removed.
+* The ability to override User::$mRights has been removed. Use
+ PermissionManager::addTemporaryUserRights() instead.
* Previously, when iterating ResultWrapper with foreach() or a similar
construct, the range of the index was 1..numRows. This has been fixed to be
0..(numRows-1).
use SpecialPage;
use Title;
use User;
+use Wikimedia\ScopedCallback;
use WikiPage;
/**
/** @var string[][] Cached user rights */
private $usersRights = null;
+ /**
+ * Temporary user rights, valid for the current request only.
+ * @var string[][][] userid => override group => rights
+ */
+ private $temporaryUserRights = [];
+
/** @var string[] Cached rights for isEveryoneAllowed */
private $cachedRights = [];
);
}
}
- return $this->usersRights[ $user->getId() ];
+ $rights = $this->usersRights[ $user->getId() ];
+ foreach ( $this->temporaryUserRights[ $user->getId() ] ?? [] as $overrides ) {
+ $rights = array_values( array_unique( array_merge( $rights, $overrides ) ) );
+ }
+ return $rights;
}
/**
return $this->allRights;
}
+ /**
+ * Add temporary user rights, only valid for the current scope.
+ * This is meant for making it possible to programatically trigger certain actions that
+ * the user wouldn't be able to trigger themselves; e.g. allow users without the bot right
+ * to make bot-flagged actions through certain special pages.
+ * Returns a "scope guard" variable; whenever that variable goes out of scope or is consumed
+ * via ScopedCallback::consume(), the temporary rights are revoked.
+ * @param UserIdentity $user
+ * @param string|string[] $rights
+ * @return ScopedCallback
+ */
+ public function addTemporaryUserRights( UserIdentity $user, $rights ) {
+ $nextKey = count( $this->temporaryUserRights[$user->getId()] ?? [] );
+ $this->temporaryUserRights[$user->getId()][$nextKey] = (array)$rights;
+ return new ScopedCallback( [ $this, 'revokeTemporaryUserRights' ], [ $user->getId(), $nextKey ] );
+ }
+
+ /**
+ * Revoke rights added by addTemporaryUserRights().
+ * @param int $userId
+ * @param int $rightsGroupKey Key in self::$temporaryUserRights
+ * @internal For use by addTemporaryUserRights() only.
+ */
+ public function revokeTemporaryUserRights( $userId, $rightsGroupKey ) {
+ unset( $this->temporaryUserRights[$userId][$rightsGroupKey] );
+ }
+
/**
* Overrides user permissions cache
*
use MediaWiki\Block\SystemBlock;
use MediaWiki\MediaWikiServices;
use MediaWiki\Permissions\PermissionManager;
+use Wikimedia\ScopedCallback;
use Wikimedia\TestingAccessWrapper;
/**
*/
protected $user, $anonUser, $userUser, $altUser;
- /**
- * @var PermissionManager
- */
- protected $permissionManager;
-
/** Constant for self::testIsBlockedFrom */
const USER_TALK_PAGE = '<user talk page>';
$this->assertFalse( $result );
}
+ /**
+ * @covers \MediaWiki\Permissions\PermissionManager::addTemporaryUserRights
+ * @covers \MediaWiki\Permissions\PermissionManager::revokeTemporaryUserRights
+ */
+ public function testTemporaryUserRights() {
+ $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
+ $this->overrideUserPermissions( $this->user, [ 'read', 'edit' ] );
+ // sanity checks
+ $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' ) );
+ }
+
}