1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-29 02:02:41 +01:00

Make external account unlinking use account IDs, not "providerType + providerDomain" nonsense

Summary: Depends on D20112. Ref T6703. When you go to unlink an account, unlink it by ID. Crazy!

Test Plan: Unlinked and relinked Google accounts.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T6703

Differential Revision: https://secure.phabricator.com/D20113
This commit is contained in:
epriestley 2019-02-06 16:00:40 -08:00
parent 541d794c13
commit e5ee656fff
3 changed files with 50 additions and 71 deletions

View file

@ -61,7 +61,7 @@ final class PhabricatorAuthApplication extends PhabricatorApplication {
'start/' => 'PhabricatorAuthStartController', 'start/' => 'PhabricatorAuthStartController',
'validate/' => 'PhabricatorAuthValidateController', 'validate/' => 'PhabricatorAuthValidateController',
'finish/' => 'PhabricatorAuthFinishController', 'finish/' => 'PhabricatorAuthFinishController',
'unlink/(?P<pkey>[^/]+)/' => 'PhabricatorAuthUnlinkController', 'unlink/(?P<id>\d+)/' => 'PhabricatorAuthUnlinkController',
'(?P<action>link|refresh)/(?P<pkey>[^/]+)/' '(?P<action>link|refresh)/(?P<pkey>[^/]+)/'
=> 'PhabricatorAuthLinkController', => 'PhabricatorAuthLinkController',
'confirmlink/(?P<akey>[^/]+)/' 'confirmlink/(?P<akey>[^/]+)/'

View file

@ -3,48 +3,45 @@
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); $account = id(new PhabricatorExternalAccountQuery())
->setViewer($viewer)
// Check that this account link actually exists. We don't require the ->withIDs(array($id))
// provider to exist because we want users to be able to delete links to ->requireCapabilities(
// dead accounts if they want. array(
$account = id(new PhabricatorExternalAccount())->loadOneWhere( PhabricatorPolicyCapability::CAN_VIEW,
'accountType = %s AND accountDomain = %s AND userPHID = %s', PhabricatorPolicyCapability::CAN_EDIT,
$type, ))
$domain, ->executeOne();
$viewer->getPHID());
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) {
@ -55,7 +52,9 @@ final class PhabricatorAuthUnlinkController
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);
} }
} }
} }
@ -67,42 +66,27 @@ final class PhabricatorAuthUnlinkController
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()
@ -116,28 +100,23 @@ final class PhabricatorAuthUnlinkController
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(
array $confirmations,
PhabricatorAuthProviderConfig $config,
$done_uri) {
$confirmations[] = 'unlink'; $confirmations[] = 'unlink';
$provider = $config->getProvider();
$provider_key = $this->providerKey;
$provider = PhabricatorAuthProvider::getEnabledProviderByKey($provider_key);
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)
@ -148,7 +127,7 @@ final class PhabricatorAuthUnlinkController
'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);
} }
} }

View file

@ -78,7 +78,7 @@ final class PhabricatorExternalAccountsSettingsPanel
->setIcon('fa-times') ->setIcon('fa-times')
->setWorkflow(true) ->setWorkflow(true)
->setDisabled(!$can_unlink) ->setDisabled(!$can_unlink)
->setHref('/auth/unlink/'.$account->getProviderKey().'/')); ->setHref('/auth/unlink/'.$account->getID().'/'));
if ($provider) { if ($provider) {
$provider->willRenderLinkedAccount($viewer, $item, $account); $provider->willRenderLinkedAccount($viewer, $item, $account);