Correct Aphlict websocket URI construction after PHP8 compatibility changes
[phabricator.git] / src / applications / auth / query / PhabricatorExternalAccountQuery.php
blobf44821d7a9ab47a9cec7da51371427fe9589297b
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 willFilterPage(array $accounts) {
70 $viewer = $this->getViewer();
72 $configs = id(new PhabricatorAuthProviderConfigQuery())
73 ->setViewer($viewer)
74 ->withPHIDs(mpull($accounts, 'getProviderConfigPHID'))
75 ->execute();
76 $configs = mpull($configs, null, 'getPHID');
78 foreach ($accounts as $key => $account) {
79 $config_phid = $account->getProviderConfigPHID();
80 $config = idx($configs, $config_phid);
82 if (!$config) {
83 unset($accounts[$key]);
84 continue;
87 $account->attachProviderConfig($config);
90 if ($this->needImages) {
91 $file_phids = mpull($accounts, 'getProfileImagePHID');
92 $file_phids = array_filter($file_phids);
94 if ($file_phids) {
95 // NOTE: We use the omnipotent viewer here because these files are
96 // usually created during registration and can't be associated with
97 // the correct policies, since the relevant user account does not exist
98 // yet. In effect, if you can see an ExternalAccount, you can see its
99 // profile image.
100 $files = id(new PhabricatorFileQuery())
101 ->setViewer(PhabricatorUser::getOmnipotentUser())
102 ->withPHIDs($file_phids)
103 ->execute();
104 $files = mpull($files, null, 'getPHID');
105 } else {
106 $files = array();
109 $default_file = null;
110 foreach ($accounts as $account) {
111 $image_phid = $account->getProfileImagePHID();
112 if ($image_phid && isset($files[$image_phid])) {
113 $account->attachProfileImageFile($files[$image_phid]);
114 } else {
115 if ($default_file === null) {
116 $default_file = PhabricatorFile::loadBuiltin(
117 $this->getViewer(),
118 'profile.png');
120 $account->attachProfileImageFile($default_file);
125 if ($this->needAccountIdentifiers) {
126 $account_phids = mpull($accounts, 'getPHID');
128 $identifiers = id(new PhabricatorExternalAccountIdentifierQuery())
129 ->setViewer($viewer)
130 ->setParentQuery($this)
131 ->withExternalAccountPHIDs($account_phids)
132 ->execute();
134 $identifiers = mgroup($identifiers, 'getExternalAccountPHID');
135 foreach ($accounts as $account) {
136 $account_phid = $account->getPHID();
137 $account_identifiers = idx($identifiers, $account_phid, array());
138 $account->attachAccountIdentifiers($account_identifiers);
142 return $accounts;
145 protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
146 $where = parent::buildWhereClauseParts($conn);
148 if ($this->ids !== null) {
149 $where[] = qsprintf(
150 $conn,
151 'account.id IN (%Ld)',
152 $this->ids);
155 if ($this->phids !== null) {
156 $where[] = qsprintf(
157 $conn,
158 'account.phid IN (%Ls)',
159 $this->phids);
162 if ($this->userPHIDs !== null) {
163 $where[] = qsprintf(
164 $conn,
165 'account.userPHID IN (%Ls)',
166 $this->userPHIDs);
169 if ($this->accountSecrets !== null) {
170 $where[] = qsprintf(
171 $conn,
172 'account.accountSecret IN (%Ls)',
173 $this->accountSecrets);
176 if ($this->providerConfigPHIDs !== null) {
177 $where[] = qsprintf(
178 $conn,
179 'account.providerConfigPHID IN (%Ls)',
180 $this->providerConfigPHIDs);
182 // If we have a list of ProviderConfig PHIDs and are joining the
183 // identifiers table, also include the list as an additional constraint
184 // on the identifiers table.
186 // This does not change the query results (an Account and its
187 // Identifiers always have the same ProviderConfig PHID) but it allows
188 // us to use keys on the Identifier table more efficiently.
190 if ($this->shouldJoinIdentifiersTable()) {
191 $where[] = qsprintf(
192 $conn,
193 'identifier.providerConfigPHID IN (%Ls)',
194 $this->providerConfigPHIDs);
198 if ($this->rawAccountIdentifiers !== null) {
199 $hashes = array();
201 foreach ($this->rawAccountIdentifiers as $raw_identifier) {
202 $hashes[] = PhabricatorHash::digestForIndex($raw_identifier);
205 $where[] = qsprintf(
206 $conn,
207 'identifier.identifierHash IN (%Ls)',
208 $hashes);
211 return $where;
214 protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
215 $joins = parent::buildJoinClauseParts($conn);
217 if ($this->shouldJoinIdentifiersTable()) {
218 $joins[] = qsprintf(
219 $conn,
220 'JOIN %R identifier ON account.phid = identifier.externalAccountPHID',
221 new PhabricatorExternalAccountIdentifier());
224 return $joins;
227 protected function shouldJoinIdentifiersTable() {
228 return ($this->rawAccountIdentifiers !== null);
231 protected function shouldGroupQueryResultRows() {
232 if ($this->shouldJoinIdentifiersTable()) {
233 return true;
236 return parent::shouldGroupQueryResultRows();
239 protected function getPrimaryTableAlias() {
240 return 'account';
243 public function getQueryApplicationClass() {
244 return 'PhabricatorPeopleApplication';