Merge "Exclude redirects from Special:Fewestrevisions"
[lhc/web/wiklou.git] / includes / Permissions / PermissionManager.php
index defcb65..a04b29c 100644 (file)
@@ -23,6 +23,8 @@ use Action;
 use Exception;
 use Hooks;
 use MediaWiki\Linker\LinkTarget;
+use MediaWiki\Revision\RevisionLookup;
+use MediaWiki\Revision\RevisionRecord;
 use MediaWiki\Session\SessionManager;
 use MediaWiki\Special\SpecialPageFactory;
 use MediaWiki\User\UserIdentity;
@@ -32,6 +34,7 @@ use RequestContext;
 use SpecialPage;
 use Title;
 use User;
+use Wikimedia\ScopedCallback;
 use WikiPage;
 
 /**
@@ -54,6 +57,9 @@ class PermissionManager {
        /** @var SpecialPageFactory */
        private $specialPageFactory;
 
+       /** @var RevisionLookup */
+       private $revisionLookup;
+
        /** @var string[] List of pages names anonymous user may see */
        private $whitelistRead;
 
@@ -84,6 +90,12 @@ class PermissionManager {
        /** @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 = [];
 
@@ -123,6 +135,7 @@ class PermissionManager {
                'editmyusercss',
                'editmyuserjson',
                'editmyuserjs',
+               'editmyuserjsredirect',
                'editmywatchlist',
                'editsemiprotected',
                'editsitecss',
@@ -177,6 +190,7 @@ class PermissionManager {
 
        /**
         * @param SpecialPageFactory $specialPageFactory
+        * @param RevisionLookup $revisionLookup
         * @param string[] $whitelistRead
         * @param string[] $whitelistReadRegexp
         * @param bool $emailConfirmToEdit
@@ -188,6 +202,7 @@ class PermissionManager {
         */
        public function __construct(
                SpecialPageFactory $specialPageFactory,
+               RevisionLookup $revisionLookup,
                $whitelistRead,
                $whitelistReadRegexp,
                $emailConfirmToEdit,
@@ -198,6 +213,7 @@ class PermissionManager {
                NamespaceInfo $nsInfo
        ) {
                $this->specialPageFactory = $specialPageFactory;
+               $this->revisionLookup = $revisionLookup;
                $this->whitelistRead = $whitelistRead;
                $this->whitelistReadRegexp = $whitelistReadRegexp;
                $this->emailConfirmToEdit = $emailConfirmToEdit;
@@ -1127,6 +1143,20 @@ class PermissionManager {
                                && !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' )
                        ) {
                                $errors[] = [ 'mycustomjsprotected', $action ];
+                       } elseif (
+                               $page->isUserJsConfigPage()
+                               && !$user->isAllowedAny( 'edituserjs', 'editmyuserjsredirect' )
+                       ) {
+                               // T207750 - do not allow users to edit a redirect if they couldn't edit the target
+                               $rev = $this->revisionLookup->getRevisionByTitle( $page );
+                               $content = $rev ? $rev->getContent( 'main', RevisionRecord::RAW ) : null;
+                               $target = $content ? $content->getUltimateRedirectTarget() : null;
+                               if ( $target && (
+                                               !$target->inNamespace( NS_USER )
+                                               || !preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $target->getText() )
+                               ) ) {
+                                       $errors[] = [ 'mycustomjsredirectprotected', $action ];
+                               }
                        }
                } else {
                        // Users need editmyuser* to edit their own CSS/JSON/JS subpages, except for
@@ -1223,7 +1253,11 @@ class PermissionManager {
                                );
                        }
                }
-               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;
        }
 
        /**
@@ -1391,6 +1425,29 @@ class PermissionManager {
                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.
+        *
+        * @since 1.34
+        *
+        * @param UserIdentity $user
+        * @param string|string[] $rights
+        * @return ScopedCallback
+        */
+       public function addTemporaryUserRights( UserIdentity $user, $rights ) {
+               $userId = $user->getId();
+               $nextKey = count( $this->temporaryUserRights[$userId] ?? [] );
+               $this->temporaryUserRights[$userId][$nextKey] = (array)$rights;
+               return new ScopedCallback( function () use ( $userId, $nextKey ) {
+                       unset( $this->temporaryUserRights[$userId][$nextKey] );
+               } );
+       }
+
        /**
         * Overrides user permissions cache
         *