public function testForAuthentication(array $reqs)
 {
     $req = AuthenticationRequest::getRequestByClass($reqs, PasswordAuthenticationRequest::class);
     if ($req) {
         $user = User::newFromName($req->username);
         $password = $req->password;
     } else {
         $user = null;
         foreach ($reqs as $req) {
             if ($req->username !== null) {
                 $user = User::newFromName($req->username);
                 break;
             }
         }
         if (!$user) {
             $this->logger->debug(__METHOD__ . ': No username in $reqs, skipping hooks');
             return StatusValue::newGood();
         }
         // Something random for the 'AbortLogin' hook.
         $password = wfRandomString(32);
     }
     $msg = null;
     if (!\Hooks::run('LoginUserMigrated', [$user, &$msg])) {
         return $this->makeFailResponse($user, null, LoginForm::USER_MIGRATED, $msg, 'LoginUserMigrated');
     }
     $abort = LoginForm::ABORTED;
     $msg = null;
     if (!\Hooks::run('AbortLogin', [$user, $password, &$abort, &$msg])) {
         return $this->makeFailResponse($user, null, $abort, $msg, 'AbortLogin');
     }
     return StatusValue::newGood();
 }
 public function beginPrimaryAccountCreation($user, $creator, array $reqs)
 {
     /** @var TemporaryPasswordAuthenticationRequest $req */
     $req = AuthenticationRequest::getRequestByClass($reqs, TemporaryPasswordAuthenticationRequest::class);
     if ($req) {
         if ($req->username !== null && $req->password !== null) {
             // Nothing we can do yet, because the user isn't in the DB yet
             if ($req->username !== $user->getName()) {
                 $req = clone $req;
                 $req->username = $user->getName();
             }
             if ($req->mailpassword) {
                 // prevent EmailNotificationSecondaryAuthenticationProvider from sending another mail
                 $this->manager->setAuthenticationSessionData('no-email', true);
             }
             $ret = AuthenticationResponse::newPass($req->username);
             $ret->createRequest = $req;
             return $ret;
         }
     }
     return AuthenticationResponse::newAbstain();
 }
 public function handleFormSubmit($data)
 {
     // remove requests do not accept user input
     $requests = $this->authRequests;
     if (static::$loadUserData) {
         $requests = AuthenticationRequest::loadRequestsFromSubmission($this->authRequests, $data);
     }
     $response = $this->performAuthenticationStep($this->authAction, $requests);
     // we can't handle FAIL or similar as failure here since it might require changing the form
     return Status::newGood($response);
 }
 public function beginPrimaryAccountCreation($user, $creator, array $reqs)
 {
     if ($this->accountCreationType() === self::TYPE_NONE) {
         throw new \BadMethodCallException('Shouldn\'t call this when accountCreationType() is NONE');
     }
     $req = AuthenticationRequest::getRequestByClass($reqs, PasswordAuthenticationRequest::class);
     if ($req) {
         if ($req->username !== null && $req->password !== null) {
             // Nothing we can do besides claim it, because the user isn't in
             // the DB yet
             if ($req->username !== $user->getName()) {
                 $req = clone $req;
                 $req->username = $user->getName();
             }
             $ret = AuthenticationResponse::newPass($req->username);
             $ret->createRequest = $req;
             return $ret;
         }
     }
     return AuthenticationResponse::newAbstain();
 }
 /**
  * Checks whether the given AuthenticationRequest has its own submit button.
  * @param AuthenticationRequest $req
  * @return bool
  */
 protected function hasOwnSubmitButton(AuthenticationRequest $req)
 {
     foreach ($req->getFieldInfo() as $field => $info) {
         if ($info['type'] === 'button') {
             return true;
         }
     }
     return false;
 }
Exemple #6
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]);
 }
 public function beginPrimaryAccountCreation($user, $creator, array $reqs)
 {
     if ($this->accountCreationType() === self::TYPE_NONE) {
         throw new \BadMethodCallException('Shouldn\'t call this when accountCreationType() is NONE');
     }
     $req = AuthenticationRequest::getRequestByClass($reqs, $this->requestType);
     if (!$req || $req->username === null || $req->password === null || $this->hasDomain && $req->domain === null) {
         return AuthenticationResponse::newAbstain();
     }
     $username = User::getCanonicalName($req->username, 'usable');
     if ($username === false) {
         return AuthenticationResponse::newAbstain();
     }
     $this->setDomain($req);
     if ($this->auth->addUser($user, $req->password, $user->getEmail(), $user->getRealName())) {
         return AuthenticationResponse::newPass();
     } else {
         return AuthenticationResponse::newFail(new \Message('authmanager-authplugin-create-fail'));
     }
 }
 /**
  * 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;
 }
 /**
  * Format an array of AuthenticationRequests for return
  * @param AuthenticationRequest[] $reqs
  * @return array Will have a 'requests' key, and also 'fields' if $module's
  *  params include 'mergerequestfields'.
  */
 public function formatRequests(array $reqs)
 {
     $params = $this->module->extractRequestParams();
     $mergeFields = !empty($params['mergerequestfields']);
     $ret = ['requests' => []];
     foreach ($reqs as $req) {
         $describe = $req->describeCredentials();
         $reqInfo = ['id' => $req->getUniqueId(), 'metadata' => $req->getMetadata()];
         switch ($req->required) {
             case AuthenticationRequest::OPTIONAL:
                 $reqInfo['required'] = 'optional';
                 break;
             case AuthenticationRequest::REQUIRED:
                 $reqInfo['required'] = 'required';
                 break;
             case AuthenticationRequest::PRIMARY_REQUIRED:
                 $reqInfo['required'] = 'primary-required';
                 break;
         }
         $this->formatMessage($reqInfo, 'provider', $describe['provider']);
         $this->formatMessage($reqInfo, 'account', $describe['account']);
         if (!$mergeFields) {
             $reqInfo['fields'] = $this->formatFields((array) $req->getFieldInfo());
         }
         $ret['requests'][] = $reqInfo;
     }
     if ($mergeFields) {
         $fields = AuthenticationRequest::mergeFieldInfo($reqs);
         $ret['fields'] = $this->formatFields($fields);
     }
     return $ret;
 }
Exemple #10
0
 /**
  * Continue an account creation flow
  * @param AuthenticationRequest[] $reqs
  * @return AuthenticationResponse
  */
 public function continueAccountCreation(array $reqs)
 {
     $session = $this->request->getSession();
     try {
         if (!$this->canCreateAccounts()) {
             // Caller should have called canCreateAccounts()
             $session->remove('AuthManager::accountCreationState');
             throw new \LogicException('Account creation is not possible');
         }
         $state = $session->getSecret('AuthManager::accountCreationState');
         if (!is_array($state)) {
             return AuthenticationResponse::newFail(wfMessage('authmanager-create-not-in-progress'));
         }
         $state['continueRequests'] = [];
         // Step 0: Prepare and validate the input
         $user = User::newFromName($state['username'], 'creatable');
         if (!is_object($user)) {
             $session->remove('AuthManager::accountCreationState');
             $this->logger->debug(__METHOD__ . ': Invalid username', ['user' => $state['username']]);
             return AuthenticationResponse::newFail(wfMessage('noname'));
         }
         if ($state['creatorid']) {
             $creator = User::newFromId($state['creatorid']);
         } else {
             $creator = new User();
             $creator->setName($state['creatorname']);
         }
         // Avoid account creation races on double submissions
         $cache = \ObjectCache::getLocalClusterInstance();
         $lock = $cache->getScopedLock($cache->makeGlobalKey('account', md5($user->getName())));
         if (!$lock) {
             // Don't clear AuthManager::accountCreationState for this code
             // path because the process that won the race owns it.
             $this->logger->debug(__METHOD__ . ': Could not acquire account creation lock', ['user' => $user->getName(), 'creator' => $creator->getName()]);
             return AuthenticationResponse::newFail(wfMessage('usernameinprogress'));
         }
         // Permissions check
         $status = $this->checkAccountCreatePermissions($creator);
         if (!$status->isGood()) {
             $this->logger->debug(__METHOD__ . ': {creator} cannot create users: {reason}', ['user' => $user->getName(), 'creator' => $creator->getName(), 'reason' => $status->getWikiText(null, null, 'en')]);
             $ret = AuthenticationResponse::newFail($status->getMessage());
             $this->callMethodOnProviders(7, 'postAccountCreation', [$user, $creator, $ret]);
             $session->remove('AuthManager::accountCreationState');
             return $ret;
         }
         // Load from master for existence check
         $user->load(User::READ_LOCKING);
         if ($state['userid'] === 0) {
             if ($user->getId() != 0) {
                 $this->logger->debug(__METHOD__ . ': User exists locally', ['user' => $user->getName(), 'creator' => $creator->getName()]);
                 $ret = AuthenticationResponse::newFail(wfMessage('userexists'));
                 $this->callMethodOnProviders(7, 'postAccountCreation', [$user, $creator, $ret]);
                 $session->remove('AuthManager::accountCreationState');
                 return $ret;
             }
         } else {
             if ($user->getId() == 0) {
                 $this->logger->debug(__METHOD__ . ': User does not exist locally when it should', ['user' => $user->getName(), 'creator' => $creator->getName(), 'expected_id' => $state['userid']]);
                 throw new \UnexpectedValueException("User \"{$state['username']}\" should exist now, but doesn't!");
             }
             if ($user->getId() != $state['userid']) {
                 $this->logger->debug(__METHOD__ . ': User ID/name mismatch', ['user' => $user->getName(), 'creator' => $creator->getName(), 'expected_id' => $state['userid'], 'actual_id' => $user->getId()]);
                 throw new \UnexpectedValueException("User \"{$state['username']}\" exists, but " . "ID {$user->getId()} != {$state['userid']}!");
             }
         }
         foreach ($state['reqs'] as $req) {
             if ($req instanceof UserDataAuthenticationRequest) {
                 $status = $req->populateUser($user);
                 if (!$status->isGood()) {
                     // This should never happen...
                     $status = Status::wrap($status);
                     $this->logger->debug(__METHOD__ . ': UserData is invalid: {reason}', ['user' => $user->getName(), 'creator' => $creator->getName(), 'reason' => $status->getWikiText(null, null, 'en')]);
                     $ret = AuthenticationResponse::newFail($status->getMessage());
                     $this->callMethodOnProviders(7, 'postAccountCreation', [$user, $creator, $ret]);
                     $session->remove('AuthManager::accountCreationState');
                     return $ret;
                 }
             }
         }
         foreach ($reqs as $req) {
             $req->returnToUrl = $state['returnToUrl'];
             $req->username = $state['username'];
         }
         // If we're coming in from a create-from-login UI response, we need
         // to extract the createRequest (if any).
         $req = AuthenticationRequest::getRequestByClass($reqs, CreateFromLoginAuthenticationRequest::class);
         if ($req && $req->createRequest) {
             $reqs[] = $req->createRequest;
         }
         // Run pre-creation tests, if we haven't already
         if (!$state['ranPreTests']) {
             $providers = $this->getPreAuthenticationProviders() + $this->getPrimaryAuthenticationProviders() + $this->getSecondaryAuthenticationProviders();
             foreach ($providers as $id => $provider) {
                 $status = $provider->testForAccountCreation($user, $creator, $reqs);
                 if (!$status->isGood()) {
                     $this->logger->debug(__METHOD__ . ": Fail in pre-authentication by {$id}", ['user' => $user->getName(), 'creator' => $creator->getName()]);
                     $ret = AuthenticationResponse::newFail(Status::wrap($status)->getMessage());
                     $this->callMethodOnProviders(7, 'postAccountCreation', [$user, $creator, $ret]);
                     $session->remove('AuthManager::accountCreationState');
                     return $ret;
                 }
             }
             $state['ranPreTests'] = true;
         }
         // Step 1: Choose a primary authentication provider and call it until it succeeds.
         if ($state['primary'] === null) {
             // We haven't picked a PrimaryAuthenticationProvider yet
             foreach ($this->getPrimaryAuthenticationProviders() as $id => $provider) {
                 if ($provider->accountCreationType() === PrimaryAuthenticationProvider::TYPE_NONE) {
                     continue;
                 }
                 $res = $provider->beginPrimaryAccountCreation($user, $creator, $reqs);
                 switch ($res->status) {
                     case AuthenticationResponse::PASS:
                         $this->logger->debug(__METHOD__ . ": Primary creation passed by {$id}", ['user' => $user->getName(), 'creator' => $creator->getName()]);
                         $state['primary'] = $id;
                         $state['primaryResponse'] = $res;
                         break 2;
                     case AuthenticationResponse::FAIL:
                         $this->logger->debug(__METHOD__ . ": Primary creation failed by {$id}", ['user' => $user->getName(), 'creator' => $creator->getName()]);
                         $this->callMethodOnProviders(7, 'postAccountCreation', [$user, $creator, $res]);
                         $session->remove('AuthManager::accountCreationState');
                         return $res;
                     case AuthenticationResponse::ABSTAIN:
                         // Continue loop
                         break;
                     case AuthenticationResponse::REDIRECT:
                     case AuthenticationResponse::UI:
                         $this->logger->debug(__METHOD__ . ": Primary creation {$res->status} by {$id}", ['user' => $user->getName(), 'creator' => $creator->getName()]);
                         $state['primary'] = $id;
                         $state['continueRequests'] = $res->neededRequests;
                         $session->setSecret('AuthManager::accountCreationState', $state);
                         return $res;
                         // @codeCoverageIgnoreStart
                     // @codeCoverageIgnoreStart
                     default:
                         throw new \DomainException(get_class($provider) . "::beginPrimaryAccountCreation() returned {$res->status}");
                         // @codeCoverageIgnoreEnd
                 }
             }
             if ($state['primary'] === null) {
                 $this->logger->debug(__METHOD__ . ': Primary creation failed because no provider accepted', ['user' => $user->getName(), 'creator' => $creator->getName()]);
                 $ret = AuthenticationResponse::newFail(wfMessage('authmanager-create-no-primary'));
                 $this->callMethodOnProviders(7, 'postAccountCreation', [$user, $creator, $ret]);
                 $session->remove('AuthManager::accountCreationState');
                 return $ret;
             }
         } elseif ($state['primaryResponse'] === null) {
             $provider = $this->getAuthenticationProvider($state['primary']);
             if (!$provider instanceof PrimaryAuthenticationProvider) {
                 // Configuration changed? Force them to start over.
                 // @codeCoverageIgnoreStart
                 $ret = AuthenticationResponse::newFail(wfMessage('authmanager-create-not-in-progress'));
                 $this->callMethodOnProviders(7, 'postAccountCreation', [$user, $creator, $ret]);
                 $session->remove('AuthManager::accountCreationState');
                 return $ret;
                 // @codeCoverageIgnoreEnd
             }
             $id = $provider->getUniqueId();
             $res = $provider->continuePrimaryAccountCreation($user, $creator, $reqs);
             switch ($res->status) {
                 case AuthenticationResponse::PASS:
                     $this->logger->debug(__METHOD__ . ": Primary creation passed by {$id}", ['user' => $user->getName(), 'creator' => $creator->getName()]);
                     $state['primaryResponse'] = $res;
                     break;
                 case AuthenticationResponse::FAIL:
                     $this->logger->debug(__METHOD__ . ": Primary creation failed by {$id}", ['user' => $user->getName(), 'creator' => $creator->getName()]);
                     $this->callMethodOnProviders(7, 'postAccountCreation', [$user, $creator, $res]);
                     $session->remove('AuthManager::accountCreationState');
                     return $res;
                 case AuthenticationResponse::REDIRECT:
                 case AuthenticationResponse::UI:
                     $this->logger->debug(__METHOD__ . ": Primary creation {$res->status} by {$id}", ['user' => $user->getName(), 'creator' => $creator->getName()]);
                     $state['continueRequests'] = $res->neededRequests;
                     $session->setSecret('AuthManager::accountCreationState', $state);
                     return $res;
                 default:
                     throw new \DomainException(get_class($provider) . "::continuePrimaryAccountCreation() returned {$res->status}");
             }
         }
         // Step 2: Primary authentication succeeded, create the User object
         // and add the user locally.
         if ($state['userid'] === 0) {
             $this->logger->info('Creating user {user} during account creation', ['user' => $user->getName(), 'creator' => $creator->getName()]);
             $status = $user->addToDatabase();
             if (!$status->isOk()) {
                 // @codeCoverageIgnoreStart
                 $ret = AuthenticationResponse::newFail($status->getMessage());
                 $this->callMethodOnProviders(7, 'postAccountCreation', [$user, $creator, $ret]);
                 $session->remove('AuthManager::accountCreationState');
                 return $ret;
                 // @codeCoverageIgnoreEnd
             }
             $this->setDefaultUserOptions($user, $creator->isAnon());
             \Hooks::run('LocalUserCreated', [$user, false]);
             $user->saveSettings();
             $state['userid'] = $user->getId();
             // Update user count
             \DeferredUpdates::addUpdate(new \SiteStatsUpdate(0, 0, 0, 0, 1));
             // Watch user's userpage and talk page
             $user->addWatch($user->getUserPage(), User::IGNORE_USER_RIGHTS);
             // Inform the provider
             $logSubtype = $provider->finishAccountCreation($user, $creator, $state['primaryResponse']);
             // Log the creation
             if ($this->config->get('NewUserLog')) {
                 $isAnon = $creator->isAnon();
                 $logEntry = new \ManualLogEntry('newusers', $logSubtype ?: ($isAnon ? 'create' : 'create2'));
                 $logEntry->setPerformer($isAnon ? $user : $creator);
                 $logEntry->setTarget($user->getUserPage());
                 $req = AuthenticationRequest::getRequestByClass($state['reqs'], CreationReasonAuthenticationRequest::class);
                 $logEntry->setComment($req ? $req->reason : '');
                 $logEntry->setParameters(['4::userid' => $user->getId()]);
                 $logid = $logEntry->insert();
                 $logEntry->publish($logid);
             }
         }
         // Step 3: Iterate over all the secondary authentication providers.
         $beginReqs = $state['reqs'];
         foreach ($this->getSecondaryAuthenticationProviders() as $id => $provider) {
             if (!isset($state['secondary'][$id])) {
                 // This provider isn't started yet, so we pass it the set
                 // of reqs from beginAuthentication instead of whatever
                 // might have been used by a previous provider in line.
                 $func = 'beginSecondaryAccountCreation';
                 $res = $provider->beginSecondaryAccountCreation($user, $creator, $beginReqs);
             } elseif (!$state['secondary'][$id]) {
                 $func = 'continueSecondaryAccountCreation';
                 $res = $provider->continueSecondaryAccountCreation($user, $creator, $reqs);
             } else {
                 continue;
             }
             switch ($res->status) {
                 case AuthenticationResponse::PASS:
                     $this->logger->debug(__METHOD__ . ": Secondary creation passed by {$id}", ['user' => $user->getName(), 'creator' => $creator->getName()]);
                     // fall through
                 // fall through
                 case AuthenticationResponse::ABSTAIN:
                     $state['secondary'][$id] = true;
                     break;
                 case AuthenticationResponse::REDIRECT:
                 case AuthenticationResponse::UI:
                     $this->logger->debug(__METHOD__ . ": Secondary creation {$res->status} by {$id}", ['user' => $user->getName(), 'creator' => $creator->getName()]);
                     $state['secondary'][$id] = false;
                     $state['continueRequests'] = $res->neededRequests;
                     $session->setSecret('AuthManager::accountCreationState', $state);
                     return $res;
                 case AuthenticationResponse::FAIL:
                     throw new \DomainException(get_class($provider) . "::{$func}() returned {$res->status}." . ' Secondary providers are not allowed to fail account creation, that' . ' should have been done via testForAccountCreation().');
                     // @codeCoverageIgnoreStart
                 // @codeCoverageIgnoreStart
                 default:
                     throw new \DomainException(get_class($provider) . "::{$func}() returned {$res->status}");
                     // @codeCoverageIgnoreEnd
             }
         }
         $id = $user->getId();
         $name = $user->getName();
         $req = new CreatedAccountAuthenticationRequest($id, $name);
         $ret = AuthenticationResponse::newPass($name);
         $ret->loginRequest = $req;
         $this->createdAccountAuthenticationRequests[] = $req;
         $this->logger->info(__METHOD__ . ': Account creation succeeded for {user}', ['user' => $user->getName(), 'creator' => $creator->getName()]);
         $this->callMethodOnProviders(7, 'postAccountCreation', [$user, $creator, $ret]);
         $session->remove('AuthManager::accountCreationState');
         $this->removeAuthenticationSessionData(null);
         return $ret;
     } catch (\Exception $ex) {
         $session->remove('AuthManager::accountCreationState');
         throw $ex;
     }
 }
 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');
     }
 }
 /**
  * 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, $requests);
     $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');
     }
     $form->suppressDefaultSubmit();
     $this->authForm = $form;
     return $form;
 }
Exemple #13
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__);
         }
     }
 }
Exemple #14
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 setPassword($user, $password)
 {
     $data = ['username' => $user->getName(), 'password' => $password];
     if ($this->domain !== null && $this->domain !== '') {
         $data['domain'] = $this->domain;
     }
     $reqs = AuthManager::singleton()->getAuthenticationRequests(AuthManager::ACTION_CHANGE);
     $reqs = AuthenticationRequest::loadRequestsFromSubmission($reqs, $data);
     foreach ($reqs as $req) {
         $status = AuthManager::singleton()->allowsAuthenticationDataChange($req);
         if (!$status->isGood()) {
             $this->logger->info(__METHOD__ . ': Password change rejected: {reason}', ['username' => $data['username'], 'reason' => $status->getWikiText(null, null, 'en')]);
             return false;
         }
     }
     foreach ($reqs as $req) {
         AuthManager::singleton()->changeAuthenticationData($req);
     }
     return true;
 }
 /**
  * Generates a HTMLForm descriptor array from a set of authentication requests.
  * @param AuthenticationRequest[] $requests
  * @param string $action AuthManager action name (one of the AuthManager::ACTION_* constants)
  * @return array
  */
 protected function getAuthFormDescriptor($requests, $action)
 {
     $fieldInfo = AuthenticationRequest::mergeFieldInfo($requests);
     $formDescriptor = $this->fieldInfoToFormDescriptor($requests, $fieldInfo, $action);
     $this->addTabIndex($formDescriptor);
     return $formDescriptor;
 }
 public function getUniqueId()
 {
     return parent::getUniqueId() . ':' . $this->name;
 }
 public function testForAuthentication(array $reqs)
 {
     if (!$this->passwordAttemptThrottle) {
         return \StatusValue::newGood();
     }
     $ip = $this->manager->getRequest()->getIP();
     try {
         $username = AuthenticationRequest::getUsernameFromRequests($reqs);
     } catch (\UnexpectedValueException $e) {
         $username = '';
     }
     // Get everything this username could normalize to, and throttle each one individually.
     // If nothing uses usernames, just throttle by IP.
     $usernames = $this->manager->normalizeUsername($username);
     $result = false;
     foreach ($usernames as $name) {
         $r = $this->passwordAttemptThrottle->increase($name, $ip, __METHOD__);
         if ($r && (!$result || $result['wait'] < $r['wait'])) {
             $result = $r;
         }
     }
     if ($result) {
         $message = wfMessage('login-throttled')->durationParams($result['wait']);
         return \StatusValue::newFatal($message);
     } else {
         $this->manager->setAuthenticationSessionData('LoginThrottle', ['users' => $usernames, 'ip' => $ip]);
         return \StatusValue::newGood();
     }
 }
 /**
  * Continue the link attempt
  * @param User $user
  * @param string $key Session key to look in
  * @param AuthenticationRequest[] $reqs
  * @return AuthenticationResponse
  */
 protected function continueLinkAttempt($user, $key, array $reqs)
 {
     $req = ButtonAuthenticationRequest::getRequestByName($reqs, 'linkOk');
     if ($req) {
         return AuthenticationResponse::newPass();
     }
     $req = AuthenticationRequest::getRequestByClass($reqs, ConfirmLinkAuthenticationRequest::class);
     if (!$req) {
         // WTF? Retry.
         return $this->beginLinkAttempt($user, $key);
     }
     $session = $this->manager->getRequest()->getSession();
     $state = $session->getSecret($key);
     if (!is_array($state)) {
         return AuthenticationResponse::newAbstain();
     }
     $maybeLink = [];
     foreach ($state['maybeLink'] as $linkReq) {
         $maybeLink[$linkReq->getUniqueId()] = $linkReq;
     }
     if (!$maybeLink) {
         return AuthenticationResponse::newAbstain();
     }
     $state['maybeLink'] = [];
     $session->setSecret($key, $state);
     $statuses = [];
     $anyFailed = false;
     foreach ($req->confirmedLinkIDs as $id) {
         if (isset($maybeLink[$id])) {
             $req = $maybeLink[$id];
             $req->username = $user->getName();
             if (!$req->action) {
                 // Make sure the action is set, but don't override it if
                 // the provider filled it in.
                 $req->action = AuthManager::ACTION_CHANGE;
             }
             $status = $this->manager->allowsAuthenticationDataChange($req);
             $statuses[] = [$req, $status];
             if ($status->isGood()) {
                 $this->manager->changeAuthenticationData($req);
             } else {
                 $anyFailed = true;
             }
         }
     }
     if (!$anyFailed) {
         return AuthenticationResponse::newPass();
     }
     $combinedStatus = \Status::newGood();
     foreach ($statuses as $data) {
         list($req, $status) = $data;
         $descriptionInfo = $req->describeCredentials();
         $description = wfMessage('authprovider-confirmlink-option', $descriptionInfo['provider']->text(), $descriptionInfo['account']->text())->text();
         if ($status->isGood()) {
             $combinedStatus->error(wfMessage('authprovider-confirmlink-success-line', $description));
         } else {
             $combinedStatus->error(wfMessage('authprovider-confirmlink-failure-line', $description, $status->getMessage()->text()));
         }
     }
     return AuthenticationResponse::newUI([new ButtonAuthenticationRequest('linkOk', wfMessage('ok'), wfMessage('authprovider-confirmlink-ok-help'))], $combinedStatus->getMessage('authprovider-confirmlink-failed'));
 }
Exemple #20
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__);
     }
 }