@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

Add an "ExternalAccountIdentifier" table

Summary:
Depends on D21010. Ref T13493. External accounts may have multiple different unique identifiers, most often when v1 of the API makes a questionable choice (and provies a mutable, non-unique, or PII identifier) and v2 of the API uses an immutable, unique, random identifier.

Allow Phabricator to store multiple identifiers per external account.

Test Plan: Storage only, see followup changes.

Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam

Maniphest Tasks: T13493

Differential Revision: https://secure.phabricator.com/D21011

+218
+10
resources/sql/autopatches/20200220.xaccount.01.sql
··· 1 + CREATE TABLE {$NAMESPACE}_user.user_externalaccountidentifier ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARBINARY(64) NOT NULL, 4 + externalAccountPHID VARBINARY(64) NOT NULL, 5 + providerConfigPHID VARBINARY(64) NOT NULL, 6 + identifierHash BINARY(12) NOT NULL, 7 + identifierRaw LONGTEXT NOT NULL, 8 + dateCreated INT UNSIGNED NOT NULL, 9 + dateModified INT UNSIGNED NOT NULL 10 + ) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT};
+9
src/__phutil_library_map__.php
··· 3318 3318 'PhabricatorExtendingPhabricatorConfigOptions' => 'applications/config/option/PhabricatorExtendingPhabricatorConfigOptions.php', 3319 3319 'PhabricatorExtensionsSetupCheck' => 'applications/config/check/PhabricatorExtensionsSetupCheck.php', 3320 3320 'PhabricatorExternalAccount' => 'applications/people/storage/PhabricatorExternalAccount.php', 3321 + 'PhabricatorExternalAccountIdentifier' => 'applications/people/storage/PhabricatorExternalAccountIdentifier.php', 3322 + 'PhabricatorExternalAccountIdentifierQuery' => 'applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php', 3321 3323 'PhabricatorExternalAccountQuery' => 'applications/auth/query/PhabricatorExternalAccountQuery.php', 3322 3324 'PhabricatorExternalAccountsSettingsPanel' => 'applications/settings/panel/PhabricatorExternalAccountsSettingsPanel.php', 3323 3325 'PhabricatorExtraConfigSetupCheck' => 'applications/config/check/PhabricatorExtraConfigSetupCheck.php', ··· 4103 4105 'PhabricatorPeopleDisableController' => 'applications/people/controller/PhabricatorPeopleDisableController.php', 4104 4106 'PhabricatorPeopleEmailLoginMailEngine' => 'applications/people/mail/PhabricatorPeopleEmailLoginMailEngine.php', 4105 4107 'PhabricatorPeopleEmpowerController' => 'applications/people/controller/PhabricatorPeopleEmpowerController.php', 4108 + 'PhabricatorPeopleExternalIdentifierPHIDType' => 'applications/people/phid/PhabricatorPeopleExternalIdentifierPHIDType.php', 4106 4109 'PhabricatorPeopleExternalPHIDType' => 'applications/people/phid/PhabricatorPeopleExternalPHIDType.php', 4107 4110 'PhabricatorPeopleIconSet' => 'applications/people/icon/PhabricatorPeopleIconSet.php', 4108 4111 'PhabricatorPeopleInviteController' => 'applications/people/controller/PhabricatorPeopleInviteController.php', ··· 9763 9766 'PhabricatorUserDAO', 9764 9767 'PhabricatorPolicyInterface', 9765 9768 ), 9769 + 'PhabricatorExternalAccountIdentifier' => array( 9770 + 'PhabricatorUserDAO', 9771 + 'PhabricatorPolicyInterface', 9772 + ), 9773 + 'PhabricatorExternalAccountIdentifierQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 9766 9774 'PhabricatorExternalAccountQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 9767 9775 'PhabricatorExternalAccountsSettingsPanel' => 'PhabricatorSettingsPanel', 9768 9776 'PhabricatorExtraConfigSetupCheck' => 'PhabricatorSetupCheck', ··· 10681 10689 'PhabricatorPeopleDisableController' => 'PhabricatorPeopleController', 10682 10690 'PhabricatorPeopleEmailLoginMailEngine' => 'PhabricatorPeopleMailEngine', 10683 10691 'PhabricatorPeopleEmpowerController' => 'PhabricatorPeopleController', 10692 + 'PhabricatorPeopleExternalIdentifierPHIDType' => 'PhabricatorPHIDType', 10684 10693 'PhabricatorPeopleExternalPHIDType' => 'PhabricatorPHIDType', 10685 10694 'PhabricatorPeopleIconSet' => 'PhabricatorIconSet', 10686 10695 'PhabricatorPeopleInviteController' => 'PhabricatorPeopleController',
+94
src/applications/auth/query/PhabricatorExternalAccountIdentifierQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorExternalAccountIdentifierQuery 4 + extends PhabricatorCursorPagedPolicyAwareQuery { 5 + 6 + private $ids; 7 + private $phids; 8 + private $providerConfigPHIDs; 9 + private $externalAccountPHIDs; 10 + private $rawIdentifiers; 11 + 12 + public function withIDs($ids) { 13 + $this->ids = $ids; 14 + return $this; 15 + } 16 + 17 + public function withPHIDs(array $phids) { 18 + $this->phids = $phids; 19 + return $this; 20 + } 21 + 22 + public function withProviderConfigPHIDs(array $phids) { 23 + $this->providerConfigPHIDs = $phids; 24 + return $this; 25 + } 26 + 27 + public function withExternalAccountPHIDs(array $phids) { 28 + $this->externalAccountPHIDs = $phids; 29 + return $this; 30 + } 31 + 32 + public function withRawIdentifiers(array $identifiers) { 33 + $this->rawIdentifiers = $identifiers; 34 + return $this; 35 + } 36 + 37 + public function newResultObject() { 38 + return new PhabricatorExternalAccountIdentifier(); 39 + } 40 + 41 + protected function loadPage() { 42 + return $this->loadStandardPage($this->newResultObject()); 43 + } 44 + 45 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 46 + $where = parent::buildWhereClauseParts($conn); 47 + 48 + if ($this->ids !== null) { 49 + $where[] = qsprintf( 50 + $conn, 51 + 'id IN (%Ld)', 52 + $this->ids); 53 + } 54 + 55 + if ($this->phids !== null) { 56 + $where[] = qsprintf( 57 + $conn, 58 + 'phid IN (%Ls)', 59 + $this->phids); 60 + } 61 + 62 + if ($this->providerConfigPHIDs !== null) { 63 + $where[] = qsprintf( 64 + $conn, 65 + 'providerConfigPHID IN (%Ls)', 66 + $this->providerConfigPHIDs); 67 + } 68 + 69 + if ($this->externalAccountPHIDs !== null) { 70 + $where[] = qsprintf( 71 + $conn, 72 + 'externalAccountPHID IN (%Ls)', 73 + $this->externalAccountPHIDs); 74 + } 75 + 76 + if ($this->rawIdentifiers !== null) { 77 + $hashes = array(); 78 + foreach ($this->rawIdentifiers as $raw_identifier) { 79 + $hashes[] = PhabricatorHash::digestForIndex($raw_identifier); 80 + } 81 + $where[] = qsprintf( 82 + $conn, 83 + 'identifierHash IN (%Ls)', 84 + $hashes); 85 + } 86 + 87 + return $where; 88 + } 89 + 90 + public function getQueryApplicationClass() { 91 + return 'PhabricatorPeopleApplication'; 92 + } 93 + 94 + }
+38
src/applications/people/phid/PhabricatorPeopleExternalIdentifierPHIDType.php
··· 1 + <?php 2 + 3 + final class PhabricatorPeopleExternalIdentifierPHIDType 4 + extends PhabricatorPHIDType { 5 + 6 + const TYPECONST = 'XIDT'; 7 + 8 + public function getTypeName() { 9 + return pht('External Account Identifier'); 10 + } 11 + 12 + public function newObject() { 13 + return new PhabricatorExternalAccountIdentifier(); 14 + } 15 + 16 + public function getPHIDTypeApplicationClass() { 17 + return 'PhabricatorPeopleApplication'; 18 + } 19 + 20 + protected function buildQueryForObjects( 21 + PhabricatorObjectQuery $query, 22 + array $phids) { 23 + 24 + return id(new PhabricatorExternalAccountIdentifierQuery()) 25 + ->withPHIDs($phids); 26 + } 27 + 28 + public function loadHandles( 29 + PhabricatorHandleQuery $query, 30 + array $handles, 31 + array $objects) { 32 + 33 + foreach ($handles as $phid => $handle) { 34 + $identifier = $objects[$phid]; 35 + } 36 + } 37 + 38 + }
+67
src/applications/people/storage/PhabricatorExternalAccountIdentifier.php
··· 1 + <?php 2 + 3 + final class PhabricatorExternalAccountIdentifier 4 + extends PhabricatorUserDAO 5 + implements PhabricatorPolicyInterface { 6 + 7 + protected $externalAccountPHID; 8 + protected $providerConfigPHID; 9 + protected $identifierHash; 10 + protected $identifierRaw; 11 + 12 + public function getPHIDType() { 13 + return PhabricatorPeopleExternalIdentifierPHIDType::TYPECONST; 14 + } 15 + 16 + protected function getConfiguration() { 17 + return array( 18 + self::CONFIG_AUX_PHID => true, 19 + self::CONFIG_COLUMN_SCHEMA => array( 20 + 'identifierHash' => 'bytes12', 21 + 'identifierRaw' => 'text', 22 + ), 23 + self::CONFIG_KEY_SCHEMA => array( 24 + 'key_identifier' => array( 25 + 'columns' => array('providerConfigPHID', 'identifierHash'), 26 + 'unique' => true, 27 + ), 28 + 'key_account' => array( 29 + 'columns' => array('externalAccountPHID'), 30 + ), 31 + ), 32 + ) + parent::getConfiguration(); 33 + } 34 + 35 + public function save() { 36 + $identifier_raw = $this->getIdentifierRaw(); 37 + $this->identiferHash = PhabricatorHash::digestForIndex($identifier_raw); 38 + return parent::save(); 39 + } 40 + 41 + 42 + /* -( PhabricatorPolicyInterface )----------------------------------------- */ 43 + 44 + // TODO: These permissions aren't very good. They should just be the same 45 + // as the associated ExternalAccount. See T13381. 46 + 47 + public function getCapabilities() { 48 + return array( 49 + PhabricatorPolicyCapability::CAN_VIEW, 50 + PhabricatorPolicyCapability::CAN_EDIT, 51 + ); 52 + } 53 + 54 + public function getPolicy($capability) { 55 + switch ($capability) { 56 + case PhabricatorPolicyCapability::CAN_VIEW: 57 + return PhabricatorPolicies::getMostOpenPolicy(); 58 + case PhabricatorPolicyCapability::CAN_EDIT: 59 + return PhabricatorPolicies::POLICY_NOONE; 60 + } 61 + } 62 + 63 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 64 + return false; 65 + } 66 + 67 + }