@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.

Add conduit.getcapabilities and a modern CLI handshake workflow

Summary:
Ref T5955.

- Add `conduit.getcapabilities` to help arc (and other clients) determine formats, protocols, etc., the server supports.
- Fixes T3117. Add a more modern version of the handshake workflow that allows all generated tokens to remain valid for an hour.
- Generally, add a CLI token type. This token type expires after an hour when generated, then becomes permanent if used.

Test Plan:
- See D10988.
- Ran `conduit.getcapabilities` and inspected output.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T3117, T5955

Differential Revision: https://secure.phabricator.com/D10989

+149 -13
+4
src/__phutil_library_map__.php
··· 199 199 'ConduitConnectConduitAPIMethod' => 'applications/conduit/method/ConduitConnectConduitAPIMethod.php', 200 200 'ConduitConnectionGarbageCollector' => 'applications/conduit/garbagecollector/ConduitConnectionGarbageCollector.php', 201 201 'ConduitException' => 'applications/conduit/protocol/exception/ConduitException.php', 202 + 'ConduitGetCapabilitiesConduitAPIMethod' => 'applications/conduit/method/ConduitGetCapabilitiesConduitAPIMethod.php', 202 203 'ConduitGetCertificateConduitAPIMethod' => 'applications/conduit/method/ConduitGetCertificateConduitAPIMethod.php', 203 204 'ConduitLogGarbageCollector' => 'applications/conduit/garbagecollector/ConduitLogGarbageCollector.php', 204 205 'ConduitMethodDoesNotExistException' => 'applications/conduit/protocol/exception/ConduitMethodDoesNotExistException.php', ··· 1434 1435 'PhabricatorConduitToken' => 'applications/conduit/storage/PhabricatorConduitToken.php', 1435 1436 'PhabricatorConduitTokenController' => 'applications/conduit/controller/PhabricatorConduitTokenController.php', 1436 1437 'PhabricatorConduitTokenEditController' => 'applications/conduit/controller/PhabricatorConduitTokenEditController.php', 1438 + 'PhabricatorConduitTokenHandshakeController' => 'applications/conduit/controller/PhabricatorConduitTokenHandshakeController.php', 1437 1439 'PhabricatorConduitTokenQuery' => 'applications/conduit/query/PhabricatorConduitTokenQuery.php', 1438 1440 'PhabricatorConduitTokenTerminateController' => 'applications/conduit/controller/PhabricatorConduitTokenTerminateController.php', 1439 1441 'PhabricatorConfigAllController' => 'applications/config/controller/PhabricatorConfigAllController.php', ··· 3212 3214 'ConduitConnectConduitAPIMethod' => 'ConduitAPIMethod', 3213 3215 'ConduitConnectionGarbageCollector' => 'PhabricatorGarbageCollector', 3214 3216 'ConduitException' => 'Exception', 3217 + 'ConduitGetCapabilitiesConduitAPIMethod' => 'ConduitAPIMethod', 3215 3218 'ConduitGetCertificateConduitAPIMethod' => 'ConduitAPIMethod', 3216 3219 'ConduitLogGarbageCollector' => 'PhabricatorGarbageCollector', 3217 3220 'ConduitMethodDoesNotExistException' => 'ConduitMethodNotFoundException', ··· 4557 4560 ), 4558 4561 'PhabricatorConduitTokenController' => 'PhabricatorConduitController', 4559 4562 'PhabricatorConduitTokenEditController' => 'PhabricatorConduitController', 4563 + 'PhabricatorConduitTokenHandshakeController' => 'PhabricatorConduitController', 4560 4564 'PhabricatorConduitTokenQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4561 4565 'PhabricatorConduitTokenTerminateController' => 'PhabricatorConduitController', 4562 4566 'PhabricatorConfigAllController' => 'PhabricatorConfigController',
+1
src/applications/conduit/application/PhabricatorConduitApplication.php
··· 50 50 'PhabricatorConduitTokenEditController', 51 51 'token/terminate/(?:(?P<id>\d+)/)?' => 52 52 'PhabricatorConduitTokenTerminateController', 53 + 'login/' => 'PhabricatorConduitTokenHandshakeController', 53 54 ), 54 55 '/api/(?P<method>[^/]+)' => 'PhabricatorConduitAPIController', 55 56 );
+25 -12
src/applications/conduit/controller/PhabricatorConduitAPIController.php
··· 301 301 'ERR-INVALID-AUTH', 302 302 pht( 303 303 'API token "%s" has the wrong length. API tokens should be '. 304 - '32 characters long.'), 304 + '32 characters long.', 305 + $token_string), 305 306 ); 306 307 } 307 308 308 309 $type = head(explode('-', $token_string)); 309 - switch ($type) { 310 - case 'api': 311 - case 'tmp': 312 - break; 313 - default: 314 - return array( 315 - 'ERR-INVALID-AUTH', 316 - pht( 317 - 'API token "%s" has the wrong format. API tokens should begin '. 318 - 'with "api-" or "tmp-" and be 32 characters long.', 319 - $token_string), 310 + $valid_types = PhabricatorConduitToken::getAllTokenTypes(); 311 + $valid_types = array_fuse($valid_types); 312 + if (empty($valid_types[$type])) { 313 + return array( 314 + 'ERR-INVALID-AUTH', 315 + pht( 316 + 'API token "%s" has the wrong format. API tokens should be '. 317 + '32 characters long and begin with one of these prefixes: %s.', 318 + $token_string, 319 + implode(', ', $valid_types)), 320 320 ); 321 321 } 322 322 ··· 345 345 'API token "%s" is not valid.', 346 346 $token_string), 347 347 ); 348 + } 349 + } 350 + 351 + // If this is a "cli-" token, it expires shortly after it is generated 352 + // by default. Once it is actually used, we extend its lifetime and make 353 + // it permanent. This allows stray tokens to get cleaned up automatically 354 + // if they aren't being used. 355 + if ($token->getTokenType() == PhabricatorConduitToken::TYPE_COMMANDLINE) { 356 + if ($token->getExpires()) { 357 + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 358 + $token->setExpires(null); 359 + $token->save(); 360 + unset($unguarded); 348 361 } 349 362 } 350 363
+46
src/applications/conduit/controller/PhabricatorConduitTokenHandshakeController.php
··· 1 + <?php 2 + 3 + final class PhabricatorConduitTokenHandshakeController 4 + extends PhabricatorConduitController { 5 + 6 + public function handleRequest(AphrontRequest $request) { 7 + $viewer = $request->getViewer(); 8 + 9 + id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession( 10 + $viewer, 11 + $request, 12 + '/'); 13 + 14 + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 15 + $token = PhabricatorConduitToken::initializeNewToken( 16 + $viewer->getPHID(), 17 + PhabricatorConduitToken::TYPE_COMMANDLINE); 18 + $token->save(); 19 + unset($unguarded); 20 + 21 + $form = id(new AphrontFormView()) 22 + ->setUser($viewer) 23 + ->appendRemarkupInstructions( 24 + pht( 25 + 'Copy-paste the API Token below to grant access to your account.')) 26 + ->appendChild( 27 + id(new AphrontFormTextControl()) 28 + ->setLabel(pht('API Token')) 29 + ->setValue($token->getToken())) 30 + ->appendRemarkupInstructions( 31 + pht( 32 + 'This will authorize the requesting script to act on your behalf '. 33 + 'permanently, like giving the script your account password.')) 34 + ->appendRemarkupInstructions( 35 + pht( 36 + 'If you change your mind, you can revoke this token later in '. 37 + '{nav icon=wrench,name=Settings > Conduit API Tokens}.')); 38 + 39 + return $this->newDialog() 40 + ->setTitle(pht('Grant Account Access')) 41 + ->setWidth(AphrontDialogView::WIDTH_FULL) 42 + ->appendForm($form) 43 + ->addCancelButton('/'); 44 + } 45 + 46 + }
+60
src/applications/conduit/method/ConduitGetCapabilitiesConduitAPIMethod.php
··· 1 + <?php 2 + 3 + final class ConduitGetCapabilitiesConduitAPIMethod extends ConduitAPIMethod { 4 + 5 + public function getAPIMethodName() { 6 + return 'conduit.getcapabilities'; 7 + } 8 + 9 + public function shouldRequireAuthentication() { 10 + return false; 11 + } 12 + 13 + public function getMethodDescription() { 14 + return pht( 15 + 'List capabilities, wire formats, and authentication protocols '. 16 + 'available on this server.'); 17 + } 18 + 19 + public function defineParamTypes() { 20 + return array(); 21 + } 22 + 23 + public function defineReturnType() { 24 + return 'dict<string, any>'; 25 + } 26 + 27 + public function defineErrorTypes() { 28 + return array(); 29 + } 30 + 31 + protected function execute(ConduitAPIRequest $request) { 32 + $authentication = array( 33 + 'token', 34 + 'asymmetric', 35 + 'session', 36 + 'sessionless', 37 + ); 38 + 39 + $oauth_app = 'PhabricatorOAuthServerApplication'; 40 + if (PhabricatorApplication::isClassInstalled($oauth_app)) { 41 + $authentication[] = 'oauth'; 42 + } 43 + 44 + return array( 45 + 'authentication' => $authentication, 46 + 'signatures' => array( 47 + 'consign', 48 + ), 49 + 'input' => array( 50 + 'json', 51 + 'urlencoded', 52 + ), 53 + 'output' => array( 54 + 'json', 55 + 'human', 56 + ), 57 + ); 58 + } 59 + 60 + }
+13 -1
src/applications/conduit/storage/PhabricatorConduitToken.php
··· 13 13 14 14 const TYPE_STANDARD = 'api'; 15 15 const TYPE_TEMPORARY = 'tmp'; 16 + const TYPE_COMMANDLINE = 'cli'; 16 17 17 18 public function getConfiguration() { 18 19 return array( ··· 53 54 $map = array( 54 55 self::TYPE_STANDARD => pht('Standard API Token'), 55 56 self::TYPE_TEMPORARY => pht('Temporary API Token'), 57 + self::TYPE_COMMANDLINE => pht('Command Line API Token'), 56 58 ); 57 59 58 60 return idx($map, $type, $type); 59 61 } 60 62 63 + public static function getAllTokenTypes() { 64 + return array( 65 + self::TYPE_STANDARD, 66 + self::TYPE_TEMPORARY, 67 + self::TYPE_COMMANDLINE, 68 + ); 69 + } 70 + 61 71 private function getTokenExpires($token_type) { 62 72 switch ($token_type) { 63 73 case self::TYPE_STANDARD: 64 74 return null; 65 75 case self::TYPE_TEMPORARY: 66 - return PhabricatorTime::getNow() + phutil_units('24h in seconds'); 76 + return PhabricatorTime::getNow() + phutil_units('24 hours in seconds'); 77 + case self::TYPE_COMMANDLINE: 78 + return PhabricatorTime::getNow() + phutil_units('1 hour in seconds'); 67 79 default: 68 80 throw new Exception( 69 81 pht('Unknown Conduit token type "%s"!', $token_type));