Changeset View
Changeset View
Standalone View
Standalone View
src/applications/auth/controller/PhabricatorAuthUnlinkController.php
<?php | <?php | ||||
final class PhabricatorAuthUnlinkController | final class PhabricatorAuthUnlinkController | ||||
extends PhabricatorAuthController { | extends PhabricatorAuthController { | ||||
private $providerKey; | |||||
public function handleRequest(AphrontRequest $request) { | public function handleRequest(AphrontRequest $request) { | ||||
$viewer = $this->getViewer(); | $viewer = $this->getViewer(); | ||||
$this->providerKey = $request->getURIData('pkey'); | $id = $request->getURIData('id'); | ||||
list($type, $domain) = explode(':', $this->providerKey, 2); | |||||
// Check that this account link actually exists. We don't require the | $account = id(new PhabricatorExternalAccountQuery()) | ||||
// provider to exist because we want users to be able to delete links to | ->setViewer($viewer) | ||||
// dead accounts if they want. | ->withIDs(array($id)) | ||||
$account = id(new PhabricatorExternalAccount())->loadOneWhere( | ->requireCapabilities( | ||||
'accountType = %s AND accountDomain = %s AND userPHID = %s', | array( | ||||
$type, | PhabricatorPolicyCapability::CAN_VIEW, | ||||
$domain, | PhabricatorPolicyCapability::CAN_EDIT, | ||||
$viewer->getPHID()); | )) | ||||
->executeOne(); | |||||
if (!$account) { | if (!$account) { | ||||
return $this->renderNoAccountErrorDialog(); | return new Aphront404Response(); | ||||
} | } | ||||
// Check that the provider (if it exists) allows accounts to be unlinked. | $done_uri = '/settings/panel/external/'; | ||||
$provider_key = $this->providerKey; | |||||
$provider = PhabricatorAuthProvider::getEnabledProviderByKey($provider_key); | $config = $account->getProviderConfig(); | ||||
if ($provider) { | $provider = $config->getProvider(); | ||||
if (!$provider->shouldAllowAccountUnlink()) { | if (!$provider->shouldAllowAccountUnlink()) { | ||||
return $this->renderNotUnlinkableErrorDialog($provider); | return $this->renderNotUnlinkableErrorDialog($provider, $done_uri); | ||||
} | |||||
} | } | ||||
$confirmations = $request->getStrList('confirmations'); | $confirmations = $request->getStrList('confirmations'); | ||||
$confirmations = array_fuse($confirmations); | $confirmations = array_fuse($confirmations); | ||||
if (!$request->isFormPost() || !isset($confirmations['unlink'])) { | if (!$request->isFormPost() || !isset($confirmations['unlink'])) { | ||||
return $this->renderConfirmDialog($confirmations); | return $this->renderConfirmDialog($confirmations, $config, $done_uri); | ||||
} | } | ||||
// Check that this account isn't the only account which can be used to | // Check that this account isn't the only account which can be used to | ||||
// login. We warn you when you remove your only login account. | // login. We warn you when you remove your only login account. | ||||
if ($account->isUsableForLogin()) { | if ($account->isUsableForLogin()) { | ||||
$other_accounts = id(new PhabricatorExternalAccount())->loadAllWhere( | $other_accounts = id(new PhabricatorExternalAccountQuery()) | ||||
'userPHID = %s', | ->setViewer($viewer) | ||||
$viewer->getPHID()); | ->withUserPHIDs(array($viewer->getPHID())) | ||||
->execute(); | |||||
$valid_accounts = 0; | $valid_accounts = 0; | ||||
foreach ($other_accounts as $other_account) { | foreach ($other_accounts as $other_account) { | ||||
if ($other_account->isUsableForLogin()) { | if ($other_account->isUsableForLogin()) { | ||||
$valid_accounts++; | $valid_accounts++; | ||||
} | } | ||||
} | } | ||||
if ($valid_accounts < 2) { | if ($valid_accounts < 2) { | ||||
if (!isset($confirmations['only'])) { | if (!isset($confirmations['only'])) { | ||||
return $this->renderOnlyUsableAccountConfirmDialog($confirmations); | return $this->renderOnlyUsableAccountConfirmDialog( | ||||
$confirmations, | |||||
$done_uri); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
$account->delete(); | $account->delete(); | ||||
id(new PhabricatorAuthSessionEngine())->terminateLoginSessions( | id(new PhabricatorAuthSessionEngine())->terminateLoginSessions( | ||||
$viewer, | $viewer, | ||||
new PhutilOpaqueEnvelope( | new PhutilOpaqueEnvelope( | ||||
$request->getCookie(PhabricatorCookies::COOKIE_SESSION))); | $request->getCookie(PhabricatorCookies::COOKIE_SESSION))); | ||||
return id(new AphrontRedirectResponse())->setURI($this->getDoneURI()); | return id(new AphrontRedirectResponse())->setURI($done_uri); | ||||
} | |||||
private function getDoneURI() { | |||||
return '/settings/panel/external/'; | |||||
} | |||||
private function renderNoAccountErrorDialog() { | |||||
$dialog = id(new AphrontDialogView()) | |||||
->setUser($this->getRequest()->getUser()) | |||||
->setTitle(pht('No Such Account')) | |||||
->appendChild( | |||||
pht( | |||||
'You can not unlink this account because it is not linked.')) | |||||
->addCancelButton($this->getDoneURI()); | |||||
return id(new AphrontDialogResponse())->setDialog($dialog); | |||||
} | } | ||||
private function renderNotUnlinkableErrorDialog( | private function renderNotUnlinkableErrorDialog( | ||||
PhabricatorAuthProvider $provider) { | PhabricatorAuthProvider $provider, | ||||
$done_uri) { | |||||
$dialog = id(new AphrontDialogView()) | return $this->newDialog() | ||||
->setUser($this->getRequest()->getUser()) | |||||
->setTitle(pht('Permanent Account Link')) | ->setTitle(pht('Permanent Account Link')) | ||||
->appendChild( | ->appendChild( | ||||
pht( | pht( | ||||
'You can not unlink this account because the administrator has '. | 'You can not unlink this account because the administrator has '. | ||||
'configured Phabricator to make links to %s accounts permanent.', | 'configured Phabricator to make links to "%s" accounts permanent.', | ||||
$provider->getProviderName())) | $provider->getProviderName())) | ||||
->addCancelButton($this->getDoneURI()); | ->addCancelButton($done_uri); | ||||
return id(new AphrontDialogResponse())->setDialog($dialog); | |||||
} | } | ||||
private function renderOnlyUsableAccountConfirmDialog(array $confirmations) { | private function renderOnlyUsableAccountConfirmDialog( | ||||
array $confirmations, | |||||
$done_uri) { | |||||
$confirmations[] = 'only'; | $confirmations[] = 'only'; | ||||
return $this->newDialog() | return $this->newDialog() | ||||
->setTitle(pht('Unlink Your Only Login Account?')) | ->setTitle(pht('Unlink Your Only Login Account?')) | ||||
->addHiddenInput('confirmations', implode(',', $confirmations)) | ->addHiddenInput('confirmations', implode(',', $confirmations)) | ||||
->appendParagraph( | ->appendParagraph( | ||||
pht( | pht( | ||||
'This is the only external login account linked to your Phabicator '. | 'This is the only external login account linked to your Phabicator '. | ||||
'account. If you remove it, you may no longer be able to log in.')) | 'account. If you remove it, you may no longer be able to log in.')) | ||||
->appendParagraph( | ->appendParagraph( | ||||
pht( | pht( | ||||
'If you lose access to your account, you can recover access by '. | 'If you lose access to your account, you can recover access by '. | ||||
'sending yourself an email login link from the login screen.')) | 'sending yourself an email login link from the login screen.')) | ||||
->addCancelButton($this->getDoneURI()) | ->addCancelButton($done_uri) | ||||
->addSubmitButton(pht('Unlink External Account')); | ->addSubmitButton(pht('Unlink External Account')); | ||||
} | } | ||||
private function renderConfirmDialog(array $confirmations) { | private function renderConfirmDialog( | ||||
$confirmations[] = 'unlink'; | array $confirmations, | ||||
PhabricatorAuthProviderConfig $config, | |||||
$done_uri) { | |||||
$provider_key = $this->providerKey; | $confirmations[] = 'unlink'; | ||||
$provider = PhabricatorAuthProvider::getEnabledProviderByKey($provider_key); | $provider = $config->getProvider(); | ||||
if ($provider) { | |||||
$title = pht('Unlink "%s" Account?', $provider->getProviderName()); | $title = pht('Unlink "%s" Account?', $provider->getProviderName()); | ||||
$body = pht( | $body = pht( | ||||
'You will no longer be able to use your %s account to '. | 'You will no longer be able to use your %s account to '. | ||||
'log in to Phabricator.', | 'log in to Phabricator.', | ||||
$provider->getProviderName()); | $provider->getProviderName()); | ||||
} else { | |||||
$title = pht('Unlink Account?'); | |||||
$body = pht( | |||||
'You will no longer be able to use this account to log in '. | |||||
'to Phabricator.'); | |||||
} | |||||
return $this->newDialog() | return $this->newDialog() | ||||
->setTitle($title) | ->setTitle($title) | ||||
->addHiddenInput('confirmations', implode(',', $confirmations)) | ->addHiddenInput('confirmations', implode(',', $confirmations)) | ||||
->appendParagraph($body) | ->appendParagraph($body) | ||||
->appendParagraph( | ->appendParagraph( | ||||
pht( | pht( | ||||
'Note: Unlinking an authentication provider will terminate any '. | 'Note: Unlinking an authentication provider will terminate any '. | ||||
'other active login sessions.')) | 'other active login sessions.')) | ||||
->addSubmitButton(pht('Unlink Account')) | ->addSubmitButton(pht('Unlink Account')) | ||||
->addCancelButton($this->getDoneURI()); | ->addCancelButton($done_uri); | ||||
} | } | ||||
} | } |