1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-09-19 00:18:50 +02: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());
$user_name = idx($host_config, 'user');
$certificate = idx($host_config, 'cert');
$conduit_token = idx($host_config, 'token');
$description = implode(' ', $original_argv);
$credentials = array(
'user' => $user_name,
'user' => $user_name,
'certificate' => $certificate,
'description' => $description,
'token' => $conduit_token,
);
$workflow->setConduitCredentials($credentials);
if ($need_auth) {
if (!$user_name || !$certificate) {
if ((!$user_name || !$certificate) && (!$conduit_token)) {
$arc = 'arc';
if ($force_conduit) {
$arc .= csprintf(' --conduit-uri=%s', $conduit_uri);
}
$conduit_domain = id(new PhutilURI($conduit_uri))->getDomain();
throw new ArcanistUsageException(
phutil_console_format(
"YOU NEED TO __INSTALL A CERTIFICATE__ TO LOGIN TO PHABRICATOR\n\n".
"You are trying to connect to '{$conduit_uri}' but do not have ".
"a certificate installed for this host. Run:\n\n".
" $ **{$arc} install-certificate**\n\n".
"...to install one."));
"YOU NEED TO AUTHENTICATE TO CONTINUE\n\n".
"You are trying to connect to a server ({$conduit_domain}) that you ".
"do not have any credentials stored for.\n\n".
"To retrieve and store credentials for this server, ".
"**run this command:**\n\n".
" $ **{$arc} install-certificate**\n"));
}
$workflow->authenticateConduit();
}

View file

@ -48,66 +48,126 @@ EOTEXT
}
public function run() {
$console = PhutilConsole::getConsole();
$uri = $this->determineConduitURI();
$this->setConduitURI($uri);
$configuration_manager = $this->getConfigurationManager();
echo "Installing certificate for '{$uri}'...\n";
$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();
try {
$conduit->callMethodSynchronous('conduit.ping', array());
} catch (Exception $ex) {
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->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 "Open this page in your browser and login to Phabricator if ".
"necessary:\n";
echo "\n";
echo " {$token_uri}\n";
echo "\n";
echo 'Then paste the token on that page below.';
echo 'Then paste the API Token on that page below.';
do {
$token = phutil_console_prompt('Paste token from that page:');
$token = phutil_console_prompt('Paste API Token from that page:');
$token = trim($token);
if (strlen($token)) {
break;
}
} while (true);
echo "\n";
echo "Downloading authentication certificate...\n";
$info = $conduit->callMethodSynchronous(
'conduit.getcertificate',
array(
'token' => $token,
'host' => $uri,
));
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));
}
$user = $info['username'];
echo "Installing certificate for '{$user}'...\n";
$config['hosts'][$uri] = array(
'user' => $user,
'cert' => $info['certificate'],
);
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 "Downloading authentication certificate...\n";
$info = $conduit->callMethodSynchronous(
'conduit.getcertificate',
array(
'token' => $token,
'host' => $uri,
));
$user = $info['username'];
echo "Installing certificate for '{$user}'...\n";
$config['hosts'][$uri] = array(
'user' => $user,
'cert' => $info['certificate'],
);
}
echo "Writing ~/.arcrc...\n";
$configuration_manager->writeUserConfigurationFile($config);
echo phutil_console_format(
"<bg:green>** SUCCESS! **</bg> Certificate installed.\n");
if ($is_token_auth) {
echo phutil_console_format(
"<bg:green>** SUCCESS! **</bg> API Token installed.\n");
} else {
echo phutil_console_format(
"<bg:green>** SUCCESS! **</bg> Certificate installed.\n");
}
return 0;
}

View file

@ -324,6 +324,31 @@ abstract class ArcanistWorkflow extends Phobject {
'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'])) {
throw new ConduitClientException(
'ERR-INVALID-USER',
@ -351,7 +376,8 @@ abstract class ArcanistWorkflow extends Phobject {
));
} catch (ConduitClientException $ex) {
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;
$message =
"\n".