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));
 }
Exemple #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));
 }
 /**
  * Return the appropriate response for failure
  * @param PasswordAuthenticationRequest $req
  * @return AuthenticationResponse
  */
 protected function failResponse(PasswordAuthenticationRequest $req)
 {
     if ($this->authoritative) {
         return AuthenticationResponse::newFail(wfMessage($req->password === '' ? 'wrongpasswordempty' : 'wrongpassword'));
     } else {
         return AuthenticationResponse::newAbstain();
     }
 }
 public function beginSecondaryAuthentication($user, array $reqs)
 {
     if (!$this->blockDisablesLogin) {
         return AuthenticationResponse::newAbstain();
     } elseif ($user->isBlocked()) {
         return AuthenticationResponse::newFail(new \Message('login-userblocked', [$user->getName()]));
     } else {
         return AuthenticationResponse::newPass();
     }
 }
Exemple #5
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);
 }
 /**
  * @param string $action One of the AuthManager::ACTION_* constants
  * @param AuthenticationRequest[] $requests
  * @return AuthenticationResponse
  * @throws LogicException if $action is invalid
  */
 protected function performAuthenticationStep($action, array $requests)
 {
     if (!in_array($action, static::$allowedActions, true)) {
         throw new InvalidArgumentException('invalid action: ' . $action);
     }
     $authManager = AuthManager::singleton();
     $returnToUrl = $this->getPageTitle('return')->getFullURL($this->getPreservedParams(true), false, PROTO_HTTPS);
     switch ($action) {
         case AuthManager::ACTION_LOGIN:
             return $authManager->beginAuthentication($requests, $returnToUrl);
         case AuthManager::ACTION_LOGIN_CONTINUE:
             return $authManager->continueAuthentication($requests);
         case AuthManager::ACTION_CREATE:
             return $authManager->beginAccountCreation($this->getUser(), $requests, $returnToUrl);
         case AuthManager::ACTION_CREATE_CONTINUE:
             return $authManager->continueAccountCreation($requests);
         case AuthManager::ACTION_LINK:
             return $authManager->beginAccountLink($this->getUser(), $requests, $returnToUrl);
         case AuthManager::ACTION_LINK_CONTINUE:
             return $authManager->continueAccountLink($requests);
         case AuthManager::ACTION_CHANGE:
         case AuthManager::ACTION_REMOVE:
         case AuthManager::ACTION_UNLINK:
             if (count($requests) > 1) {
                 throw new InvalidArgumentException('only one auth request can be changed at a time');
             } elseif (!$requests) {
                 throw new InvalidArgumentException('no auth request');
             }
             $req = reset($requests);
             $status = $authManager->allowsAuthenticationDataChange($req);
             Hooks::run('ChangeAuthenticationDataAudit', [$req, $status]);
             if (!$status->isOK()) {
                 return AuthenticationResponse::newFail($status->getMessage());
             }
             $authManager->changeAuthenticationData($req);
             return AuthenticationResponse::newPass();
         default:
             // should never reach here but makes static code analyzers happy
             throw new InvalidArgumentException('invalid action: ' . $action);
     }
 }
 public function provideAccountLink()
 {
     $req = $this->getMockForAbstractClass(AuthenticationRequest::class);
     $good = StatusValue::newGood();
     return ['Pre-link test fail in pre' => [StatusValue::newFatal('fail-from-pre'), [], [AuthenticationResponse::newFail($this->message('fail-from-pre'))]], 'Failure in primary' => [$good, $tmp = [AuthenticationResponse::newFail($this->message('fail-from-primary'))], $tmp], 'All primary abstain' => [$good, [AuthenticationResponse::newAbstain()], [AuthenticationResponse::newFail($this->message('authmanager-link-no-primary'))]], 'Primary UI, then redirect, then fail' => [$good, $tmp = [AuthenticationResponse::newUI([$req], $this->message('...')), AuthenticationResponse::newRedirect([$req], '/foo.html', ['foo' => 'bar']), AuthenticationResponse::newFail($this->message('fail-in-primary-continue'))], $tmp], 'Primary redirect, then abstain' => [$good, [$tmp = AuthenticationResponse::newRedirect([$req], '/foo.html', ['foo' => 'bar']), AuthenticationResponse::newAbstain()], [$tmp, new \DomainException('MockPrimaryAuthenticationProvider::continuePrimaryAccountLink() returned ABSTAIN')]], 'Primary UI, then pass' => [$good, [$tmp1 = AuthenticationResponse::newUI([$req], $this->message('...')), AuthenticationResponse::newPass()], [$tmp1, AuthenticationResponse::newPass('')]], 'Primary pass' => [$good, [AuthenticationResponse::newPass('')], [AuthenticationResponse::newPass('')]]];
 }
 public function beginPrimaryAuthentication(array $reqs)
 {
     $req = AuthenticationRequest::getRequestByClass($reqs, PasswordAuthenticationRequest::class);
     if (!$req || $req->username === null || $req->password === null) {
         return AuthenticationResponse::newAbstain();
     }
     $username = User::getCanonicalName($req->username, 'usable');
     if ($username === false) {
         return AuthenticationResponse::newAbstain();
     }
     $dbr = wfGetDB(DB_REPLICA);
     $row = $dbr->selectRow('user', ['user_id', 'user_newpassword', 'user_newpass_time'], ['user_name' => $username], __METHOD__);
     if (!$row) {
         return AuthenticationResponse::newAbstain();
     }
     $status = $this->checkPasswordValidity($username, $req->password);
     if (!$status->isOK()) {
         // Fatal, can't log in
         return AuthenticationResponse::newFail($status->getMessage());
     }
     $pwhash = $this->getPassword($row->user_newpassword);
     if (!$pwhash->equals($req->password)) {
         return $this->failResponse($req);
     }
     if (!$this->isTimestampValid($row->user_newpass_time)) {
         return $this->failResponse($req);
     }
     $this->setPasswordResetFlag($username, $status);
     return AuthenticationResponse::newPass($username);
 }
 public function beginPrimaryAuthentication(array $reqs)
 {
     $req = AuthenticationRequest::getRequestByClass($reqs, PasswordAuthenticationRequest::class);
     if (!$req) {
         return AuthenticationResponse::newAbstain();
     }
     if ($req->username === null || $req->password === null) {
         return AuthenticationResponse::newAbstain();
     }
     $username = User::getCanonicalName($req->username, 'usable');
     if ($username === false) {
         return AuthenticationResponse::newAbstain();
     }
     $fields = ['user_id', 'user_password', 'user_password_expires'];
     $dbr = wfGetDB(DB_REPLICA);
     $row = $dbr->selectRow('user', $fields, ['user_name' => $username], __METHOD__);
     if (!$row) {
         return AuthenticationResponse::newAbstain();
     }
     $oldRow = clone $row;
     // Check for *really* old password hashes that don't even have a type
     // The old hash format was just an md5 hex hash, with no type information
     if (preg_match('/^[0-9a-f]{32}$/', $row->user_password)) {
         if ($this->config->get('PasswordSalt')) {
             $row->user_password = "******";
         } else {
             $row->user_password = "******";
         }
     }
     $status = $this->checkPasswordValidity($username, $req->password);
     if (!$status->isOK()) {
         // Fatal, can't log in
         return AuthenticationResponse::newFail($status->getMessage());
     }
     $pwhash = $this->getPassword($row->user_password);
     if (!$pwhash->equals($req->password)) {
         if ($this->config->get('LegacyEncoding')) {
             // 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', $req->password);
             if ($cp1252Password === $req->password || !$pwhash->equals($cp1252Password)) {
                 return $this->failResponse($req);
             }
         } else {
             return $this->failResponse($req);
         }
     }
     // @codeCoverageIgnoreStart
     if ($this->getPasswordFactory()->needsUpdate($pwhash)) {
         $pwhash = $this->getPasswordFactory()->newFromPlaintext($req->password);
         \DeferredUpdates::addCallableUpdate(function () use($pwhash, $oldRow) {
             $dbw = wfGetDB(DB_MASTER);
             $dbw->update('user', ['user_password' => $pwhash->toString()], ['user_id' => $oldRow->user_id, 'user_password' => $oldRow->user_password], __METHOD__);
         });
     }
     // @codeCoverageIgnoreEnd
     $this->setPasswordResetFlag($username, $status, $row);
     return AuthenticationResponse::newPass($username);
 }
Exemple #10
0
 /**
  * Continue an account linking flow
  * @param AuthenticationRequest[] $reqs
  * @return AuthenticationResponse
  */
 public function continueAccountLink(array $reqs)
 {
     $session = $this->request->getSession();
     try {
         if (!$this->canLinkAccounts()) {
             // Caller should have called canLinkAccounts()
             $session->remove('AuthManager::accountLinkState');
             throw new \LogicException('Account linking is not possible');
         }
         $state = $session->getSecret('AuthManager::accountLinkState');
         if (!is_array($state)) {
             return AuthenticationResponse::newFail(wfMessage('authmanager-link-not-in-progress'));
         }
         $state['continueRequests'] = [];
         // Step 0: Prepare and validate the input
         $user = User::newFromName($state['username'], 'usable');
         if (!is_object($user)) {
             $session->remove('AuthManager::accountLinkState');
             return AuthenticationResponse::newFail(wfMessage('noname'));
         }
         if ($user->getId() != $state['userid']) {
             throw new \UnexpectedValueException("User \"{$state['username']}\" is valid, but " . "ID {$user->getId()} != {$state['userid']}!");
         }
         foreach ($reqs as $req) {
             $req->username = $state['username'];
             $req->returnToUrl = $state['returnToUrl'];
         }
         // Step 1: Call the primary again until it succeeds
         $provider = $this->getAuthenticationProvider($state['primary']);
         if (!$provider instanceof PrimaryAuthenticationProvider) {
             // Configuration changed? Force them to start over.
             // @codeCoverageIgnoreStart
             $ret = AuthenticationResponse::newFail(wfMessage('authmanager-link-not-in-progress'));
             $this->callMethodOnProviders(3, 'postAccountLink', [$user, $ret]);
             $session->remove('AuthManager::accountLinkState');
             return $ret;
             // @codeCoverageIgnoreEnd
         }
         $id = $provider->getUniqueId();
         $res = $provider->continuePrimaryAccountLink($user, $reqs);
         switch ($res->status) {
             case AuthenticationResponse::PASS:
                 $this->logger->info("Account linked to {user} by {$id}", ['user' => $user->getName()]);
                 $this->callMethodOnProviders(3, 'postAccountLink', [$user, $res]);
                 $session->remove('AuthManager::accountLinkState');
                 return $res;
             case AuthenticationResponse::FAIL:
                 $this->logger->debug(__METHOD__ . ": Account linking failed by {$id}", ['user' => $user->getName()]);
                 $this->callMethodOnProviders(3, 'postAccountLink', [$user, $res]);
                 $session->remove('AuthManager::accountLinkState');
                 return $res;
             case AuthenticationResponse::REDIRECT:
             case AuthenticationResponse::UI:
                 $this->logger->debug(__METHOD__ . ": Account linking {$res->status} by {$id}", ['user' => $user->getName()]);
                 $state['continueRequests'] = $res->neededRequests;
                 $session->setSecret('AuthManager::accountLinkState', $state);
                 return $res;
             default:
                 throw new \DomainException(get_class($provider) . "::continuePrimaryAccountLink() returned {$res->status}");
         }
     } catch (\Exception $ex) {
         $session->remove('AuthManager::accountLinkState');
         throw $ex;
     }
 }
 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'));
     }
 }