Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/auth/adapter/PhutilOAuth1AuthAdapter.php b/src/applications/auth/adapter/PhutilOAuth1AuthAdapter.php
index aad5f0649d..08cc65a235 100644
--- a/src/applications/auth/adapter/PhutilOAuth1AuthAdapter.php
+++ b/src/applications/auth/adapter/PhutilOAuth1AuthAdapter.php
@@ -1,211 +1,211 @@
<?php
/**
* Abstract adapter for OAuth1 providers.
*/
abstract class PhutilOAuth1AuthAdapter extends PhutilAuthAdapter {
private $consumerKey;
private $consumerSecret;
private $token;
private $tokenSecret;
private $verifier;
private $handshakeData;
private $callbackURI;
private $privateKey;
public function setPrivateKey(PhutilOpaqueEnvelope $private_key) {
$this->privateKey = $private_key;
return $this;
}
public function getPrivateKey() {
return $this->privateKey;
}
public function setCallbackURI($callback_uri) {
$this->callbackURI = $callback_uri;
return $this;
}
public function getCallbackURI() {
return $this->callbackURI;
}
public function setVerifier($verifier) {
$this->verifier = $verifier;
return $this;
}
public function getVerifier() {
return $this->verifier;
}
public function setConsumerSecret(PhutilOpaqueEnvelope $consumer_secret) {
$this->consumerSecret = $consumer_secret;
return $this;
}
public function getConsumerSecret() {
return $this->consumerSecret;
}
public function setConsumerKey($consumer_key) {
$this->consumerKey = $consumer_key;
return $this;
}
public function getConsumerKey() {
return $this->consumerKey;
}
public function setTokenSecret($token_secret) {
$this->tokenSecret = $token_secret;
return $this;
}
public function getTokenSecret() {
return $this->tokenSecret;
}
public function setToken($token) {
$this->token = $token;
return $this;
}
public function getToken() {
return $this->token;
}
protected function getHandshakeData() {
if ($this->handshakeData === null) {
$this->finishOAuthHandshake();
}
return $this->handshakeData;
}
abstract protected function getRequestTokenURI();
abstract protected function getAuthorizeTokenURI();
abstract protected function getValidateTokenURI();
protected function getSignatureMethod() {
return 'HMAC-SHA1';
}
public function getContentSecurityPolicyFormActions() {
return array(
$this->getAuthorizeTokenURI(),
);
}
protected function newOAuth1Future($uri, $data = array()) {
$future = id(new PhutilOAuth1Future($uri, $data))
->setMethod('POST')
->setSignatureMethod($this->getSignatureMethod());
$consumer_key = $this->getConsumerKey();
if (strlen($consumer_key)) {
$future->setConsumerKey($consumer_key);
} else {
throw new Exception(
pht(
'%s is required!',
'setConsumerKey()'));
}
$consumer_secret = $this->getConsumerSecret();
if ($consumer_secret) {
$future->setConsumerSecret($consumer_secret);
}
if (strlen($this->getToken())) {
$future->setToken($this->getToken());
}
if (strlen($this->getTokenSecret())) {
$future->setTokenSecret($this->getTokenSecret());
}
if ($this->getPrivateKey()) {
$future->setPrivateKey($this->getPrivateKey());
}
return $future;
}
public function getClientRedirectURI() {
$request_token_uri = $this->getRequestTokenURI();
$future = $this->newOAuth1Future($request_token_uri);
if (strlen($this->getCallbackURI())) {
$future->setCallbackURI($this->getCallbackURI());
}
list($body) = $future->resolvex();
$data = id(new PhutilQueryStringParser())->parseQueryString($body);
// NOTE: Per the spec, this value MUST be the string 'true'.
$confirmed = idx($data, 'oauth_callback_confirmed');
if ($confirmed !== 'true') {
throw new Exception(
pht("Expected '%s' to be '%s'!", 'oauth_callback_confirmed', 'true'));
}
$this->readTokenAndTokenSecret($data);
$authorize_token_uri = new PhutilURI($this->getAuthorizeTokenURI());
$authorize_token_uri->replaceQueryParam('oauth_token', $this->getToken());
- return (string)$authorize_token_uri;
+ return phutil_string_cast($authorize_token_uri);
}
protected function finishOAuthHandshake() {
$this->willFinishOAuthHandshake();
if (!$this->getToken()) {
throw new Exception(pht('Expected token to finish OAuth handshake!'));
}
if (!$this->getVerifier()) {
throw new Exception(pht('Expected verifier to finish OAuth handshake!'));
}
$validate_uri = $this->getValidateTokenURI();
$params = array(
'oauth_verifier' => $this->getVerifier(),
);
list($body) = $this->newOAuth1Future($validate_uri, $params)->resolvex();
$data = id(new PhutilQueryStringParser())->parseQueryString($body);
$this->readTokenAndTokenSecret($data);
$this->handshakeData = $data;
}
private function readTokenAndTokenSecret(array $data) {
$token = idx($data, 'oauth_token');
if (!$token) {
throw new Exception(pht("Expected '%s' in response!", 'oauth_token'));
}
$token_secret = idx($data, 'oauth_token_secret');
if (!$token_secret) {
throw new Exception(
pht("Expected '%s' in response!", 'oauth_token_secret'));
}
$this->setToken($token);
$this->setTokenSecret($token_secret);
return $this;
}
/**
* Hook that allows subclasses to take actions before the OAuth handshake
* is completed.
*/
protected function willFinishOAuthHandshake() {
return;
}
}
diff --git a/src/applications/auth/provider/PhabricatorAuthProvider.php b/src/applications/auth/provider/PhabricatorAuthProvider.php
index df31c92e50..cfc028441c 100644
--- a/src/applications/auth/provider/PhabricatorAuthProvider.php
+++ b/src/applications/auth/provider/PhabricatorAuthProvider.php
@@ -1,601 +1,601 @@
<?php
abstract class PhabricatorAuthProvider extends Phobject {
private $providerConfig;
public function attachProviderConfig(PhabricatorAuthProviderConfig $config) {
$this->providerConfig = $config;
return $this;
}
public function hasProviderConfig() {
return (bool)$this->providerConfig;
}
public function getProviderConfig() {
if ($this->providerConfig === null) {
throw new PhutilInvalidStateException('attachProviderConfig');
}
return $this->providerConfig;
}
public function getProviderConfigPHID() {
return $this->getProviderConfig()->getPHID();
}
public function getConfigurationHelp() {
return null;
}
public function getDefaultProviderConfig() {
return id(new PhabricatorAuthProviderConfig())
->setProviderClass(get_class($this))
->setIsEnabled(1)
->setShouldAllowLogin(1)
->setShouldAllowRegistration(1)
->setShouldAllowLink(1)
->setShouldAllowUnlink(1);
}
public function getNameForCreate() {
return $this->getProviderName();
}
public function getDescriptionForCreate() {
return null;
}
public function getProviderKey() {
return $this->getAdapter()->getAdapterKey();
}
public function getProviderType() {
return $this->getAdapter()->getAdapterType();
}
public function getProviderDomain() {
return $this->getAdapter()->getAdapterDomain();
}
public static function getAllBaseProviders() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->execute();
}
public static function getAllProviders() {
static $providers;
if ($providers === null) {
$objects = self::getAllBaseProviders();
$configs = id(new PhabricatorAuthProviderConfigQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->execute();
$providers = array();
foreach ($configs as $config) {
if (!isset($objects[$config->getProviderClass()])) {
// This configuration is for a provider which is not installed.
continue;
}
$object = clone $objects[$config->getProviderClass()];
$object->attachProviderConfig($config);
$key = $object->getProviderKey();
if (isset($providers[$key])) {
throw new Exception(
pht(
"Two authentication providers use the same provider key ".
"('%s'). Each provider must be identified by a unique key.",
$key));
}
$providers[$key] = $object;
}
}
return $providers;
}
public static function getAllEnabledProviders() {
$providers = self::getAllProviders();
foreach ($providers as $key => $provider) {
if (!$provider->isEnabled()) {
unset($providers[$key]);
}
}
return $providers;
}
public static function getEnabledProviderByKey($provider_key) {
return idx(self::getAllEnabledProviders(), $provider_key);
}
abstract public function getProviderName();
abstract public function getAdapter();
public function isEnabled() {
return $this->getProviderConfig()->getIsEnabled();
}
public function shouldAllowLogin() {
return $this->getProviderConfig()->getShouldAllowLogin();
}
public function shouldAllowRegistration() {
if (!$this->shouldAllowLogin()) {
return false;
}
return $this->getProviderConfig()->getShouldAllowRegistration();
}
public function shouldAllowAccountLink() {
return $this->getProviderConfig()->getShouldAllowLink();
}
public function shouldAllowAccountUnlink() {
return $this->getProviderConfig()->getShouldAllowUnlink();
}
public function shouldTrustEmails() {
return $this->shouldAllowEmailTrustConfiguration() &&
$this->getProviderConfig()->getShouldTrustEmails();
}
/**
* Should we allow the adapter to be marked as "trusted". This is true for
* all adapters except those that allow the user to type in emails (see
* @{class:PhabricatorPasswordAuthProvider}).
*/
public function shouldAllowEmailTrustConfiguration() {
return true;
}
public function buildLoginForm(PhabricatorAuthStartController $controller) {
return $this->renderLoginForm($controller->getRequest(), $mode = 'start');
}
public function buildInviteForm(PhabricatorAuthStartController $controller) {
return $this->renderLoginForm($controller->getRequest(), $mode = 'invite');
}
abstract public function processLoginRequest(
PhabricatorAuthLoginController $controller);
public function buildLinkForm($controller) {
return $this->renderLoginForm($controller->getRequest(), $mode = 'link');
}
public function shouldAllowAccountRefresh() {
return true;
}
public function buildRefreshForm(
PhabricatorAuthLinkController $controller) {
return $this->renderLoginForm($controller->getRequest(), $mode = 'refresh');
}
protected function renderLoginForm(AphrontRequest $request, $mode) {
throw new PhutilMethodNotImplementedException();
}
public function createProviders() {
return array($this);
}
protected function willSaveAccount(PhabricatorExternalAccount $account) {
return;
}
final protected function newExternalAccountForIdentifiers(
array $identifiers) {
assert_instances_of($identifiers, 'PhabricatorExternalAccountIdentifier');
if (!$identifiers) {
throw new Exception(
pht(
'Authentication provider (of class "%s") is attempting to '.
'load or create an external account, but provided no account '.
'identifiers.',
get_class($this)));
}
if (count($identifiers) !== 1) {
throw new Exception(
pht(
'Unexpected number of account identifiers returned (by class "%s").',
get_class($this)));
}
$config = $this->getProviderConfig();
$viewer = PhabricatorUser::getOmnipotentUser();
$raw_identifiers = mpull($identifiers, 'getIdentifierRaw');
$accounts = id(new PhabricatorExternalAccountQuery())
->setViewer($viewer)
->withProviderConfigPHIDs(array($config->getPHID()))
- ->withAccountIDs($raw_identifiers)
+ ->withRawAccountIdentifiers($raw_identifiers)
->needAccountIdentifiers(true)
->execute();
if (!$accounts) {
- $account = $this->newExternalAccount()
- ->setAccountID(head($raw_identifiers));
+ $account = $this->newExternalAccount();
} else if (count($accounts) === 1) {
$account = head($accounts);
} else {
throw new Exception(
pht(
'Authentication provider (of class "%s") is attempting to load '.
'or create an external account, but provided a list of '.
'account identifiers which map to more than one account: %s.',
get_class($this),
implode(', ', $raw_identifiers)));
}
// See T13493. Add all the identifiers to the account. In the case where
// an account initially has a lower-quality identifier (like an email
// address) and later adds a higher-quality identifier (like a GUID), this
// allows us to automatically upgrade toward the higher-quality identifier
// and survive API changes which remove the lower-quality identifier more
// gracefully.
foreach ($identifiers as $identifier) {
$account->appendIdentifier($identifier);
}
return $this->didUpdateAccount($account);
}
final protected function newExternalAccountForUser(PhabricatorUser $user) {
$config = $this->getProviderConfig();
// When a user logs in with a provider like username/password, they
// always already have a Phabricator account (since there's no way they
// could have a username otherwise).
// These users should never go to registration, so we're building a
// dummy "external account" which just links directly back to their
// internal account.
$account = id(new PhabricatorExternalAccountQuery())
->setViewer($user)
->withProviderConfigPHIDs(array($config->getPHID()))
->withUserPHIDs(array($user->getPHID()))
->executeOne();
if (!$account) {
$account = $this->newExternalAccount()
->setUserPHID($user->getPHID());
-
- // TODO: Remove this when "accountID" is removed; the column is not
- // nullable.
- $account->setAccountID('');
}
return $this->didUpdateAccount($account);
}
private function didUpdateAccount(PhabricatorExternalAccount $account) {
$adapter = $this->getAdapter();
$account->setUsername($adapter->getAccountName());
$account->setRealName($adapter->getAccountRealName());
$account->setEmail($adapter->getAccountEmail());
$account->setAccountURI($adapter->getAccountURI());
$account->setProfileImagePHID(null);
$image_uri = $adapter->getAccountImageURI();
if ($image_uri) {
try {
$name = PhabricatorSlug::normalize($this->getProviderName());
$name = $name.'-profile.jpg';
// TODO: If the image has not changed, we do not need to make a new
// file entry for it, but there's no convenient way to do this with
// PhabricatorFile right now. The storage will get shared, so the impact
// here is negligible.
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$image_file = PhabricatorFile::newFromFileDownload(
$image_uri,
array(
'name' => $name,
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
));
if ($image_file->isViewableImage()) {
$image_file
->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy())
->setCanCDN(true)
->save();
$account->setProfileImagePHID($image_file->getPHID());
} else {
$image_file->delete();
}
unset($unguarded);
} catch (Exception $ex) {
// Log this but proceed, it's not especially important that we
// be able to pull profile images.
phlog($ex);
}
}
$this->willSaveAccount($account);
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$account->save();
unset($unguarded);
return $account;
}
public function getLoginURI() {
$app = PhabricatorApplication::getByClass('PhabricatorAuthApplication');
return $app->getApplicationURI('/login/'.$this->getProviderKey().'/');
}
public function getSettingsURI() {
return '/settings/panel/external/';
}
public function getStartURI() {
$app = PhabricatorApplication::getByClass('PhabricatorAuthApplication');
$uri = $app->getApplicationURI('/start/');
return $uri;
}
public function isDefaultRegistrationProvider() {
return false;
}
public function shouldRequireRegistrationPassword() {
return false;
}
public function newDefaultExternalAccount() {
return $this->newExternalAccount();
}
protected function newExternalAccount() {
$config = $this->getProviderConfig();
$adapter = $this->getAdapter();
$account = id(new PhabricatorExternalAccount())
->setProviderConfigPHID($config->getPHID())
->attachAccountIdentifiers(array());
// TODO: Remove this when these columns are removed. They no longer have
// readers or writers (other than this callsite).
$account
->setAccountType($adapter->getAdapterType())
->setAccountDomain($adapter->getAdapterDomain());
+ // TODO: Remove this when "accountID" is removed; the column is not
+ // nullable.
+
+ $account->setAccountID('');
+
return $account;
}
public function getLoginOrder() {
return '500-'.$this->getProviderName();
}
protected function getLoginIcon() {
return 'Generic';
}
public function newIconView() {
return id(new PHUIIconView())
->setSpriteSheet(PHUIIconView::SPRITE_LOGIN)
->setSpriteIcon($this->getLoginIcon());
}
public function isLoginFormAButton() {
return false;
}
public function renderConfigPropertyTransactionTitle(
PhabricatorAuthProviderConfigTransaction $xaction) {
return null;
}
public function readFormValuesFromProvider() {
return array();
}
public function readFormValuesFromRequest(AphrontRequest $request) {
return array();
}
public function processEditForm(
AphrontRequest $request,
array $values) {
$errors = array();
$issues = array();
return array($errors, $issues, $values);
}
public function extendEditForm(
AphrontRequest $request,
AphrontFormView $form,
array $values,
array $issues) {
return;
}
public function willRenderLinkedAccount(
PhabricatorUser $viewer,
PHUIObjectItemView $item,
PhabricatorExternalAccount $account) {
$account_view = id(new PhabricatorAuthAccountView())
->setExternalAccount($account)
->setAuthProvider($this);
$item->appendChild(
phutil_tag(
'div',
array(
'class' => 'mmr mml mst mmb',
),
$account_view));
}
/**
* Return true to use a two-step configuration (setup, configure) instead of
* the default single-step configuration. In practice, this means that
* creating a new provider instance will redirect back to the edit page
* instead of the provider list.
*
* @return bool True if this provider uses two-step configuration.
*/
public function hasSetupStep() {
return false;
}
/**
* Render a standard login/register button element.
*
* The `$attributes` parameter takes these keys:
*
* - `uri`: URI the button should take the user to when clicked.
* - `method`: Optional HTTP method the button should use, defaults to GET.
*
* @param AphrontRequest HTTP request.
* @param string Request mode string.
* @param map Additional parameters, see above.
* @return wild Log in button.
*/
protected function renderStandardLoginButton(
AphrontRequest $request,
$mode,
array $attributes = array()) {
PhutilTypeSpec::checkMap(
$attributes,
array(
'method' => 'optional string',
'uri' => 'string',
'sigil' => 'optional string',
));
$viewer = $request->getUser();
$adapter = $this->getAdapter();
if ($mode == 'link') {
$button_text = pht('Link External Account');
} else if ($mode == 'refresh') {
$button_text = pht('Refresh Account Link');
} else if ($mode == 'invite') {
$button_text = pht('Register Account');
} else if ($this->shouldAllowRegistration()) {
$button_text = pht('Log In or Register');
} else {
$button_text = pht('Log In');
}
$icon = id(new PHUIIconView())
->setSpriteSheet(PHUIIconView::SPRITE_LOGIN)
->setSpriteIcon($this->getLoginIcon());
$button = id(new PHUIButtonView())
->setSize(PHUIButtonView::BIG)
->setColor(PHUIButtonView::GREY)
->setIcon($icon)
->setText($button_text)
->setSubtext($this->getProviderName());
$uri = $attributes['uri'];
$uri = new PhutilURI($uri);
$params = $uri->getQueryParamsAsPairList();
$uri->removeAllQueryParams();
$content = array($button);
foreach ($params as $pair) {
list($key, $value) = $pair;
$content[] = phutil_tag(
'input',
array(
'type' => 'hidden',
'name' => $key,
'value' => $value,
));
}
$static_response = CelerityAPI::getStaticResourceResponse();
$static_response->addContentSecurityPolicyURI('form-action', (string)$uri);
foreach ($this->getContentSecurityPolicyFormActions() as $csp_uri) {
$static_response->addContentSecurityPolicyURI('form-action', $csp_uri);
}
return phabricator_form(
$viewer,
array(
'method' => idx($attributes, 'method', 'GET'),
'action' => (string)$uri,
'sigil' => idx($attributes, 'sigil'),
),
$content);
}
public function renderConfigurationFooter() {
return null;
}
public function getAuthCSRFCode(AphrontRequest $request) {
$phcid = $request->getCookie(PhabricatorCookies::COOKIE_CLIENTID);
if (!strlen($phcid)) {
throw new AphrontMalformedRequestException(
pht('Missing Client ID Cookie'),
pht(
'Your browser did not submit a "%s" cookie with client state '.
'information in the request. Check that cookies are enabled. '.
'If this problem persists, you may need to clear your cookies.',
PhabricatorCookies::COOKIE_CLIENTID),
true);
}
return PhabricatorHash::weakDigest($phcid);
}
protected function verifyAuthCSRFCode(AphrontRequest $request, $actual) {
$expect = $this->getAuthCSRFCode($request);
if (!strlen($actual)) {
throw new Exception(
pht(
'The authentication provider did not return a client state '.
'parameter in its response, but one was expected. If this '.
'problem persists, you may need to clear your cookies.'));
}
if (!phutil_hashes_are_identical($actual, $expect)) {
throw new Exception(
pht(
'The authentication provider did not return the correct client '.
'state parameter in its response. If this problem persists, you may '.
'need to clear your cookies.'));
}
}
public function supportsAutoLogin() {
return false;
}
public function getAutoLoginURI(AphrontRequest $request) {
throw new PhutilMethodNotImplementedException();
}
protected function getContentSecurityPolicyFormActions() {
return array();
}
}
diff --git a/src/applications/auth/query/PhabricatorExternalAccountQuery.php b/src/applications/auth/query/PhabricatorExternalAccountQuery.php
index 2d8041e80c..bdc030f20c 100644
--- a/src/applications/auth/query/PhabricatorExternalAccountQuery.php
+++ b/src/applications/auth/query/PhabricatorExternalAccountQuery.php
@@ -1,201 +1,251 @@
<?php
/**
* NOTE: When loading ExternalAccounts for use in an authentication context
* (that is, you're going to act as the account or link identities or anything
* like that) you should require CAN_EDIT capability even if you aren't actually
* editing the ExternalAccount.
*
* ExternalAccounts have a permissive CAN_VIEW policy (like users) because they
* interact directly with objects and can leave comments, sign documents, etc.
* However, CAN_EDIT is restricted to users who own the accounts.
*/
final class PhabricatorExternalAccountQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
- private $accountIDs;
private $userPHIDs;
private $needImages;
private $accountSecrets;
private $providerConfigPHIDs;
private $needAccountIdentifiers;
+ private $rawAccountIdentifiers;
public function withUserPHIDs(array $user_phids) {
$this->userPHIDs = $user_phids;
return $this;
}
- public function withAccountIDs(array $account_ids) {
- $this->accountIDs = $account_ids;
- return $this;
- }
-
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withIDs($ids) {
$this->ids = $ids;
return $this;
}
public function withAccountSecrets(array $secrets) {
$this->accountSecrets = $secrets;
return $this;
}
public function needImages($need) {
$this->needImages = $need;
return $this;
}
public function needAccountIdentifiers($need) {
$this->needAccountIdentifiers = $need;
return $this;
}
public function withProviderConfigPHIDs(array $phids) {
$this->providerConfigPHIDs = $phids;
return $this;
}
+ public function withRawAccountIdentifiers(array $identifiers) {
+ $this->rawAccountIdentifiers = $identifiers;
+ return $this;
+ }
+
public function newResultObject() {
return new PhabricatorExternalAccount();
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
protected function willFilterPage(array $accounts) {
$viewer = $this->getViewer();
$configs = id(new PhabricatorAuthProviderConfigQuery())
->setViewer($viewer)
->withPHIDs(mpull($accounts, 'getProviderConfigPHID'))
->execute();
$configs = mpull($configs, null, 'getPHID');
foreach ($accounts as $key => $account) {
$config_phid = $account->getProviderConfigPHID();
$config = idx($configs, $config_phid);
if (!$config) {
unset($accounts[$key]);
continue;
}
$account->attachProviderConfig($config);
}
if ($this->needImages) {
$file_phids = mpull($accounts, 'getProfileImagePHID');
$file_phids = array_filter($file_phids);
if ($file_phids) {
// NOTE: We use the omnipotent viewer here because these files are
// usually created during registration and can't be associated with
// the correct policies, since the relevant user account does not exist
// yet. In effect, if you can see an ExternalAccount, you can see its
// profile image.
$files = id(new PhabricatorFileQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs($file_phids)
->execute();
$files = mpull($files, null, 'getPHID');
} else {
$files = array();
}
$default_file = null;
foreach ($accounts as $account) {
$image_phid = $account->getProfileImagePHID();
if ($image_phid && isset($files[$image_phid])) {
$account->attachProfileImageFile($files[$image_phid]);
} else {
if ($default_file === null) {
$default_file = PhabricatorFile::loadBuiltin(
$this->getViewer(),
'profile.png');
}
$account->attachProfileImageFile($default_file);
}
}
}
if ($this->needAccountIdentifiers) {
$account_phids = mpull($accounts, 'getPHID');
$identifiers = id(new PhabricatorExternalAccountIdentifierQuery())
->setViewer($viewer)
->setParentQuery($this)
->withExternalAccountPHIDs($account_phids)
->execute();
$identifiers = mgroup($identifiers, 'getExternalAccountPHID');
foreach ($accounts as $account) {
$account_phid = $account->getPHID();
$account_identifiers = idx($identifiers, $account_phid, array());
$account->attachAccountIdentifiers($account_identifiers);
}
}
return $accounts;
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
- 'id IN (%Ld)',
+ 'account.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
- 'phid IN (%Ls)',
+ 'account.phid IN (%Ls)',
$this->phids);
}
- if ($this->accountIDs !== null) {
- $where[] = qsprintf(
- $conn,
- 'accountID IN (%Ls)',
- $this->accountIDs);
- }
-
if ($this->userPHIDs !== null) {
$where[] = qsprintf(
$conn,
- 'userPHID IN (%Ls)',
+ 'account.userPHID IN (%Ls)',
$this->userPHIDs);
}
if ($this->accountSecrets !== null) {
$where[] = qsprintf(
$conn,
- 'accountSecret IN (%Ls)',
+ 'account.accountSecret IN (%Ls)',
$this->accountSecrets);
}
if ($this->providerConfigPHIDs !== null) {
$where[] = qsprintf(
$conn,
- 'providerConfigPHID IN (%Ls)',
+ 'account.providerConfigPHID IN (%Ls)',
$this->providerConfigPHIDs);
+
+ // If we have a list of ProviderConfig PHIDs and are joining the
+ // identifiers table, also include the list as an additional constraint
+ // on the identifiers table.
+
+ // This does not change the query results (an Account and its
+ // Identifiers always have the same ProviderConfig PHID) but it allows
+ // us to use keys on the Identifier table more efficiently.
+
+ if ($this->shouldJoinIdentifiersTable()) {
+ $where[] = qsprintf(
+ $conn,
+ 'identifier.providerConfigPHID IN (%Ls)',
+ $this->providerConfigPHIDs);
+ }
+ }
+
+ if ($this->rawAccountIdentifiers !== null) {
+ $hashes = array();
+
+ foreach ($this->rawAccountIdentifiers as $raw_identifier) {
+ $hashes[] = PhabricatorHash::digestForIndex($raw_identifier);
+ }
+
+ $where[] = qsprintf(
+ $conn,
+ 'identifier.identifierHash IN (%Ls)',
+ $hashes);
}
return $where;
}
+ protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
+ $joins = parent::buildJoinClauseParts($conn);
+
+ if ($this->shouldJoinIdentifiersTable()) {
+ $joins[] = qsprintf(
+ $conn,
+ 'JOIN %R identifier ON account.phid = identifier.externalAccountPHID',
+ new PhabricatorExternalAccountIdentifier());
+ }
+
+ return $joins;
+ }
+
+ protected function shouldJoinIdentifiersTable() {
+ return ($this->rawAccountIdentifiers !== null);
+ }
+
+ protected function shouldGroupQueryResultRows() {
+ if ($this->shouldJoinIdentifiersTable()) {
+ return true;
+ }
+
+ return parent::shouldGroupQueryResultRows();
+ }
+
+ protected function getPrimaryTableAlias() {
+ return 'account';
+ }
+
public function getQueryApplicationClass() {
return 'PhabricatorPeopleApplication';
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Aug 14, 5:57 AM (2 d, 15 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
192557
Default Alt Text
(31 KB)

Event Timeline