Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI-1212: [auth:logout] remove secrets #1643

Merged
merged 4 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Command/Auth/AuthAcsfLoginCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(name: 'auth:acsf-login', description: 'Register your Site Factory API key and secret to use API functionality')]
#[AsCommand(name: 'auth:acsf-login', description: 'Register Site Factory API credentials')]
final class AuthAcsfLoginCommand extends CommandBase {

protected function configure(): void {
Expand Down
2 changes: 1 addition & 1 deletion src/Command/Auth/AuthAcsfLogoutCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(name: 'auth:acsf-logout', description: 'Remove your Site Factory key and secret from your local machine.')]
#[AsCommand(name: 'auth:acsf-logout', description: 'Remove Site Factory API credentials')]
final class AuthAcsfLogoutCommand extends CommandBase {

protected function execute(InputInterface $input, OutputInterface $output): int {
Expand Down
28 changes: 16 additions & 12 deletions src/Command/Auth/AuthLoginCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,40 @@
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(name: 'auth:login', description: 'Register your Cloud API key and secret to use API functionality', aliases: ['login'])]
#[AsCommand(name: 'auth:login', description: 'Register Cloud Platform API credentials', aliases: ['login'])]
final class AuthLoginCommand extends CommandBase {

protected function configure(): void {
$this
->addOption('key', 'k', InputOption::VALUE_REQUIRED, 'Your Cloud API key')
->addOption('secret', 's', InputOption::VALUE_REQUIRED, 'Your Cloud API secret');
->addOption('key', 'k', InputOption::VALUE_REQUIRED, 'Your Cloud Platform API key')
->addOption('secret', 's', InputOption::VALUE_REQUIRED, 'Your Cloud Platform API secret')
->setHelp('Acquia CLI can store multiple sets of credentials in case you have multiple Cloud Platform accounts. However, only a single account can be active at a time. This command allows you to activate a new or existing set of credentials.');
}

protected function execute(InputInterface $input, OutputInterface $output): int {
if ($this->cloudApiClientService->isMachineAuthenticated()) {
$answer = $this->io->confirm('Your machine has already been authenticated with the Cloud Platform API, would you like to re-authenticate?');
if (!$answer) {
return Command::SUCCESS;
}
$keys = $this->datastoreCloud->get('keys');
$activeKey = $this->datastoreCloud->get('acli_key');
if ($activeKey) {
$activeKeyLabel = $keys[$activeKey]['label'];
$output->write("The following Cloud Platform API key is active: <options=bold>$activeKeyLabel</>");

Check warning on line 29 in src/Command/Auth/AuthLoginCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/Auth/AuthLoginCommand.php#L28-L29

Added lines #L28 - L29 were not covered by tests
}
else {
$output->write('No Cloud Platform API key is active');

Check warning on line 32 in src/Command/Auth/AuthLoginCommand.php

View workflow job for this annotation

GitHub Actions / Mutation Testing

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ $activeKeyLabel = $keys[$activeKey]['label']; $output->write("The following Cloud Platform API key is active: <options=bold>{$activeKeyLabel}</>"); } else { - $output->write('No Cloud Platform API key is active'); + } // If keys already are saved locally, prompt to select. if ($keys && $input->isInteractive()) {
}

// If keys already are saved locally, prompt to select.
if ($input->isInteractive() && $keys = $this->datastoreCloud->get('keys')) {
if ($keys && $input->isInteractive()) {

Check warning on line 36 in src/Command/Auth/AuthLoginCommand.php

View workflow job for this annotation

GitHub Actions / Mutation Testing

Escaped Mutant for Mutator "LogicalAnd": --- Original +++ New @@ @@ $output->write('No Cloud Platform API key is active'); } // If keys already are saved locally, prompt to select. - if ($keys && $input->isInteractive()) { + if ($keys || $input->isInteractive()) { foreach ($keys as $uuid => $key) { $keys[$uuid]['uuid'] = $uuid; }

Check warning on line 36 in src/Command/Auth/AuthLoginCommand.php

View workflow job for this annotation

GitHub Actions / Mutation Testing

Escaped Mutant for Mutator "LogicalAndSingleSubExprNegation": --- Original +++ New @@ @@ $output->write('No Cloud Platform API key is active'); } // If keys already are saved locally, prompt to select. - if ($keys && $input->isInteractive()) { + if (!$keys && $input->isInteractive()) { foreach ($keys as $uuid => $key) { $keys[$uuid]['uuid'] = $uuid; }

Check warning on line 36 in src/Command/Auth/AuthLoginCommand.php

View workflow job for this annotation

GitHub Actions / Mutation Testing

Escaped Mutant for Mutator "LogicalAndSingleSubExprNegation": --- Original +++ New @@ @@ $output->write('No Cloud Platform API key is active'); } // If keys already are saved locally, prompt to select. - if ($keys && $input->isInteractive()) { + if ($keys && !$input->isInteractive()) { foreach ($keys as $uuid => $key) { $keys[$uuid]['uuid'] = $uuid; }
foreach ($keys as $uuid => $key) {
$keys[$uuid]['uuid'] = $uuid;
}
$keys['create_new'] = [
'label' => 'Enter a new API key',
'uuid' => 'create_new',
];
$selectedKey = $this->promptChooseFromObjectsOrArrays($keys, 'uuid', 'label', 'Choose which API key to use');
$selectedKey = $this->promptChooseFromObjectsOrArrays($keys, 'uuid', 'label', 'Activate a Cloud Platform API key');

Check warning on line 44 in src/Command/Auth/AuthLoginCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/Auth/AuthLoginCommand.php#L44

Added line #L44 was not covered by tests
if ($selectedKey['uuid'] !== 'create_new') {
$this->datastoreCloud->set('acli_key', $selectedKey['uuid']);
$output->writeln("<info>Acquia CLI will use the API Key <options=bold>{$selectedKey['label']}</></info>");
$output->writeln("<info>Acquia CLI will use the API key <options=bold>{$selectedKey['label']}</></info>");

Check warning on line 47 in src/Command/Auth/AuthLoginCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/Auth/AuthLoginCommand.php#L47

Added line #L47 was not covered by tests
$this->reAuthenticate($this->cloudCredentials->getCloudKey(), $this->cloudCredentials->getCloudSecret(), $this->cloudCredentials->getBaseUri(), $this->cloudCredentials->getAccountsUri());
return Command::SUCCESS;
}
Expand All @@ -57,7 +61,7 @@
}

private function writeApiCredentialsToDisk(string $apiKey, string $apiSecret): void {
$tokenInfo = $this->cloudApiClientService->getClient()->request('get', "/account/tokens/{$apiKey}");
$tokenInfo = $this->cloudApiClientService->getClient()->request('get', "/account/tokens/$apiKey");
$keys = $this->datastoreCloud->get('keys');
$keys[$apiKey] = [
'label' => $tokenInfo->label,
Expand Down
29 changes: 21 additions & 8 deletions src/Command/Auth/AuthLogoutCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,37 @@
namespace Acquia\Cli\Command\Auth;

use Acquia\Cli\Command\CommandBase;
use Acquia\Cli\Exception\AcquiaCliException;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(name: 'auth:logout', description: 'Remove Cloud API key and secret from local machine.', aliases: ['logout'])]
#[AsCommand(name: 'auth:logout', description: 'Remove Cloud Platform API credentials', aliases: ['logout'])]
final class AuthLogoutCommand extends CommandBase {

protected function configure(): void {
$this->addOption('delete', NULL, InputOption::VALUE_NEGATABLE, 'Delete the active Cloud Platform API credentials');
}

protected function execute(InputInterface $input, OutputInterface $output): int {
if ($this->cloudApiClientService->isMachineAuthenticated()) {
$answer = $this->io->confirm('Are you sure you\'d like to unset the Acquia Cloud API key for Acquia CLI?');
if (!$answer) {
return Command::SUCCESS;
}
$keys = $this->datastoreCloud->get('keys');
$activeKey = $this->datastoreCloud->get('acli_key');
if (!$activeKey) {
throw new AcquiaCliException('There is no active Cloud Platform API key');

Check warning on line 26 in src/Command/Auth/AuthLogoutCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/Auth/AuthLogoutCommand.php#L26

Added line #L26 was not covered by tests
}
$activeKeyLabel = $keys[$activeKey]['label'];
$output->writeln("<info>The key <options=bold>$activeKeyLabel</> will be deactivated on this machine. However, the credentials will remain on disk and can be reactivated by running <options=bold>acli auth:login</> unless you also choose to delete them.</info>");
$delete = $this->determineOption('delete', FALSE, NULL, NULL, FALSE);
$this->datastoreCloud->remove('acli_key');

$output->writeln("Unset the Acquia Cloud API key for Acquia CLI</info>");
$action = 'deactivated';
if ($delete) {
$this->datastoreCloud->remove("keys.$activeKey");
$action = 'deleted';

Check warning on line 35 in src/Command/Auth/AuthLogoutCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Command/Auth/AuthLogoutCommand.php#L34-L35

Added lines #L34 - L35 were not covered by tests
}
$output->writeln("<info>The active Cloud Platform API credentials were $action</info>");
$output->writeln('<info>No Cloud Platform API key is active. Run <options=bold>acli auth:login</> to continue using the Cloud Platform API.</info>');

Check warning on line 38 in src/Command/Auth/AuthLogoutCommand.php

View workflow job for this annotation

GitHub Actions / Mutation Testing

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ $action = 'deleted'; } $output->writeln("<info>The active Cloud Platform API credentials were {$action}</info>"); - $output->writeln('<info>No Cloud Platform API key is active. Run <options=bold>acli auth:login</> to continue using the Cloud Platform API.</info>'); + return Command::SUCCESS; } }

return Command::SUCCESS;
}
Expand Down
23 changes: 19 additions & 4 deletions src/Command/CommandBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Terminal;
Expand Down Expand Up @@ -1349,7 +1350,7 @@
* explicitly or by default. In other words, we can't prompt for the value of
* an option that already has a default value.
*/
protected function determineOption(string $optionName, bool $hidden = FALSE, ?Closure $validator = NULL, ?Closure $normalizer = NULL, ?string $default = NULL): string|int|null {
protected function determineOption(string $optionName, bool $hidden = FALSE, ?Closure $validator = NULL, ?Closure $normalizer = NULL, string|bool|null $default = NULL): string|int|bool|null {

Check warning on line 1353 in src/Command/CommandBase.php

View workflow job for this annotation

GitHub Actions / Mutation Testing

Escaped Mutant for Mutator "FalseValue": --- Original +++ New @@ @@ * explicitly or by default. In other words, we can't prompt for the value of * an option that already has a default value. */ - protected function determineOption(string $optionName, bool $hidden = FALSE, ?Closure $validator = NULL, ?Closure $normalizer = NULL, string|bool|null $default = NULL) : string|int|bool|null + protected function determineOption(string $optionName, bool $hidden = true, ?Closure $validator = NULL, ?Closure $normalizer = NULL, string|bool|null $default = NULL) : string|int|bool|null { if ($optionValue = $this->input->getOption($optionName)) { if (isset($normalizer)) {
if ($optionValue = $this->input->getOption($optionName)) {
if (isset($normalizer)) {
$optionValue = $normalizer($optionValue);
Expand All @@ -1360,18 +1361,32 @@
return $optionValue;
}
$option = $this->getDefinition()->getOption($optionName);
if ($option->isNegatable() && $this->input->getOption("no-$optionName")) {
return FALSE;

Check warning on line 1365 in src/Command/CommandBase.php

View check run for this annotation

Codecov / codecov/patch

src/Command/CommandBase.php#L1365

Added line #L1365 was not covered by tests
}
$optionShortcut = $option->getShortcut();
$description = lcfirst($option->getDescription());
if ($optionShortcut) {
$message = "Enter $description (option <options=bold>-$optionShortcut</>, <options=bold>--$optionName</>)";
$optionString = "option <options=bold>-$optionShortcut</>, <options=bold>--$optionName</>";
}
else {
$optionString = "option <options=bold>--$optionName</>";
}
if ($option->acceptValue()) {
$message = "Enter $description ($optionString)";
}
else {
$message = "Enter $description (option <options=bold>--$optionName</>)";
$message = "Do you want to $description ($optionString)?";
}
$optional = $option->isValueOptional();
$message .= $optional ? ' (optional)' : '';
$message .= $hidden ? ' (input will be hidden)' : '';
$question = new Question($message, $default);
if ($option->acceptValue()) {
$question = new Question($message, $default);
}
else {
$question = new ConfirmationQuestion($message, $default);
}
$question->setHidden($this->localMachineHelper->useTty() && $hidden);
$question->setHiddenFallback($hidden);
if (isset($normalizer)) {
Expand Down
8 changes: 4 additions & 4 deletions tests/phpunit/src/Application/KernelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ private function getEnd(): string {
archive
archive:export Export an archive of the Drupal application including code, files, and database
auth
auth:acsf-login Register your Site Factory API key and secret to use API functionality
auth:acsf-logout Remove your Site Factory key and secret from your local machine.
auth:login [login] Register your Cloud API key and secret to use API functionality
auth:logout [logout] Remove Cloud API key and secret from local machine.
auth:acsf-login Register Site Factory API credentials
auth:acsf-logout Remove Site Factory API credentials
auth:login [login] Register Cloud Platform API credentials
auth:logout [logout] Remove Cloud Platform API credentials
codestudio
codestudio:php-version Change the PHP version in Code Studio
codestudio:wizard [cs:wizard] Create and/or configure a new Code Studio project for a given Acquia Cloud application
Expand Down
73 changes: 0 additions & 73 deletions tests/phpunit/src/Commands/Auth/AuthLoginCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,79 +22,6 @@ protected function createCommand(): Command {
}

public function providerTestAuthLoginCommand(): Generator {
yield 'Interactive keys' => [
// $machineIsAuthenticated
FALSE,
// $assertCloudPrompts
TRUE,
[
// Would you like to share anonymous performance usage and data? (yes/no) [yes]
'yes',
// Do you want to open this page to generate a token now?
'no',
// Enter your API Key:
$this->key,
// Enter your API Secret:
$this->secret,
],
// No arguments, all interactive.
[],
// Output to assert.
'Saved credentials',
];
yield 'Already authenticated, enter new key' => [
// $machineIsAuthenticated
TRUE,
// $assertCloudPrompts
TRUE,
[
// Your machine has already been authenticated with the Cloud Platform API, would you like to re-authenticate?
'yes',
// Choose which API key to use:
"Enter a new API key",
// Do you want to open this page to generate a token now?
'no',
// Enter your API Key:
$this->key,
// Enter your API Secret:
$this->secret,
],
// No arguments, all interactive.
[],
// Output to assert.
'Saved credentials',
];
yield 'Already authenticated, use existing key' => [
// $machineIsAuthenticated
TRUE,
// $assertCloudPrompts
FALSE,
[
// Your machine has already been authenticated with the Cloud Platform API, would you like to re-authenticate?
'yes',
// Choose which API key to use:
'Test Key',
// @todo Make sure this key has the right value to assert.
],
// No arguments, all interactive.
[],
// Output to assert.
'Acquia CLI will use the API Key',
];
yield 'Already authenticated, abort' => [
// $machineIsAuthenticated
TRUE,
// $assertCloudPrompts
FALSE,
[
// Your machine has already been authenticated with the Cloud Platform API, would you like to re-authenticate?
'no',
],
// No arguments, all interactive.
[],
// Output to assert.
'Your machine has already been authenticated',
];
yield 'Keys as args' => [
// $machineIsAuthenticated
FALSE,
Expand Down
32 changes: 6 additions & 26 deletions tests/phpunit/src/Commands/Auth/AuthLogoutCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,16 @@ protected function createCommand(): Command {
return $this->injectCommand(AuthLogoutCommand::class);
}

/**
* @return array<mixed>
*/
public function providerTestAuthLogoutCommand(): array {
return [
[FALSE, []],
[
TRUE,
// Are you sure you'd like to remove your Cloud API login credentials from this machine?
['y'],
],
];
}

/**
* @dataProvider providerTestAuthLogoutCommand
* @param array $inputs
*/
public function testAuthLogoutCommand(bool $machineIsAuthenticated, array $inputs): void {
if (!$machineIsAuthenticated) {
$this->clientServiceProphecy->isMachineAuthenticated()->willReturn(FALSE);
$this->removeMockCloudConfigFile();
}

$this->executeCommand([], $inputs);
public function testAuthLogoutCommand(): void {
$this->executeCommand();
$output = $this->getDisplay();
// Assert creds are removed locally.
$this->assertFileExists($this->cloudConfigFilepath);
$config = new CloudDataStore($this->localMachineHelper, new CloudDataConfig(), $this->cloudConfigFilepath);
$this->assertFalse($config->exists('acli_key'));
$this->assertNotEmpty($config->get('keys'));
$this->assertStringContainsString('The key Test Key will be deactivated on this machine.', $output);
$this->assertStringContainsString('Do you want to delete the active Cloud Platform API credentials (option --delete)? (yes/no) [no]:', $output);
$this->assertStringContainsString('The active Cloud Platform API credentials were deactivated', $output);
}

}