1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-30 09:20:58 +01:00

Generate and use "cluster" Conduit API tokens

Summary:
Ref T5955. Ref T2783.

  - Removes the "temporary" type. I was going to use this for T3628 but it started taking more time than I wanted to spend on it.
  - Add a "cluster" type, which is an internal-only token type used within a cluster. This token value is never shown to the user.
  - Automatically generate, use, and cycle cluster tokens.

Test Plan:
  - Diffusion (mostly) works with a repository configured to use a remote service.
  - Saw cluster tokens generate; terminated a cluster token and saw it regenerate.
  - Viewed cluster token in settings panel and saw nice explanatory text instead, as expected (we might just hide these eventually).

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T2783, T5955

Differential Revision: https://secure.phabricator.com/D10990
This commit is contained in:
epriestley 2014-12-15 11:15:14 -08:00
parent 288498f8d0
commit f18ee5c237
5 changed files with 86 additions and 13 deletions

View file

@ -83,11 +83,20 @@ final class PhabricatorConduitTokenEditController
->addCancelButton($panel_uri); ->addCancelButton($panel_uri);
} else { } else {
$form = id(new AphrontFormView()) $form = id(new AphrontFormView())
->setUser($viewer) ->setUser($viewer);
->appendChild(
if ($token->getTokenType() === PhabricatorConduitToken::TYPE_CLUSTER) {
$dialog->appendChild(
pht(
'This token is automatically generated by Phabricator, and used '.
'to make requests between nodes in a Phabricator cluster. You '.
'can not use this token in external applications.'));
} else {
$form->appendChild(
id(new AphrontFormTextControl()) id(new AphrontFormTextControl())
->setLabel(pht('Token')) ->setLabel(pht('Token'))
->setValue($token->getToken())); ->setValue($token->getToken()));
}
$dialog $dialog
->appendForm($form) ->appendForm($form)

View file

@ -7,6 +7,7 @@ final class PhabricatorConduitTokenQuery
private $objectPHIDs; private $objectPHIDs;
private $expired; private $expired;
private $tokens; private $tokens;
private $tokenTypes;
public function withExpired($expired) { public function withExpired($expired) {
$this->expired = $expired; $this->expired = $expired;
@ -28,6 +29,11 @@ final class PhabricatorConduitTokenQuery
return $this; return $this;
} }
public function withTokenTypes(array $types) {
$this->tokenTypes = $types;
return $this;
}
public function loadPage() { public function loadPage() {
$table = new PhabricatorConduitToken(); $table = new PhabricatorConduitToken();
$conn_r = $table->establishConnection('r'); $conn_r = $table->establishConnection('r');
@ -67,6 +73,13 @@ final class PhabricatorConduitTokenQuery
$this->tokens); $this->tokens);
} }
if ($this->tokenTypes !== null) {
$where[] = qsprintf(
$conn_r,
'tokenType IN (%Ls)',
$this->tokenTypes);
}
if ($this->expired !== null) { if ($this->expired !== null) {
if ($this->expired) { if ($this->expired) {
$where[] = qsprintf( $where[] = qsprintf(

View file

@ -47,7 +47,7 @@ final class PhabricatorConduitSettingsPanel
'href' => '/conduit/token/edit/'.$token->getID().'/', 'href' => '/conduit/token/edit/'.$token->getID().'/',
'sigil' => 'workflow', 'sigil' => 'workflow',
), ),
substr($token->getToken(), 0, 8).'...'), $token->getPublicTokenName()),
PhabricatorConduitToken::getTokenTypeName($token->getTokenType()), PhabricatorConduitToken::getTokenTypeName($token->getTokenType()),
phabricator_datetime($token->getDateCreated(), $viewer), phabricator_datetime($token->getDateCreated(), $viewer),
($token->getExpires() ($token->getExpires()

View file

@ -12,8 +12,8 @@ final class PhabricatorConduitToken
private $object = self::ATTACHABLE; private $object = self::ATTACHABLE;
const TYPE_STANDARD = 'api'; const TYPE_STANDARD = 'api';
const TYPE_TEMPORARY = 'tmp';
const TYPE_COMMANDLINE = 'cli'; const TYPE_COMMANDLINE = 'cli';
const TYPE_CLUSTER = 'clr';
public function getConfiguration() { public function getConfiguration() {
return array( return array(
@ -37,6 +37,43 @@ final class PhabricatorConduitToken
) + parent::getConfiguration(); ) + parent::getConfiguration();
} }
public static function loadClusterTokenForUser(PhabricatorUser $user) {
if (!$user->isLoggedIn()) {
return null;
}
$tokens = id(new PhabricatorConduitTokenQuery())
->setViewer($user)
->withObjectPHIDs(array($user->getPHID()))
->withTokenTypes(array(self::TYPE_CLUSTER))
->withExpired(false)
->execute();
// Only return a token if it has at least 5 minutes left before
// expiration. Cluster tokens cycle regularly, so we don't want to use
// one that's going to expire momentarily.
$now = PhabricatorTime::getNow();
$must_expire_after = $now + phutil_units('5 minutes in seconds');
foreach ($tokens as $token) {
if ($token->getExpires() > $must_expire_after) {
return $token;
}
}
// We didn't find any existing tokens (or the existing tokens are all about
// to expire) so generate a new token.
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$token = PhabricatorConduitToken::initializeNewToken(
$user->getPHID(),
self::TYPE_CLUSTER);
$token->save();
unset($unguarded);
return $token;
}
public static function initializeNewToken($object_phid, $token_type) { public static function initializeNewToken($object_phid, $token_type) {
$token = new PhabricatorConduitToken(); $token = new PhabricatorConduitToken();
$token->objectPHID = $object_phid; $token->objectPHID = $object_phid;
@ -53,8 +90,8 @@ final class PhabricatorConduitToken
public static function getTokenTypeName($type) { public static function getTokenTypeName($type) {
$map = array( $map = array(
self::TYPE_STANDARD => pht('Standard API Token'), self::TYPE_STANDARD => pht('Standard API Token'),
self::TYPE_TEMPORARY => pht('Temporary API Token'),
self::TYPE_COMMANDLINE => pht('Command Line API Token'), self::TYPE_COMMANDLINE => pht('Command Line API Token'),
self::TYPE_CLUSTER => pht('Cluster API Token'),
); );
return idx($map, $type, $type); return idx($map, $type, $type);
@ -63,25 +100,35 @@ final class PhabricatorConduitToken
public static function getAllTokenTypes() { public static function getAllTokenTypes() {
return array( return array(
self::TYPE_STANDARD, self::TYPE_STANDARD,
self::TYPE_TEMPORARY,
self::TYPE_COMMANDLINE, self::TYPE_COMMANDLINE,
self::TYPE_CLUSTER,
); );
} }
private function getTokenExpires($token_type) { private function getTokenExpires($token_type) {
$now = PhabricatorTime::getNow();
switch ($token_type) { switch ($token_type) {
case self::TYPE_STANDARD: case self::TYPE_STANDARD:
return null; return null;
case self::TYPE_TEMPORARY:
return PhabricatorTime::getNow() + phutil_units('24 hours in seconds');
case self::TYPE_COMMANDLINE: case self::TYPE_COMMANDLINE:
return PhabricatorTime::getNow() + phutil_units('1 hour in seconds'); return $now + phutil_units('1 hour in seconds');
case self::TYPE_CLUSTER:
return $now + phutil_units('30 minutes in seconds');
default: default:
throw new Exception( throw new Exception(
pht('Unknown Conduit token type "%s"!', $token_type)); pht('Unknown Conduit token type "%s"!', $token_type));
} }
} }
public function getPublicTokenName() {
switch ($this->getTokenType()) {
case self::TYPE_CLUSTER:
return pht('Cluster API Token');
default:
return substr($this->getToken(), 0, 8).'...';
}
}
public function getObject() { public function getObject() {
return $this->assertAttached($this->object); return $this->assertAttached($this->object);
} }

View file

@ -112,11 +112,15 @@ abstract class DiffusionQuery extends PhabricatorQuery {
$domain = id(new PhutilURI(PhabricatorEnv::getURI('/')))->getDomain(); $domain = id(new PhutilURI(PhabricatorEnv::getURI('/')))->getDomain();
// TODO: This call needs authentication, which is blocked by T5955. $client = id(new ConduitClient($uri))
->setHost($domain);
return id(new ConduitClient($uri)) $token = PhabricatorConduitToken::loadClusterTokenForUser($user);
->setHost($domain) if ($token) {
->callMethodSynchronous($method, $params); $client->setConduitToken($token->getToken());
}
return $client->callMethodSynchronous($method, $params);
} }
public function execute() { public function execute() {