@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+125 -4
+1 -1
src/applications/auth/controller/PhabricatorAuthUnlinkController.php
··· 67 67 ->setWorkflowKey($workflow_key) 68 68 ->requireHighSecurityToken($viewer, $request, $done_uri); 69 69 70 - $account->delete(); 70 + $account->unlinkAccount(); 71 71 72 72 id(new PhabricatorAuthSessionEngine())->terminateLoginSessions( 73 73 $viewer,
+14 -1
src/applications/auth/provider/PhabricatorAuthProvider.php
··· 216 216 ->setViewer($viewer) 217 217 ->withProviderConfigPHIDs(array($config->getPHID())) 218 218 ->withAccountIDs($raw_identifiers) 219 + ->needAccountIdentifiers(true) 219 220 ->execute(); 220 221 if (!$accounts) { 221 222 $account = $this->newExternalAccount() ··· 230 231 'account identifiers which map to more than one account: %s.', 231 232 get_class($this), 232 233 implode(', ', $raw_identifiers))); 234 + } 235 + 236 + // See T13493. Add all the identifiers to the account. In the case where 237 + // an account initially has a lower-quality identifier (like an email 238 + // address) and later adds a higher-quality identifier (like a GUID), this 239 + // allows us to automatically upgrade toward the higher-quality identifier 240 + // and survive API changes which remove the lower-quality identifier more 241 + // gracefully. 242 + 243 + foreach ($identifiers as $identifier) { 244 + $account->appendIdentifier($identifier); 233 245 } 234 246 235 247 return $this->didUpdateAccount($account); ··· 351 363 return id(new PhabricatorExternalAccount()) 352 364 ->setAccountType($adapter->getAdapterType()) 353 365 ->setAccountDomain($adapter->getAdapterDomain()) 354 - ->setProviderConfigPHID($config->getPHID()); 366 + ->setProviderConfigPHID($config->getPHID()) 367 + ->attachAccountIdentifiers(array()); 355 368 } 356 369 357 370 public function getLoginOrder() {
+23
src/applications/auth/query/PhabricatorExternalAccountQuery.php
··· 22 22 private $needImages; 23 23 private $accountSecrets; 24 24 private $providerConfigPHIDs; 25 + private $needAccountIdentifiers; 25 26 26 27 public function withUserPHIDs(array $user_phids) { 27 28 $this->userPHIDs = $user_phids; ··· 60 61 61 62 public function needImages($need) { 62 63 $this->needImages = $need; 64 + return $this; 65 + } 66 + 67 + public function needAccountIdentifiers($need) { 68 + $this->needAccountIdentifiers = $need; 63 69 return $this; 64 70 } 65 71 ··· 129 135 } 130 136 $account->attachProfileImageFile($default_file); 131 137 } 138 + } 139 + } 140 + 141 + if ($this->needAccountIdentifiers) { 142 + $account_phids = mpull($accounts, 'getPHID'); 143 + 144 + $identifiers = id(new PhabricatorExternalAccountIdentifierQuery()) 145 + ->setViewer($viewer) 146 + ->setParentQuery($this) 147 + ->withExternalAccountPHIDs($account_phids) 148 + ->execute(); 149 + 150 + $identifiers = mgroup($identifiers, 'getExternalAccountPHID'); 151 + foreach ($accounts as $account) { 152 + $account_phid = $account->getPHID(); 153 + $account_identifiers = idx($identifiers, $account_phid, array()); 154 + $account->attachAccountIdentifiers($account_identifiers); 132 155 } 133 156 } 134 157
+83 -1
src/applications/people/storage/PhabricatorExternalAccount.php
··· 23 23 24 24 private $profileImageFile = self::ATTACHABLE; 25 25 private $providerConfig = self::ATTACHABLE; 26 + private $accountIdentifiers = self::ATTACHABLE; 26 27 27 28 public function getProfileImageFile() { 28 29 return $this->assertAttached($this->profileImageFile); ··· 78 79 if (!$this->getAccountSecret()) { 79 80 $this->setAccountSecret(Filesystem::readRandomCharacters(32)); 80 81 } 81 - return parent::save(); 82 + 83 + $this->openTransaction(); 84 + 85 + $result = parent::save(); 86 + 87 + $account_phid = $this->getPHID(); 88 + $config_phid = $this->getProviderConfigPHID(); 89 + 90 + if ($this->accountIdentifiers !== self::ATTACHABLE) { 91 + foreach ($this->getAccountIdentifiers() as $identifier) { 92 + $identifier 93 + ->setExternalAccountPHID($account_phid) 94 + ->setProviderConfigPHID($config_phid) 95 + ->save(); 96 + } 97 + } 98 + 99 + $this->saveTransaction(); 100 + 101 + return $result; 102 + } 103 + 104 + public function unlinkAccount() { 105 + 106 + // When unlinking an account, we disassociate it from the user and 107 + // remove all the identifying information. We retain the PHID, the 108 + // object itself, and the "ExternalAccountIdentifier" objects in the 109 + // external table. 110 + 111 + // TODO: This unlinks (but does not destroy) any profile image. 112 + 113 + return $this 114 + ->setUserPHID(null) 115 + ->setDisplayName(null) 116 + ->setUsername(null) 117 + ->setRealName(null) 118 + ->setEmail(null) 119 + ->setEmailVerified(0) 120 + ->setProfileImagePHID(null) 121 + ->setAccountURI(null) 122 + ->setProperties(array()) 123 + ->save(); 82 124 } 83 125 84 126 public function setProperty($key, $value) { ··· 131 173 return $this->assertAttached($this->providerConfig); 132 174 } 133 175 176 + public function getAccountIdentifiers() { 177 + $raw = $this->assertAttached($this->accountIdentifiers); 178 + return array_values($raw); 179 + } 180 + 181 + public function attachAccountIdentifiers(array $identifiers) { 182 + assert_instances_of($identifiers, 'PhabricatorExternalAccountIdentifier'); 183 + $this->accountIdentifiers = mpull($identifiers, null, 'getIdentifierRaw'); 184 + return $this; 185 + } 186 + 187 + public function appendIdentifier( 188 + PhabricatorExternalAccountIdentifier $identifier) { 189 + 190 + $this->assertAttached($this->accountIdentifiers); 191 + 192 + $map = $this->accountIdentifiers; 193 + $raw = $identifier->getIdentifierRaw(); 194 + 195 + $old = idx($map, $raw); 196 + $new = $identifier; 197 + 198 + if ($old === null) { 199 + $result = $new; 200 + } else { 201 + // Here, we already know about an identifier and have rediscovered it. 202 + 203 + // We could copy properties from the new version of the identifier here, 204 + // or merge them in some other way (for example, update a "last seen 205 + // from the provider" timestamp), but no such properties currently exist. 206 + $result = $old; 207 + } 208 + 209 + $this->accountIdentifiers[$raw] = $result; 210 + 211 + return $this; 212 + } 213 + 134 214 135 215 /* -( PhabricatorPolicyInterface )----------------------------------------- */ 136 216 ··· 181 261 foreach ($identifiers as $identifier) { 182 262 $engine->destroyObject($identifier); 183 263 } 264 + 265 + // TODO: This may leave a profile image behind. 184 266 185 267 $this->delete(); 186 268 }
+4 -1
src/applications/people/storage/PhabricatorExternalAccountIdentifier.php
··· 36 36 37 37 public function save() { 38 38 $identifier_raw = $this->getIdentifierRaw(); 39 - $this->identiferHash = PhabricatorHash::digestForIndex($identifier_raw); 39 + 40 + $identifier_hash = PhabricatorHash::digestForIndex($identifier_raw); 41 + $this->setIdentifierHash($identifier_hash); 42 + 40 43 return parent::save(); 41 44 } 42 45