Merge "Remove patrol config check in User::isAllowed()"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 13 Jan 2016 21:47:55 +0000 (21:47 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 13 Jan 2016 21:47:55 +0000 (21:47 +0000)
1  2 
includes/user/User.php

diff --combined includes/user/User.php
@@@ -20,8 -20,6 +20,8 @@@
   * @file
   */
  
 +use MediaWiki\Session\SessionManager;
 +
  /**
   * String Some punctuation to prevent editing from broken text-mangling proxies.
   * @ingroup Constants
@@@ -101,7 -99,6 +101,7 @@@ class User implements IDBAccessObject 
                'apihighlimits',
                'applychangetags',
                'autoconfirmed',
 +              'autocreateaccount',
                'autopatrol',
                'bigdelete',
                'block',
         *  - 'defaults'   anonymous user initialised from class defaults
         *  - 'name'       initialise from mName
         *  - 'id'         initialise from mId
 -       *  - 'session'    log in from cookies or session if possible
 +       *  - 'session'    log in from session if possible
         *
         * Use the User::newFrom*() family of functions to set this.
         */
         * @param integer $flags User::READ_* constant bitfield
         */
        public function load( $flags = self::READ_NORMAL ) {
 +              global $wgFullyInitialised;
 +
                if ( $this->mLoadedItems === true ) {
                        return;
                }
  
                // Set it now to avoid infinite recursion in accessors
 +              $oldLoadedItems = $this->mLoadedItems;
                $this->mLoadedItems = true;
                $this->queryFlagsUsed = $flags;
  
 +              // If this is called too early, things are likely to break.
 +              if ( $this->mFrom === 'session' && empty( $wgFullyInitialised ) ) {
 +                      \MediaWiki\Logger\LoggerFactory::getInstance( 'session' )
 +                              ->warning( 'User::loadFromSession called before the end of Setup.php' );
 +                      $this->loadDefaults();
 +                      $this->mLoadedItems = $oldLoadedItems;
 +                      return;
 +              }
 +
                switch ( $this->mFrom ) {
                        case 'defaults':
                                $this->loadDefaults();
        }
  
        /**
 -       * Create a new user object using data from session or cookies. If the
 -       * login credentials are invalid, the result is an anonymous user.
 +       * Create a new user object using data from session. If the login
 +       * credentials are invalid, the result is an anonymous user.
         *
         * @param WebRequest|null $request Object to use; $wgRequest will be used if omitted.
         * @return User
                        $user->saveSettings();
                }
  
 +              SessionManager::singleton()->preventSessionsForUser( $user->getName() );
 +
                return $user;
        }
  
                $this->mOptionOverrides = null;
                $this->mOptionsLoaded = false;
  
 -              $loggedOut = $this->getRequest()->getCookie( 'LoggedOut' );
 -              if ( $loggedOut !== null ) {
 +              $request = $this->getRequest();
 +              $loggedOut = $request ? $request->getSession()->getLoggedOutTimestamp() : 0;
 +              if ( $loggedOut !== 0 ) {
                        $this->mTouched = wfTimestamp( TS_MW, $loggedOut );
                } else {
                        $this->mTouched = '1'; # Allow any pages to be cached
        }
  
        /**
 -       * Load user data from the session or login cookie.
 +       * Load user data from the session.
         *
         * @return bool True if the user is logged in, false otherwise.
         */
        private function loadFromSession() {
 +              // Deprecated hook
                $result = null;
 -              Hooks::run( 'UserLoadFromSession', array( $this, &$result ) );
 +              Hooks::run( 'UserLoadFromSession', array( $this, &$result ), '1.27' );
                if ( $result !== null ) {
                        return $result;
                }
  
 -              $request = $this->getRequest();
 -
 -              $cookieId = $request->getCookie( 'UserID' );
 -              $sessId = $request->getSessionData( 'wsUserID' );
 -
 -              if ( $cookieId !== null ) {
 -                      $sId = intval( $cookieId );
 -                      if ( $sessId !== null && $cookieId != $sessId ) {
 -                              wfDebugLog( 'loginSessions', "Session user ID ($sessId) and
 -                                      cookie user ID ($sId) don't match!" );
 -                              return false;
 -                      }
 -                      $request->setSessionData( 'wsUserID', $sId );
 -              } elseif ( $sessId !== null && $sessId != 0 ) {
 -                      $sId = $sessId;
 -              } else {
 -                      return false;
 -              }
 -
 -              if ( $request->getSessionData( 'wsUserName' ) !== null ) {
 -                      $sName = $request->getSessionData( 'wsUserName' );
 -              } elseif ( $request->getCookie( 'UserName' ) !== null ) {
 -                      $sName = $request->getCookie( 'UserName' );
 -                      $request->setSessionData( 'wsUserName', $sName );
 -              } else {
 -                      return false;
 -              }
 -
 -              $proposedUser = User::newFromId( $sId );
 -              if ( !$proposedUser->isLoggedIn() ) {
 -                      // Not a valid ID
 -                      return false;
 -              }
 -
 -              global $wgBlockDisablesLogin;
 -              if ( $wgBlockDisablesLogin && $proposedUser->isBlocked() ) {
 -                      // User blocked and we've disabled blocked user logins
 -                      return false;
 -              }
 -
 -              if ( $request->getSessionData( 'wsToken' ) ) {
 -                      $passwordCorrect =
 -                              ( $proposedUser->getToken( false ) === $request->getSessionData( 'wsToken' ) );
 -                      $from = 'session';
 -              } elseif ( $request->getCookie( 'Token' ) ) {
 -                      # Get the token from DB/cache and clean it up to remove garbage padding.
 -                      # This deals with historical problems with bugs and the default column value.
 -                      $token = rtrim( $proposedUser->getToken( false ) ); // correct token
 -                      // Make comparison in constant time (bug 61346)
 -                      $passwordCorrect = strlen( $token )
 -                              && hash_equals( $token, $request->getCookie( 'Token' ) );
 -                      $from = 'cookie';
 -              } else {
 -                      // No session or persistent login cookie
 -                      return false;
 -              }
 -
 -              if ( ( $sName === $proposedUser->getName() ) && $passwordCorrect ) {
 -                      $this->loadFromUserObject( $proposedUser );
 -                      $request->setSessionData( 'wsToken', $this->mToken );
 -                      wfDebug( "User: logged in from $from\n" );
 +              // MediaWiki\Session\Session already did the necessary authentication of the user
 +              // returned here, so just use it if applicable.
 +              $session = $this->getRequest()->getSession();
 +              $user = $session->getUser();
 +              if ( $user->isLoggedIn() ) {
 +                      $this->loadFromUserObject( $user );
 +                      // Other code expects these to be set in the session, so set them.
 +                      $session->set( 'wsUserID', $this->getId() );
 +                      $session->set( 'wsUserName', $this->getName() );
 +                      $session->set( 'wsToken', $this->mToken );
                        return true;
 -              } else {
 -                      // Invalid credentials
 -                      wfDebug( "User: can't log in from $from, invalid credentials\n" );
 -                      return false;
                }
 +
 +              return false;
        }
  
        /**
                        ),
                        __METHOD__
                );
 +
 +              // When the main password is changed, invalidate all bot passwords too
 +              BotPassword::invalidateAllPasswordsForUser( $this->getName() );
        }
  
        /**
        public function getRights() {
                if ( is_null( $this->mRights ) ) {
                        $this->mRights = self::getGroupPermissions( $this->getEffectiveGroups() );
 +
 +                      $allowedRights = $this->getRequest()->getSession()->getAllowedUserRights();
 +                      if ( $allowedRights !== null ) {
 +                              $this->mRights = array_intersect( $this->mRights, $allowedRights );
 +                      }
 +
                        Hooks::run( 'UserGetRights', array( $this, &$this->mRights ) );
                        // Force reindexation of rights when a hook has unset one of them
                        $this->mRights = array_values( array_unique( $this->mRights ) );
                if ( $action === '' ) {
                        return true; // In the spirit of DWIM
                }
-               // Patrolling may not be enabled
-               if ( $action === 'patrol' || $action === 'autopatrol' ) {
-                       global $wgUseRCPatrol, $wgUseNPPatrol;
-                       if ( !$wgUseRCPatrol && !$wgUseNPPatrol ) {
-                               return false;
-                       }
-               }
                // Use strict parameter to avoid matching numeric 0 accidentally inserted
                // by misconfiguration: 0 == 'foo'
                return in_array( $action, $this->getRights(), true );
        /**
         * Set a cookie on the user's client. Wrapper for
         * WebResponse::setCookie
 +       * @deprecated since 1.27
         * @param string $name Name of the cookie to set
         * @param string $value Value to set
         * @param int $exp Expiration time, as a UNIX time value;
        protected function setCookie(
                $name, $value, $exp = 0, $secure = null, $params = array(), $request = null
        ) {
 +              wfDeprecated( __METHOD__, '1.27' );
                if ( $request === null ) {
                        $request = $this->getRequest();
                }
  
        /**
         * Clear a cookie on the user's client
 +       * @deprecated since 1.27
         * @param string $name Name of the cookie to clear
         * @param bool $secure
         *  true: Force setting the secure attribute when setting the cookie
         * @param array $params Array of options sent passed to WebResponse::setcookie()
         */
        protected function clearCookie( $name, $secure = null, $params = array() ) {
 +              wfDeprecated( __METHOD__, '1.27' );
                $this->setCookie( $name, '', time() - 86400, $secure, $params );
        }
  
         *
         * @see User::setCookie
         *
 +       * @deprecated since 1.27
         * @param string $name Name of the cookie to set
         * @param string $value Value to set
         * @param bool $secure
        protected function setExtendedLoginCookie( $name, $value, $secure ) {
                global $wgExtendedLoginCookieExpiration, $wgCookieExpiration;
  
 +              wfDeprecated( __METHOD__, '1.27' );
 +
                $exp = time();
                $exp += $wgExtendedLoginCookieExpiration !== null
                        ? $wgExtendedLoginCookieExpiration
        }
  
        /**
 -       * Set the default cookies for this session on the user's client.
 +       * Persist this user's session (e.g. set cookies)
         *
         * @param WebRequest|null $request WebRequest object to use; $wgRequest will be used if null
         *        is passed.
         * @param bool $rememberMe Whether to add a Token cookie for elongated sessions
         */
        public function setCookies( $request = null, $secure = null, $rememberMe = false ) {
 -              global $wgExtendedLoginCookies;
 -
 -              if ( $request === null ) {
 -                      $request = $this->getRequest();
 -              }
 -
                $this->load();
                if ( 0 == $this->mId ) {
                        return;
                }
 -              if ( !$this->mToken ) {
 -                      // When token is empty or NULL generate a new one and then save it to the database
 -                      // This allows a wiki to re-secure itself after a leak of it's user table or $wgSecretKey
 -                      // Simply by setting every cell in the user_token column to NULL and letting them be
 -                      // regenerated as users log back into the wiki.
 -                      $this->setToken();
 -                      if ( !wfReadOnly() ) {
 -                              $this->saveSettings();
 -                      }
 -              }
 -              $session = array(
 -                      'wsUserID' => $this->mId,
 -                      'wsToken' => $this->mToken,
 -                      'wsUserName' => $this->getName()
 -              );
 -              $cookies = array(
 -                      'UserID' => $this->mId,
 -                      'UserName' => $this->getName(),
 -              );
 -              if ( $rememberMe ) {
 -                      $cookies['Token'] = $this->mToken;
 -              } else {
 -                      $cookies['Token'] = false;
 -              }
  
 -              Hooks::run( 'UserSetCookies', array( $this, &$session, &$cookies ) );
 -
 -              foreach ( $session as $name => $value ) {
 -                      $request->setSessionData( $name, $value );
 +              $session = $this->getRequest()->getSession();
 +              if ( $request && $session->getRequest() !== $request ) {
 +                      $session = $session->sessionWithRequest( $request );
                }
 -              foreach ( $cookies as $name => $value ) {
 -                      if ( $value === false ) {
 -                              $this->clearCookie( $name );
 -                      } elseif ( $rememberMe && in_array( $name, $wgExtendedLoginCookies ) ) {
 -                              $this->setExtendedLoginCookie( $name, $value, $secure );
 -                      } else {
 -                              $this->setCookie( $name, $value, 0, $secure, array(), $request );
 +              $delay = $session->delaySave();
 +
 +              if ( !$session->getUser()->equals( $this ) ) {
 +                      if ( !$session->canSetUser() ) {
 +                              \MediaWiki\Logger\LoggerFactory::getInstance( 'session' )
 +                                      ->warning( __METHOD__ .
 +                                              ": Cannot save user \"$this\" to a user \"{$session->getUser()}\"'s immutable session"
 +                                      );
 +                              return;
                        }
 +                      $session->setUser( $this );
                }
  
 -              /**
 -               * If wpStickHTTPS was selected, also set an insecure cookie that
 -               * will cause the site to redirect the user to HTTPS, if they access
 -               * it over HTTP. Bug 29898. Use an un-prefixed cookie, so it's the same
 -               * as the one set by centralauth (bug 53538). Also set it to session, or
 -               * standard time setting, based on if rememberme was set.
 -               */
 -              if ( $request->getCheck( 'wpStickHTTPS' ) || $this->requiresHTTPS() ) {
 -                      $this->setCookie(
 -                              'forceHTTPS',
 -                              'true',
 -                              $rememberMe ? 0 : null,
 -                              false,
 -                              array( 'prefix' => '' ) // no prefix
 -                      );
 +              $session->setRememberUser( $rememberMe );
 +              if ( $secure !== null ) {
 +                      $session->setForceHTTPS( $secure );
                }
 +
 +              $session->persist();
 +
 +              ScopedCallback::consume( $delay );
        }
  
        /**
        }
  
        /**
 -       * Clear the user's cookies and session, and reset the instance cache.
 +       * Clear the user's session, and reset the instance cache.
         * @see logout()
         */
        public function doLogout() {
 -              $this->clearInstanceCache( 'defaults' );
 -
 -              $this->getRequest()->setSessionData( 'wsUserID', 0 );
 -
 -              $this->clearCookie( 'UserID' );
 -              $this->clearCookie( 'Token' );
 -              $this->clearCookie( 'forceHTTPS', false, array( 'prefix' => '' ) );
 -
 -              // Remember when user logged out, to prevent seeing cached pages
 -              $this->setCookie( 'LoggedOut', time(), time() + 86400 );
 +              $session = $this->getRequest()->getSession();
 +              if ( !$session->canSetUser() ) {
 +                      \MediaWiki\Logger\LoggerFactory::getInstance( 'session' )
 +                              ->warning( __METHOD__ . ": Cannot log out of an immutable session" );
 +              } elseif ( !$session->getUser()->equals( $this ) ) {
 +                      \MediaWiki\Logger\LoggerFactory::getInstance( 'session' )
 +                              ->warning( __METHOD__ .
 +                                      ": Cannot log user \"$this\" out of a user \"{$session->getUser()}\"'s session"
 +                              );
 +                      // But we still may as well make this user object anon
 +                      $this->clearInstanceCache( 'defaults' );
 +              } else {
 +                      $this->clearInstanceCache( 'defaults' );
 +                      $delay = $session->delaySave();
 +                      $session->setLoggedOutTimestamp( time() );
 +                      $session->setUser( new User );
 +                      $session->set( 'wsUserID', 0 ); // Other code expects this
 +                      ScopedCallback::consume( $delay );
 +              }
        }
  
        /**
                );
        }
  
 -      /**
 -       * Generate a looking random token for various uses.
 -       *
 -       * @return string The new random token
 -       * @deprecated since 1.20: Use MWCryptRand for secure purposes or
 -       *   wfRandomString for pseudo-randomness.
 -       */
 -      public static function generateToken() {
 -              return MWCryptRand::generateHex( 32 );
 -      }
 -
        /**
         * Get the embedded timestamp from a token.
         * @param string $val Input token
        }
  
        /**
 -       * Check if all users have the given permission
 +       * Check if all users may be assumed to have the given permission
 +       *
 +       * We generally assume so if the right is granted to '*' and isn't revoked
 +       * on any group. It doesn't attempt to take grants or other extension
 +       * limitations on rights into account in the general case, though, as that
 +       * would require it to always return false and defeat the purpose.
 +       * Specifically, session-based rights restrictions (such as OAuth or bot
 +       * passwords) are applied based on the current session.
         *
         * @since 1.22
         * @param string $right Right to check
                        }
                }
  
 -              // Allow extensions (e.g. OAuth) to say false
 +              // Remove any rights that aren't allowed to the global-session user
 +              $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', array( $right ) ) ) {
                        $cache[$right] = false;
                        return false;