Remove all "FileHasObject" edge reads and writes
[phabricator.git] / src / applications / auth / query / PhabricatorExternalAccountQuery.php
blobbdc030f20cd936e23f91ec5405aa86939778ef63
1 <?php
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.
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.
13 final class PhabricatorExternalAccountQuery
14 extends PhabricatorCursorPagedPolicyAwareQuery {
16 private $ids;
17 private $phids;
18 private $userPHIDs;
19 private $needImages;
20 private $accountSecrets;
21 private $providerConfigPHIDs;
22 private $needAccountIdentifiers;
23 private $rawAccountIdentifiers;
25 public function withUserPHIDs(array $user_phids) {
26 $this->userPHIDs = $user_phids;
27 return $this;
30 public function withPHIDs(array $phids) {
31 $this->phids = $phids;
32 return $this;
35 public function withIDs($ids) {
36 $this->ids = $ids;
37 return $this;
40 public function withAccountSecrets(array $secrets) {
41 $this->accountSecrets = $secrets;
42 return $this;
45 public function needImages($need) {
46 $this->needImages = $need;
47 return $this;
50 public function needAccountIdentifiers($need) {
51 $this->needAccountIdentifiers = $need;
52 return $this;
55 public function withProviderConfigPHIDs(array $phids) {
56 $this->providerConfigPHIDs = $phids;
57 return $this;
60 public function withRawAccountIdentifiers(array $identifiers) {
61 $this->rawAccountIdentifiers = $identifiers;
62 return $this;
65 public function newResultObject() {
66 return new PhabricatorExternalAccount();
69 protected function loadPage() {
70 return $this->loadStandardPage($this->newResultObject());
73 protected function willFilterPage(array $accounts) {
74 $viewer = $this->getViewer();
76 $configs = id(new PhabricatorAuthProviderConfigQuery())
77 ->setViewer($viewer)
78 ->withPHIDs(mpull($accounts, 'getProviderConfigPHID'))
79 ->execute();
80 $configs = mpull($configs, null, 'getPHID');
82 foreach ($accounts as $key => $account) {
83 $config_phid = $account->getProviderConfigPHID();
84 $config = idx($configs, $config_phid);
86 if (!$config) {
87 unset($accounts[$key]);
88 continue;
91 $account->attachProviderConfig($config);
94 if ($this->needImages) {
95 $file_phids = mpull($accounts, 'getProfileImagePHID');
96 $file_phids = array_filter($file_phids);
98 if ($file_phids) {
99 // NOTE: We use the omnipotent viewer here because these files are
100 // usually created during registration and can't be associated with
101 // the correct policies, since the relevant user account does not exist
102 // yet. In effect, if you can see an ExternalAccount, you can see its
103 // profile image.
104 $files = id(new PhabricatorFileQuery())
105 ->setViewer(PhabricatorUser::getOmnipotentUser())
106 ->withPHIDs($file_phids)
107 ->execute();
108 $files = mpull($files, null, 'getPHID');
109 } else {
110 $files = array();
113 $default_file = null;
114 foreach ($accounts as $account) {
115 $image_phid = $account->getProfileImagePHID();
116 if ($image_phid && isset($files[$image_phid])) {
117 $account->attachProfileImageFile($files[$image_phid]);
118 } else {
119 if ($default_file === null) {
120 $default_file = PhabricatorFile::loadBuiltin(
121 $this->getViewer(),
122 'profile.png');
124 $account->attachProfileImageFile($default_file);
129 if ($this->needAccountIdentifiers) {
130 $account_phids = mpull($accounts, 'getPHID');
132 $identifiers = id(new PhabricatorExternalAccountIdentifierQuery())
133 ->setViewer($viewer)
134 ->setParentQuery($this)
135 ->withExternalAccountPHIDs($account_phids)
136 ->execute();
138 $identifiers = mgroup($identifiers, 'getExternalAccountPHID');
139 foreach ($accounts as $account) {
140 $account_phid = $account->getPHID();
141 $account_identifiers = idx($identifiers, $account_phid, array());
142 $account->attachAccountIdentifiers($account_identifiers);
146 return $accounts;
149 protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
150 $where = parent::buildWhereClauseParts($conn);
152 if ($this->ids !== null) {
153 $where[] = qsprintf(
154 $conn,
155 'account.id IN (%Ld)',
156 $this->ids);
159 if ($this->phids !== null) {
160 $where[] = qsprintf(
161 $conn,
162 'account.phid IN (%Ls)',
163 $this->phids);
166 if ($this->userPHIDs !== null) {
167 $where[] = qsprintf(
168 $conn,
169 'account.userPHID IN (%Ls)',
170 $this->userPHIDs);
173 if ($this->accountSecrets !== null) {
174 $where[] = qsprintf(
175 $conn,
176 'account.accountSecret IN (%Ls)',
177 $this->accountSecrets);
180 if ($this->providerConfigPHIDs !== null) {
181 $where[] = qsprintf(
182 $conn,
183 'account.providerConfigPHID IN (%Ls)',
184 $this->providerConfigPHIDs);
186 // If we have a list of ProviderConfig PHIDs and are joining the
187 // identifiers table, also include the list as an additional constraint
188 // on the identifiers table.
190 // This does not change the query results (an Account and its
191 // Identifiers always have the same ProviderConfig PHID) but it allows
192 // us to use keys on the Identifier table more efficiently.
194 if ($this->shouldJoinIdentifiersTable()) {
195 $where[] = qsprintf(
196 $conn,
197 'identifier.providerConfigPHID IN (%Ls)',
198 $this->providerConfigPHIDs);
202 if ($this->rawAccountIdentifiers !== null) {
203 $hashes = array();
205 foreach ($this->rawAccountIdentifiers as $raw_identifier) {
206 $hashes[] = PhabricatorHash::digestForIndex($raw_identifier);
209 $where[] = qsprintf(
210 $conn,
211 'identifier.identifierHash IN (%Ls)',
212 $hashes);
215 return $where;
218 protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
219 $joins = parent::buildJoinClauseParts($conn);
221 if ($this->shouldJoinIdentifiersTable()) {
222 $joins[] = qsprintf(
223 $conn,
224 'JOIN %R identifier ON account.phid = identifier.externalAccountPHID',
225 new PhabricatorExternalAccountIdentifier());
228 return $joins;
231 protected function shouldJoinIdentifiersTable() {
232 return ($this->rawAccountIdentifiers !== null);
235 protected function shouldGroupQueryResultRows() {
236 if ($this->shouldJoinIdentifiersTable()) {
237 return true;
240 return parent::shouldGroupQueryResultRows();
243 protected function getPrimaryTableAlias() {
244 return 'account';
247 public function getQueryApplicationClass() {
248 return 'PhabricatorPeopleApplication';