mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-24 15:52: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:
parent
565a96e0ee
commit
5118987757
3 changed files with 123 additions and 32 deletions
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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".
|
||||||
|
|
Loading…
Reference in a new issue