Skip to content

Commit

Permalink
Add SSH password authentication method
Browse files Browse the repository at this point in the history
  • Loading branch information
Benoit Wannepain committed Nov 27, 2018
1 parent cc54547 commit 5345854
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 16 deletions.
14 changes: 10 additions & 4 deletions src/Infrastructure/Cli/Ssh.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ public function __construct(string $host, int $port = 22)
$this->port = $port;
}

public function exec(string $command, string $username): ?string
public function exec(string $command, string $username, ?string $password = null): ?string
{
$connection = $this->getAuthenticatedConnection($username);
$connection = $this->getAuthenticatedConnection($username, $password);

$stream = ssh2_exec($connection, $command);
if (!is_resource($stream)) {
Expand All @@ -49,7 +49,7 @@ public function exec(string $command, string $username): ?string
return $output;
}

public function getAuthenticatedConnection(string $username)
public function getAuthenticatedConnection(string $username, ?$password = null)
{
$connection = ssh2_connect($this->host, $this->port);

Expand All @@ -63,7 +63,13 @@ public function getAuthenticatedConnection(string $username)
);
}

if (false === ssh2_auth_agent($connection, $username)) {
if (!empty($password)) {
$res = ssh2_auth_password($connection, $username, $password);
} else {
$res = ssh2_auth_agent($connection, $username);
}

if (false === $res) {
throw new ImpossibleConnectionException(
sprintf(
'Impossible to login to %s@%s:%d using ssh local agent, try to run ssh-add before retry',
Expand Down
6 changes: 3 additions & 3 deletions src/Infrastructure/Cli/SshConsole.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public function execute(Command $command, Pim $pim): CommandResult

$this->logger->debug(sprintf('SshConsole: executing MySqlQueryCommand -> %s', $command->getCommand()));

$output = $ssh->exec($query, $connection->getUsername());
$output = $ssh->exec($query, $connection->getUsername(), $connection->getPassword());

return new CommandResult($output !== false ? 0 : 1, '');
}
Expand All @@ -74,7 +74,7 @@ public function execute(Command $command, Pim $pim): CommandResult

$this->logger->debug(sprintf('SshConsole: executing MySqlQueryCommand -> %s', $command->getCommand()));

$output = $ssh->exec($query, $connection->getUsername());
$output = $ssh->exec($query, $connection->getUsername(), $connection->getPassword());

$lines = array_filter(explode(PHP_EOL, $output), function ($element) {
return !empty(trim($element));
Expand All @@ -96,7 +96,7 @@ public function execute(Command $command, Pim $pim): CommandResult

$this->logger->debug(sprintf('SshConsole: executing %s command -> %s', get_class($command), $processedCommand));

$output = $ssh->exec($processedCommand, $connection->getUsername());
$output = $ssh->exec($processedCommand, $connection->getUsername(), $connection->getPassword());

return new CommandResult($output !== false ? 0 : 1, $output);
}
Expand Down
1 change: 1 addition & 0 deletions src/Infrastructure/Common/config/parameters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ parameters:
ssh_hostname_source_pim: ''
ssh_port_source_pim: '22'
ssh_user_source_pim: ''
ssh_passwd_source_pim: ''

# Base URI (with http://) to request the API
api_base_uri_source_pim: ''
Expand Down
2 changes: 2 additions & 0 deletions src/Infrastructure/Common/messages.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ from_ready_to_source_pim_configured:
ssh_port_question: "What is the SSH port of the source PIM? "
ssh_port_error: "The SSH port should be an int. "
ssh_user_question: "What is the SSH user you want to connect with to the source PIM? "
ssh_passwd_question: "What is the SSH password you want to connect with to the source PIM?"
ssh_auth_mode_question: "How to you want to authenticate on the source PIM SSH server?"
ssh_key_path_question: "What is the absolute path of the private SSH key able to connect to the source PIM? "
ssh_key_path_error: "Your SSH key path should be an absolute one."
ssh_key_protected: "Is your ssh key protected by a passphrase ? "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ class S010FromReadyToSourcePimConfigured extends AbstractStateMachineSubscriber
private const LOCAL_SOURCE_PIM = 'locally';
private const REMOTE_SOURCE_PIM = 'on a remote server';

private const SSH_AUTH_KEYPAIR = "ssh keypair";
private const SSH_AUTH_PASSWORD = "password";

private const YES = 'yes';
private const NO = 'no';

Expand Down Expand Up @@ -161,8 +164,19 @@ function ($answer) use ($transPrefix) {
$this->translator->trans($transPrefix.'ssh_user_question'),
$stateMachine->getDefaultResponse('ssh_user_source_pim')
);
$sshAuthMode = $this->printerAndAsker->askChoiceQuestion(
$this->translator->trans($transPrefix.'ssh_auth_mode_question'),
[self::SSH_AUTH_KEYPAIR, self::SSH_AUTH_PASSWORD]
);
$password = null;
if ($sshAuthMode === self::SSH_AUTH_PASSWORD) {
$password = $this->printerAndAsker->askSimpleQuestion(
$this->translator->trans($transPrefix . 'ssh_passwd_question'),
$stateMachine->getDefaultResponse('ssh_passwd_source_pim')
);
}

$stateMachine->setSourcePimConnection(new SshConnection($host, $port, $user));
$stateMachine->setSourcePimConnection(new SshConnection($host, $port, $user, $password));

$pimProjectPath = $this
->printerAndAsker
Expand Down
11 changes: 10 additions & 1 deletion src/Infrastructure/Pim/SshConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@ class SshConnection implements PimConnection
/** @var string */
private $username;

public function __construct(string $host, int $port, string $username)
/** @var string */
private $password;

public function __construct(string $host, int $port, string $username, ?string $password = null)
{
$this->host = $host;
$this->port = $port;
$this->username = $username;
$this->password = $password;
}

public static function fromString(string $serverInformation)
Expand Down Expand Up @@ -56,4 +60,9 @@ public function getUsername(): string
{
return $this->username;
}

public function getPassword(): ?string
{
return $this->password;
}
}
5 changes: 3 additions & 2 deletions src/Infrastructure/SshFileFetcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ public function fetch(PimConnection $connection, string $filePath, bool $withLoc

$output = $ssh->exec(
sprintf('test -f %s ; echo "$?"', $filePath),
$connection->getUsername()
$connection->getUsername(),
$connection->getPassword()
);

if ("0" !== trim($output)) {
Expand All @@ -40,7 +41,7 @@ public function fetch(PimConnection $connection, string $filePath, bool $withLoc
$varDir = sprintf('%s/../../var', __DIR__);
$localPath = realpath($varDir).DIRECTORY_SEPARATOR.$fileName;

$sshConnection = $ssh->getAuthenticatedConnection($connection->getUsername());
$sshConnection = $ssh->getAuthenticatedConnection($connection->getUsername(), $connection->getPassword());
$result = ssh2_scp_recv($sshConnection, $filePath, $localPath);
$ssh->disconnect($sshConnection);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public function it_guards_the_distant_source_pim_configuration(
$this->guardDistantSourcePimConfiguration($guardEvent);
}

public function it_configures_a_source_pim_from_a_server(
public function it_configures_a_source_pim_from_a_server_with_ssh_keypair(
Event $event,
TransporteoStateMachine $stateMachine,
PimConfiguration $sourcePimConfiguration,
Expand All @@ -117,6 +117,8 @@ public function it_configures_a_source_pim_from_a_server(
$projectPathQuestion = 'What is the absolute path of the source PIM on the server? ';
$sshKeyProtected = 'Is your ssh key protected by a passphrase ? ';
$sshKeyPassphrase = 'Enter passphrase for %s ';
$sshPasswdQuestion = 'What is the SSH password you want to connect with to the source PIM?';
$sshAuthMode = 'How to you want to authenticate on the source PIM SSH server?';

$transPrefix = 'from_ready_to_source_pim_configured.on_distant_configuration.';
$translations = [
Expand All @@ -127,6 +129,8 @@ public function it_configures_a_source_pim_from_a_server(
$transPrefix . 'project_path_question' => $projectPathQuestion,
$transPrefix . 'ssh_key_protected' => $sshKeyProtected,
$transPrefix . 'ssh_key_passphrase' => $sshKeyPassphrase,
$transPrefix . 'ssh_passwd_question' => $sshPasswdQuestion,
$transPrefix . 'ssh_auth_mode_question' => $sshAuthMode,
];

foreach ($translations as $translationKey => $translation) {
Expand All @@ -142,14 +146,76 @@ public function it_configures_a_source_pim_from_a_server(
$printerAndAsker->askSimpleQuestion($sshUserQuestion, Argument::any(), Argument::any())->willReturn('akeneo');
$sshKeyPath = ResourcesFileLocator::getSshKeyPath();

$printerAndAsker->askChoiceQuestion($sshAuthMode, ['ssh keypair', 'password'])->willReturn('key pair');

$printerAndAsker->askSimpleQuestion($sshKeyPathQuestion, Argument::any(), Argument::any())->willReturn($sshKeyPath);
$printerAndAsker->askChoiceQuestion($sshKeyProtected, ['yes' => 'yes', 'no' => 'no'])->willReturn('no');

$sshKey = new SshKey($sshKeyPath);
$serverAccessInformation = new SshConnection('my-super-pim.akeneo.com', 22, 'akeneo', $sshKey);
$serverAccessInformation = new SshConnection('my-super-pim.akeneo.com', 22, 'akeneo');
$stateMachine->setSourcePimConnection($serverAccessInformation)->shouldBeCalled();
$stateMachine->getSourcePimConnection()->willReturn($serverAccessInformation);
$composerJsonPath = ResourcesFileLocator::getStepOneAbsoluteComposerJsonLocalPath();
$projectPath = str_replace('composer.json', '', $composerJsonPath);

$printerAndAsker->askSimpleQuestion($projectPathQuestion, Argument::any(), Argument::any())->willReturn($projectPath);
$sourcePimServerInformation = new PimServerInformation(ResourcesFileLocator::getStepOneAbsoluteComposerJsonLocalPath(), 'a-super-project');
$stateMachine->setSourcePimServerInformation($sourcePimServerInformation)->shouldBeCalled();

$sourcePimConfigurator->configure($serverAccessInformation, $sourcePimServerInformation)->willReturn($sourcePimConfiguration);
$stateMachine->setSourcePimConfiguration($sourcePimConfiguration)->shouldBeCalled();

$this->onDistantConfiguration($event);
}

public function it_configures_a_source_pim_from_a_server_with_ssh_passwd(
Event $event,
TransporteoStateMachine $stateMachine,
PimConfiguration $sourcePimConfiguration,
$sourcePimConfigurator,
$printerAndAsker,
$translator
) {
$hostNameQuestion = 'What is the hostname of the source PIM server? ';
$portQuestion = 'What is the SSH port of the source PIM server? ';
$sshUserQuestion = 'What is the SSH user you want to connect with ? ';
$sshKeyPathQuestion = 'What is the absolute path of the private SSH key able to connect to the server? ';
$projectPathQuestion = 'What is the absolute path of the source PIM on the server? ';
$sshKeyProtected = 'Is your ssh key protected by a passphrase ? ';
$sshKeyPassphrase = 'Enter passphrase for %s ';
$sshPasswdQuestion = 'What is the SSH password you want to connect with to the source PIM?';
$sshAuthMode = 'How to you want to authenticate on the source PIM SSH server?';

$transPrefix = 'from_ready_to_source_pim_configured.on_distant_configuration.';
$translations = [
$transPrefix . 'hostname_question' => $hostNameQuestion,
$transPrefix . 'ssh_port_question' => $portQuestion,
$transPrefix . 'ssh_user_question' => $sshUserQuestion,
$transPrefix . 'ssh_key_path_question' => $sshKeyPathQuestion,
$transPrefix . 'project_path_question' => $projectPathQuestion,
$transPrefix . 'ssh_key_protected' => $sshKeyProtected,
$transPrefix . 'ssh_key_passphrase' => $sshKeyPassphrase,
$transPrefix . 'ssh_passwd_question' => $sshPasswdQuestion,
$transPrefix . 'ssh_auth_mode_question' => $sshAuthMode,
];

foreach ($translations as $translationKey => $translation) {
$translator->trans($translationKey)->willReturn($translation);
}

$event->getSubject()->willReturn($stateMachine);
$stateMachine->getProjectName()->willReturn('a-super-project');
$stateMachine->getDefaultResponse(Argument::any())->willReturn('');

$printerAndAsker->askSimpleQuestion($hostNameQuestion, Argument::any(), Argument::any())->willReturn('my-super-pim.akeneo.com');
$printerAndAsker->askSimpleQuestion($portQuestion, '', Argument::any())->willReturn('22');
$printerAndAsker->askSimpleQuestion($sshUserQuestion, Argument::any(), Argument::any())->willReturn('akeneo');

$printerAndAsker->askChoiceQuestion($sshAuthMode, ['ssh keypair', 'password'])->willReturn('password');
$printerAndAsker->askSimpleQuestion($sshPasswdQuestion, Argument::any(), Argument::any())->willReturn('akeneo-passwd');

$serverAccessInformation = new SshConnection('my-super-pim.akeneo.com', 22, 'akeneo', 'akeneo-passwd');
$stateMachine->setSourcePimConnection($serverAccessInformation)->shouldBeCalled();
$stateMachine->getSourcePimConnection()->willReturn($serverAccessInformation);
$composerJsonPath = ResourcesFileLocator::getStepOneAbsoluteComposerJsonLocalPath();
$projectPath = str_replace('composer.json', '', $composerJsonPath);

Expand Down Expand Up @@ -218,6 +284,8 @@ public function it_throws_business_exception_from_technical(
$projectPathQuestion = 'What is the absolute path of the source PIM on the server? ';
$sshKeyProtected = 'Is your ssh key protected by a passphrase ? ';
$sshKeyPassphrase = 'Enter passphrase for %s ';
$sshPasswdQuestion = 'What is the SSH password you want to connect with to the source PIM?';
$sshAuthMode = 'How to you want to authenticate on the source PIM SSH server?';

$transPrefix = 'from_ready_to_source_pim_configured.on_distant_configuration.';
$translations = [
Expand All @@ -228,6 +296,8 @@ public function it_throws_business_exception_from_technical(
$transPrefix . 'project_path_question' => $projectPathQuestion,
$transPrefix . 'ssh_key_protected' => $sshKeyProtected,
$transPrefix . 'ssh_key_passphrase' => $sshKeyPassphrase,
$transPrefix . 'ssh_passwd_question' => $sshPasswdQuestion,
$transPrefix . 'ssh_auth_mode_question' => $sshAuthMode,
];

foreach ($translations as $translationKey => $translation) {
Expand All @@ -238,13 +308,14 @@ public function it_throws_business_exception_from_technical(
$printerAndAsker->askSimpleQuestion($portQuestion, '', Argument::any())->willReturn('22');
$printerAndAsker->askSimpleQuestion($sshUserQuestion, Argument::any(), Argument::any())->willReturn('akeneo');

$printerAndAsker->askChoiceQuestion($sshAuthMode, ['ssh keypair', 'password'])->willReturn('key pair');

$sshKeyPath = ResourcesFileLocator::getSshKeyPath();

$printerAndAsker->askSimpleQuestion($sshKeyPathQuestion, Argument::any(), Argument::any())->willReturn($sshKeyPath);
$printerAndAsker->askChoiceQuestion($sshKeyProtected, ['yes' => 'yes', 'no' => 'no'])->willReturn('no');

$sshKey = new SshKey($sshKeyPath);
$serverAccessInformation = new SshConnection('my-super-pim.akeneo.com', 22, 'akeneo', $sshKey);
$serverAccessInformation = new SshConnection('my-super-pim.akeneo.com', 22, 'akeneo');
$stateMachine->setSourcePimConnection($serverAccessInformation)->shouldBeCalled();
$stateMachine->getSourcePimConnection()->willReturn($serverAccessInformation);

Expand Down

0 comments on commit 5345854

Please sign in to comment.