Fix epic fail in r64860
[lhc/web/wiklou.git] / includes / specials / SpecialUserlogin.php
index d78cdea..bfc6c26 100644 (file)
@@ -34,17 +34,21 @@ class LoginForm {
        const ABORTED = 8;
        const CREATE_BLOCKED = 9;
        const THROTTLED = 10;
+       const USER_BLOCKED = 11;
+       const NEED_TOKEN = 12;
+       const WRONG_TOKEN = 13;
 
        var $mName, $mPassword, $mRetype, $mReturnTo, $mCookieCheck, $mPosted;
        var $mAction, $mCreateaccount, $mCreateaccountMail, $mMailmypassword;
        var $mLoginattempt, $mRemember, $mEmail, $mDomain, $mLanguage;
-       var $mSkipCookieCheck, $mReturnToQuery;
+       var $mSkipCookieCheck, $mReturnToQuery, $mToken;
 
        private $mExtUser = null;
 
        /**
         * Constructor
-        * @param WebRequest $request A WebRequest object passed by reference
+        * @param $request WebRequest: a WebRequest object passed by reference
+        * @param $par String: subpage parameter
         */
        function LoginForm( &$request, $par = '' ) {
                global $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
@@ -68,6 +72,7 @@ class LoginForm {
                $this->mRemember = $request->getCheck( 'wpRemember' );
                $this->mLanguage = $request->getText( 'uselang' );
                $this->mSkipCookieCheck = $request->getCheck( 'wpSkipCookieCheck' );
+               $this->mToken = $request->getVal( 'wpLoginToken' );
 
                if ( $wgRedirectOnLogin ) {
                        $this->mReturnTo = $wgRedirectOnLogin;
@@ -122,14 +127,14 @@ class LoginForm {
        function addNewAccountMailPassword() {
                global $wgOut;
 
-               if ('' == $this->mEmail) {
+               if ( $this->mEmail == '' ) {
                        $this->mainLoginForm( wfMsg( 'noemail', htmlspecialchars( $this->mName ) ) );
                        return;
                }
 
                $u = $this->addNewaccountInternal();
 
-               if ($u == NULL) {
+               if ($u == null) {
                        return;
                }
 
@@ -163,7 +168,7 @@ class LoginForm {
 
                # Create the account and abort if there's a problem doing so
                $u = $this->addNewAccountInternal();
-               if( $u == NULL )
+               if( $u == null )
                        return;
 
                # If we showed up language selection links, and one was in use, be
@@ -219,7 +224,6 @@ class LoginForm {
         */
        function addNewAccountInternal() {
                global $wgUser, $wgOut;
-               global $wgEnableSorbs, $wgProxyWhitelist;
                global $wgMemc, $wgAccountCreationThrottle;
                global $wgAuth, $wgMinimalPasswordLength;
                global $wgEmailConfirmToEdit;
@@ -235,7 +239,7 @@ class LoginForm {
                // cation server before they create an account (otherwise, they can
                // create a local account and login as any domain user). We only need
                // to check this for domains that aren't local.
-               if( 'local' != $this->mDomain && '' != $this->mDomain ) {
+               if( 'local' != $this->mDomain && $this->mDomain != '' ) {
                        if( !$wgAuth->canCreateAccounts() && ( !$wgAuth->userExists( $this->mName ) || !$wgAuth->authenticate( $this->mName, $this->mPassword ) ) ) {
                                $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
                                return false;
@@ -257,9 +261,7 @@ class LoginForm {
                }
 
                $ip = wfGetIP();
-               if ( $wgEnableSorbs && !in_array( $ip, $wgProxyWhitelist ) &&
-                 $wgUser->inSorbsBlacklist( $ip ) )
-               {
+               if ( $wgUser->isDnsBlacklisted( $ip, true /* check $wgProxyWhitelist */ ) ) {
                        $this->mainLoginForm( wfMsg( 'sorbs_create_account_reason' ) . ' (' . htmlspecialchars( $ip ) . ')' );
                        return;
                }
@@ -267,12 +269,7 @@ class LoginForm {
                # Now create a dummy user ($u) and check if it is valid
                $name = trim( $this->mName );
                $u = User::newFromName( $name, 'creatable' );
-               if ( WikiError::isError( $u ) ) {
-                       $this->mainLoginForm( wfMsg( $u->getMessage() ) );
-                       return false;
-               }
-
-               if ( !$u instanceof User ) {
+               if ( !is_object( $u ) ) {
                        $this->mainLoginForm( wfMsg( 'noname' ) );
                        return false;
                }
@@ -394,15 +391,28 @@ class LoginForm {
         * This may create a local account as a side effect if the
         * authentication plugin allows transparent local account
         * creation.
-        *
-        * @public
         */
-       function authenticateUserData() {
+       public function authenticateUserData() {
                global $wgUser, $wgAuth;
-               if ( '' == $this->mName ) {
+               if ( $this->mName == '' ) {
                        return self::NO_NAME;
                }
                
+               // We require a login token to prevent login CSRF
+               // Handle part of this before incrementing the throttle so
+               // token-less login attempts don't count towards the throttle
+               // but wrong-token attempts do.
+               
+               // If the user doesn't have a login token yet, set one.
+               if ( !self::getLoginToken() ) {
+                       self::setLoginToken();
+                       return self::NEED_TOKEN;
+               }
+               // If the user didn't pass a login token, tell them we need one
+               if ( !$this->mToken ) {
+                       return self::NEED_TOKEN;
+               }
+               
                global $wgPasswordAttemptThrottle;
 
                $throttleCount = 0;
@@ -421,6 +431,11 @@ class LoginForm {
                                return self::THROTTLED;
                        }
                }
+               
+               // Validate the login token
+               if ( $this->mToken !== self::getLoginToken() ) {
+                       return self::WRONG_TOKEN;
+               }
 
                // Load $wgUser now, and check to see if we're logging in as the same
                // name. This is necessary because loading $wgUser (say by calling
@@ -438,7 +453,7 @@ class LoginForm {
                # TODO: Allow some magic here for invalid external names, e.g., let the
                # user choose a different wiki name.
                $u = User::newFromName( $this->mName );
-               if( is_null( $u ) || !User::isUsableName( $u->getName() ) ) {
+               if( !( $u instanceof User ) || !User::isUsableName( $u->getName() ) ) {
                        return self::ILLEGAL;
                }
 
@@ -451,6 +466,15 @@ class LoginForm {
                                $isAutoCreated = true;
                        }
                } else {
+                       global $wgExternalAuthType, $wgAutocreatePolicy;
+                       if ( $wgExternalAuthType && $wgAutocreatePolicy != 'never'
+                       && is_object( $this->mExtUser )
+                       && $this->mExtUser->authenticate( $this->mPassword ) ) {
+                               # The external user and local user have the same name and
+                               # password, so we assume they're the same.
+                               $this->mExtUser->linkToLocal( $u->getID() );
+                       }
+
                        $u->load();
                }
 
@@ -460,6 +484,7 @@ class LoginForm {
                        return $abort;
                }
 
+               global $wgBlockDisablesLogin;
                if (!$u->checkPassword( $this->mPassword )) {
                        if( $u->checkTemporaryPassword( $this->mPassword ) ) {
                                // The e-mailed temporary password should not be used for actu-
@@ -488,8 +513,11 @@ class LoginForm {
                                // faces etc will probably just fail cleanly here.
                                $retval = self::RESET_PASS;
                        } else {
-                               $retval = '' == $this->mPassword ? self::EMPTY_PASS : self::WRONG_PASS;
+                               $retval = ($this->mPassword  == '') ? self::EMPTY_PASS : self::WRONG_PASS;
                        }
+               } elseif ( $wgBlockDisablesLogin && $u->isBlocked() ) {
+                       // If we've enabled it, make it so that a blocked user cannot login
+                       $retval = self::USER_BLOCKED;
                } else {
                        $wgAuth->updateUser( $u );
                        $wgUser = $u;
@@ -570,6 +598,7 @@ class LoginForm {
                                        $wgUser->invalidateCache();
                                }
                                $wgUser->setCookies();
+                               self::clearLoginToken();
 
                                // Reset the throttle
                                $key = wfMemcKey( 'password-throttle', wfGetIP(), md5( $this->mName ) );
@@ -588,7 +617,11 @@ class LoginForm {
                                        return $this->cookieRedirectCheck( 'login' );
                                }
                                break;
-
+                       
+                       case self::NEED_TOKEN:
+                       case self::WRONG_TOKEN:
+                               $this->mainLoginForm( wfMsg( 'sessionfailure' ) );
+                               break;
                        case self::NO_NAME:
                        case self::ILLEGAL:
                                $this->mainLoginForm( wfMsg( 'noname' ) );
@@ -618,6 +651,10 @@ class LoginForm {
                        case self::THROTTLED:
                                $this->mainLoginForm( wfMsg( 'login-throttled' ) );
                                break;
+                       case self::USER_BLOCKED:
+                               $this->mainLoginForm( wfMsgExt( 'login-userblocked',
+                                       array( 'parsemag', 'escape' ), $this->mName ) );
+                               break;
                        default:
                                throw new MWException( "Unhandled case value" );
                }
@@ -666,12 +703,12 @@ class LoginForm {
                        return;
                }
 
-               if ( '' == $this->mName ) {
+               if ( $this->mName == '' ) {
                        $this->mainLoginForm( wfMsg( 'noname' ) );
                        return;
                }
                $u = User::newFromName( $this->mName );
-               if( is_null( $u ) ) {
+               if( !$u instanceof User ) {
                        $this->mainLoginForm( wfMsg( 'noname' ) );
                        return;
                }
@@ -700,17 +737,17 @@ class LoginForm {
 
 
        /**
-        * @param object user
-        * @param bool throttle
-        * @param string message name of email title
-        * @param string message name of email text
-        * @return mixed true on success, WikiError on failure
+        * @param $u User object
+        * @param $throttle Boolean
+        * @param $emailTitle String: message name of email title
+        * @param $emailText String: message name of email text
+        * @return Mixed: true on success, WikiError on failure
         * @private
         */
        function mailPasswordInternal( $u, $throttle = true, $emailTitle = 'passwordremindertitle', $emailText = 'passwordremindertext' ) {
                global $wgServer, $wgScript, $wgUser, $wgNewPasswordExpiry;
 
-               if ( '' == $u->getEmail() ) {
+               if ( $u->getEmail() == '' ) {
                        return new WikiError( wfMsg( 'noemail', $u->getName() ) );
                }
                $ip = wfGetIP();
@@ -723,10 +760,10 @@ class LoginForm {
                $np = $u->randomPassword();
                $u->setNewpassword( $np, $throttle );
                $u->saveSettings();
-
-               $m = wfMsgExt( $emailText, array( 'parsemag' ), $ip, $u->getName(), $np,
+               $userLanguage = $u->getOption( 'language' );
+               $m = wfMsgExt( $emailText, array( 'parsemag', 'language' => $userLanguage ), $ip, $u->getName(), $np,
                                $wgServer . $wgScript, round( $wgNewPasswordExpiry / 86400 ) );
-               $result = $u->sendMail( wfMsg( $emailTitle ), $m );
+               $result = $u->sendMail( wfMsgExt( $emailTitle, array( 'parsemag', 'language' => $userLanguage ) ), $m );
 
                return $result;
        }
@@ -864,7 +901,7 @@ class LoginForm {
                        }
                }
 
-               if ( '' == $this->mName ) {
+               if ( $this->mName == '' ) {
                        if ( $wgUser->isLoggedIn() ) {
                                $this->mName = $wgUser->getName();
                        } else {
@@ -928,6 +965,11 @@ class LoginForm {
                $template->set( 'canremember', ( $wgCookieExpiration > 0 ) );
                $template->set( 'remember', $wgUser->getOption( 'rememberpassword' ) or $this->mRemember  );
 
+               if ( !self::getLoginToken() ) {
+                       self::setLoginToken();
+               }
+               $template->set( 'token', self::getLoginToken() );
+
                # Prepare language selection links as needed
                if( $wgLoginLanguageSelector ) {
                        $template->set( 'languages', $this->makeLanguageSelector() );
@@ -943,7 +985,13 @@ class LoginForm {
                        wfRunHooks( 'UserLoginForm', array( &$template ) );
                }
 
-               $wgOut->setPageTitle( wfMsg( 'userlogin' ) );
+               //Changes the title depending on permissions for creating account
+               if ( $wgUser->isAllowed( 'createaccount' ) ) {
+                       $wgOut->setPageTitle( wfMsg( 'userlogin' ) );
+               } else {
+                       $wgOut->setPageTitle( wfMsg( 'userloginnocreate' ) );
+               }
+
                $wgOut->setRobotPolicy( 'noindex,nofollow' );
                $wgOut->setArticleRelated( false );
                $wgOut->disallowUserJs();  // just in case...
@@ -976,6 +1024,32 @@ class LoginForm {
                global $wgDisableCookieCheck, $wgRequest;
                return $wgDisableCookieCheck ? true : $wgRequest->checkSessionCookie();
        }
+       
+       /**
+        * Get the login token from the current session
+        */
+       public static function getLoginToken() {
+               global $wgRequest;
+               return $wgRequest->getSessionData( 'wsLoginToken' );
+       }
+       
+       /**
+        * Generate a new login token and attach it to the current session
+        */
+       public static function setLoginToken() {
+               global $wgRequest;
+               // Use User::generateToken() instead of $user->editToken()
+               // because the latter reuses $_SESSION['wsEditToken']
+               $wgRequest->setSessionData( 'wsLoginToken', User::generateToken() );
+       }
+       
+       /**
+        * Remove any login token attached to the current session
+        */
+       public static  function clearLoginToken() {
+               global $wgRequest;
+               $wgRequest->setSessionData( 'wsLoginToken', null );
+       }
 
        /**
         * @private