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:
parent
288498f8d0
commit
f18ee5c237
5 changed files with 86 additions and 13 deletions
|
@ -83,11 +83,20 @@ final class PhabricatorConduitTokenEditController
|
|||
->addCancelButton($panel_uri);
|
||||
} else {
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->appendChild(
|
||||
->setUser($viewer);
|
||||
|
||||
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())
|
||||
->setLabel(pht('Token'))
|
||||
->setValue($token->getToken()));
|
||||
}
|
||||
|
||||
$dialog
|
||||
->appendForm($form)
|
||||
|
|
|
@ -7,6 +7,7 @@ final class PhabricatorConduitTokenQuery
|
|||
private $objectPHIDs;
|
||||
private $expired;
|
||||
private $tokens;
|
||||
private $tokenTypes;
|
||||
|
||||
public function withExpired($expired) {
|
||||
$this->expired = $expired;
|
||||
|
@ -28,6 +29,11 @@ final class PhabricatorConduitTokenQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withTokenTypes(array $types) {
|
||||
$this->tokenTypes = $types;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function loadPage() {
|
||||
$table = new PhabricatorConduitToken();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
|
@ -67,6 +73,13 @@ final class PhabricatorConduitTokenQuery
|
|||
$this->tokens);
|
||||
}
|
||||
|
||||
if ($this->tokenTypes !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'tokenType IN (%Ls)',
|
||||
$this->tokenTypes);
|
||||
}
|
||||
|
||||
if ($this->expired !== null) {
|
||||
if ($this->expired) {
|
||||
$where[] = qsprintf(
|
||||
|
|
|
@ -47,7 +47,7 @@ final class PhabricatorConduitSettingsPanel
|
|||
'href' => '/conduit/token/edit/'.$token->getID().'/',
|
||||
'sigil' => 'workflow',
|
||||
),
|
||||
substr($token->getToken(), 0, 8).'...'),
|
||||
$token->getPublicTokenName()),
|
||||
PhabricatorConduitToken::getTokenTypeName($token->getTokenType()),
|
||||
phabricator_datetime($token->getDateCreated(), $viewer),
|
||||
($token->getExpires()
|
||||
|
|
|
@ -12,8 +12,8 @@ final class PhabricatorConduitToken
|
|||
private $object = self::ATTACHABLE;
|
||||
|
||||
const TYPE_STANDARD = 'api';
|
||||
const TYPE_TEMPORARY = 'tmp';
|
||||
const TYPE_COMMANDLINE = 'cli';
|
||||
const TYPE_CLUSTER = 'clr';
|
||||
|
||||
public function getConfiguration() {
|
||||
return array(
|
||||
|
@ -37,6 +37,43 @@ final class PhabricatorConduitToken
|
|||
) + 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) {
|
||||
$token = new PhabricatorConduitToken();
|
||||
$token->objectPHID = $object_phid;
|
||||
|
@ -53,8 +90,8 @@ final class PhabricatorConduitToken
|
|||
public static function getTokenTypeName($type) {
|
||||
$map = array(
|
||||
self::TYPE_STANDARD => pht('Standard API Token'),
|
||||
self::TYPE_TEMPORARY => pht('Temporary API Token'),
|
||||
self::TYPE_COMMANDLINE => pht('Command Line API Token'),
|
||||
self::TYPE_CLUSTER => pht('Cluster API Token'),
|
||||
);
|
||||
|
||||
return idx($map, $type, $type);
|
||||
|
@ -63,25 +100,35 @@ final class PhabricatorConduitToken
|
|||
public static function getAllTokenTypes() {
|
||||
return array(
|
||||
self::TYPE_STANDARD,
|
||||
self::TYPE_TEMPORARY,
|
||||
self::TYPE_COMMANDLINE,
|
||||
self::TYPE_CLUSTER,
|
||||
);
|
||||
}
|
||||
|
||||
private function getTokenExpires($token_type) {
|
||||
$now = PhabricatorTime::getNow();
|
||||
switch ($token_type) {
|
||||
case self::TYPE_STANDARD:
|
||||
return null;
|
||||
case self::TYPE_TEMPORARY:
|
||||
return PhabricatorTime::getNow() + phutil_units('24 hours in seconds');
|
||||
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:
|
||||
throw new Exception(
|
||||
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() {
|
||||
return $this->assertAttached($this->object);
|
||||
}
|
||||
|
|
|
@ -112,11 +112,15 @@ abstract class DiffusionQuery extends PhabricatorQuery {
|
|||
|
||||
$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))
|
||||
->setHost($domain)
|
||||
->callMethodSynchronous($method, $params);
|
||||
$token = PhabricatorConduitToken::loadClusterTokenForUser($user);
|
||||
if ($token) {
|
||||
$client->setConduitToken($token->getToken());
|
||||
}
|
||||
|
||||
return $client->callMethodSynchronous($method, $params);
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
|
|
Loading…
Reference in a new issue