Esempio n. 1
0
 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);
     }
 }
Esempio n. 2
0
 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));
 }
Esempio n. 3
0
 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']);
 }
Esempio n. 6
0
 public function checkPermissions()
 {
     parent::checkPermissions();
     $user = $this->getUser();
     $status = AuthManager::singleton()->checkAccountCreatePermissions($user);
     if (!$status->isGood()) {
         throw new ErrorPageError('createacct-error', $status->getMessage());
     }
 }
Esempio n. 7
0
 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']);
 }
Esempio n. 9
0
 /**
  * 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]);
 }
Esempio n. 10
0
 /**
  * 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();
 }
Esempio n. 11
0
 /**
  * @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;
 }
Esempio n. 12
0
 /**
  * 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__);
         }
     }
 }
Esempio n. 13
0
 /**
  * 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);
 }
Esempio n. 14
0
 /**
  * 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);
 }
Esempio n. 15
0
 /**
  * @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];
             }
         }
     }
 }
Esempio n. 16
0
 /**
  * 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());
 }
Esempio n. 18
0
 /**
  * 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);
 }
Esempio n. 21
0
 /**
  * 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');
     }
 }
Esempio n. 23
0
 /**
  * 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);
     }
 }
Esempio n. 25
0
 public function canCreateAccounts()
 {
     return AuthManager::singleton()->canCreateAccounts();
 }
Esempio n. 26
0
 /**
  * @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());
 }
Esempio n. 27
0
 /**
  * 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;
 }
Esempio n. 29
0
 /**
  * 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__);
     }
 }