1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-28 09:42:40 +01:00

Support simpler, token-based Conduit authentication in Arcanist

Summary:
Ref T5955. If the server supports token-based authentication, prefer it over certificate-based authentication.

Also fixes T3117.

Test Plan:
  - Used `arc install-certificate` to install credentials from both token-based and certificate-based hosts.
  - Used `arc list` with a token-based host.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T3117, T2878, T5955

Differential Revision: https://secure.phabricator.com/D10988
This commit is contained in:
epriestley 2014-12-15 11:12:38 -08:00
parent 565a96e0ee
commit 5118987757
3 changed files with 123 additions and 32 deletions

View file

@ -275,29 +275,34 @@ try {
$host_config = idx($hosts_config, $conduit_uri, array()); $host_config = idx($hosts_config, $conduit_uri, array());
$user_name = idx($host_config, 'user'); $user_name = idx($host_config, 'user');
$certificate = idx($host_config, 'cert'); $certificate = idx($host_config, 'cert');
$conduit_token = idx($host_config, 'token');
$description = implode(' ', $original_argv); $description = implode(' ', $original_argv);
$credentials = array( $credentials = array(
'user' => $user_name, 'user' => $user_name,
'certificate' => $certificate, 'certificate' => $certificate,
'description' => $description, 'description' => $description,
'token' => $conduit_token,
); );
$workflow->setConduitCredentials($credentials); $workflow->setConduitCredentials($credentials);
if ($need_auth) { if ($need_auth) {
if (!$user_name || !$certificate) { if ((!$user_name || !$certificate) && (!$conduit_token)) {
$arc = 'arc'; $arc = 'arc';
if ($force_conduit) { if ($force_conduit) {
$arc .= csprintf(' --conduit-uri=%s', $conduit_uri); $arc .= csprintf(' --conduit-uri=%s', $conduit_uri);
} }
$conduit_domain = id(new PhutilURI($conduit_uri))->getDomain();
throw new ArcanistUsageException( throw new ArcanistUsageException(
phutil_console_format( phutil_console_format(
"YOU NEED TO __INSTALL A CERTIFICATE__ TO LOGIN TO PHABRICATOR\n\n". "YOU NEED TO AUTHENTICATE TO CONTINUE\n\n".
"You are trying to connect to '{$conduit_uri}' but do not have ". "You are trying to connect to a server ({$conduit_domain}) that you ".
"a certificate installed for this host. Run:\n\n". "do not have any credentials stored for.\n\n".
" $ **{$arc} install-certificate**\n\n". "To retrieve and store credentials for this server, ".
"...to install one.")); "**run this command:**\n\n".
" $ **{$arc} install-certificate**\n"));
} }
$workflow->authenticateConduit(); $workflow->authenticateConduit();
} }

View file

@ -48,45 +48,99 @@ EOTEXT
} }
public function run() { public function run() {
$console = PhutilConsole::getConsole();
$uri = $this->determineConduitURI(); $uri = $this->determineConduitURI();
$this->setConduitURI($uri); $this->setConduitURI($uri);
$configuration_manager = $this->getConfigurationManager(); $configuration_manager = $this->getConfigurationManager();
echo "Installing certificate for '{$uri}'...\n";
$config = $configuration_manager->readUserConfigurationFile(); $config = $configuration_manager->readUserConfigurationFile();
echo "Trying to connect to server...\n"; $console->writeOut(
"%s\n",
pht('Trying to connect to server...'));
$conduit = $this->establishConduit()->getConduit(); $conduit = $this->establishConduit()->getConduit();
try { try {
$conduit->callMethodSynchronous('conduit.ping', array()); $conduit->callMethodSynchronous('conduit.ping', array());
} catch (Exception $ex) { } catch (Exception $ex) {
throw new ArcanistUsageException( throw new ArcanistUsageException(
'Failed to connect to server: '.$ex->getMessage()); pht(
'Failed to connect to server (%s): %s',
$uri,
$ex->getMessage()));
} }
echo "Connection OK!\n";
$token_uri = new PhutilURI($uri); $token_uri = new PhutilURI($uri);
$token_uri->setPath('/conduit/token/'); $token_uri->setPath('/conduit/token/');
echo "\n"; // Check if this server supports the more modern token-based login.
$is_token_auth = false;
try {
$capabilities = $conduit->callMethodSynchronous(
'conduit.getcapabilities',
array());
$auth = idx($capabilities, 'authentication', array());
if (in_array('token', $auth)) {
$token_uri->setPath('/conduit/login/');
$is_token_auth = true;
}
} catch (Exception $ex) {
// Ignore.
}
echo phutil_console_format("**LOGIN TO PHABRICATOR**\n"); echo phutil_console_format("**LOGIN TO PHABRICATOR**\n");
echo "Open this page in your browser and login to Phabricator if ". echo "Open this page in your browser and login to Phabricator if ".
"necessary:\n"; "necessary:\n";
echo "\n"; echo "\n";
echo " {$token_uri}\n"; echo " {$token_uri}\n";
echo "\n"; echo "\n";
echo 'Then paste the token on that page below.'; echo 'Then paste the API Token on that page below.';
do { do {
$token = phutil_console_prompt('Paste token from that page:'); $token = phutil_console_prompt('Paste API Token from that page:');
$token = trim($token); $token = trim($token);
if (strlen($token)) { if (strlen($token)) {
break; break;
} }
} while (true); } while (true);
if ($is_token_auth) {
if (strlen($token) != 32) {
throw new ArcanistUsageException(
pht(
'The token "%s" is not formatted correctly. API tokens should '.
'be 32 characters long. Make sure you visited the correct URI '.
'and copy/pasted the token correctly.',
$token));
}
if (strncmp($token, 'cli-', 4) !== 0) {
throw new ArcanistUsageException(
pht(
'The token "%s" is not formatted correctly. Valid API tokens '.
'should begin "cli-" and be 32 characters long. Make sure you '.
'visited the correct URI and copy/pasted the token correctly.',
$token));
}
$conduit->setConduitToken($token);
try {
$conduit->callMethodSynchronous('user.whoami', array());
} catch (Exception $ex) {
throw new ArcanistUsageException(
pht(
'The token "%s" is not a valid API Token. The server returned '.
'this response when trying to use it as a token: %s',
$token,
$ex->getMessage()));
}
$config['hosts'][$uri] = array(
'token' => $token,
);
} else {
echo "\n"; echo "\n";
echo "Downloading authentication certificate...\n"; echo "Downloading authentication certificate...\n";
$info = $conduit->callMethodSynchronous( $info = $conduit->callMethodSynchronous(
@ -102,12 +156,18 @@ EOTEXT
'user' => $user, 'user' => $user,
'cert' => $info['certificate'], 'cert' => $info['certificate'],
); );
}
echo "Writing ~/.arcrc...\n"; echo "Writing ~/.arcrc...\n";
$configuration_manager->writeUserConfigurationFile($config); $configuration_manager->writeUserConfigurationFile($config);
if ($is_token_auth) {
echo phutil_console_format(
"<bg:green>** SUCCESS! **</bg> API Token installed.\n");
} else {
echo phutil_console_format( echo phutil_console_format(
"<bg:green>** SUCCESS! **</bg> Certificate installed.\n"); "<bg:green>** SUCCESS! **</bg> Certificate installed.\n");
}
return 0; return 0;
} }

View file

@ -324,6 +324,31 @@ abstract class ArcanistWorkflow extends Phobject {
'authenticating conduit!'); 'authenticating conduit!');
} }
// If we have `token`, this server supports the simpler, new-style
// token-based authentication. Use that instead of all the certificate
// stuff.
if (isset($credentials['token'])) {
$conduit = $this->getConduit();
$conduit->setConduitToken($credentials['token']);
try {
$result = $this->getConduit()->callMethodSynchronous(
'user.whoami',
array());
$this->userName = $result['userName'];
$this->userPHID = $result['phid'];
$this->conduitAuthenticated = true;
return;
} catch (Exception $ex) {
$conduit->setConduitToken(null);
throw $ex;
}
}
if (empty($credentials['user'])) { if (empty($credentials['user'])) {
throw new ConduitClientException( throw new ConduitClientException(
'ERR-INVALID-USER', 'ERR-INVALID-USER',
@ -351,7 +376,8 @@ abstract class ArcanistWorkflow extends Phobject {
)); ));
} catch (ConduitClientException $ex) { } catch (ConduitClientException $ex) {
if ($ex->getErrorCode() == 'ERR-NO-CERTIFICATE' || if ($ex->getErrorCode() == 'ERR-NO-CERTIFICATE' ||
$ex->getErrorCode() == 'ERR-INVALID-USER') { $ex->getErrorCode() == 'ERR-INVALID-USER' ||
$ex->getErrorCode() == 'ERR-INVALID-AUTH') {
$conduit_uri = $this->conduitURI; $conduit_uri = $this->conduitURI;
$message = $message =
"\n". "\n".