mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-21 04:50:55 +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);
|
->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)
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
Loading…
Reference in a new issue