Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/auth/provider/PhabricatorAuthProviderOAuth1.php b/src/applications/auth/provider/PhabricatorAuthProviderOAuth1.php
index 260d97fc48..f255ae5556 100644
--- a/src/applications/auth/provider/PhabricatorAuthProviderOAuth1.php
+++ b/src/applications/auth/provider/PhabricatorAuthProviderOAuth1.php
@@ -1,200 +1,275 @@
<?php
abstract class PhabricatorAuthProviderOAuth1
extends PhabricatorAuthProviderOAuth {
protected $adapter;
const PROPERTY_CONSUMER_KEY = 'oauth1:consumer:key';
const PROPERTY_CONSUMER_SECRET = 'oauth1:consumer:secret';
const PROPERTY_PRIVATE_KEY = 'oauth1:private:key';
+ const TEMPORARY_TOKEN_TYPE = 'oauth1:request:secret';
+
protected function getIDKey() {
return self::PROPERTY_CONSUMER_KEY;
}
protected function getSecretKey() {
return self::PROPERTY_CONSUMER_SECRET;
}
protected function configureAdapter(PhutilAuthAdapterOAuth1 $adapter) {
$config = $this->getProviderConfig();
$adapter->setConsumerKey($config->getProperty(self::PROPERTY_CONSUMER_KEY));
$secret = $config->getProperty(self::PROPERTY_CONSUMER_SECRET);
if (strlen($secret)) {
$adapter->setConsumerSecret(new PhutilOpaqueEnvelope($secret));
}
$adapter->setCallbackURI(PhabricatorEnv::getURI($this->getLoginURI()));
return $adapter;
}
protected function renderLoginForm(AphrontRequest $request, $mode) {
$attributes = array(
'method' => 'POST',
'uri' => $this->getLoginURI(),
);
return $this->renderStandardLoginButton($request, $mode, $attributes);
}
public function processLoginRequest(
PhabricatorAuthLoginController $controller) {
$request = $controller->getRequest();
$adapter = $this->getAdapter();
$account = null;
$response = null;
if ($request->isHTTPPost()) {
// Add a CSRF code to the callback URI, which we'll verify when
// performing the login.
$client_code = $this->getAuthCSRFCode($request);
$callback_uri = $adapter->getCallbackURI();
$callback_uri = $callback_uri.$client_code.'/';
$adapter->setCallbackURI($callback_uri);
$uri = $adapter->getClientRedirectURI();
+
+ $this->saveHandshakeTokenSecret(
+ $client_code,
+ $adapter->getTokenSecret());
+
$response = id(new AphrontRedirectResponse())->setURI($uri);
return array($account, $response);
}
$denied = $request->getStr('denied');
if (strlen($denied)) {
// Twitter indicates that the user cancelled the login attempt by
// returning "denied" as a parameter.
throw new PhutilAuthUserAbortedException();
}
// NOTE: You can get here via GET, this should probably be a bit more
// user friendly.
$this->verifyAuthCSRFCode($request, $controller->getExtraURIData());
$token = $request->getStr('oauth_token');
$verifier = $request->getStr('oauth_verifier');
if (!$token) {
throw new Exception("Expected 'oauth_token' in request!");
}
if (!$verifier) {
throw new Exception("Expected 'oauth_verifier' in request!");
}
$adapter->setToken($token);
$adapter->setVerifier($verifier);
+ $client_code = $this->getAuthCSRFCode($request);
+ $token_secret = $this->loadHandshakeTokenSecret($client_code);
+ $adapter->setTokenSecret($token_secret);
+
// NOTE: As a side effect, this will cause the OAuth adapter to request
// an access token.
try {
$account_id = $adapter->getAccountID();
} catch (Exception $ex) {
// TODO: Handle this in a more user-friendly way.
throw $ex;
}
if (!strlen($account_id)) {
$response = $controller->buildProviderErrorResponse(
$this,
pht(
'The OAuth provider failed to retrieve an account ID.'));
return array($account, $response);
}
return array($this->loadOrCreateAccount($account_id), $response);
}
public function processEditForm(
AphrontRequest $request,
array $values) {
$key_ckey = self::PROPERTY_CONSUMER_KEY;
$key_csecret = self::PROPERTY_CONSUMER_SECRET;
return $this->processOAuthEditForm(
$request,
$values,
pht('Consumer key is required.'),
pht('Consumer secret is required.'));
}
public function extendEditForm(
AphrontRequest $request,
AphrontFormView $form,
array $values,
array $issues) {
return $this->extendOAuthEditForm(
$request,
$form,
$values,
$issues,
pht('OAuth Consumer Key'),
pht('OAuth Consumer Secret'));
}
public function renderConfigPropertyTransactionTitle(
PhabricatorAuthProviderConfigTransaction $xaction) {
$author_phid = $xaction->getAuthorPHID();
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
$key = $xaction->getMetadataValue(
PhabricatorAuthProviderConfigTransaction::PROPERTY_KEY);
switch ($key) {
case self::PROPERTY_CONSUMER_KEY:
if (strlen($old)) {
return pht(
'%s updated the OAuth consumer key for this provider from '.
'"%s" to "%s".',
$xaction->renderHandleLink($author_phid),
$old,
$new);
} else {
return pht(
'%s set the OAuth consumer key for this provider to '.
'"%s".',
$xaction->renderHandleLink($author_phid),
$new);
}
case self::PROPERTY_CONSUMER_SECRET:
if (strlen($old)) {
return pht(
'%s updated the OAuth consumer secret for this provider.',
$xaction->renderHandleLink($author_phid));
} else {
return pht(
'%s set the OAuth consumer secret for this provider.',
$xaction->renderHandleLink($author_phid));
}
}
return parent::renderConfigPropertyTransactionTitle($xaction);
}
protected function synchronizeOAuthAccount(
PhabricatorExternalAccount $account) {
$adapter = $this->getAdapter();
$oauth_token = $adapter->getToken();
$oauth_token_secret = $adapter->getTokenSecret();
$account->setProperty('oauth1.token', $oauth_token);
$account->setProperty('oauth1.token.secret', $oauth_token_secret);
}
public function willRenderLinkedAccount(
PhabricatorUser $viewer,
PHUIObjectItemView $item,
PhabricatorExternalAccount $account) {
$item->addAttribute(pht('OAuth1 Account'));
parent::willRenderLinkedAccount($viewer, $item, $account);
}
+
+/* -( Temporary Secrets )-------------------------------------------------- */
+
+
+ private function saveHandshakeTokenSecret($client_code, $secret) {
+ $key = $this->getHandshakeTokenKeyFromClientCode($client_code);
+ $type = $this->getTemporaryTokenType(self::TEMPORARY_TOKEN_TYPE);
+
+ // Wipe out an existing token, if one exists.
+ $token = id(new PhabricatorAuthTemporaryTokenQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withObjectPHIDs(array($key))
+ ->withTokenTypes(array($type))
+ ->executeOne();
+ if ($token) {
+ $token->delete();
+ }
+
+ // Save the new secret.
+ id(new PhabricatorAuthTemporaryToken())
+ ->setObjectPHID($key)
+ ->setTokenType($type)
+ ->setTokenExpires(time() + phutil_units('1 hour in seconds'))
+ ->setTokenCode($secret)
+ ->save();
+ }
+
+ private function loadHandshakeTokenSecret($client_code) {
+ $key = $this->getHandshakeTokenKeyFromClientCode($client_code);
+ $type = $this->getTemporaryTokenType(self::TEMPORARY_TOKEN_TYPE);
+
+ $token = id(new PhabricatorAuthTemporaryTokenQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withObjectPHIDs(array($key))
+ ->withTokenTypes(array($type))
+ ->withExpired(false)
+ ->executeOne();
+
+ if (!$token) {
+ throw new Exception(
+ pht(
+ 'Unable to load your OAuth1 token secret from storage. It may '.
+ 'have expired. Try authenticating again.'));
+ }
+
+ return $token->getTokenCode();
+ }
+
+ private function getTemporaryTokenType($core_type) {
+ // Namespace the type so that multiple providers don't step on each
+ // others' toes if a user starts Mediawiki and Bitbucket auth at the
+ // same time.
+
+ return $core_type.':'.$this->getProviderConfig()->getID();
+ }
+
+ private function getHandshakeTokenKeyFromClientCode($client_code) {
+ // NOTE: This is very slightly coersive since the TemporaryToken table
+ // expects an "objectPHID" as an identifier, but nothing about the storage
+ // is bound to PHIDs.
+
+ return 'oauth1:secret/'.$client_code;
+ }
+
}
diff --git a/src/applications/auth/storage/PhabricatorAuthTemporaryToken.php b/src/applications/auth/storage/PhabricatorAuthTemporaryToken.php
index 3c4652434e..6956afda60 100644
--- a/src/applications/auth/storage/PhabricatorAuthTemporaryToken.php
+++ b/src/applications/auth/storage/PhabricatorAuthTemporaryToken.php
@@ -1,41 +1,44 @@
<?php
final class PhabricatorAuthTemporaryToken extends PhabricatorAuthDAO
implements PhabricatorPolicyInterface {
+ // TODO: OAuth1 stores a client identifier here, which is not a real PHID.
+ // At some point, we should rename this column to be a little more generic.
protected $objectPHID;
+
protected $tokenType;
protected $tokenExpires;
protected $tokenCode;
public function getConfiguration() {
return array(
self::CONFIG_TIMESTAMPS => false,
) + parent::getConfiguration();
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
// We're just implement this interface to get access to the standard
// query infrastructure.
return PhabricatorPolicies::getMostOpenPolicy();
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
public function describeAutomaticCapability($capability) {
return null;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Jul 28, 11:14 PM (1 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
187619
Default Alt Text
(10 KB)

Event Timeline