@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
at recaptime-dev/main 249 lines 7.1 kB view raw
1<?php 2 3/** 4 * NOTE: When loading ExternalAccounts for use in an authentication context 5 * (that is, you're going to act as the account or link identities or anything 6 * like that) you should require CAN_EDIT capability even if you aren't actually 7 * editing the ExternalAccount. 8 * 9 * ExternalAccounts have a permissive CAN_VIEW policy (like users) because they 10 * interact directly with objects and can leave comments, sign documents, etc. 11 * However, CAN_EDIT is restricted to users who own the accounts. 12 * 13 * @extends PhabricatorCursorPagedPolicyAwareQuery<PhabricatorExternalAccount> 14 */ 15final class PhabricatorExternalAccountQuery 16 extends PhabricatorCursorPagedPolicyAwareQuery { 17 18 private $ids; 19 private $phids; 20 private $userPHIDs; 21 private $needImages; 22 private $accountSecrets; 23 private $providerConfigPHIDs; 24 private $needAccountIdentifiers; 25 private $rawAccountIdentifiers; 26 27 public function withUserPHIDs(array $user_phids) { 28 $this->userPHIDs = $user_phids; 29 return $this; 30 } 31 32 public function withPHIDs(array $phids) { 33 $this->phids = $phids; 34 return $this; 35 } 36 37 public function withIDs($ids) { 38 $this->ids = $ids; 39 return $this; 40 } 41 42 public function withAccountSecrets(array $secrets) { 43 $this->accountSecrets = $secrets; 44 return $this; 45 } 46 47 public function needImages($need) { 48 $this->needImages = $need; 49 return $this; 50 } 51 52 public function needAccountIdentifiers($need) { 53 $this->needAccountIdentifiers = $need; 54 return $this; 55 } 56 57 public function withProviderConfigPHIDs(array $phids) { 58 $this->providerConfigPHIDs = $phids; 59 return $this; 60 } 61 62 public function withRawAccountIdentifiers(array $identifiers) { 63 $this->rawAccountIdentifiers = $identifiers; 64 return $this; 65 } 66 67 public function newResultObject() { 68 return new PhabricatorExternalAccount(); 69 } 70 71 protected function willFilterPage(array $accounts) { 72 $viewer = $this->getViewer(); 73 74 $configs = id(new PhabricatorAuthProviderConfigQuery()) 75 ->setViewer($viewer) 76 ->withPHIDs(mpull($accounts, 'getProviderConfigPHID')) 77 ->execute(); 78 $configs = mpull($configs, null, 'getPHID'); 79 80 foreach ($accounts as $key => $account) { 81 $config_phid = $account->getProviderConfigPHID(); 82 $config = idx($configs, $config_phid); 83 84 if (!$config) { 85 unset($accounts[$key]); 86 continue; 87 } 88 89 $account->attachProviderConfig($config); 90 } 91 92 if ($this->needImages) { 93 $file_phids = mpull($accounts, 'getProfileImagePHID'); 94 $file_phids = array_filter($file_phids); 95 96 if ($file_phids) { 97 // NOTE: We use the omnipotent viewer here because these files are 98 // usually created during registration and can't be associated with 99 // the correct policies, since the relevant user account does not exist 100 // yet. In effect, if you can see an ExternalAccount, you can see its 101 // profile image. 102 $files = id(new PhabricatorFileQuery()) 103 ->setViewer(PhabricatorUser::getOmnipotentUser()) 104 ->withPHIDs($file_phids) 105 ->execute(); 106 $files = mpull($files, null, 'getPHID'); 107 } else { 108 $files = array(); 109 } 110 111 $default_file = null; 112 foreach ($accounts as $account) { 113 $image_phid = $account->getProfileImagePHID(); 114 if ($image_phid && isset($files[$image_phid])) { 115 $account->attachProfileImageFile($files[$image_phid]); 116 } else { 117 if ($default_file === null) { 118 $default_file = PhabricatorFile::loadBuiltin( 119 $this->getViewer(), 120 'profile.png'); 121 } 122 $account->attachProfileImageFile($default_file); 123 } 124 } 125 } 126 127 if ($this->needAccountIdentifiers) { 128 $account_phids = mpull($accounts, 'getPHID'); 129 130 $identifiers = id(new PhabricatorExternalAccountIdentifierQuery()) 131 ->setViewer($viewer) 132 ->setParentQuery($this) 133 ->withExternalAccountPHIDs($account_phids) 134 ->execute(); 135 136 $identifiers = mgroup($identifiers, 'getExternalAccountPHID'); 137 foreach ($accounts as $account) { 138 $account_phid = $account->getPHID(); 139 $account_identifiers = idx($identifiers, $account_phid, array()); 140 $account->attachAccountIdentifiers($account_identifiers); 141 } 142 } 143 144 return $accounts; 145 } 146 147 protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 148 $where = parent::buildWhereClauseParts($conn); 149 150 if ($this->ids !== null) { 151 $where[] = qsprintf( 152 $conn, 153 'account.id IN (%Ld)', 154 $this->ids); 155 } 156 157 if ($this->phids !== null) { 158 $where[] = qsprintf( 159 $conn, 160 'account.phid IN (%Ls)', 161 $this->phids); 162 } 163 164 if ($this->userPHIDs !== null) { 165 $where[] = qsprintf( 166 $conn, 167 'account.userPHID IN (%Ls)', 168 $this->userPHIDs); 169 } 170 171 if ($this->accountSecrets !== null) { 172 $where[] = qsprintf( 173 $conn, 174 'account.accountSecret IN (%Ls)', 175 $this->accountSecrets); 176 } 177 178 if ($this->providerConfigPHIDs !== null) { 179 $where[] = qsprintf( 180 $conn, 181 'account.providerConfigPHID IN (%Ls)', 182 $this->providerConfigPHIDs); 183 184 // If we have a list of ProviderConfig PHIDs and are joining the 185 // identifiers table, also include the list as an additional constraint 186 // on the identifiers table. 187 188 // This does not change the query results (an Account and its 189 // Identifiers always have the same ProviderConfig PHID) but it allows 190 // us to use keys on the Identifier table more efficiently. 191 192 if ($this->shouldJoinIdentifiersTable()) { 193 $where[] = qsprintf( 194 $conn, 195 'identifier.providerConfigPHID IN (%Ls)', 196 $this->providerConfigPHIDs); 197 } 198 } 199 200 if ($this->rawAccountIdentifiers !== null) { 201 $hashes = array(); 202 203 foreach ($this->rawAccountIdentifiers as $raw_identifier) { 204 $hashes[] = PhabricatorHash::digestForIndex($raw_identifier); 205 } 206 207 $where[] = qsprintf( 208 $conn, 209 'identifier.identifierHash IN (%Ls)', 210 $hashes); 211 } 212 213 return $where; 214 } 215 216 protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { 217 $joins = parent::buildJoinClauseParts($conn); 218 219 if ($this->shouldJoinIdentifiersTable()) { 220 $joins[] = qsprintf( 221 $conn, 222 'JOIN %R identifier ON account.phid = identifier.externalAccountPHID', 223 new PhabricatorExternalAccountIdentifier()); 224 } 225 226 return $joins; 227 } 228 229 protected function shouldJoinIdentifiersTable() { 230 return ($this->rawAccountIdentifiers !== null); 231 } 232 233 protected function shouldGroupQueryResultRows() { 234 if ($this->shouldJoinIdentifiersTable()) { 235 return true; 236 } 237 238 return parent::shouldGroupQueryResultRows(); 239 } 240 241 protected function getPrimaryTableAlias() { 242 return 'account'; 243 } 244 245 public function getQueryApplicationClass() { 246 return PhabricatorPeopleApplication::class; 247 } 248 249}