Path: blob/master/src/applications/auth/controller/PhabricatorAuthLoginController.php
13402 views
<?php12final class PhabricatorAuthLoginController3extends PhabricatorAuthController {45private $providerKey;6private $extraURIData;7private $provider;89public function shouldRequireLogin() {10return false;11}1213public function shouldAllowRestrictedParameter($parameter_name) {14// Whitelist the OAuth 'code' parameter.1516if ($parameter_name == 'code') {17return true;18}1920return parent::shouldAllowRestrictedParameter($parameter_name);21}2223public function getExtraURIData() {24return $this->extraURIData;25}2627public function handleRequest(AphrontRequest $request) {28$viewer = $this->getViewer();29$this->providerKey = $request->getURIData('pkey');30$this->extraURIData = $request->getURIData('extra');3132$response = $this->loadProvider();33if ($response) {34return $response;35}3637$invite = $this->loadInvite();38$provider = $this->provider;3940try {41list($account, $response) = $provider->processLoginRequest($this);42} catch (PhutilAuthUserAbortedException $ex) {43if ($viewer->isLoggedIn()) {44// If a logged-in user cancels, take them back to the external accounts45// panel.46$next_uri = '/settings/panel/external/';47} else {48// If a logged-out user cancels, take them back to the auth start page.49$next_uri = '/';50}5152// User explicitly hit "Cancel".53$dialog = id(new AphrontDialogView())54->setUser($viewer)55->setTitle(pht('Authentication Canceled'))56->appendChild(57pht('You canceled authentication.'))58->addCancelButton($next_uri, pht('Continue'));59return id(new AphrontDialogResponse())->setDialog($dialog);60}6162if ($response) {63return $response;64}6566if (!$account) {67throw new Exception(68pht(69'Auth provider failed to load an account from %s!',70'processLoginRequest()'));71}7273if ($account->getUserPHID()) {74// The account is already attached to a Phabricator user, so this is75// either a login or a bad account link request.76if (!$viewer->isLoggedIn()) {77if ($provider->shouldAllowLogin()) {78return $this->processLoginUser($account);79} else {80return $this->renderError(81pht(82'The external service ("%s") you just authenticated with is '.83'not configured to allow logins on this server. An '.84'administrator may have recently disabled it.',85$provider->getProviderName()));86}87} else if ($viewer->getPHID() == $account->getUserPHID()) {88// This is either an attempt to re-link an existing and already89// linked account (which is silly) or a refresh of an external account90// (e.g., an OAuth account).91return id(new AphrontRedirectResponse())92->setURI('/settings/panel/external/');93} else {94return $this->renderError(95pht(96'The external service ("%s") you just used to log in is already '.97'associated with another %s user account. Log in to the '.98'other %s account and unlink the external account before '.99'linking it to a new %s account.',100$provider->getProviderName(),101PlatformSymbols::getPlatformServerName(),102PlatformSymbols::getPlatformServerName(),103PlatformSymbols::getPlatformServerName()));104}105} else {106// The account is not yet attached to a Phabricator user, so this is107// either a registration or an account link request.108if (!$viewer->isLoggedIn()) {109if ($provider->shouldAllowRegistration() || $invite) {110return $this->processRegisterUser($account);111} else {112return $this->renderError(113pht(114'The external service ("%s") you just authenticated with is '.115'not configured to allow registration on this server. An '.116'administrator may have recently disabled it.',117$provider->getProviderName()));118}119} else {120121// If the user already has a linked account on this provider, prevent122// them from linking a second account. This can happen if they swap123// logins and then refresh the account link.124125// There's no technical reason we can't allow you to link multiple126// accounts from a single provider; disallowing this is currently a127// product deciison. See T2549.128129$existing_accounts = id(new PhabricatorExternalAccountQuery())130->setViewer($viewer)131->withUserPHIDs(array($viewer->getPHID()))132->withProviderConfigPHIDs(133array(134$provider->getProviderConfigPHID(),135))136->execute();137if ($existing_accounts) {138return $this->renderError(139pht(140'Your %s account is already connected to an external '.141'account on this service ("%s"), but you are currently logged '.142'in to the service with a different account. Log out of the '.143'external service, then log back in with the correct account '.144'before refreshing the account link.',145PlatformSymbols::getPlatformServerName(),146$provider->getProviderName()));147}148149if ($provider->shouldAllowAccountLink()) {150return $this->processLinkUser($account);151} else {152return $this->renderError(153pht(154'The external service ("%s") you just authenticated with is '.155'not configured to allow account linking on this server. An '.156'administrator may have recently disabled it.',157$provider->getProviderName()));158}159}160}161162// This should be unreachable, but fail explicitly if we get here somehow.163return new Aphront400Response();164}165166private function processLoginUser(PhabricatorExternalAccount $account) {167$user = id(new PhabricatorUser())->loadOneWhere(168'phid = %s',169$account->getUserPHID());170171if (!$user) {172return $this->renderError(173pht(174'The external account you just logged in with is not associated '.175'with a valid %s user account.',176PlatformSymbols::getPlatformServerName()));177}178179return $this->loginUser($user);180}181182private function processRegisterUser(PhabricatorExternalAccount $account) {183$account_secret = $account->getAccountSecret();184$register_uri = $this->getApplicationURI('register/'.$account_secret.'/');185return $this->setAccountKeyAndContinue($account, $register_uri);186}187188private function processLinkUser(PhabricatorExternalAccount $account) {189$account_secret = $account->getAccountSecret();190$confirm_uri = $this->getApplicationURI('confirmlink/'.$account_secret.'/');191return $this->setAccountKeyAndContinue($account, $confirm_uri);192}193194private function setAccountKeyAndContinue(195PhabricatorExternalAccount $account,196$next_uri) {197198if ($account->getUserPHID()) {199throw new Exception(pht('Account is already registered or linked.'));200}201202// Regenerate the registration secret key, set it on the external account,203// set a cookie on the user's machine, and redirect them to registration.204// See PhabricatorAuthRegisterController for discussion of the registration205// key.206207$registration_key = Filesystem::readRandomCharacters(32);208$account->setProperty(209'registrationKey',210PhabricatorHash::weakDigest($registration_key));211212$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();213$account->save();214unset($unguarded);215216$this->getRequest()->setTemporaryCookie(217PhabricatorCookies::COOKIE_REGISTRATION,218$registration_key);219220return id(new AphrontRedirectResponse())->setURI($next_uri);221}222223private function loadProvider() {224$provider = PhabricatorAuthProvider::getEnabledProviderByKey(225$this->providerKey);226227if (!$provider) {228return $this->renderError(229pht(230'The account you are attempting to log in with uses a nonexistent '.231'or disabled authentication provider (with key "%s"). An '.232'administrator may have recently disabled this provider.',233$this->providerKey));234}235236$this->provider = $provider;237238return null;239}240241protected function renderError($message) {242return $this->renderErrorPage(243pht('Login Failed'),244array($message));245}246247public function buildProviderPageResponse(248PhabricatorAuthProvider $provider,249$content) {250251$crumbs = $this->buildApplicationCrumbs();252$viewer = $this->getViewer();253254if ($viewer->isLoggedIn()) {255$crumbs->addTextCrumb(pht('Link Account'), $provider->getSettingsURI());256} else {257$crumbs->addTextCrumb(pht('Login'), $this->getApplicationURI('start/'));258259$content = array(260$this->newCustomStartMessage(),261$content,262);263}264265$crumbs->addTextCrumb($provider->getProviderName());266$crumbs->setBorder(true);267268return $this->newPage()269->setTitle(pht('Login'))270->setCrumbs($crumbs)271->appendChild($content);272}273274public function buildProviderErrorResponse(275PhabricatorAuthProvider $provider,276$message) {277278$message = pht(279'Authentication provider ("%s") encountered an error while attempting '.280'to log in. %s', $provider->getProviderName(), $message);281282return $this->renderError($message);283}284285}286287288