public function execute() { if (!$this->hasAnyRoutes()) { $this->dieUsage('No password reset routes are available.', 'moduledisabled'); } $params = $this->extractRequestParams() + ['user' => null, 'email' => null]; $this->requireOnlyOneParameter($params, 'user', 'email'); $passwordReset = new PasswordReset($this->getConfig(), AuthManager::singleton()); $status = $passwordReset->isAllowed($this->getUser(), $params['capture']); if (!$status->isOK()) { $this->dieStatus(Status::wrap($status)); } $status = $passwordReset->execute($this->getUser(), $params['user'], $params['email'], $params['capture']); if (!$status->isOK()) { $status->value = null; $this->dieStatus(Status::wrap($status)); } $result = $this->getResult(); $result->addValue(['resetpassword'], 'status', 'success'); if ($params['capture']) { $passwords = $status->getValue() ?: []; ApiResult::setArrayType($passwords, 'kvp', 'user'); ApiResult::setIndexedTagName($passwords, 'p'); $result->addValue(['resetpassword'], 'passwords', $passwords); } }
public function execute() { if (!$this->getUser()->isLoggedIn()) { $this->dieUsage('Must be logged in to link accounts', 'notloggedin'); } $params = $this->extractRequestParams(); $this->requireAtLeastOneParameter($params, 'continue', 'returnurl'); if ($params['returnurl'] !== null) { $bits = wfParseUrl($params['returnurl']); if (!$bits || $bits['scheme'] === '') { $encParamName = $this->encodeParamName('returnurl'); $this->dieUsage("Invalid value '{$params['returnurl']}' for url parameter {$encParamName}", "badurl_{$encParamName}"); } } $helper = new ApiAuthManagerHelper($this); $manager = AuthManager::singleton(); // Check security-sensitive operation status $helper->securitySensitiveOperation('LinkAccounts'); // Make sure it's possible to link accounts if (!$manager->canLinkAccounts()) { $this->getResult()->addValue(null, 'linkaccount', $helper->formatAuthenticationResponse(AuthenticationResponse::newFail($this->msg('userlogin-cannot-' . AuthManager::ACTION_LINK)))); return; } // Perform the link step if ($params['continue']) { $reqs = $helper->loadAuthenticationRequests(AuthManager::ACTION_LINK_CONTINUE); $res = $manager->continueAccountLink($reqs); } else { $reqs = $helper->loadAuthenticationRequests(AuthManager::ACTION_LINK); $res = $manager->beginAccountLink($this->getUser(), $reqs, $params['returnurl']); } $this->getResult()->addValue(null, 'linkaccount', $helper->formatAuthenticationResponse($res)); }
public function execute() { $params = $this->extractRequestParams(); $this->requireAtLeastOneParameter($params, 'continue', 'returnurl'); if ($params['returnurl'] !== null) { $bits = wfParseUrl($params['returnurl']); if (!$bits || $bits['scheme'] === '') { $encParamName = $this->encodeParamName('returnurl'); $this->dieUsage("Invalid value '{$params['returnurl']}' for url parameter {$encParamName}", "badurl_{$encParamName}"); } } $helper = new ApiAuthManagerHelper($this); $manager = AuthManager::singleton(); // Make sure it's possible to log in if (!$manager->canAuthenticateNow()) { $this->getResult()->addValue(null, 'clientlogin', $helper->formatAuthenticationResponse(AuthenticationResponse::newFail($this->msg('userlogin-cannot-' . AuthManager::ACTION_LOGIN)))); return; } // Perform the login step if ($params['continue']) { $reqs = $helper->loadAuthenticationRequests(AuthManager::ACTION_LOGIN_CONTINUE); $res = $manager->continueAuthentication($reqs); } else { $reqs = $helper->loadAuthenticationRequests(AuthManager::ACTION_LOGIN); if ($params['preservestate']) { $req = $helper->getPreservedRequest(); if ($req) { $reqs[] = $req; } } $res = $manager->beginAuthentication($reqs, $params['returnurl']); } $this->getResult()->addValue(null, 'clientlogin', $helper->formatAuthenticationResponse($res)); }
public function execute() { $params = $this->extractRequestParams(); $helper = new ApiAuthManagerHelper($this); $manager = AuthManager::singleton(); $ret = ['canauthenticatenow' => $manager->canAuthenticateNow(), 'cancreateaccounts' => $manager->canCreateAccounts(), 'canlinkaccounts' => $manager->canLinkAccounts()]; if ($params['securitysensitiveoperation'] !== null) { $ret['securitysensitiveoperationstatus'] = $manager->securitySensitiveOperationStatus($params['securitysensitiveoperation']); } if ($params['requestsfor']) { $action = $params['requestsfor']; $preservedReq = $helper->getPreservedRequest(); if ($preservedReq) { $ret += ['haspreservedstate' => $preservedReq->hasStateForAction($action), 'hasprimarypreservedstate' => $preservedReq->hasPrimaryStateForAction($action), 'preservedusername' => (string) $preservedReq->username]; } else { $ret += ['haspreservedstate' => false, 'hasprimarypreservedstate' => false, 'preservedusername' => '']; } $reqs = $manager->getAuthenticationRequests($action, $this->getUser()); // Filter out blacklisted requests, depending on the action switch ($action) { case AuthManager::ACTION_CHANGE: $reqs = ApiAuthManagerHelper::blacklistAuthenticationRequests($reqs, $this->getConfig()->get('ChangeCredentialsBlacklist')); break; case AuthManager::ACTION_REMOVE: $reqs = ApiAuthManagerHelper::blacklistAuthenticationRequests($reqs, $this->getConfig()->get('RemoveCredentialsBlacklist')); break; } $ret += $helper->formatRequests($reqs); } $this->getResult()->addValue(['query'], $this->getModuleName(), $ret); }
public function execute() { if (!$this->getUser()->isLoggedIn()) { $this->dieUsage('Must be logged in to remove authentication data', 'notloggedin'); } $params = $this->extractRequestParams(); $manager = AuthManager::singleton(); // Check security-sensitive operation status ApiAuthManagerHelper::newForModule($this)->securitySensitiveOperation($this->operation); // Fetch the request. No need to load from the request, so don't use // ApiAuthManagerHelper's method. $blacklist = $this->authAction === AuthManager::ACTION_REMOVE ? array_flip($this->getConfig()->get('RemoveCredentialsBlacklist')) : []; $reqs = array_filter($manager->getAuthenticationRequests($this->authAction, $this->getUser()), function ($req) use($params, $blacklist) { return $req->getUniqueId() === $params['request'] && !isset($blacklist[get_class($req)]); }); if (count($reqs) !== 1) { $this->dieUsage('Failed to create change request', 'badrequest'); } $req = reset($reqs); // Perform the removal $status = $manager->allowsAuthenticationDataChange($req, true); Hooks::run('ChangeAuthenticationDataAudit', [$req, $status]); if (!$status->isGood()) { $this->dieStatus($status); } $manager->changeAuthenticationData($req); $this->getResult()->addValue(null, $this->getModuleName(), ['status' => 'success']); }
public function checkPermissions() { parent::checkPermissions(); $user = $this->getUser(); $status = AuthManager::singleton()->checkAccountCreatePermissions($user); if (!$status->isGood()) { throw new ErrorPageError('createacct-error', $status->getMessage()); } }
public function execute() { $params = $this->extractRequestParams(); $this->requireAtLeastOneParameter($params, 'continue', 'returnurl'); if ($params['returnurl'] !== null) { $bits = wfParseUrl($params['returnurl']); if (!$bits || $bits['scheme'] === '') { $encParamName = $this->encodeParamName('returnurl'); $this->dieUsage("Invalid value '{$params['returnurl']}' for url parameter {$encParamName}", "badurl_{$encParamName}"); } } $helper = new ApiAuthManagerHelper($this); $manager = AuthManager::singleton(); // Make sure it's possible to log in if (!$manager->canAuthenticateNow()) { $this->getResult()->addValue(null, 'clientlogin', $helper->formatAuthenticationResponse(AuthenticationResponse::newFail($this->msg('userlogin-cannot-' . AuthManager::ACTION_LOGIN)))); $helper->logAuthenticationResult('login', 'userlogin-cannot-' . AuthManager::ACTION_LOGIN); return; } // Perform the login step if ($params['continue']) { $reqs = $helper->loadAuthenticationRequests(AuthManager::ACTION_LOGIN_CONTINUE); $res = $manager->continueAuthentication($reqs); } else { $reqs = $helper->loadAuthenticationRequests(AuthManager::ACTION_LOGIN); if ($params['preservestate']) { $req = $helper->getPreservedRequest(); if ($req) { $reqs[] = $req; } } $res = $manager->beginAuthentication($reqs, $params['returnurl']); } // Remove CreateFromLoginAuthenticationRequest from $res->neededRequests. // It's there so a RESTART treated as UI will work right, but showing // it to the API client is just confusing. $res->neededRequests = ApiAuthManagerHelper::blacklistAuthenticationRequests($res->neededRequests, [CreateFromLoginAuthenticationRequest::class]); $this->getResult()->addValue(null, 'clientlogin', $helper->formatAuthenticationResponse($res)); $helper->logAuthenticationResult('login', $res); }
public function execute() { if (!$this->getUser()->isLoggedIn()) { $this->dieUsage('Must be logged in to change authentication data', 'notloggedin'); } $helper = new ApiAuthManagerHelper($this); $manager = AuthManager::singleton(); // Check security-sensitive operation status $helper->securitySensitiveOperation('ChangeCredentials'); // Fetch the request $reqs = ApiAuthManagerHelper::blacklistAuthenticationRequests($helper->loadAuthenticationRequests(AuthManager::ACTION_CHANGE), $this->getConfig()->get('ChangeCredentialsBlacklist')); if (count($reqs) !== 1) { $this->dieUsage('Failed to create change request', 'badrequest'); } $req = reset($reqs); // Make the change $status = $manager->allowsAuthenticationDataChange($req, true); if (!$status->isGood()) { $this->dieStatus($status); } $manager->changeAuthenticationData($req); $this->getResult()->addValue(null, 'changeauthenticationdata', ['status' => 'success']); }
/** * Executes the log-in attempt using the parameters passed. If * the log-in succeeds, it attaches a cookie to the session * and outputs the user id, username, and session token. If a * log-in fails, as the result of a bad password, a nonexistent * user, or any other reason, the host is cached with an expiry * and no log-in attempts will be accepted until that expiry * is reached. The expiry is $this->mLoginThrottle. */ public function execute() { // If we're in a mode that breaks the same-origin policy, no tokens can // be obtained if ($this->lacksSameOriginSecurity()) { $this->getResult()->addValue(null, 'login', ['result' => 'Aborted', 'reason' => 'Cannot log in when the same-origin policy is not applied']); return; } try { $this->requirePostedParameters(['password', 'token']); } catch (UsageException $ex) { // Make this a warning for now, upgrade to an error in 1.29. $this->setWarning($ex->getMessage()); $this->logFeatureUsage('login-params-in-query-string'); } $params = $this->extractRequestParams(); $result = []; // Make sure session is persisted $session = MediaWiki\Session\SessionManager::getGlobalSession(); $session->persist(); // Make sure it's possible to log in if (!$session->canSetUser()) { $this->getResult()->addValue(null, 'login', ['result' => 'Aborted', 'reason' => 'Cannot log in when using ' . $session->getProvider()->describe(Language::factory('en'))]); return; } $authRes = false; $context = new DerivativeContext($this->getContext()); $loginType = 'N/A'; // Check login token $token = $session->getToken('', 'login'); if ($token->wasNew() || !$params['token']) { $authRes = 'NeedToken'; } elseif (!$token->match($params['token'])) { $authRes = 'WrongToken'; } // Try bot passwords if ($authRes === false && $this->getConfig()->get('EnableBotPasswords') && ($botLoginData = BotPassword::canonicalizeLoginData($params['name'], $params['password']))) { $status = BotPassword::login($botLoginData[0], $botLoginData[1], $this->getRequest()); if ($status->isOK()) { $session = $status->getValue(); $authRes = 'Success'; $loginType = 'BotPassword'; } elseif (!$botLoginData[2]) { $authRes = 'Failed'; $message = $status->getMessage(); LoggerFactory::getInstance('authentication')->info('BotPassword login failed: ' . $status->getWikiText(false, false, 'en')); } } if ($authRes === false) { // Simplified AuthManager login, for backwards compatibility $manager = AuthManager::singleton(); $reqs = AuthenticationRequest::loadRequestsFromSubmission($manager->getAuthenticationRequests(AuthManager::ACTION_LOGIN, $this->getUser()), ['username' => $params['name'], 'password' => $params['password'], 'domain' => $params['domain'], 'rememberMe' => true]); $res = AuthManager::singleton()->beginAuthentication($reqs, 'null:'); switch ($res->status) { case AuthenticationResponse::PASS: if ($this->getConfig()->get('EnableBotPasswords')) { $warn = 'Main-account login via action=login is deprecated and may stop working ' . 'without warning.'; $warn .= ' To continue login with action=login, see [[Special:BotPasswords]].'; $warn .= ' To safely continue using main-account login, see action=clientlogin.'; } else { $warn = 'Login via action=login is deprecated and may stop working without warning.'; $warn .= ' To safely log in, see action=clientlogin.'; } $this->setWarning($warn); $authRes = 'Success'; $loginType = 'AuthManager'; break; case AuthenticationResponse::FAIL: // Hope it's not a PreAuthenticationProvider that failed... $authRes = 'Failed'; $message = $res->message; \MediaWiki\Logger\LoggerFactory::getInstance('authentication')->info(__METHOD__ . ': Authentication failed: ' . $message->inLanguage('en')->plain()); break; default: \MediaWiki\Logger\LoggerFactory::getInstance('authentication')->info(__METHOD__ . ': Authentication failed due to unsupported response type: ' . $res->status, $this->getAuthenticationResponseLogData($res)); $authRes = 'Aborted'; break; } } $result['result'] = $authRes; switch ($authRes) { case 'Success': $user = $session->getUser(); ApiQueryInfo::resetTokenCache(); // Deprecated hook $injected_html = ''; Hooks::run('UserLoginComplete', [&$user, &$injected_html, true]); $result['lguserid'] = intval($user->getId()); $result['lgusername'] = $user->getName(); break; case 'NeedToken': $result['token'] = $token->toString(); $this->setWarning('Fetching a token via action=login is deprecated. ' . 'Use action=query&meta=tokens&type=login instead.'); $this->logFeatureUsage('action=login&!lgtoken'); break; case 'WrongToken': break; case 'Failed': $result['reason'] = $message->useDatabase('false')->inLanguage('en')->text(); break; case 'Aborted': $result['reason'] = 'Authentication requires user interaction, ' . 'which is not supported by action=login.'; if ($this->getConfig()->get('EnableBotPasswords')) { $result['reason'] .= ' To be able to login with action=login, see [[Special:BotPasswords]].'; $result['reason'] .= ' To continue using main-account login, see action=clientlogin.'; } else { $result['reason'] .= ' To log in, see action=clientlogin.'; } break; default: ApiBase::dieDebug(__METHOD__, "Unhandled case value: {$authRes}"); } $this->getResult()->addValue(null, 'login', $result); if ($loginType === 'LoginForm' && isset(LoginForm::$statusCodes[$authRes])) { $authRes = LoginForm::$statusCodes[$authRes]; } LoggerFactory::getInstance('authevents')->info('Login attempt', ['event' => 'login', 'successful' => $authRes === 'Success', 'loginType' => $loginType, 'status' => $authRes]); }
/** * Auto-create the given user, if necessary * @private Don't call this yourself. Let Setup.php do it for you at the right time. * @deprecated since 1.27, use MediaWiki\Auth\AuthManager::autoCreateUser instead * @param User $user User to auto-create * @return bool Success * @codeCoverageIgnore */ public static function autoCreateUser(User $user) { wfDeprecated(__METHOD__, '1.27'); return \MediaWiki\Auth\AuthManager::singleton()->autoCreateUser($user, \MediaWiki\Auth\AuthManager::AUTOCREATE_SOURCE_SESSION, false)->isGood(); }
/** * @param User $user * @param string $newaddr * @return Status */ private function attemptChange(User $user, $newaddr) { $authManager = AuthManager::singleton(); if ($newaddr != '' && !Sanitizer::validateEmail($newaddr)) { return Status::newFatal('invalidemailaddress'); } if ($newaddr === $user->getEmail()) { return Status::newFatal('changeemail-nochange'); } $oldaddr = $user->getEmail(); $status = $user->setEmailWithConfirmation($newaddr); if (!$status->isGood()) { return $status; } Hooks::run('PrefsEmailAudit', [$user, $oldaddr, $newaddr]); $user->saveSettings(); MediaWiki\Auth\AuthManager::callLegacyAuthPlugin('updateExternalDB', [$user]); return $status; }
/** * Check to see if the given clear-text password is one of the accepted passwords * @deprecated since 1.27, use AuthManager instead * @param string $password User password * @return bool True if the given password is correct, otherwise False */ public function checkPassword($password) { global $wgAuth, $wgLegacyEncoding, $wgDisableAuthManager; if ($wgDisableAuthManager) { $this->load(); // Some passwords will give a fatal Status, which means there is // some sort of technical or security reason for this password to // be completely invalid and should never be checked (e.g., T64685) if (!$this->checkPasswordValidity($password)->isOK()) { return false; } // Certain authentication plugins do NOT want to save // domain passwords in a mysql database, so we should // check this (in case $wgAuth->strict() is false). if ($wgAuth->authenticate($this->getName(), $password)) { return true; } elseif ($wgAuth->strict()) { // Auth plugin doesn't allow local authentication return false; } elseif ($wgAuth->strictUserAuth($this->getName())) { // Auth plugin doesn't allow local authentication for this user name return false; } $passwordFactory = new PasswordFactory(); $passwordFactory->init(RequestContext::getMain()->getConfig()); $db = $this->queryFlagsUsed & self::READ_LATEST ? wfGetDB(DB_MASTER) : wfGetDB(DB_SLAVE); try { $mPassword = $passwordFactory->newFromCiphertext($db->selectField('user', 'user_password', ['user_id' => $this->getId()], __METHOD__)); } catch (PasswordError $e) { wfDebug('Invalid password hash found in database.'); $mPassword = PasswordFactory::newInvalidPassword(); } if (!$mPassword->equals($password)) { if ($wgLegacyEncoding) { // Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted // Check for this with iconv $cp1252Password = iconv('UTF-8', 'WINDOWS-1252//TRANSLIT', $password); if ($cp1252Password === $password || !$mPassword->equals($cp1252Password)) { return false; } } else { return false; } } if ($passwordFactory->needsUpdate($mPassword) && !wfReadOnly()) { $this->setPasswordInternal($password); } return true; } else { $manager = AuthManager::singleton(); $reqs = AuthenticationRequest::loadRequestsFromSubmission($manager->getAuthenticationRequests(AuthManager::ACTION_LOGIN), ['username' => $this->getName(), 'password' => $password]); $res = AuthManager::singleton()->beginAuthentication($reqs, 'null:'); switch ($res->status) { case AuthenticationResponse::PASS: return true; case AuthenticationResponse::FAIL: // Hope it's not a PreAuthenticationProvider that failed... \MediaWiki\Logger\LoggerFactory::getInstance('authentication')->info(__METHOD__ . ': Authentication failed: ' . $res->message->plain()); return false; default: throw new BadMethodCallException('AuthManager returned a response unsupported by ' . __METHOD__); } } }
/** * Do a password reset. Authorization is the caller's responsibility. * * Process the form. At this point we know that the user passes all the criteria in * userCanExecute(), and if the data array contains 'Username', etc, then Username * resets are allowed. * @param User $performingUser The user that does the password reset * @param string $username The user whose password is reset * @param string $email Alternative way to specify the user * @param bool $displayPassword Whether to display the password * @return StatusValue Will contain the passwords as a username => password array if the * $displayPassword flag was set * @throws LogicException When the user is not allowed to perform the action * @throws MWException On unexpected DB errors */ public function execute(User $performingUser, $username = null, $email = null, $displayPassword = false) { if (!$this->isAllowed($performingUser, $displayPassword)->isGood()) { $action = $this->isAllowed($performingUser)->isGood() ? 'display' : 'reset'; throw new LogicException('User ' . $performingUser->getName() . ' is not allowed to ' . $action . ' passwords'); } $resetRoutes = $this->config->get('PasswordResetRoutes') + ['username' => false, 'email' => false]; if ($resetRoutes['username'] && $username) { $method = 'username'; $users = [User::newFromName($username)]; } elseif ($resetRoutes['email'] && $email) { if (!Sanitizer::validateEmail($email)) { return StatusValue::newFatal('passwordreset-invalidemail'); } $method = 'email'; $users = $this->getUsersByEmail($email); } else { // The user didn't supply any data return StatusValue::newFatal('passwordreset-nodata'); } // Check for hooks (captcha etc), and allow them to modify the users list $error = []; $data = ['Username' => $username, 'Email' => $email, 'Capture' => $displayPassword ? '1' : null]; if (!Hooks::run('SpecialPasswordResetOnSubmit', [&$users, $data, &$error])) { return StatusValue::newFatal(Message::newFromSpecifier($error)); } if (!$users) { if ($method === 'email') { // Don't reveal whether or not an email address is in use return StatusValue::newGood([]); } else { return StatusValue::newFatal('noname'); } } $firstUser = $users[0]; if (!$firstUser instanceof User || !$firstUser->getId()) { // Don't parse username as wikitext (bug 65501) return StatusValue::newFatal(wfMessage('nosuchuser', wfEscapeWikiText($username))); } // Check against the rate limiter if ($performingUser->pingLimiter('mailpassword')) { return StatusValue::newFatal('actionthrottledtext'); } // All the users will have the same email address if (!$firstUser->getEmail()) { // This won't be reachable from the email route, so safe to expose the username return StatusValue::newFatal(wfMessage('noemail', wfEscapeWikiText($firstUser->getName()))); } // We need to have a valid IP address for the hook, but per bug 18347, we should // send the user's name if they're logged in. $ip = $performingUser->getRequest()->getIP(); if (!$ip) { return StatusValue::newFatal('badipaddress'); } Hooks::run('User::mailPasswordInternal', [&$performingUser, &$ip, &$firstUser]); $result = StatusValue::newGood(); $reqs = []; foreach ($users as $user) { $req = TemporaryPasswordAuthenticationRequest::newRandom(); $req->username = $user->getName(); $req->mailpassword = true; $req->hasBackchannel = $displayPassword; $req->caller = $performingUser->getName(); $status = $this->authManager->allowsAuthenticationDataChange($req, true); if ($status->isGood() && $status->getValue() !== 'ignored') { $reqs[] = $req; } elseif ($result->isGood()) { // only record the first error, to avoid exposing the number of users having the // same email address if ($status->getValue() === 'ignored') { $status = StatusValue::newFatal('passwordreset-ignored'); } $result->merge($status); } } if (!$result->isGood()) { return $result; } $passwords = []; foreach ($reqs as $req) { $this->authManager->changeAuthenticationData($req); // TODO record mail sending errors if ($displayPassword) { $passwords[$req->username] = $req->password; } } return StatusValue::newGood($passwords); }
/** * Verifies that the user meets the security level, possibly reauthenticating them in the process. * * This should be used when the page does something security-sensitive and needs extra defense * against a stolen account (e.g. a reauthentication). The authentication framework will make * an extra effort to make sure the user account is not compromised. What that exactly means * will depend on the system and user settings; e.g. the user might be required to log in again * unless their last login happened recently, or they might be given a second-factor challenge. * * Calling this method will result in one if these actions: * - return true: all good. * - return false and set a redirect: caller should abort; the redirect will take the user * to the login page for reauthentication, and back. * - throw an exception if there is no way for the user to meet the requirements without using * a different access method (e.g. this functionality is only available from a specific IP). * * Note that this does not in any way check that the user is authorized to use this special page * (use checkPermissions() for that). * * @param string $level A security level. Can be an arbitrary string, defaults to the page name. * @return bool False means a redirect to the reauthentication page has been set and processing * of the special page should be aborted. * @throws ErrorPageError If the security level cannot be met, even with reauthentication. */ protected function checkLoginSecurityLevel($level = null) { $level = $level ?: $this->getName(); $securityStatus = AuthManager::singleton()->securitySensitiveOperationStatus($level); if ($securityStatus === AuthManager::SEC_OK) { return true; } elseif ($securityStatus === AuthManager::SEC_REAUTH) { $request = $this->getRequest(); $title = SpecialPage::getTitleFor('Userlogin'); $query = ['returnto' => $this->getFullTitle()->getPrefixedDBkey(), 'returntoquery' => wfArrayToCgi(array_diff_key($request->getQueryValues(), ['title' => true])), 'force' => $level]; $url = $title->getFullURL($query, false, PROTO_HTTPS); $this->getOutput()->redirect($url); return false; } $titleMessage = wfMessage('specialpage-securitylevel-not-allowed-title'); $errorMessage = wfMessage('specialpage-securitylevel-not-allowed'); throw new ErrorPageError($titleMessage, $errorMessage); }
/** * @param User $user * @param IContextSource $context * @param array $defaultPreferences * @return void */ static function profilePreferences($user, IContextSource $context, &$defaultPreferences) { global $wgContLang, $wgParser; $authManager = AuthManager::singleton(); $config = $context->getConfig(); // retrieving user name for GENDER and misc. $userName = $user->getName(); # # User info ##################################### // Information panel $defaultPreferences['username'] = ['type' => 'info', 'label-message' => ['username', $userName], 'default' => $userName, 'section' => 'personal/info']; # Get groups to which the user belongs $userEffectiveGroups = $user->getEffectiveGroups(); $userGroups = $userMembers = []; foreach ($userEffectiveGroups as $ueg) { if ($ueg == '*') { // Skip the default * group, seems useless here continue; } $groupName = User::getGroupName($ueg); $userGroups[] = User::makeGroupLinkHTML($ueg, $groupName); $memberName = User::getGroupMember($ueg, $userName); $userMembers[] = User::makeGroupLinkHTML($ueg, $memberName); } asort($userGroups); asort($userMembers); $lang = $context->getLanguage(); $defaultPreferences['usergroups'] = ['type' => 'info', 'label' => $context->msg('prefs-memberingroups')->numParams(count($userGroups))->params($userName)->parse(), 'default' => $context->msg('prefs-memberingroups-type')->rawParams($lang->commaList($userGroups), $lang->commaList($userMembers))->escaped(), 'raw' => true, 'section' => 'personal/info']; $editCount = Linker::link(SpecialPage::getTitleFor("Contributions", $userName), $lang->formatNum($user->getEditCount())); $defaultPreferences['editcount'] = ['type' => 'info', 'raw' => true, 'label-message' => 'prefs-edits', 'default' => $editCount, 'section' => 'personal/info']; if ($user->getRegistration()) { $displayUser = $context->getUser(); $userRegistration = $user->getRegistration(); $defaultPreferences['registrationdate'] = ['type' => 'info', 'label-message' => 'prefs-registration', 'default' => $context->msg('prefs-registration-date-time', $lang->userTimeAndDate($userRegistration, $displayUser), $lang->userDate($userRegistration, $displayUser), $lang->userTime($userRegistration, $displayUser))->parse(), 'section' => 'personal/info']; } $canViewPrivateInfo = $user->isAllowed('viewmyprivateinfo'); $canEditPrivateInfo = $user->isAllowed('editmyprivateinfo'); // Actually changeable stuff $defaultPreferences['realname'] = ['type' => $canEditPrivateInfo && $authManager->allowsPropertyChange('realname') ? 'text' : 'info', 'default' => $user->getRealName(), 'section' => 'personal/info', 'label-message' => 'yourrealname', 'help-message' => 'prefs-help-realname']; if ($canEditPrivateInfo && $authManager->allowsAuthenticationDataChange(new PasswordAuthenticationRequest(), false)->isGood()) { $link = Linker::link(SpecialPage::getTitleFor('ChangePassword'), $context->msg('prefs-resetpass')->escaped(), [], ['returnto' => SpecialPage::getTitleFor('Preferences')->getPrefixedText()]); $defaultPreferences['password'] = ['type' => 'info', 'raw' => true, 'default' => $link, 'label-message' => 'yourpassword', 'section' => 'personal/info']; } // Only show prefershttps if secure login is turned on if ($config->get('SecureLogin') && wfCanIPUseHTTPS($context->getRequest()->getIP())) { $defaultPreferences['prefershttps'] = ['type' => 'toggle', 'label-message' => 'tog-prefershttps', 'help-message' => 'prefs-help-prefershttps', 'section' => 'personal/info']; } // Language $languages = Language::fetchLanguageNames(null, 'mw'); $languageCode = $config->get('LanguageCode'); if (!array_key_exists($languageCode, $languages)) { $languages[$languageCode] = $languageCode; } ksort($languages); $options = []; foreach ($languages as $code => $name) { $display = wfBCP47($code) . ' - ' . $name; $options[$display] = $code; } $defaultPreferences['language'] = ['type' => 'select', 'section' => 'personal/i18n', 'options' => $options, 'label-message' => 'yourlanguage']; $defaultPreferences['gender'] = ['type' => 'radio', 'section' => 'personal/i18n', 'options' => [$context->msg('parentheses')->params($context->msg('gender-unknown')->plain())->escaped() => 'unknown', $context->msg('gender-female')->escaped() => 'female', $context->msg('gender-male')->escaped() => 'male'], 'label-message' => 'yourgender', 'help-message' => 'prefs-help-gender']; // see if there are multiple language variants to choose from if (!$config->get('DisableLangConversion')) { foreach (LanguageConverter::$languagesWithVariants as $langCode) { if ($langCode == $wgContLang->getCode()) { $variants = $wgContLang->getVariants(); if (count($variants) <= 1) { continue; } $variantArray = []; foreach ($variants as $v) { $v = str_replace('_', '-', strtolower($v)); $variantArray[$v] = $lang->getVariantname($v, false); } $options = []; foreach ($variantArray as $code => $name) { $display = wfBCP47($code) . ' - ' . $name; $options[$display] = $code; } $defaultPreferences['variant'] = ['label-message' => 'yourvariant', 'type' => 'select', 'options' => $options, 'section' => 'personal/i18n', 'help-message' => 'prefs-help-variant']; } else { $defaultPreferences["variant-{$langCode}"] = ['type' => 'api']; } } } // Stuff from Language::getExtraUserToggles() // FIXME is this dead code? $extraUserToggles doesn't seem to be defined for any language $toggles = $wgContLang->getExtraUserToggles(); foreach ($toggles as $toggle) { $defaultPreferences[$toggle] = ['type' => 'toggle', 'section' => 'personal/i18n', 'label-message' => "tog-{$toggle}"]; } // show a preview of the old signature first $oldsigWikiText = $wgParser->preSaveTransform('~~~', $context->getTitle(), $user, ParserOptions::newFromContext($context)); $oldsigHTML = $context->getOutput()->parseInline($oldsigWikiText, true, true); $defaultPreferences['oldsig'] = ['type' => 'info', 'raw' => true, 'label-message' => 'tog-oldsig', 'default' => $oldsigHTML, 'section' => 'personal/signature']; $defaultPreferences['nickname'] = ['type' => $authManager->allowsPropertyChange('nickname') ? 'text' : 'info', 'maxlength' => $config->get('MaxSigChars'), 'label-message' => 'yournick', 'validation-callback' => ['Preferences', 'validateSignature'], 'section' => 'personal/signature', 'filter-callback' => ['Preferences', 'cleanSignature']]; $defaultPreferences['fancysig'] = ['type' => 'toggle', 'label-message' => 'tog-fancysig', 'help-message' => 'prefs-help-signature', 'section' => 'personal/signature']; # # Email stuff if ($config->get('EnableEmail')) { if ($canViewPrivateInfo) { $helpMessages[] = $config->get('EmailConfirmToEdit') ? 'prefs-help-email-required' : 'prefs-help-email'; if ($config->get('EnableUserEmail')) { // additional messages when users can send email to each other $helpMessages[] = 'prefs-help-email-others'; } $emailAddress = $user->getEmail() ? htmlspecialchars($user->getEmail()) : ''; if ($canEditPrivateInfo && $authManager->allowsPropertyChange('emailaddress')) { $link = Linker::link(SpecialPage::getTitleFor('ChangeEmail'), $context->msg($user->getEmail() ? 'prefs-changeemail' : 'prefs-setemail')->escaped(), [], ['returnto' => SpecialPage::getTitleFor('Preferences')->getPrefixedText()]); $emailAddress .= $emailAddress == '' ? $link : $context->msg('word-separator')->escaped() . $context->msg('parentheses')->rawParams($link)->escaped(); } $defaultPreferences['emailaddress'] = ['type' => 'info', 'raw' => true, 'default' => $emailAddress, 'label-message' => 'youremail', 'section' => 'personal/email', 'help-messages' => $helpMessages]; } $disableEmailPrefs = false; if ($config->get('EmailAuthentication')) { $emailauthenticationclass = 'mw-email-not-authenticated'; if ($user->getEmail()) { if ($user->getEmailAuthenticationTimestamp()) { // date and time are separate parameters to facilitate localisation. // $time is kept for backward compat reasons. // 'emailauthenticated' is also used in SpecialConfirmemail.php $displayUser = $context->getUser(); $emailTimestamp = $user->getEmailAuthenticationTimestamp(); $time = $lang->userTimeAndDate($emailTimestamp, $displayUser); $d = $lang->userDate($emailTimestamp, $displayUser); $t = $lang->userTime($emailTimestamp, $displayUser); $emailauthenticated = $context->msg('emailauthenticated', $time, $d, $t)->parse() . '<br />'; $disableEmailPrefs = false; $emailauthenticationclass = 'mw-email-authenticated'; } else { $disableEmailPrefs = true; $emailauthenticated = $context->msg('emailnotauthenticated')->parse() . '<br />' . Linker::linkKnown(SpecialPage::getTitleFor('Confirmemail'), $context->msg('emailconfirmlink')->escaped()) . '<br />'; $emailauthenticationclass = "mw-email-not-authenticated"; } } else { $disableEmailPrefs = true; $emailauthenticated = $context->msg('noemailprefs')->escaped(); $emailauthenticationclass = 'mw-email-none'; } if ($canViewPrivateInfo) { $defaultPreferences['emailauthentication'] = ['type' => 'info', 'raw' => true, 'section' => 'personal/email', 'label-message' => 'prefs-emailconfirm-label', 'default' => $emailauthenticated, 'cssclass' => $emailauthenticationclass]; } } if ($config->get('EnableUserEmail') && $user->isAllowed('sendemail')) { $defaultPreferences['disablemail'] = ['type' => 'toggle', 'invert' => true, 'section' => 'personal/email', 'label-message' => 'allowemail', 'disabled' => $disableEmailPrefs]; $defaultPreferences['ccmeonemails'] = ['type' => 'toggle', 'section' => 'personal/email', 'label-message' => 'tog-ccmeonemails', 'disabled' => $disableEmailPrefs]; } if ($config->get('EnotifWatchlist')) { $defaultPreferences['enotifwatchlistpages'] = ['type' => 'toggle', 'section' => 'personal/email', 'label-message' => 'tog-enotifwatchlistpages', 'disabled' => $disableEmailPrefs]; } if ($config->get('EnotifUserTalk')) { $defaultPreferences['enotifusertalkpages'] = ['type' => 'toggle', 'section' => 'personal/email', 'label-message' => 'tog-enotifusertalkpages', 'disabled' => $disableEmailPrefs]; } if ($config->get('EnotifUserTalk') || $config->get('EnotifWatchlist')) { if ($config->get('EnotifMinorEdits')) { $defaultPreferences['enotifminoredits'] = ['type' => 'toggle', 'section' => 'personal/email', 'label-message' => 'tog-enotifminoredits', 'disabled' => $disableEmailPrefs]; } if ($config->get('EnotifRevealEditorAddress')) { $defaultPreferences['enotifrevealaddr'] = ['type' => 'toggle', 'section' => 'personal/email', 'label-message' => 'tog-enotifrevealaddr', 'disabled' => $disableEmailPrefs]; } } } }
/** * Handle the form submission if everything validated properly * * @param array $formData * @param PreferencesForm $form * @return bool|Status|string */ static function tryFormSubmit($formData, $form) { $user = $form->getModifiedUser(); $hiddenPrefs = $form->getConfig()->get('HiddenPrefs'); $result = true; if (!$user->isAllowedAny('editmyprivateinfo', 'editmyoptions')) { return Status::newFatal('mypreferencesprotected'); } // Filter input foreach (array_keys($formData) as $name) { if (isset(self::$saveFilters[$name])) { $formData[$name] = call_user_func(self::$saveFilters[$name], $formData[$name], $formData); } } // Fortunately, the realname field is MUCH simpler // (not really "private", but still shouldn't be edited without permission) if (!in_array('realname', $hiddenPrefs) && $user->isAllowed('editmyprivateinfo') && array_key_exists('realname', $formData)) { $realName = $formData['realname']; $user->setRealName($realName); } if ($user->isAllowed('editmyoptions')) { foreach (self::$saveBlacklist as $b) { unset($formData[$b]); } # If users have saved a value for a preference which has subsequently been disabled # via $wgHiddenPrefs, we don't want to destroy that setting in case the preference # is subsequently re-enabled foreach ($hiddenPrefs as $pref) { # If the user has not set a non-default value here, the default will be returned # and subsequently discarded $formData[$pref] = $user->getOption($pref, null, true); } // Keep old preferences from interfering due to back-compat code, etc. $user->resetOptions('unused', $form->getContext()); foreach ($formData as $key => $value) { $user->setOption($key, $value); } Hooks::run('PreferencesFormPreSave', [$formData, $form, $user, &$result]); } MediaWiki\Auth\AuthManager::callLegacyAuthPlugin('updateExternalDB', [$user]); $user->saveSettings(); return $result; }
public function __construct() { parent::__construct('PasswordReset', 'editmyprivateinfo'); $this->passwordReset = new PasswordReset($this->getConfig(), AuthManager::singleton()); }
/** * Executes the log-in attempt using the parameters passed. If * the log-in succeeds, it attaches a cookie to the session * and outputs the user id, username, and session token. If a * log-in fails, as the result of a bad password, a nonexistent * user, or any other reason, the host is cached with an expiry * and no log-in attempts will be accepted until that expiry * is reached. The expiry is $this->mLoginThrottle. */ public function execute() { // If we're in a mode that breaks the same-origin policy, no tokens can // be obtained if ($this->lacksSameOriginSecurity()) { $this->getResult()->addValue(null, 'login', ['result' => 'Aborted', 'reason' => 'Cannot log in when the same-origin policy is not applied']); return; } $params = $this->extractRequestParams(); $result = []; // Make sure session is persisted $session = MediaWiki\Session\SessionManager::getGlobalSession(); $session->persist(); // Make sure it's possible to log in if (!$session->canSetUser()) { $this->getResult()->addValue(null, 'login', ['result' => 'Aborted', 'reason' => 'Cannot log in when using ' . $session->getProvider()->describe(Language::factory('en'))]); return; } $authRes = false; $context = new DerivativeContext($this->getContext()); $loginType = 'N/A'; // Check login token $token = $session->getToken('', 'login'); if ($token->wasNew() || !$params['token']) { $authRes = 'NeedToken'; } elseif (!$token->match($params['token'])) { $authRes = 'WrongToken'; } // Try bot passwords if ($authRes === false && $this->getConfig()->get('EnableBotPasswords') && strpos($params['name'], BotPassword::getSeparator()) !== false) { $status = BotPassword::login($params['name'], $params['password'], $this->getRequest()); if ($status->isOK()) { $session = $status->getValue(); $authRes = 'Success'; $loginType = 'BotPassword'; } else { $authRes = 'Failed'; $message = $status->getMessage(); LoggerFactory::getInstance('authmanager')->info('BotPassword login failed: ' . $status->getWikiText(false, false, 'en')); } } if ($authRes === false) { if ($this->getConfig()->get('DisableAuthManager')) { // Non-AuthManager login $context->setRequest(new DerivativeRequest($this->getContext()->getRequest(), ['wpName' => $params['name'], 'wpPassword' => $params['password'], 'wpDomain' => $params['domain'], 'wpLoginToken' => $params['token'], 'wpRemember' => ''])); $loginForm = new LoginForm(); $loginForm->setContext($context); $authRes = $loginForm->authenticateUserData(); $loginType = 'LoginForm'; switch ($authRes) { case LoginForm::SUCCESS: $authRes = 'Success'; break; case LoginForm::NEED_TOKEN: $authRes = 'NeedToken'; break; } } else { // Simplified AuthManager login, for backwards compatibility $manager = AuthManager::singleton(); $reqs = AuthenticationRequest::loadRequestsFromSubmission($manager->getAuthenticationRequests(AuthManager::ACTION_LOGIN, $this->getUser()), ['username' => $params['name'], 'password' => $params['password'], 'domain' => $params['domain'], 'rememberMe' => true]); $res = AuthManager::singleton()->beginAuthentication($reqs, 'null:'); switch ($res->status) { case AuthenticationResponse::PASS: if ($this->getConfig()->get('EnableBotPasswords')) { $warn = 'Main-account login via action=login is deprecated and may stop working ' . 'without warning.'; $warn .= ' To continue login with action=login, see [[Special:BotPasswords]].'; $warn .= ' To safely continue using main-account login, see action=clientlogin.'; } else { $warn = 'Login via action=login is deprecated and may stop working without warning.'; $warn .= ' To safely log in, see action=clientlogin.'; } $this->setWarning($warn); $authRes = 'Success'; $loginType = 'AuthManager'; break; case AuthenticationResponse::FAIL: // Hope it's not a PreAuthenticationProvider that failed... $authRes = 'Failed'; $message = $res->message; \MediaWiki\Logger\LoggerFactory::getInstance('authentication')->info(__METHOD__ . ': Authentication failed: ' . $message->plain()); break; default: $authRes = 'Aborted'; break; } } } $result['result'] = $authRes; switch ($authRes) { case 'Success': if ($this->getConfig()->get('DisableAuthManager')) { $user = $context->getUser(); $this->getContext()->setUser($user); $user->setCookies($this->getRequest(), null, true); } else { $user = $session->getUser(); } ApiQueryInfo::resetTokenCache(); // Deprecated hook $injected_html = ''; Hooks::run('UserLoginComplete', [&$user, &$injected_html]); $result['lguserid'] = intval($user->getId()); $result['lgusername'] = $user->getName(); // @todo: These are deprecated, and should be removed at some // point (1.28 at the earliest, and see T121527). They were ok // when the core cookie-based login was the only thing, but // CentralAuth broke that a while back and // SessionManager/AuthManager *really* break it. $result['lgtoken'] = $user->getToken(); $result['cookieprefix'] = $this->getConfig()->get('CookiePrefix'); $result['sessionid'] = $session->getId(); break; case 'NeedToken': $result['token'] = $token->toString(); $this->setWarning('Fetching a token via action=login is deprecated. ' . 'Use action=query&meta=tokens&type=login instead.'); $this->logFeatureUsage('action=login&!lgtoken'); // @todo: See above about deprecation $result['cookieprefix'] = $this->getConfig()->get('CookiePrefix'); $result['sessionid'] = $session->getId(); break; case 'WrongToken': break; case 'Failed': $result['reason'] = $message->useDatabase('false')->inLanguage('en')->text(); break; case 'Aborted': $result['reason'] = 'Authentication requires user interaction, ' . 'which is not supported by action=login.'; if ($this->getConfig()->get('EnableBotPasswords')) { $result['reason'] .= ' To be able to login with action=login, see [[Special:BotPasswords]].'; $result['reason'] .= ' To continue using main-account login, see action=clientlogin.'; } else { $result['reason'] .= ' To log in, see action=clientlogin.'; } break; // Results from LoginForm for when $wgDisableAuthManager is true // Results from LoginForm for when $wgDisableAuthManager is true case LoginForm::WRONG_TOKEN: $result['result'] = 'WrongToken'; break; case LoginForm::NO_NAME: $result['result'] = 'NoName'; break; case LoginForm::ILLEGAL: $result['result'] = 'Illegal'; break; case LoginForm::WRONG_PLUGIN_PASS: $result['result'] = 'WrongPluginPass'; break; case LoginForm::NOT_EXISTS: $result['result'] = 'NotExists'; break; // bug 20223 - Treat a temporary password as wrong. Per SpecialUserLogin: // The e-mailed temporary password should not be used for actual logins. // bug 20223 - Treat a temporary password as wrong. Per SpecialUserLogin: // The e-mailed temporary password should not be used for actual logins. case LoginForm::RESET_PASS: case LoginForm::WRONG_PASS: $result['result'] = 'WrongPass'; break; case LoginForm::EMPTY_PASS: $result['result'] = 'EmptyPass'; break; case LoginForm::CREATE_BLOCKED: $result['result'] = 'CreateBlocked'; $result['details'] = 'Your IP address is blocked from account creation'; $block = $context->getUser()->getBlock(); if ($block) { $result = array_merge($result, ApiQueryUserInfo::getBlockInfo($block)); } break; case LoginForm::THROTTLED: $result['result'] = 'Throttled'; $result['wait'] = intval($loginForm->mThrottleWait); break; case LoginForm::USER_BLOCKED: $result['result'] = 'Blocked'; $block = User::newFromName($params['name'])->getBlock(); if ($block) { $result = array_merge($result, ApiQueryUserInfo::getBlockInfo($block)); } break; case LoginForm::ABORTED: $result['result'] = 'Aborted'; $result['reason'] = $loginForm->mAbortLoginErrorMsg; break; default: ApiBase::dieDebug(__METHOD__, "Unhandled case value: {$authRes}"); } $this->getResult()->addValue(null, 'login', $result); if ($loginType === 'LoginForm' && isset(LoginForm::$statusCodes[$authRes])) { $authRes = LoginForm::$statusCodes[$authRes]; } LoggerFactory::getInstance('authmanager')->info('Login attempt', ['event' => 'login', 'successful' => $authRes === 'Success', 'loginType' => $loginType, 'status' => $authRes]); }
public function isListed() { return AuthManager::singleton()->canLinkAccounts(); }
/** * Fetch and load the AuthenticationRequests for an action * @param string $action One of the AuthManager::ACTION_* constants * @return AuthenticationRequest[] */ public function loadAuthenticationRequests($action) { $params = $this->module->extractRequestParams(); $manager = AuthManager::singleton(); $reqs = $manager->getAuthenticationRequests($action, $this->module->getUser()); // Filter requests, if requested to do so $wantedRequests = null; if (isset($params['requests'])) { $wantedRequests = array_flip($params['requests']); } elseif (isset($params['request'])) { $wantedRequests = [$params['request'] => true]; } if ($wantedRequests !== null) { $reqs = array_filter($reqs, function ($req) use($wantedRequests) { return isset($wantedRequests[$req->getUniqueId()]); }); } // Collect the fields for all the requests $fields = []; foreach ($reqs as $req) { $fields += (array) $req->getFieldInfo(); } // Extract the request data for the fields and mark those request // parameters as used $data = array_intersect_key($this->module->getRequest()->getValues(), $fields); $this->module->getMain()->markParamsUsed(array_keys($data)); return AuthenticationRequest::loadRequestsFromSubmission($reqs, $data); }
/** * Fetch and load the AuthenticationRequests for an action * @param string $action One of the AuthManager::ACTION_* constants * @return AuthenticationRequest[] */ public function loadAuthenticationRequests($action) { $params = $this->module->extractRequestParams(); $manager = AuthManager::singleton(); $reqs = $manager->getAuthenticationRequests($action, $this->module->getUser()); // Filter requests, if requested to do so $wantedRequests = null; if (isset($params['requests'])) { $wantedRequests = array_flip($params['requests']); } elseif (isset($params['request'])) { $wantedRequests = [$params['request'] => true]; } if ($wantedRequests !== null) { $reqs = array_filter($reqs, function ($req) use($wantedRequests) { return isset($wantedRequests[$req->getUniqueId()]); }); } // Collect the fields for all the requests $fields = []; $sensitive = []; foreach ($reqs as $req) { $info = (array) $req->getFieldInfo(); $fields += $info; $sensitive += array_filter($info, function ($opts) { return !empty($opts['sensitive']); }); } // Extract the request data for the fields and mark those request // parameters as used $data = array_intersect_key($this->module->getRequest()->getValues(), $fields); $this->module->getMain()->markParamsUsed(array_keys($data)); if ($sensitive) { try { $this->module->requirePostedParameters(array_keys($sensitive), 'noprefix'); } catch (UsageException $ex) { // Make this a warning for now, upgrade to an error in 1.29. $this->module->setWarning($ex->getMessage()); $this->module->logFeatureUsage($this->module->getModuleName() . '-params-in-query-string'); } } return AuthenticationRequest::loadRequestsFromSubmission($reqs, $data); }
public function addUser($user, $password, $email = '', $realname = '') { global $wgUser; $data = ['username' => $user->getName(), 'password' => $password, 'retype' => $password, 'email' => $email, 'realname' => $realname]; if ($this->domain !== null && $this->domain !== '') { $data['domain'] = $this->domain; } $reqs = AuthManager::singleton()->getAuthenticationRequests(AuthManager::ACTION_CREATE); $reqs = AuthenticationRequest::loadRequestsFromSubmission($reqs, $data); $res = AuthManager::singleton()->beginAccountCreation($wgUser, $reqs, 'null:'); switch ($res->status) { case AuthenticationResponse::PASS: return true; case AuthenticationResponse::FAIL: // Hope it's not a PreAuthenticationProvider that failed... $msg = $res->message instanceof \Message ? $res->message : new \Message($res->message); $this->logger->info(__METHOD__ . ': Authentication failed: ' . $msg->plain()); return false; default: throw new \BadMethodCallException('AuthManager does not support such simplified account creation'); } }
/** * Create a HTMLForm descriptor for the core login fields. * @param FakeAuthTemplate $template B/C data (not used but needed by getBCFieldDefinitions) * @return array */ protected function getFieldDefinitions($template) { global $wgEmailConfirmToEdit, $wgLoginLanguageSelector; $isLoggedIn = $this->getUser()->isLoggedIn(); $continuePart = $this->isContinued() ? 'continue-' : ''; $anotherPart = $isLoggedIn ? 'another-' : ''; $expiration = $this->getRequest()->getSession()->getProvider()->getRememberUserDuration(); $expirationDays = ceil($expiration / (3600 * 24)); $secureLoginLink = ''; if ($this->mSecureLoginUrl) { $secureLoginLink = Html::element('a', ['href' => $this->mSecureLoginUrl, 'class' => 'mw-ui-flush-right mw-secure'], $this->msg('userlogin-signwithsecure')->text()); } $usernameHelpLink = ''; if (!$this->msg('createacct-helpusername')->isDisabled()) { $usernameHelpLink = Html::rawElement('span', ['class' => 'mw-ui-flush-right'], $this->msg('createacct-helpusername')->parse()); } if ($this->isSignup()) { $fieldDefinitions = ['statusarea' => ['type' => 'info', 'raw' => true, 'default' => Html::element('div', ['id' => 'mw-createacct-status-area']), 'weight' => -105], 'username' => ['label-raw' => $this->msg('userlogin-yourname')->escaped() . $usernameHelpLink, 'id' => 'wpName2', 'placeholder-message' => $isLoggedIn ? 'createacct-another-username-ph' : 'userlogin-yourname-ph'], 'mailpassword' => ['type' => 'check', 'label-message' => 'createaccountmail', 'name' => 'wpCreateaccountMail', 'id' => 'wpCreateaccountMail'], 'password' => ['id' => 'wpPassword2', 'placeholder-message' => 'createacct-yourpassword-ph', 'hide-if' => ['===', 'wpCreateaccountMail', '1']], 'domain' => [], 'retype' => ['baseField' => 'password', 'type' => 'password', 'label-message' => 'createacct-yourpasswordagain', 'id' => 'wpRetype', 'cssclass' => 'loginPassword', 'size' => 20, 'validation-callback' => function ($value, $alldata) { if (empty($alldata['mailpassword']) && !empty($alldata['password'])) { if (!$value) { return $this->msg('htmlform-required'); } elseif ($value !== $alldata['password']) { return $this->msg('badretype'); } } return true; }, 'hide-if' => ['===', 'wpCreateaccountMail', '1'], 'placeholder-message' => 'createacct-yourpasswordagain-ph'], 'email' => ['type' => 'email', 'label-message' => $wgEmailConfirmToEdit ? 'createacct-emailrequired' : 'createacct-emailoptional', 'id' => 'wpEmail', 'cssclass' => 'loginText', 'size' => '20', 'required' => $wgEmailConfirmToEdit, 'validation-callback' => function ($value, $alldata) { global $wgEmailConfirmToEdit; // AuthManager will check most of these, but that will make the auth // session fail and this won't, so nicer to do it this way if (!$value && $wgEmailConfirmToEdit) { // no point in allowing registration without email when email is // required to edit return $this->msg('noemailtitle'); } elseif (!$value && !empty($alldata['mailpassword'])) { // cannot send password via email when there is no email address return $this->msg('noemailcreate'); } elseif ($value && !Sanitizer::validateEmail($value)) { return $this->msg('invalidemailaddress'); } return true; }, 'placeholder-message' => 'createacct-' . $anotherPart . 'email-ph'], 'realname' => ['type' => 'text', 'help-message' => $isLoggedIn ? 'createacct-another-realname-tip' : 'prefs-help-realname', 'label-message' => 'createacct-realname', 'cssclass' => 'loginText', 'size' => 20, 'id' => 'wpRealName'], 'reason' => ['type' => 'text', 'label-message' => 'createacct-reason', 'cssclass' => 'loginText', 'id' => 'wpReason', 'size' => '20', 'placeholder-message' => 'createacct-reason-ph'], 'extrainput' => [], 'createaccount' => ['type' => 'submit', 'default' => $this->msg('createacct-' . $anotherPart . $continuePart . 'submit')->text(), 'name' => 'wpCreateaccount', 'id' => 'wpCreateaccount', 'weight' => 100]]; } else { $fieldDefinitions = ['username' => ['label-raw' => $this->msg('userlogin-yourname')->escaped() . $secureLoginLink, 'id' => 'wpName1', 'placeholder-message' => 'userlogin-yourname-ph'], 'password' => ['id' => 'wpPassword1', 'placeholder-message' => 'userlogin-yourpassword-ph'], 'domain' => [], 'extrainput' => [], 'rememberMe' => ['type' => 'check', 'name' => 'wpRemember', 'label-message' => $this->msg('userlogin-remembermypassword')->numParams($expirationDays), 'id' => 'wpRemember'], 'loginattempt' => ['type' => 'submit', 'default' => $this->msg('pt-login-' . $continuePart . 'button')->text(), 'id' => 'wpLoginAttempt', 'weight' => 100], 'linkcontainer' => ['type' => 'info', 'cssclass' => 'mw-form-related-link-container mw-userlogin-help', 'raw' => true, 'default' => Html::element('a', ['href' => Skin::makeInternalOrExternalUrl(wfMessage('helplogin-url')->inContentLanguage()->text())], $this->msg('userlogin-helplink2')->text()), 'weight' => 200], 'skipReset' => ['weight' => 110, 'flags' => []]]; } $fieldDefinitions['username'] += ['type' => 'text', 'name' => 'wpName', 'cssclass' => 'loginText', 'size' => 20]; $fieldDefinitions['password'] += ['type' => 'password', 'name' => 'wpPassword', 'cssclass' => 'loginPassword', 'size' => 20]; if ($template->get('header') || $template->get('formheader')) { // B/C for old extensions that haven't been converted to AuthManager (or have been // but somebody is using the old version) and still use templates via the // UserCreateForm/UserLoginForm hook. // 'header' used by ConfirmEdit, CondfirmAccount, Persona, WikimediaIncubator, SemanticSignup // 'formheader' used by MobileFrontend $fieldDefinitions['header'] = ['type' => 'info', 'raw' => true, 'default' => $template->get('header') ?: $template->get('formheader'), 'weight' => -110]; } if ($this->mEntryError) { $fieldDefinitions['entryError'] = ['type' => 'info', 'default' => Html::rawElement('div', ['class' => $this->mEntryErrorType . 'box'], $this->mEntryError), 'raw' => true, 'rawrow' => true, 'weight' => -100]; } if (!$this->showExtraInformation()) { unset($fieldDefinitions['linkcontainer'], $fieldDefinitions['signupend']); } if ($this->isSignup() && $this->showExtraInformation()) { // blank signup footer for site customization // uses signupend-https for HTTPS requests if it's not blank, signupend otherwise $signupendMsg = $this->msg('signupend'); $signupendHttpsMsg = $this->msg('signupend-https'); if (!$signupendMsg->isDisabled()) { $usingHTTPS = $this->getRequest()->getProtocol() === 'https'; $signupendText = $usingHTTPS && !$signupendHttpsMsg->isBlank() ? $signupendHttpsMsg->parse() : $signupendMsg->parse(); $fieldDefinitions['signupend'] = ['type' => 'info', 'raw' => true, 'default' => Html::rawElement('div', ['id' => 'signupend'], $signupendText), 'weight' => 225]; } } if (!$this->isSignup() && $this->showExtraInformation()) { $passwordReset = new PasswordReset($this->getConfig(), AuthManager::singleton()); if ($passwordReset->isAllowed($this->getUser())->isGood()) { $fieldDefinitions['passwordReset'] = ['type' => 'info', 'raw' => true, 'cssclass' => 'mw-form-related-link-container', 'default' => Linker::link(SpecialPage::getTitleFor('PasswordReset'), $this->msg('userlogin-resetpassword-link')->escaped()), 'weight' => 230]; } // Don't show a "create account" link if the user can't. if ($this->showCreateAccountLink()) { // link to the other action $linkTitle = $this->getTitleFor($this->isSignup() ? 'Userlogin' : 'CreateAccount'); $linkq = $this->getReturnToQueryStringFragment(); // Pass any language selection on to the mode switch link if ($wgLoginLanguageSelector && $this->mLanguage) { $linkq .= '&uselang=' . $this->mLanguage; } $loggedIn = $this->getUser()->isLoggedIn(); $fieldDefinitions['createOrLogin'] = ['type' => 'info', 'raw' => true, 'linkQuery' => $linkq, 'default' => function ($params) use($loggedIn, $linkTitle) { return Html::rawElement('div', ['id' => 'mw-createaccount' . (!$loggedIn ? '-cta' : ''), 'class' => $loggedIn ? 'mw-form-related-link-container' : 'mw-ui-vform-field'], ($loggedIn ? '' : $this->msg('userlogin-noaccount')->escaped()) . Html::element('a', ['id' => 'mw-createaccount-join' . ($loggedIn ? '-loggedin' : ''), 'href' => $linkTitle->getLocalURL($params['linkQuery']), 'class' => $loggedIn ? '' : 'mw-ui-button', 'tabindex' => 100], $this->msg($loggedIn ? 'userlogin-createanother' : 'userlogin-joinproject')->escaped())); }, 'weight' => 235]; } } $fieldDefinitions = $this->getBCFieldDefinitions($fieldDefinitions, $template); $fieldDefinitions = array_filter($fieldDefinitions); return $fieldDefinitions; }
/** * @param string $action One of the AuthManager::ACTION_* constants * @param AuthenticationRequest[] $requests * @return AuthenticationResponse * @throws LogicException if $action is invalid */ protected function performAuthenticationStep($action, array $requests) { if (!in_array($action, static::$allowedActions, true)) { throw new InvalidArgumentException('invalid action: ' . $action); } $authManager = AuthManager::singleton(); $returnToUrl = $this->getPageTitle('return')->getFullURL($this->getPreservedParams(true), false, PROTO_HTTPS); switch ($action) { case AuthManager::ACTION_LOGIN: return $authManager->beginAuthentication($requests, $returnToUrl); case AuthManager::ACTION_LOGIN_CONTINUE: return $authManager->continueAuthentication($requests); case AuthManager::ACTION_CREATE: return $authManager->beginAccountCreation($this->getUser(), $requests, $returnToUrl); case AuthManager::ACTION_CREATE_CONTINUE: return $authManager->continueAccountCreation($requests); case AuthManager::ACTION_LINK: return $authManager->beginAccountLink($this->getUser(), $requests, $returnToUrl); case AuthManager::ACTION_LINK_CONTINUE: return $authManager->continueAccountLink($requests); case AuthManager::ACTION_CHANGE: case AuthManager::ACTION_REMOVE: case AuthManager::ACTION_UNLINK: if (count($requests) > 1) { throw new InvalidArgumentException('only one auth request can be changed at a time'); } elseif (!$requests) { throw new InvalidArgumentException('no auth request'); } $req = reset($requests); $status = $authManager->allowsAuthenticationDataChange($req); Hooks::run('ChangeAuthenticationDataAudit', [$req, $status]); if (!$status->isOK()) { return AuthenticationResponse::newFail($status->getMessage()); } $authManager->changeAuthenticationData($req); return AuthenticationResponse::newPass(); default: // should never reach here but makes static code analyzers happy throw new InvalidArgumentException('invalid action: ' . $action); } }
public function canCreateAccounts() { return AuthManager::singleton()->canCreateAccounts(); }
/** * @dataProvider provideAccountLink * @param StatusValue $preTest * @param array $primaryResponses * @param array $managerResponses */ public function testAccountLink(StatusValue $preTest, array $primaryResponses, array $managerResponses) { $user = \User::newFromName('UTSysop'); $this->initializeManager(); // Set up lots of mocks... $req = $this->getMockForAbstractClass(AuthenticationRequest::class); $req->primary = $primaryResponses; $mocks = []; foreach (['pre', 'primary'] as $key) { $class = ucfirst($key) . 'AuthenticationProvider'; $mocks[$key] = $this->getMockForAbstractClass("MediaWiki\\Auth\\{$class}", [], "Mock{$class}"); $mocks[$key]->expects($this->any())->method('getUniqueId')->will($this->returnValue($key)); for ($i = 2; $i <= 3; $i++) { $mocks[$key . $i] = $this->getMockForAbstractClass("MediaWiki\\Auth\\{$class}", [], "Mock{$class}"); $mocks[$key . $i]->expects($this->any())->method('getUniqueId')->will($this->returnValue($key . $i)); } } $mocks['pre']->expects($this->any())->method('testForAccountLink')->will($this->returnCallback(function ($u) use($user, $preTest) { $this->assertSame($user->getId(), $u->getId()); $this->assertSame($user->getName(), $u->getName()); return $preTest; })); $mocks['pre2']->expects($this->atMost(1))->method('testForAccountLink')->will($this->returnValue(StatusValue::newGood())); $mocks['primary']->expects($this->any())->method('accountCreationType')->will($this->returnValue(PrimaryAuthenticationProvider::TYPE_LINK)); $ct = count($req->primary); $callback = $this->returnCallback(function ($u, $reqs) use($user, $req) { $this->assertSame($user->getId(), $u->getId()); $this->assertSame($user->getName(), $u->getName()); $foundReq = false; foreach ($reqs as $r) { $this->assertSame($user->getName(), $r->username); $foundReq = $foundReq || get_class($r) === get_class($req); } $this->assertTrue($foundReq, '$reqs contains $req'); return array_shift($req->primary); }); $mocks['primary']->expects($this->exactly(min(1, $ct)))->method('beginPrimaryAccountLink')->will($callback); $mocks['primary']->expects($this->exactly(max(0, $ct - 1)))->method('continuePrimaryAccountLink')->will($callback); $abstain = AuthenticationResponse::newAbstain(); $mocks['primary2']->expects($this->any())->method('accountCreationType')->will($this->returnValue(PrimaryAuthenticationProvider::TYPE_LINK)); $mocks['primary2']->expects($this->atMost(1))->method('beginPrimaryAccountLink')->will($this->returnValue($abstain)); $mocks['primary2']->expects($this->never())->method('continuePrimaryAccountLink'); $mocks['primary3']->expects($this->any())->method('accountCreationType')->will($this->returnValue(PrimaryAuthenticationProvider::TYPE_CREATE)); $mocks['primary3']->expects($this->never())->method('beginPrimaryAccountLink'); $mocks['primary3']->expects($this->never())->method('continuePrimaryAccountLink'); $this->preauthMocks = [$mocks['pre'], $mocks['pre2']]; $this->primaryauthMocks = [$mocks['primary3'], $mocks['primary2'], $mocks['primary']]; $this->logger = new \TestLogger(true, function ($message, $level) { return $level === LogLevel::DEBUG ? null : $message; }); $this->initializeManager(true); $constraint = \PHPUnit_Framework_Assert::logicalOr($this->equalTo(AuthenticationResponse::PASS), $this->equalTo(AuthenticationResponse::FAIL)); $providers = array_merge($this->preauthMocks, $this->primaryauthMocks); foreach ($providers as $p) { $p->postCalled = false; $p->expects($this->atMost(1))->method('postAccountLink')->willReturnCallback(function ($user, $response) use($constraint, $p) { $this->assertInstanceOf('User', $user); $this->assertSame('UTSysop', $user->getName()); $this->assertInstanceOf(AuthenticationResponse::class, $response); $this->assertThat($response->status, $constraint); $p->postCalled = $response->status; }); } $first = true; $created = false; $expectLog = []; foreach ($managerResponses as $i => $response) { if ($response instanceof AuthenticationResponse && $response->status === AuthenticationResponse::PASS) { $expectLog[] = [LogLevel::INFO, 'Account linked to {user} by primary']; } $ex = null; try { if ($first) { $ret = $this->manager->beginAccountLink($user, [$req], 'http://localhost/'); } else { $ret = $this->manager->continueAccountLink([$req]); } if ($response instanceof \Exception) { $this->fail('Expected exception not thrown', "Response {$i}"); } } catch (\Exception $ex) { if (!$response instanceof \Exception) { throw $ex; } $this->assertEquals($response->getMessage(), $ex->getMessage(), "Response {$i}, exception"); $this->assertNull($this->request->getSession()->getSecret('AuthManager::accountLinkState'), "Response {$i}, exception, session state"); return; } $this->assertSame('http://localhost/', $req->returnToUrl); $ret->message = $this->message($ret->message); $this->assertEquals($response, $ret, "Response {$i}, response"); if ($response->status === AuthenticationResponse::PASS || $response->status === AuthenticationResponse::FAIL) { $this->assertNull($this->request->getSession()->getSecret('AuthManager::accountLinkState'), "Response {$i}, session state"); foreach ($providers as $p) { $this->assertSame($response->status, $p->postCalled, "Response {$i}, post-auth callback called"); } } else { $this->assertNotNull($this->request->getSession()->getSecret('AuthManager::accountLinkState'), "Response {$i}, session state"); $this->assertEquals($ret->neededRequests, $this->manager->getAuthenticationRequests(AuthManager::ACTION_LINK_CONTINUE), "Response {$i}, continuation check"); foreach ($providers as $p) { $this->assertFalse($p->postCalled, "Response {$i}, post-auth callback not called"); } } $first = false; } $this->assertSame($expectLog, $this->logger->getBuffer()); }
/** * build array of urls for personal toolbar * @return array */ protected function buildPersonalUrls() { $title = $this->getTitle(); $request = $this->getRequest(); $pageurl = $title->getLocalURL(); $authManager = AuthManager::singleton(); /* set up the default links for the personal toolbar */ $personal_urls = []; # Due to bug 32276, if a user does not have read permissions, # $this->getTitle() will just give Special:Badtitle, which is # not especially useful as a returnto parameter. Use the title # from the request instead, if there was one. if ($this->getUser()->isAllowed('read')) { $page = $this->getTitle(); } else { $page = Title::newFromText($request->getVal('title', '')); } $page = $request->getVal('returnto', $page); $a = []; if (strval($page) !== '') { $a['returnto'] = $page; $query = $request->getVal('returntoquery', $this->thisquery); if ($query != '') { $a['returntoquery'] = $query; } } $returnto = wfArrayToCgi($a); if ($this->loggedin) { $personal_urls['userpage'] = ['text' => $this->username, 'href' => &$this->userpageUrlDetails['href'], 'class' => $this->userpageUrlDetails['exists'] ? false : 'new', 'active' => $this->userpageUrlDetails['href'] == $pageurl, 'dir' => 'auto']; $usertalkUrlDetails = $this->makeTalkUrlDetails($this->userpage); $personal_urls['mytalk'] = ['text' => $this->msg('mytalk')->text(), 'href' => &$usertalkUrlDetails['href'], 'class' => $usertalkUrlDetails['exists'] ? false : 'new', 'active' => $usertalkUrlDetails['href'] == $pageurl]; $href = self::makeSpecialUrl('Preferences'); $personal_urls['preferences'] = ['text' => $this->msg('mypreferences')->text(), 'href' => $href, 'active' => $href == $pageurl]; if ($this->getUser()->isAllowed('viewmywatchlist')) { $href = self::makeSpecialUrl('Watchlist'); $personal_urls['watchlist'] = ['text' => $this->msg('mywatchlist')->text(), 'href' => $href, 'active' => $href == $pageurl]; } # We need to do an explicit check for Special:Contributions, as we # have to match both the title, and the target, which could come # from request values (Special:Contributions?target=Jimbo_Wales) # or be specified in "sub page" form # (Special:Contributions/Jimbo_Wales). The plot # thickens, because the Title object is altered for special pages, # so it doesn't contain the original alias-with-subpage. $origTitle = Title::newFromText($request->getText('title')); if ($origTitle instanceof Title && $origTitle->isSpecialPage()) { list($spName, $spPar) = SpecialPageFactory::resolveAlias($origTitle->getText()); $active = $spName == 'Contributions' && ($spPar && $spPar == $this->username || $request->getText('target') == $this->username); } else { $active = false; } $href = self::makeSpecialUrlSubpage('Contributions', $this->username); $personal_urls['mycontris'] = ['text' => $this->msg('mycontris')->text(), 'href' => $href, 'active' => $active]; // if we can't set the user, we can't unset it either if ($request->getSession()->canSetUser()) { $personal_urls['logout'] = ['text' => $this->msg('pt-userlogout')->text(), 'href' => self::makeSpecialUrl('Userlogout', $title->isSpecial('Preferences') ? 'noreturnto' : $returnto), 'active' => false]; } } else { $useCombinedLoginLink = $this->useCombinedLoginLink(); if (!$authManager->canCreateAccounts() || !$authManager->canAuthenticateNow()) { // don't show combined login/signup link if one of those is actually not available $useCombinedLoginLink = false; } $loginlink = $this->getUser()->isAllowed('createaccount') && $useCombinedLoginLink ? 'nav-login-createaccount' : 'pt-login'; $login_url = ['text' => $this->msg($loginlink)->text(), 'href' => self::makeSpecialUrl('Userlogin', $returnto), 'active' => $title->isSpecial('Userlogin') || $title->isSpecial('CreateAccount') && $useCombinedLoginLink]; $createaccount_url = ['text' => $this->msg('pt-createaccount')->text(), 'href' => self::makeSpecialUrl('CreateAccount', $returnto), 'active' => $title->isSpecial('CreateAccount')]; // No need to show Talk and Contributions to anons if they can't contribute! if (User::groupHasPermission('*', 'edit')) { // Because of caching, we can't link directly to the IP talk and // contributions pages. Instead we use the special page shortcuts // (which work correctly regardless of caching). This means we can't // determine whether these links are active or not, but since major // skins (MonoBook, Vector) don't use this information, it's not a // huge loss. $personal_urls['anontalk'] = ['text' => $this->msg('anontalk')->text(), 'href' => self::makeSpecialUrlSubpage('Mytalk', false), 'active' => false]; $personal_urls['anoncontribs'] = ['text' => $this->msg('anoncontribs')->text(), 'href' => self::makeSpecialUrlSubpage('Mycontributions', false), 'active' => false]; } if ($authManager->canCreateAccounts() && $this->getUser()->isAllowed('createaccount') && !$useCombinedLoginLink) { $personal_urls['createaccount'] = $createaccount_url; } if ($authManager->canAuthenticateNow()) { $personal_urls['login'] = $login_url; } } Hooks::run('PersonalUrls', [&$personal_urls, &$title, $this]); return $personal_urls; }
/** * Generates a form from the given request. * @param AuthenticationRequest[] $requests * @param string $action AuthManager action name * @param string|Message $msg * @param string $msgType * @return HTMLForm */ protected function getAuthForm(array $requests, $action, $msg = '', $msgType = 'error') { global $wgSecureLogin, $wgLoginLanguageSelector; // FIXME merge this with parent if (isset($this->authForm)) { return $this->authForm; } $usingHTTPS = $this->getRequest()->getProtocol() === 'https'; // get basic form description from the auth logic $fieldInfo = AuthenticationRequest::mergeFieldInfo($requests); $fakeTemplate = $this->getFakeTemplate($msg, $msgType); $this->fakeTemplate = $fakeTemplate; // FIXME there should be a saner way to pass this to the hook // this will call onAuthChangeFormFields() $formDescriptor = static::fieldInfoToFormDescriptor($requests, $fieldInfo, $this->authAction); $this->postProcessFormDescriptor($formDescriptor); $context = $this->getContext(); if ($context->getRequest() !== $this->getRequest()) { // We have overridden the request, need to make sure the form uses that too. $context = new DerivativeContext($this->getContext()); $context->setRequest($this->getRequest()); } $form = HTMLForm::factory('vform', $formDescriptor, $context); $form->addHiddenField('authAction', $this->authAction); if ($wgLoginLanguageSelector) { $form->addHiddenField('uselang', $this->mLanguage); } $form->addHiddenField('force', $this->securityLevel); $form->addHiddenField($this->getTokenName(), $this->getToken()->toString()); if ($wgSecureLogin) { // If using HTTPS coming from HTTP, then the 'fromhttp' parameter must be preserved if (!$this->isSignup()) { $form->addHiddenField('wpForceHttps', (int) $this->mStickHTTPS); $form->addHiddenField('wpFromhttp', $usingHTTPS); } } // set properties of the form itself $form->setAction($this->getPageTitle()->getLocalURL($this->getReturnToQueryStringFragment())); $form->setName('userlogin' . ($this->isSignup() ? '2' : '')); if ($this->isSignup()) { $form->setId('userlogin2'); } // add pre/post text // header used by ConfirmEdit, CondfirmAccount, Persona, WikimediaIncubator, SemanticSignup // should be above the error message but HTMLForm doesn't support that $form->addHeaderText($fakeTemplate->html('header')); // FIXME the old form used this for error/warning messages which does not play well with // HTMLForm (maybe it could with a subclass?); for now only display it for signups // (where the JS username validation needs it) and alway empty if ($this->isSignup()) { // used by the mediawiki.special.userlogin.signup.js module $statusAreaAttribs = ['id' => 'mw-createacct-status-area']; // $statusAreaAttribs += $msg ? [ 'class' => "{$msgType}box" ] : [ 'style' => 'display: none;' ]; $form->addHeaderText(Html::element('div', $statusAreaAttribs)); } // header used by MobileFrontend $form->addHeaderText($fakeTemplate->html('formheader')); // blank signup footer for site customization if ($this->isSignup() && $this->showExtraInformation()) { // Use signupend-https for HTTPS requests if it's not blank, signupend otherwise $signupendMsg = $this->msg('signupend'); $signupendHttpsMsg = $this->msg('signupend-https'); if (!$signupendMsg->isDisabled()) { $signupendText = $usingHTTPS && !$signupendHttpsMsg->isBlank() ? $signupendHttpsMsg->parse() : $signupendMsg->parse(); $form->addPostText(Html::rawElement('div', ['id' => 'signupend'], $signupendText)); } } // warning header for non-standard workflows (e.g. security reauthentication) if (!$this->isSignup() && $this->getUser()->isLoggedIn()) { $reauthMessage = $this->securityLevel ? 'userlogin-reauth' : 'userlogin-loggedin'; $form->addHeaderText(Html::rawElement('div', ['class' => 'warningbox'], $this->msg($reauthMessage)->params($this->getUser()->getName())->parse())); } if (!$this->isSignup() && $this->showExtraInformation()) { $passwordReset = new PasswordReset($this->getConfig(), AuthManager::singleton()); if ($passwordReset->isAllowed($this->getUser())) { $form->addFooterText(Html::rawElement('div', ['class' => 'mw-ui-vform-field mw-form-related-link-container'], Linker::link(SpecialPage::getTitleFor('PasswordReset'), $this->msg('userlogin-resetpassword-link')->escaped()))); } // Don't show a "create account" link if the user can't. if ($this->showCreateAccountLink()) { // link to the other action $linkTitle = $this->getTitleFor($this->isSignup() ? 'Userlogin' : 'CreateAccount'); $linkq = $this->getReturnToQueryStringFragment(); // Pass any language selection on to the mode switch link if ($wgLoginLanguageSelector && $this->mLanguage) { $linkq .= '&uselang=' . $this->mLanguage; } $createOrLoginHref = $linkTitle->getLocalURL($linkq); if ($this->getUser()->isLoggedIn()) { $createOrLoginHtml = Html::rawElement('div', ['class' => 'mw-ui-vform-field'], Html::element('a', ['id' => 'mw-createaccount-join', 'href' => $createOrLoginHref, 'tabindex' => 100], $this->msg('userlogin-createanother')->escaped())); } else { $createOrLoginHtml = Html::rawElement('div', ['id' => 'mw-createaccount-cta', 'class' => 'mw-ui-vform-field'], $this->msg('userlogin-noaccount')->escaped() . Html::element('a', ['id' => 'mw-createaccount-join', 'href' => $createOrLoginHref, 'class' => 'mw-ui-button', 'tabindex' => 100], $this->msg('userlogin-joinproject')->escaped())); } $form->addFooterText($createOrLoginHtml); } } $form->suppressDefaultSubmit(); $this->authForm = $form; return $form; }
/** * Check to see if the given clear-text password is one of the accepted passwords * @deprecated since 1.27, use AuthManager instead * @param string $password User password * @return bool True if the given password is correct, otherwise False */ public function checkPassword($password) { $manager = AuthManager::singleton(); $reqs = AuthenticationRequest::loadRequestsFromSubmission($manager->getAuthenticationRequests(AuthManager::ACTION_LOGIN), ['username' => $this->getName(), 'password' => $password]); $res = AuthManager::singleton()->beginAuthentication($reqs, 'null:'); switch ($res->status) { case AuthenticationResponse::PASS: return true; case AuthenticationResponse::FAIL: // Hope it's not a PreAuthenticationProvider that failed... \MediaWiki\Logger\LoggerFactory::getInstance('authentication')->info(__METHOD__ . ': Authentication failed: ' . $res->message->plain()); return false; default: throw new BadMethodCallException('AuthManager returned a response unsupported by ' . __METHOD__); } }