/** * UserLogin: send a confirmation email a new account has been created */ public function sendConfirmationEmail() { $this->response->setFormat('json'); $this->response->setCacheValidity(\WikiaResponse::CACHE_DISABLED); $this->response->setVal('success', false); if ($this->getVal('secret') != $this->wg->TheSchwartzSecretToken) { $this->response->setVal('message', 'invalid secret'); return; } if (!$this->wg->EmailAuthentication) { $this->response->setVal('message', 'email authentication is not required'); return; } $username = $this->getVal('username'); wfWaitForSlaves($this->wg->ExternalSharedDB); $user = \User::newFromName($username); if (!$user instanceof \User) { $this->response->setVal('message', 'unable to create a \\User object from name'); return; } if (!$user->getId()) { $this->response->setVal('message', 'no such user'); return; } if ($user->isEmailConfirmed()) { $this->response->setVal('message', 'already confirmed'); return; } $userLoginHelper = new \UserLoginHelper(); $memcKey = $userLoginHelper->getMemKeyConfirmationEmailsSent($user->getId()); $emailsSent = intval($this->wg->Memc->get($memcKey)); if ($user->isEmailConfirmationPending() && strtotime($user->mEmailTokenExpires) - strtotime('+6 days') > 0 && $emailsSent >= \UserLoginHelper::LIMIT_EMAILS_SENT) { $this->response->setVal('message', 'confirmation emails limit reached'); return; } if (!\Sanitizer::validateEmail($user->getEmail())) { $this->response->setVal('message', 'invalid email'); return; } $langCode = $this->getVal('langCode', 'en'); $mailTemplate = $this->app->renderView('UserLogin', 'GeneralMail', ['language' => $langCode, 'type' => 'confirmation-email']); $lang = \Language::factory($langCode); $mailStatus = (new GlobalStateWrapper(['wgLang' => $lang]))->wrap(function () use($user, $mailTemplate, $langCode) { return $user->sendConfirmationMail(false, 'ConfirmationMail', 'usersignup-confirmation-email', true, $mailTemplate, $langCode); }); if (!$mailStatus->isGood()) { $this->response->setVal('message', 'could not send an email message'); return; } $this->response->setVal('success', true); }
/** * Listens for any user data save events and purges the authentication cache * * @param \User $user User * @return bool true - hook handler */ public static function onUserSave(\User $user) { self::purgeAuthenticationCache($user->getName()); return true; }
/** * Get the User object * * @return User */ public function getUser() { // Wikia change - begin - @author: Michał Roszka global $wgEnableHeliosExt; if ($this->user === null && $wgEnableHeliosExt) { $this->user = \Wikia\Helios\User::newFromToken($this->getRequest()); } // Wikia change - end // Wikia change - begin - @author: wladek global $wgUserForceAnon; if ($this->user === null && $wgUserForceAnon) { $this->user = new User(); } if ($this->user === null) { // Wikia change - end $this->user = User::newFromSession($this->getRequest()); } // Replace the user object according to the context, e.g. Piggyback. wfRunHooks('RequestContextOverrideUser', [&$this->user, $this->getRequest()]); return $this->user; }
/** * 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; }