@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+86 -13
+11 -2
src/applications/conduit/controller/PhabricatorConduitTokenEditController.php
··· 83 83 ->addCancelButton($panel_uri); 84 84 } else { 85 85 $form = id(new AphrontFormView()) 86 - ->setUser($viewer) 87 - ->appendChild( 86 + ->setUser($viewer); 87 + 88 + if ($token->getTokenType() === PhabricatorConduitToken::TYPE_CLUSTER) { 89 + $dialog->appendChild( 90 + pht( 91 + 'This token is automatically generated by Phabricator, and used '. 92 + 'to make requests between nodes in a Phabricator cluster. You '. 93 + 'can not use this token in external applications.')); 94 + } else { 95 + $form->appendChild( 88 96 id(new AphrontFormTextControl()) 89 97 ->setLabel(pht('Token')) 90 98 ->setValue($token->getToken())); 99 + } 91 100 92 101 $dialog 93 102 ->appendForm($form)
+13
src/applications/conduit/query/PhabricatorConduitTokenQuery.php
··· 7 7 private $objectPHIDs; 8 8 private $expired; 9 9 private $tokens; 10 + private $tokenTypes; 10 11 11 12 public function withExpired($expired) { 12 13 $this->expired = $expired; ··· 25 26 26 27 public function withTokens(array $tokens) { 27 28 $this->tokens = $tokens; 29 + return $this; 30 + } 31 + 32 + public function withTokenTypes(array $types) { 33 + $this->tokenTypes = $types; 28 34 return $this; 29 35 } 30 36 ··· 65 71 $conn_r, 66 72 'token IN (%Ls)', 67 73 $this->tokens); 74 + } 75 + 76 + if ($this->tokenTypes !== null) { 77 + $where[] = qsprintf( 78 + $conn_r, 79 + 'tokenType IN (%Ls)', 80 + $this->tokenTypes); 68 81 } 69 82 70 83 if ($this->expired !== null) {
+1 -1
src/applications/conduit/settings/PhabricatorConduitSettingsPanel.php
··· 47 47 'href' => '/conduit/token/edit/'.$token->getID().'/', 48 48 'sigil' => 'workflow', 49 49 ), 50 - substr($token->getToken(), 0, 8).'...'), 50 + $token->getPublicTokenName()), 51 51 PhabricatorConduitToken::getTokenTypeName($token->getTokenType()), 52 52 phabricator_datetime($token->getDateCreated(), $viewer), 53 53 ($token->getExpires()
+53 -6
src/applications/conduit/storage/PhabricatorConduitToken.php
··· 12 12 private $object = self::ATTACHABLE; 13 13 14 14 const TYPE_STANDARD = 'api'; 15 - const TYPE_TEMPORARY = 'tmp'; 16 15 const TYPE_COMMANDLINE = 'cli'; 16 + const TYPE_CLUSTER = 'clr'; 17 17 18 18 public function getConfiguration() { 19 19 return array( ··· 37 37 ) + parent::getConfiguration(); 38 38 } 39 39 40 + public static function loadClusterTokenForUser(PhabricatorUser $user) { 41 + if (!$user->isLoggedIn()) { 42 + return null; 43 + } 44 + 45 + $tokens = id(new PhabricatorConduitTokenQuery()) 46 + ->setViewer($user) 47 + ->withObjectPHIDs(array($user->getPHID())) 48 + ->withTokenTypes(array(self::TYPE_CLUSTER)) 49 + ->withExpired(false) 50 + ->execute(); 51 + 52 + // Only return a token if it has at least 5 minutes left before 53 + // expiration. Cluster tokens cycle regularly, so we don't want to use 54 + // one that's going to expire momentarily. 55 + $now = PhabricatorTime::getNow(); 56 + $must_expire_after = $now + phutil_units('5 minutes in seconds'); 57 + 58 + foreach ($tokens as $token) { 59 + if ($token->getExpires() > $must_expire_after) { 60 + return $token; 61 + } 62 + } 63 + 64 + // We didn't find any existing tokens (or the existing tokens are all about 65 + // to expire) so generate a new token. 66 + 67 + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 68 + $token = PhabricatorConduitToken::initializeNewToken( 69 + $user->getPHID(), 70 + self::TYPE_CLUSTER); 71 + $token->save(); 72 + unset($unguarded); 73 + 74 + return $token; 75 + } 76 + 40 77 public static function initializeNewToken($object_phid, $token_type) { 41 78 $token = new PhabricatorConduitToken(); 42 79 $token->objectPHID = $object_phid; ··· 53 90 public static function getTokenTypeName($type) { 54 91 $map = array( 55 92 self::TYPE_STANDARD => pht('Standard API Token'), 56 - self::TYPE_TEMPORARY => pht('Temporary API Token'), 57 93 self::TYPE_COMMANDLINE => pht('Command Line API Token'), 94 + self::TYPE_CLUSTER => pht('Cluster API Token'), 58 95 ); 59 96 60 97 return idx($map, $type, $type); ··· 63 100 public static function getAllTokenTypes() { 64 101 return array( 65 102 self::TYPE_STANDARD, 66 - self::TYPE_TEMPORARY, 67 103 self::TYPE_COMMANDLINE, 104 + self::TYPE_CLUSTER, 68 105 ); 69 106 } 70 107 71 108 private function getTokenExpires($token_type) { 109 + $now = PhabricatorTime::getNow(); 72 110 switch ($token_type) { 73 111 case self::TYPE_STANDARD: 74 112 return null; 75 - case self::TYPE_TEMPORARY: 76 - return PhabricatorTime::getNow() + phutil_units('24 hours in seconds'); 77 113 case self::TYPE_COMMANDLINE: 78 - return PhabricatorTime::getNow() + phutil_units('1 hour in seconds'); 114 + return $now + phutil_units('1 hour in seconds'); 115 + case self::TYPE_CLUSTER: 116 + return $now + phutil_units('30 minutes in seconds'); 79 117 default: 80 118 throw new Exception( 81 119 pht('Unknown Conduit token type "%s"!', $token_type)); 120 + } 121 + } 122 + 123 + public function getPublicTokenName() { 124 + switch ($this->getTokenType()) { 125 + case self::TYPE_CLUSTER: 126 + return pht('Cluster API Token'); 127 + default: 128 + return substr($this->getToken(), 0, 8).'...'; 82 129 } 83 130 } 84 131
+8 -4
src/applications/diffusion/query/DiffusionQuery.php
··· 112 112 113 113 $domain = id(new PhutilURI(PhabricatorEnv::getURI('/')))->getDomain(); 114 114 115 - // TODO: This call needs authentication, which is blocked by T5955. 115 + $client = id(new ConduitClient($uri)) 116 + ->setHost($domain); 116 117 117 - return id(new ConduitClient($uri)) 118 - ->setHost($domain) 119 - ->callMethodSynchronous($method, $params); 118 + $token = PhabricatorConduitToken::loadClusterTokenForUser($user); 119 + if ($token) { 120 + $client->setConduitToken($token->getToken()); 121 + } 122 + 123 + return $client->callMethodSynchronous($method, $params); 120 124 } 121 125 122 126 public function execute() {