Skip to content

Commit

Permalink
feat: --skip-if-exists and --overwrite functions
Browse files Browse the repository at this point in the history
  • Loading branch information
bpolaszek committed Jan 1, 2021
1 parent dfa4861 commit 9b47099
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 5 deletions.
37 changes: 36 additions & 1 deletion Command/GenerateKeyPairCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ public function __construct(Filesystem $filesystem, string $secretKey, string $p
protected function configure()
{
$this->setDescription('Generate public/private keys for use in your application.');
$this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not update config files.');
$this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not update key files.');
$this->addOption('skip-if-exists', null, InputOption::VALUE_NONE, 'Do not update key files if they already exist.');
$this->addOption('overwrite', null, InputOption::VALUE_NONE, 'Overwrite key files if they already exist.');
}

protected function execute(InputInterface $input, OutputInterface $output)
Expand All @@ -57,6 +59,24 @@ protected function execute(InputInterface $input, OutputInterface $output)

list($privateKey, $publicKey) = self::generateKeyPair($this->passphrase);

$alreadyExists = $this->filesystem->exists($this->secretKey) || $this->filesystem->exists($this->publicKey);

if (true === $alreadyExists) {
try {
$this->handleExistingKeys($input);
} catch (\RuntimeException $e) {
if (0 === $e->getCode()) {
$io->comment($e->getMessage());

return 0;
}

$io->error($e->getMessage());

return 1;
}
}

if (true === $input->getOption('dry-run')) {
$io->success('Your keys have been generated!');
$io->newLine();
Expand All @@ -83,6 +103,21 @@ protected function execute(InputInterface $input, OutputInterface $output)
return 0;
}

private function handleExistingKeys(InputInterface $input): void
{
if (true === $input->getOption('skip-if-exists') && true === $input->getOption('overwrite')) {
throw new \RuntimeException('Both options `--skip-if-exists` and `--overwrite` cannot be combined.', 1);
}

if (true === $input->getOption('skip-if-exists')) {
throw new \RuntimeException('Your key files already exist, they won\'t be overriden.', 0);
}

if (false === $input->getOption('overwrite')) {
throw new \RuntimeException('Your keys already exist. Use the `--overwrite` option to force regeneration.', 1);
}
}

private static function generateKeyPair($passphrase)
{
$config = [
Expand Down
8 changes: 8 additions & 0 deletions Resources/doc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ return [
$ php bin/console lexik:jwt:generate-keypair
```

Your keys will land in `config/jwt/private.pem` and `config/jwt/public.pem` (unless you configured a different path).

Available options:
- `--skip-if-exists` will silently do nothing if keys already exist.
- `--overwrite` will overwrite your keys if they already exist.

Otherwise, an error will be raised to prevent you from overwriting your keys accidentally.

Configuration
-------------

Expand Down
167 changes: 163 additions & 4 deletions Tests/Functional/Command/GenerateKeyPairCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ class GenerateKeyPairCommandTest extends TestCase
/**
* @dataProvider providePassphrase
*/
public function testRun($passphrase)
public function testItGeneratesKeyPair($passphrase)
{
$privateKeyFile = \tempnam(\sys_get_temp_dir(), 'private_');
$publicKeyFile = \tempnam(\sys_get_temp_dir(), 'public_');

// tempnam() actually create the files, but we have to simulate they don't exist
\unlink($privateKeyFile);
\unlink($publicKeyFile);

$tester = new CommandTester(
new GenerateKeyPairCommand(
new Filesystem(),
Expand All @@ -25,16 +30,170 @@ public function testRun($passphrase)
)
);

$this->assertSame(0, $tester->execute([], ['interactive' => false]));
$this->assertNotFalse($privateKey = \file_get_contents($privateKeyFile));
$returnCode = $tester->execute([], ['interactive' => false]);
$this->assertSame(0, $returnCode);

$privateKey = \file_get_contents($privateKeyFile);
$publicKey = \file_get_contents($publicKeyFile);
$this->assertStringContainsString('Done!', $tester->getDisplay(true));
$this->assertNotFalse($privateKey);
$this->assertNotFalse($publicKey);
$this->assertStringContainsString('PRIVATE KEY', $privateKey);
$this->assertNotFalse($publicKey = \file_get_contents($publicKeyFile));
$this->assertStringContainsString('PUBLIC KEY', $publicKey);

// Encryption / decryption test
$payload = 'Despite the constant negative press covfefe';
\openssl_public_encrypt($payload, $encryptedData, \openssl_pkey_get_public($publicKey));
\openssl_private_decrypt($encryptedData, $decryptedData, \openssl_pkey_get_private($privateKey, $passphrase));
$this->assertSame($payload, $decryptedData);

}

public function providePassphrase()
{
yield [null];
yield ['dummy'];
}

public function testOverwriteAndSkipCannotBeCombined()
{
$privateKeyFile = \tempnam(\sys_get_temp_dir(), 'private_');
$publicKeyFile = \tempnam(\sys_get_temp_dir(), 'public_');

\file_put_contents($privateKeyFile, 'foobar');
\file_put_contents($publicKeyFile, 'foobar');

$tester = new CommandTester(
new GenerateKeyPairCommand(
new Filesystem(),
$privateKeyFile,
$publicKeyFile,
null
)
);
$input = ['--overwrite' => true, '--skip-if-exists' => true];
$returnCode = $tester->execute($input, ['interactive' => false]);
$this->assertSame(1, $returnCode);
$this->assertStringContainsString(
'Both options `--skip-if-exists` and `--overwrite` cannot be combined.',
$tester->getDisplay(true)
);

$privateKey = \file_get_contents($privateKeyFile);
$publicKey = \file_get_contents($publicKeyFile);
$this->assertStringContainsString('foobar', $privateKey);
$this->assertStringContainsString('foobar', $publicKey);
}

public function testNoOverwriteDoesNotOverwrite()
{
$privateKeyFile = \tempnam(\sys_get_temp_dir(), 'private_');
$publicKeyFile = \tempnam(\sys_get_temp_dir(), 'public_');

\file_put_contents($privateKeyFile, 'foobar');
\file_put_contents($publicKeyFile, 'foobar');

$tester = new CommandTester(
new GenerateKeyPairCommand(
new Filesystem(),
$privateKeyFile,
$publicKeyFile,
null
)
);

$returnCode = $tester->execute([], ['interactive' => false]);
$this->assertSame(1, $returnCode);
$this->assertStringContainsString(
'Your keys already exist. Use the `--overwrite` option to force regeneration.',
\strtr($tester->getDisplay(true), ["\n" => ''])
);

$privateKey = \file_get_contents($privateKeyFile);
$publicKey = \file_get_contents($publicKeyFile);
$this->assertStringContainsString('foobar', $privateKey);
$this->assertStringContainsString('foobar', $publicKey);
}

public function testOverwriteActuallyOverwrites()
{
$privateKeyFile = \tempnam(\sys_get_temp_dir(), 'private_');
$publicKeyFile = \tempnam(\sys_get_temp_dir(), 'public_');

\file_put_contents($privateKeyFile, 'foobar');
\file_put_contents($publicKeyFile, 'foobar');

$tester = new CommandTester(
new GenerateKeyPairCommand(
new Filesystem(),
$privateKeyFile,
$publicKeyFile,
null
)
);

$returnCode = $tester->execute(['--overwrite' => true], ['interactive' => false]);
$privateKey = \file_get_contents($privateKeyFile);
$publicKey = \file_get_contents($publicKeyFile);

$this->assertSame(0, $returnCode);
$this->assertStringContainsString('PRIVATE KEY', $privateKey);
$this->assertStringContainsString('PUBLIC KEY', $publicKey);
}

public function testSkipIfExistsWritesIfNotExists()
{
$privateKeyFile = \tempnam(\sys_get_temp_dir(), 'private_');
$publicKeyFile = \tempnam(\sys_get_temp_dir(), 'public_');

// tempnam() actually create the files, but we have to simulate they don't exist
\unlink($privateKeyFile);
\unlink($publicKeyFile);

$tester = new CommandTester(
new GenerateKeyPairCommand(
new Filesystem(),
$privateKeyFile,
$publicKeyFile,
null
)
);

$this->assertSame(0, $tester->execute(['--skip-if-exists' => true], ['interactive' => false]));
$this->assertStringContainsString('Done!', $tester->getDisplay(true));
$privateKey = \file_get_contents($privateKeyFile);
$publicKey = \file_get_contents($publicKeyFile);
$this->assertStringContainsString('PRIVATE KEY', $privateKey);
$this->assertStringContainsString('PUBLIC KEY', $publicKey);
}

public function testSkipIfExistsDoesNothingIfExists()
{
$privateKeyFile = \tempnam(\sys_get_temp_dir(), 'private_');
$publicKeyFile = \tempnam(\sys_get_temp_dir(), 'public_');

\file_put_contents($privateKeyFile, 'foobar');
\file_put_contents($publicKeyFile, 'foobar');

$tester = new CommandTester(
new GenerateKeyPairCommand(
new Filesystem(),
$privateKeyFile,
$publicKeyFile,
null
)
);

$this->assertSame(0, $tester->execute(['--skip-if-exists' => true], ['interactive' => false]));
$this->assertStringContainsString(
'Your key files already exist, they won\'t be overriden.',
$tester->getDisplay(true)
);

$privateKey = \file_get_contents($privateKeyFile);
$publicKey = \file_get_contents($publicKeyFile);
$this->assertStringContainsString('foobar', $privateKey);
$this->assertStringContainsString('foobar', $publicKey);
}

}

0 comments on commit 9b47099

Please sign in to comment.