1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-22 13:30:55 +01:00

Allow users to unlink their last external account with a warning, instead of preventing the action

Summary:
Depends on D20105. Fixes T7732. T7732 describes a case where a user had their Google credentials swapped and had trouble regaining access to their account.

Since we now allow email login even if password auth is disabled, it's okay to let users unlink their final account, and it's even reasonable for users to unlink their final account if it is mis-linked.

Just give them a warning that what they're doing is a little sketchy, rather than preventing the workflow.

Test Plan: Unlinked my only login account, got a stern warning instead of a dead end.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T7732

Differential Revision: https://secure.phabricator.com/D20106
This commit is contained in:
epriestley 2019-02-05 11:43:01 -08:00
parent d6f691cf5d
commit fc3b90e1d1
2 changed files with 38 additions and 39 deletions

View file

@ -32,8 +32,15 @@ final class PhabricatorAuthUnlinkController
}
}
// Check that this account isn't the last account which can be used to
// login. We prevent you from removing the last account.
$confirmations = $request->getStrList('confirmations');
$confirmations = array_fuse($confirmations);
if (!$request->isFormPost() || !isset($confirmations['unlink'])) {
return $this->renderConfirmDialog($confirmations);
}
// 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.
if ($account->isUsableForLogin()) {
$other_accounts = id(new PhabricatorExternalAccount())->loadAllWhere(
'userPHID = %s',
@ -47,22 +54,20 @@ final class PhabricatorAuthUnlinkController
}
if ($valid_accounts < 2) {
return $this->renderLastUsableAccountErrorDialog();
if (!isset($confirmations['only'])) {
return $this->renderOnlyUsableAccountConfirmDialog($confirmations);
}
}
}
if ($request->isDialogFormPost()) {
$account->delete();
$account->delete();
id(new PhabricatorAuthSessionEngine())->terminateLoginSessions(
$viewer,
new PhutilOpaqueEnvelope(
$request->getCookie(PhabricatorCookies::COOKIE_SESSION)));
id(new PhabricatorAuthSessionEngine())->terminateLoginSessions(
$viewer,
new PhutilOpaqueEnvelope(
$request->getCookie(PhabricatorCookies::COOKIE_SESSION)));
return id(new AphrontRedirectResponse())->setURI($this->getDoneURI());
}
return $this->renderConfirmDialog();
return id(new AphrontRedirectResponse())->setURI($this->getDoneURI());
}
private function getDoneURI() {
@ -97,22 +102,27 @@ final class PhabricatorAuthUnlinkController
return id(new AphrontDialogResponse())->setDialog($dialog);
}
private function renderLastUsableAccountErrorDialog() {
$dialog = id(new AphrontDialogView())
->setUser($this->getRequest()->getUser())
->setTitle(pht('Last Valid Account'))
->appendChild(
pht(
'You can not unlink this account because you have no other '.
'valid login accounts. If you removed it, you would be unable '.
'to log in. Add another authentication method before removing '.
'this one.'))
->addCancelButton($this->getDoneURI());
private function renderOnlyUsableAccountConfirmDialog(array $confirmations) {
$confirmations[] = 'only';
return id(new AphrontDialogResponse())->setDialog($dialog);
return $this->newDialog()
->setTitle(pht('Unlink Your Only Login Account?'))
->addHiddenInput('confirmations', implode(',', $confirmations))
->appendParagraph(
pht(
'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.'))
->appendParagraph(
pht(
'If you lose access to your account, you can recover access by '.
'sending yourself an email login link from the login screen.'))
->addCancelButton($this->getDoneURI())
->addSubmitButton(pht('Unlink External Account'));
}
private function renderConfirmDialog() {
private function renderConfirmDialog(array $confirmations) {
$confirmations[] = 'unlink';
$provider_key = $this->providerKey;
$provider = PhabricatorAuthProvider::getEnabledProviderByKey($provider_key);
@ -129,9 +139,9 @@ final class PhabricatorAuthUnlinkController
'to Phabricator.');
}
$dialog = id(new AphrontDialogView())
->setUser($this->getRequest()->getUser())
return $this->newDialog()
->setTitle($title)
->addHiddenInput('confirmations', implode(',', $confirmations))
->appendParagraph($body)
->appendParagraph(
pht(
@ -139,8 +149,6 @@ final class PhabricatorAuthUnlinkController
'other active login sessions.'))
->addSubmitButton(pht('Unlink Account'))
->addCancelButton($this->getDoneURI());
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}

View file

@ -41,13 +41,6 @@ final class PhabricatorExternalAccountsSettingsPanel
->setUser($viewer)
->setNoDataString(pht('You have no linked accounts.'));
$login_accounts = 0;
foreach ($accounts as $account) {
if ($account->isUsableForLogin()) {
$login_accounts++;
}
}
foreach ($accounts as $account) {
$item = new PHUIObjectItemView();
@ -72,8 +65,6 @@ final class PhabricatorExternalAccountsSettingsPanel
'account provider).'));
}
$can_unlink = $can_unlink && (!$can_login || ($login_accounts > 1));
$can_refresh = $provider && $provider->shouldAllowAccountRefresh();
if ($can_refresh) {
$item->addAction(