/** * Internally authenticate the login request. * * This may create a local account as a side effect if the * authentication plugin allows transparent local account * creation. */ public function authenticateUserData() { global $wgUser, $wgAuth; $this->load(); /* Wikia change - begin */ // This might not be needed once we're upgraded to MW 1.19 since that throws new ReadOnlyError in a few spots (test that). global $wgOut; if (wfReadOnly()) { if (is_object($wgOut)) { $wgOut->readOnlyPage(); return false; } else { $this->mAbortLoginErrorMsg = wfMsg('login-abort-readonly'); // msg is in languages/messages/wikia/MessagesEn.php if we end up deleting this after the upgrade to MW 1.19 return self::ABORTED; } } /* Wikia change - end */ if ($this->mUsername == '') { 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; } $throttleCount = self::incLoginThrottle($this->mUsername); if ($throttleCount === true) { return self::THROTTLED; } // Validate the login token if ($this->mToken !== self::getLoginToken()) { return self::WRONG_TOKEN; } // Load the current user now, and check to see if we're logging in as // the same name. This is necessary because loading the current user // (say by calling getName()) calls the UserLoadFromSession hook, which // potentially creates the user in the database. Until we load $wgUser, // checking for user existence using User::newFromName($name)->getId() below // will effectively be using stale data. if ($this->getUser()->getName() === $this->mUsername) { wfDebug(__METHOD__ . ": already logged in as {$this->mUsername}\n"); return self::SUCCESS; } $this->mExtUser = ExternalUser_Wikia::newFromName($this->mUsername); global $wgEnableHeliosExt; if ($wgEnableHeliosExt) { \Wikia\Helios\User::debugLogin($this->mPassword, __METHOD__); } global $wgExternalAuthType; if ($wgExternalAuthType && 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($this->mExtUser->getId()); } // Wikia change - begin - author: @wladek if ($wgExternalAuthType && is_object($this->mExtUser) && $this->mExtUser->getLastAuthenticationError()) { $this->mAbortLoginErrorMsg = $this->mExtUser->getLastAuthenticationError(); return self::ABORTED; } // Wikia change - end # TODO: Allow some magic here for invalid external names, e.g., let the # user choose a different wiki name. $u = User::newFromName($this->mUsername); if (!$u instanceof User || !User::isUsableName($u->getName())) { return self::ILLEGAL; } $isAutoCreated = false; if (0 == $u->getID()) { $status = $this->attemptAutoCreate($u); if ($status !== self::SUCCESS) { return $status; } else { $isAutoCreated = true; } } // Give general extensions, such as a captcha, a chance to abort logins $abort = self::ABORTED; if (!wfRunHooks('AbortLogin', array($u, $this->mPassword, &$abort, &$this->mAbortLoginErrorMsg))) { return $abort; } global $wgBlockDisablesLogin; $abortedMessageKey = null; if (!$u->checkPassword($this->mPassword, $abortedMessageKey)) { if ($abortedMessageKey) { $this->mAbortLoginErrorMsg = $abortedMessageKey; return self::ABORTED; } if ($u->checkTemporaryPassword($this->mPassword)) { // At this point we just return an appropriate code/ indicating // that the UI should show a password reset form; bot inter- // faces etc will probably just fail cleanly here. $retval = self::RESET_PASS; } else { $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 { $retval = self::SUCCESS; } if (in_array($retval, [self::SUCCESS, self::RESET_PASS])) { wfRunHooks('LoginSuccessModifyRetval', [$u->getName(), $this->mPassword, &$retval]); } switch ($retval) { case self::SUCCESS: $this->onAuthenticateUserDataSuccess($u, $isAutoCreated, $throttleCount); break; case self::RESET_PASS: $this->onAuthenticateUserDataResetPass($u); break; } wfRunHooks('LoginAuthenticateAudit', array($u, $this->mPassword, $retval)); return $retval; }
/** * Internally authenticate the login request. * * This may create a local account as a side effect if the * authentication plugin allows transparent local account * creation. */ public function authenticateUserData() { global $wgUser, $wgAuth; $this->load(); /* Wikia change - begin */ // This might not be needed once we're upgraded to MW 1.19 since that throws new ReadOnlyError in a few spots (test that). global $wgOut; if (wfReadOnly()) { if (is_object($wgOut)) { $wgOut->readOnlyPage(); return false; } else { $this->mAbortLoginErrorMsg = wfMsg('login-abort-readonly'); // msg is in languages/messages/wikia/MessagesEn.php if we end up deleting this after the upgrade to MW 1.19 return self::ABORTED; } } /* Wikia change - end */ if ($this->mUsername == '') { 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; } $throttleCount = self::incLoginThrottle($this->mUsername); if ($throttleCount === true) { return self::THROTTLED; } // Validate the login token if ($this->mToken !== self::getLoginToken()) { return self::WRONG_TOKEN; } // Load the current user now, and check to see if we're logging in as // the same name. This is necessary because loading the current user // (say by calling getName()) calls the UserLoadFromSession hook, which // potentially creates the user in the database. Until we load $wgUser, // checking for user existence using User::newFromName($name)->getId() below // will effectively be using stale data. if ($this->getUser()->getName() === $this->mUsername) { wfDebug(__METHOD__ . ": already logged in as {$this->mUsername}\n"); return self::SUCCESS; } $this->mExtUser = ExternalUser_Wikia::newFromName($this->mUsername); 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($this->mExtUser->getId()); } # TODO: Allow some magic here for invalid external names, e.g., let the # user choose a different wiki name. $u = User::newFromName($this->mUsername); if (!$u instanceof User || !User::isUsableName($u->getName())) { return self::ILLEGAL; } $isAutoCreated = false; if (0 == $u->getID()) { $status = $this->attemptAutoCreate($u); if ($status !== self::SUCCESS) { return $status; } else { $isAutoCreated = true; } } // Give general extensions, such as a captcha, a chance to abort logins $abort = self::ABORTED; if (!wfRunHooks('AbortLogin', array($u, $this->mPassword, &$abort, &$this->mAbortLoginErrorMsg))) { 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- // al logins; that's a very sloppy habit, and insecure if an // attacker has a few seconds to click "search" on someone's o- // pen mail reader. // // Allow it to be used only to reset the password a single time // to a new value, which won't be in the user's e-mail ar- // chives. // // For backwards compatibility, we'll still recognize it at the // login form to minimize surprises for people who have been // logging in with a temporary password for some time. // // As a side-effect, we can authenticate the user's e-mail ad- // dress if it's not already done, since the temporary password // was sent via e-mail. if (!$u->isEmailConfirmed()) { $u->confirmEmail(); $u->saveSettings(); } // At this point we just return an appropriate code/ indicating // that the UI should show a password reset form; bot inter- // faces etc will probably just fail cleanly here. $retval = self::RESET_PASS; } else { $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; // This should set it for OutputPage and the Skin // which is needed or the personal links will be // wrong. $this->getContext()->setUser($u); // Please reset throttle for successful logins, thanks! if ($throttleCount) { self::clearLoginThrottle($this->mUsername); } if ($isAutoCreated) { // Must be run after $wgUser is set, for correct new user log wfRunHooks('AuthPluginAutoCreate', array($u)); } $retval = self::SUCCESS; } wfRunHooks('LoginAuthenticateAudit', array($u, $this->mPassword, $retval)); return $retval; }