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

Write ExternalAccountIdentifiers when interacting with external authentication providers

Summary:
Depends on D21015. When we sync an external account and get a list of account identifiers, write them to the database.

Nothing reads them yet and we still write "accountId", this just prepares us for reads.

Test Plan: Linked, refreshed, unlinked, and re-linked an external account. Peeked at the database and saw a sensible-looking row.

Differential Revision: https://secure.phabricator.com/D21016
This commit is contained in:
epriestley 2020-02-21 07:57:19 -08:00
parent 0872051bfa
commit bcaf60015a
5 changed files with 125 additions and 4 deletions

View file

@ -67,7 +67,7 @@ final class PhabricatorAuthUnlinkController
->setWorkflowKey($workflow_key) ->setWorkflowKey($workflow_key)
->requireHighSecurityToken($viewer, $request, $done_uri); ->requireHighSecurityToken($viewer, $request, $done_uri);
$account->delete(); $account->unlinkAccount();
id(new PhabricatorAuthSessionEngine())->terminateLoginSessions( id(new PhabricatorAuthSessionEngine())->terminateLoginSessions(
$viewer, $viewer,

View file

@ -216,6 +216,7 @@ abstract class PhabricatorAuthProvider extends Phobject {
->setViewer($viewer) ->setViewer($viewer)
->withProviderConfigPHIDs(array($config->getPHID())) ->withProviderConfigPHIDs(array($config->getPHID()))
->withAccountIDs($raw_identifiers) ->withAccountIDs($raw_identifiers)
->needAccountIdentifiers(true)
->execute(); ->execute();
if (!$accounts) { if (!$accounts) {
$account = $this->newExternalAccount() $account = $this->newExternalAccount()
@ -232,6 +233,17 @@ abstract class PhabricatorAuthProvider extends Phobject {
implode(', ', $raw_identifiers))); implode(', ', $raw_identifiers)));
} }
// See T13493. Add all the identifiers to the account. In the case where
// an account initially has a lower-quality identifier (like an email
// address) and later adds a higher-quality identifier (like a GUID), this
// allows us to automatically upgrade toward the higher-quality identifier
// and survive API changes which remove the lower-quality identifier more
// gracefully.
foreach ($identifiers as $identifier) {
$account->appendIdentifier($identifier);
}
return $this->didUpdateAccount($account); return $this->didUpdateAccount($account);
} }
@ -351,7 +363,8 @@ abstract class PhabricatorAuthProvider extends Phobject {
return id(new PhabricatorExternalAccount()) return id(new PhabricatorExternalAccount())
->setAccountType($adapter->getAdapterType()) ->setAccountType($adapter->getAdapterType())
->setAccountDomain($adapter->getAdapterDomain()) ->setAccountDomain($adapter->getAdapterDomain())
->setProviderConfigPHID($config->getPHID()); ->setProviderConfigPHID($config->getPHID())
->attachAccountIdentifiers(array());
} }
public function getLoginOrder() { public function getLoginOrder() {

View file

@ -22,6 +22,7 @@ final class PhabricatorExternalAccountQuery
private $needImages; private $needImages;
private $accountSecrets; private $accountSecrets;
private $providerConfigPHIDs; private $providerConfigPHIDs;
private $needAccountIdentifiers;
public function withUserPHIDs(array $user_phids) { public function withUserPHIDs(array $user_phids) {
$this->userPHIDs = $user_phids; $this->userPHIDs = $user_phids;
@ -63,6 +64,11 @@ final class PhabricatorExternalAccountQuery
return $this; return $this;
} }
public function needAccountIdentifiers($need) {
$this->needAccountIdentifiers = $need;
return $this;
}
public function withProviderConfigPHIDs(array $phids) { public function withProviderConfigPHIDs(array $phids) {
$this->providerConfigPHIDs = $phids; $this->providerConfigPHIDs = $phids;
return $this; return $this;
@ -132,6 +138,23 @@ final class PhabricatorExternalAccountQuery
} }
} }
if ($this->needAccountIdentifiers) {
$account_phids = mpull($accounts, 'getPHID');
$identifiers = id(new PhabricatorExternalAccountIdentifierQuery())
->setViewer($viewer)
->setParentQuery($this)
->withExternalAccountPHIDs($account_phids)
->execute();
$identifiers = mgroup($identifiers, 'getExternalAccountPHID');
foreach ($accounts as $account) {
$account_phid = $account->getPHID();
$account_identifiers = idx($identifiers, $account_phid, array());
$account->attachAccountIdentifiers($account_identifiers);
}
}
return $accounts; return $accounts;
} }

View file

@ -23,6 +23,7 @@ final class PhabricatorExternalAccount
private $profileImageFile = self::ATTACHABLE; private $profileImageFile = self::ATTACHABLE;
private $providerConfig = self::ATTACHABLE; private $providerConfig = self::ATTACHABLE;
private $accountIdentifiers = self::ATTACHABLE;
public function getProfileImageFile() { public function getProfileImageFile() {
return $this->assertAttached($this->profileImageFile); return $this->assertAttached($this->profileImageFile);
@ -78,7 +79,48 @@ final class PhabricatorExternalAccount
if (!$this->getAccountSecret()) { if (!$this->getAccountSecret()) {
$this->setAccountSecret(Filesystem::readRandomCharacters(32)); $this->setAccountSecret(Filesystem::readRandomCharacters(32));
} }
return parent::save();
$this->openTransaction();
$result = parent::save();
$account_phid = $this->getPHID();
$config_phid = $this->getProviderConfigPHID();
if ($this->accountIdentifiers !== self::ATTACHABLE) {
foreach ($this->getAccountIdentifiers() as $identifier) {
$identifier
->setExternalAccountPHID($account_phid)
->setProviderConfigPHID($config_phid)
->save();
}
}
$this->saveTransaction();
return $result;
}
public function unlinkAccount() {
// When unlinking an account, we disassociate it from the user and
// remove all the identifying information. We retain the PHID, the
// object itself, and the "ExternalAccountIdentifier" objects in the
// external table.
// TODO: This unlinks (but does not destroy) any profile image.
return $this
->setUserPHID(null)
->setDisplayName(null)
->setUsername(null)
->setRealName(null)
->setEmail(null)
->setEmailVerified(0)
->setProfileImagePHID(null)
->setAccountURI(null)
->setProperties(array())
->save();
} }
public function setProperty($key, $value) { public function setProperty($key, $value) {
@ -131,6 +173,44 @@ final class PhabricatorExternalAccount
return $this->assertAttached($this->providerConfig); return $this->assertAttached($this->providerConfig);
} }
public function getAccountIdentifiers() {
$raw = $this->assertAttached($this->accountIdentifiers);
return array_values($raw);
}
public function attachAccountIdentifiers(array $identifiers) {
assert_instances_of($identifiers, 'PhabricatorExternalAccountIdentifier');
$this->accountIdentifiers = mpull($identifiers, null, 'getIdentifierRaw');
return $this;
}
public function appendIdentifier(
PhabricatorExternalAccountIdentifier $identifier) {
$this->assertAttached($this->accountIdentifiers);
$map = $this->accountIdentifiers;
$raw = $identifier->getIdentifierRaw();
$old = idx($map, $raw);
$new = $identifier;
if ($old === null) {
$result = $new;
} else {
// Here, we already know about an identifier and have rediscovered it.
// We could copy properties from the new version of the identifier here,
// or merge them in some other way (for example, update a "last seen
// from the provider" timestamp), but no such properties currently exist.
$result = $old;
}
$this->accountIdentifiers[$raw] = $result;
return $this;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */ /* -( PhabricatorPolicyInterface )----------------------------------------- */
@ -182,6 +262,8 @@ final class PhabricatorExternalAccount
$engine->destroyObject($identifier); $engine->destroyObject($identifier);
} }
// TODO: This may leave a profile image behind.
$this->delete(); $this->delete();
} }

View file

@ -36,7 +36,10 @@ final class PhabricatorExternalAccountIdentifier
public function save() { public function save() {
$identifier_raw = $this->getIdentifierRaw(); $identifier_raw = $this->getIdentifierRaw();
$this->identiferHash = PhabricatorHash::digestForIndex($identifier_raw);
$identifier_hash = PhabricatorHash::digestForIndex($identifier_raw);
$this->setIdentifierHash($identifier_hash);
return parent::save(); return parent::save();
} }