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

Make external account identifier APIs return multiple identifiers

Summary:
Depends on D21012. Ref T13493. Currently, auth adapters return a single identifier for each external account.

Allow them to return more than one identifier, to better handle cases where an API changes from providing a lower-quality identifier to a higher-quality identifier.

On its own, this change doesn't change any user-facing behavior.

Test Plan: Linked and unlinked external accounts.

Maniphest Tasks: T13493

Differential Revision: https://secure.phabricator.com/D21013
This commit is contained in:
epriestley 2020-02-20 19:01:12 -08:00
parent 4094624828
commit e43ecad8af
6 changed files with 62 additions and 41 deletions

View file

@ -34,6 +34,11 @@ abstract class PhutilAuthAdapter extends Phobject {
return $identifiers; return $identifiers;
} }
final protected function newAccountIdentifier($raw_identifier) {
return id(new PhabricatorExternalAccountIdentifier())
->setIdentifierRaw($raw_identifier);
}
/** /**
* Get a unique identifier associated with the account. * Get a unique identifier associated with the account.
* *

View file

@ -56,9 +56,12 @@ final class PhabricatorAuthManagementLDAPWorkflow
$console->writeOut("\n"); $console->writeOut("\n");
$console->writeOut("%s\n", pht('Connecting to LDAP...')); $console->writeOut("%s\n", pht('Connecting to LDAP...'));
$account_id = $adapter->getAccountID(); $account_ids = $adapter->getAccountIdentifiers();
if ($account_id) { if ($account_ids) {
$console->writeOut("%s\n", pht('Found LDAP Account: %s', $account_id)); $value_list = mpull($account_ids, 'getIdentifierRaw');
$value_list = implode(', ', $value_list);
$console->writeOut("%s\n", pht('Found LDAP Account: %s', $value_list));
} else { } else {
$console->writeOut("%s\n", pht('Unable to find LDAP account!')); $console->writeOut("%s\n", pht('Unable to find LDAP account!'));
} }

View file

@ -190,39 +190,51 @@ abstract class PhabricatorAuthProvider extends Phobject {
return; return;
} }
protected function loadOrCreateAccount($account_id) { protected function loadOrCreateAccount(array $identifiers) {
if (!strlen($account_id)) { assert_instances_of($identifiers, 'PhabricatorExternalAccountIdentifier');
throw new Exception(pht('Empty account ID!'));
if (!$identifiers) {
throw new Exception(
pht(
'Authentication provider (of class "%s") is attempting to '.
'load or create an external account, but provided no account '.
'identifiers.',
get_class($this)));
}
if (count($identifiers) !== 1) {
throw new Exception(
pht(
'Unexpected number of account identifiers returned (by class "%s").',
get_class($this)));
}
$config = $this->getProviderConfig();
$viewer = PhabricatorUser::getOmnipotentUser();
$raw_identifiers = mpull($identifiers, 'getIdentifierRaw');
$accounts = id(new PhabricatorExternalAccountQuery())
->setViewer($viewer)
->withProviderConfigPHIDs(array($config->getPHID()))
->withAccountIDs($raw_identifiers)
->execute();
if (!$accounts) {
$account = $this->newExternalAccount()
->setAccountID(head($raw_identifiers));
} else if (count($accounts) === 1) {
$account = head($accounts);
} else {
throw new Exception(
pht(
'Authentication provider (of class "%s") is attempting to load '.
'or create an external account, but provided a list of '.
'account identifiers which map to more than one account: %s.',
get_class($this),
implode(', ', $raw_identifiers)));
} }
$adapter = $this->getAdapter(); $adapter = $this->getAdapter();
$adapter_class = get_class($adapter);
if (!strlen($adapter->getAdapterType())) {
throw new Exception(
pht(
"AuthAdapter (of class '%s') has an invalid implementation: ".
"no adapter type.",
$adapter_class));
}
if (!strlen($adapter->getAdapterDomain())) {
throw new Exception(
pht(
"AuthAdapter (of class '%s') has an invalid implementation: ".
"no adapter domain.",
$adapter_class));
}
$account = id(new PhabricatorExternalAccount())->loadOneWhere(
'accountType = %s AND accountDomain = %s AND accountID = %s',
$adapter->getAdapterType(),
$adapter->getAdapterDomain(),
$account_id);
if (!$account) {
$account = $this->newExternalAccount()
->setAccountID($account_id);
}
$account->setUsername($adapter->getAccountName()); $account->setUsername($adapter->getAccountName());
$account->setRealName($adapter->getAccountRealName()); $account->setRealName($adapter->getAccountRealName());
@ -240,6 +252,7 @@ abstract class PhabricatorAuthProvider extends Phobject {
// file entry for it, but there's no convenient way to do this with // file entry for it, but there's no convenient way to do this with
// PhabricatorFile right now. The storage will get shared, so the impact // PhabricatorFile right now. The storage will get shared, so the impact
// here is negligible. // here is negligible.
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$image_file = PhabricatorFile::newFromFileDownload( $image_file = PhabricatorFile::newFromFileDownload(
$image_uri, $image_uri,

View file

@ -164,7 +164,7 @@ final class PhabricatorLDAPAuthProvider extends PhabricatorAuthProvider {
// See T3351. // See T3351.
DarkConsoleErrorLogPluginAPI::enableDiscardMode(); DarkConsoleErrorLogPluginAPI::enableDiscardMode();
$account_id = $adapter->getAccountID(); $identifiers = $adapter->getAccountIdentifiers();
DarkConsoleErrorLogPluginAPI::disableDiscardMode(); DarkConsoleErrorLogPluginAPI::disableDiscardMode();
} else { } else {
throw new Exception(pht('Username and password are required!')); throw new Exception(pht('Username and password are required!'));
@ -180,7 +180,7 @@ final class PhabricatorLDAPAuthProvider extends PhabricatorAuthProvider {
} }
} }
return array($this->loadOrCreateAccount($account_id), $response); return array($this->loadOrCreateAccount($identifiers), $response);
} }

View file

@ -100,13 +100,13 @@ abstract class PhabricatorOAuth1AuthProvider
// an access token. // an access token.
try { try {
$account_id = $adapter->getAccountID(); $identifiers = $adapter->getAccountIdentifiers();
} catch (Exception $ex) { } catch (Exception $ex) {
// TODO: Handle this in a more user-friendly way. // TODO: Handle this in a more user-friendly way.
throw $ex; throw $ex;
} }
if (!strlen($account_id)) { if (!$identifiers) {
$response = $controller->buildProviderErrorResponse( $response = $controller->buildProviderErrorResponse(
$this, $this,
pht( pht(
@ -115,7 +115,7 @@ abstract class PhabricatorOAuth1AuthProvider
return array($account, $response); return array($account, $response);
} }
return array($this->loadOrCreateAccount($account_id), $response); return array($this->loadOrCreateAccount($identifiers), $response);
} }
public function processEditForm( public function processEditForm(

View file

@ -80,13 +80,13 @@ abstract class PhabricatorOAuth2AuthProvider
// an access token. // an access token.
try { try {
$account_id = $adapter->getAccountID(); $identifiers = $adapter->getAccountIdentifiers();
} catch (Exception $ex) { } catch (Exception $ex) {
// TODO: Handle this in a more user-friendly way. // TODO: Handle this in a more user-friendly way.
throw $ex; throw $ex;
} }
if (!strlen($account_id)) { if (!$identifiers) {
$response = $controller->buildProviderErrorResponse( $response = $controller->buildProviderErrorResponse(
$this, $this,
pht( pht(
@ -95,7 +95,7 @@ abstract class PhabricatorOAuth2AuthProvider
return array($account, $response); return array($account, $response);
} }
return array($this->loadOrCreateAccount($account_id), $response); return array($this->loadOrCreateAccount($identifiers), $response);
} }
public function processEditForm( public function processEditForm(