From 2ec9647e750feb5f3d52baf3d8b6e888e12b7abd Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 26 Oct 2019 14:59:20 +0200 Subject: [PATCH] [FrameworkBundle] Remove suffix convention when using env vars to override secrets from the vault --- .../Command/SecretsDecryptToLocalCommand.php | 2 +- .../SecretsEncryptFromLocalCommand.php | 26 +++++------------ .../Command/SecretsListCommand.php | 6 ++-- .../Command/SecretsSetCommand.php | 6 ++++ .../FrameworkBundle/Secrets/DotenvVault.php | 19 ++++++------- .../Secrets/SecretEnvVarProcessor.php | 4 +-- .../FrameworkBundle/Secrets/SodiumVault.php | 28 +++++++++++++++---- .../Tests/Secrets/DotenvVaultTest.php | 10 +++---- 8 files changed, 54 insertions(+), 47 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsDecryptToLocalCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsDecryptToLocalCommand.php index c9370768f0a03..e4fbfd287edee 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsDecryptToLocalCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsDecryptToLocalCommand.php @@ -45,7 +45,7 @@ protected function configure() ->setDescription('Decrypts all secrets and stores them in the local vault.') ->addOption('force', 'f', InputOption::VALUE_NONE, 'Forces overriding of secrets that already exist in the local vault') ->setHelp(<<<'EOF' -The %command.name% command list decrypts all secrets and stores them in the local vault.. +The %command.name% command decrypts all secrets and copies them in the local vault. %command.full_name% diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsEncryptFromLocalCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsEncryptFromLocalCommand.php index 3161a2198206b..607140e616486 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsEncryptFromLocalCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsEncryptFromLocalCommand.php @@ -14,7 +14,6 @@ use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; @@ -43,15 +42,10 @@ protected function configure() { $this ->setDescription('Encrypts all local secrets to the vault.') - ->addOption('force', 'f', InputOption::VALUE_NONE, 'Forces overriding of secrets that already exist in the vault') ->setHelp(<<<'EOF' -The %command.name% command list encrypts all local secrets and stores them in the vault.. +The %command.name% command encrypts all locally overridden secrets to the vault. %command.full_name% - -When the option --force is provided, secrets that already exist in the vault are overriden. - - %command.full_name% --force EOF ) ; @@ -67,22 +61,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 1; } - $secrets = $this->localVault->list(true); - - if (!$input->getOption('force')) { - foreach ($this->vault->list() as $k => $v) { - unset($secrets[$k]); - } - } + foreach ($this->vault->list(true) as $name => $value) { + $localValue = $this->localVault->reveal($name); - foreach ($secrets as $k => $v) { - if (null === $v) { - $io->error($this->localVault->getLastMessage()); + if (null !== $localValue && $value !== $localValue) { + $this->vault->seal($name, $localValue); + } elseif (null !== $message = $this->localVault->getLastMessage()) { + $io->error($message); return 1; } - - $this->vault->seal($k, $v); } return 0; diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsListCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsListCommand.php index 4ae190435db82..1b0fbdf4cec44 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsListCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsListCommand.php @@ -89,11 +89,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int } foreach ($localSecrets ?? [] as $name => $value) { - $rows[$name] = [$name, $rows[$name][1] ?? '', $dump($value)]; + if (isset($rows[$name])) { + $rows[$name][] = $dump($value); + } } - uksort($rows, 'strnatcmp'); - if (null !== $this->localVault && null !== $message = $this->localVault->getLastMessage()) { $io->comment($message); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsSetCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsSetCommand.php index 850cf08ee32b6..555d616712504 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/SecretsSetCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/SecretsSetCommand.php @@ -86,6 +86,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 1; } + if ($this->localVault === $vault && !\array_key_exists($name, $this->vault->list())) { + $io->error(sprintf('Secret "%s" does not exist in the vault, you cannot override it locally.', $name)); + + return 1; + } + if (0 < $random = $input->getOption('random') ?? 16) { $value = strtr(substr(base64_encode(random_bytes($random)), 0, $random), '+/', '-_'); } elseif (!$file = $input->getArgument('file')) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Secrets/DotenvVault.php b/src/Symfony/Bundle/FrameworkBundle/Secrets/DotenvVault.php index 16df2a6045f78..93d8989502d07 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Secrets/DotenvVault.php +++ b/src/Symfony/Bundle/FrameworkBundle/Secrets/DotenvVault.php @@ -36,14 +36,13 @@ public function seal(string $name, string $value): void { $this->lastMessage = null; $this->validateName($name); - $k = $name.'_SECRET'; $v = str_replace("'", "'\\''", $value); $content = file_exists($this->dotenvFile) ? file_get_contents($this->dotenvFile) : ''; - $content = preg_replace("/^$k=((\\\\'|'[^']++')++|.*)/m", "$k='$v'", $content, -1, $count); + $content = preg_replace("/^$name=((\\\\'|'[^']++')++|.*)/m", "$name='$v'", $content, -1, $count); if (!$count) { - $content .= "$k='$v'\n"; + $content .= "$name='$v'\n"; } file_put_contents($this->dotenvFile, $content); @@ -55,8 +54,7 @@ public function reveal(string $name): ?string { $this->lastMessage = null; $this->validateName($name); - $k = $name.'_SECRET'; - $v = \is_string($_SERVER[$k] ?? null) ? $_SERVER[$k] : ($_ENV[$k] ?? null); + $v = \is_string($_SERVER[$name] ?? null) ? $_SERVER[$name] : ($_ENV[$name] ?? null); if (null === $v) { $this->lastMessage = sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath($this->dotenvFile)); @@ -71,10 +69,9 @@ public function remove(string $name): bool { $this->lastMessage = null; $this->validateName($name); - $k = $name.'_SECRET'; $content = file_exists($this->dotenvFile) ? file_get_contents($this->dotenvFile) : ''; - $content = preg_replace("/^$k=((\\\\'|'[^']++')++|.*)\n?/m", '', $content, -1, $count); + $content = preg_replace("/^$name=((\\\\'|'[^']++')++|.*)\n?/m", '', $content, -1, $count); if ($count) { file_put_contents($this->dotenvFile, $content); @@ -94,14 +91,14 @@ public function list(bool $reveal = false): array $secrets = []; foreach ($_ENV as $k => $v) { - if (preg_match('/^(\w+)_SECRET$/D', $k, $m)) { - $secrets[$m[1]] = $reveal ? $v : null; + if (preg_match('/^\w+$/D', $k)) { + $secrets[$k] = $reveal ? $v : null; } } foreach ($_SERVER as $k => $v) { - if (\is_string($v) && preg_match('/^(\w+)_SECRET$/D', $k, $m)) { - $secrets[$m[1]] = $reveal ? $v : null; + if (\is_string($v) && preg_match('/^\w+$/D', $k)) { + $secrets[$k] = $reveal ? $v : null; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Secrets/SecretEnvVarProcessor.php b/src/Symfony/Bundle/FrameworkBundle/Secrets/SecretEnvVarProcessor.php index 5a4771fa365f4..e3a15febe4722 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Secrets/SecretEnvVarProcessor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Secrets/SecretEnvVarProcessor.php @@ -44,9 +44,9 @@ public static function getProvidedTypes() /** * {@inheritdoc} */ - public function getEnv($prefix, $name, \Closure $getEnv) + public function getEnv($prefix, $name, \Closure $getEnv): string { - if (null !== $this->localVault && null !== $secret = $this->localVault->reveal($name)) { + if (null !== $this->localVault && null !== ($secret = $this->localVault->reveal($name)) && \array_key_exists($name, $this->vault->list())) { return $secret; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php b/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php index 8aae669d8f537..17328b2a6dd6c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php +++ b/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php @@ -87,6 +87,12 @@ public function seal(string $name, string $value): void $this->validateName($name); $this->loadKeys(); $this->export($name.'.'.substr_replace(md5($name), '.sodium', -26), sodium_crypto_box_seal($value, $this->encryptionKey ?? sodium_crypto_box_publickey($this->decryptionKey))); + + $list = $this->list(); + $list[$name] = null; + uksort($list, 'strnatcmp'); + file_put_contents($this->pathPrefix.'sodium.list', sprintf("lastMessage = sprintf('Secret "%s" encrypted in "%s"; you can commit it.', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); } @@ -123,6 +129,10 @@ public function remove(string $name): bool return false; } + $list = $this->list(); + unset($list[$name]); + file_put_contents($this->pathPrefix.'sodium.list', sprintf("lastMessage = sprintf('Secret "%s" removed from "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); return @unlink($file) || !file_exists($file); @@ -131,13 +141,19 @@ public function remove(string $name): bool public function list(bool $reveal = false): array { $this->lastMessage = null; - $secrets = []; - $regexp = sprintf('{^%s(\w++)\.[0-9a-f]{6}\.sodium$}D', preg_quote(basename($this->pathPrefix))); - foreach (scandir(\dirname($this->pathPrefix)) as $name) { - if (preg_match($regexp, $name, $m)) { - $secrets[$m[1]] = $reveal ? $this->reveal($m[1]) : null; - } + if (!file_exists($file = $this->pathPrefix.'sodium.list')) { + return []; + } + + $secrets = include $file; + + if (!$reveal) { + return $secrets; + } + + foreach ($secrets as $name => $value) { + $secrets[$name] = $this->reveal($name); } return $secrets; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Secrets/DotenvVaultTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Secrets/DotenvVaultTest.php index ba234349d76ba..d494c82e68c4d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Secrets/DotenvVaultTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Secrets/DotenvVaultTest.php @@ -37,21 +37,21 @@ public function testEncryptAndDecrypt() $vault->seal('foo', $plain); - unset($_SERVER['foo_SECRET'], $_ENV['foo_SECRET']); + unset($_SERVER['foo'], $_ENV['foo']); (new Dotenv(false))->load($this->envFile); $decrypted = $vault->reveal('foo'); $this->assertSame($plain, $decrypted); - $this->assertSame(['foo' => null], $vault->list()); - $this->assertSame(['foo' => $plain], $vault->list(true)); + $this->assertSame(['foo' => null], array_intersect_key($vault->list(), ['foo' => 123])); + $this->assertSame(['foo' => $plain], array_intersect_key($vault->list(true), ['foo' => 123])); $this->assertTrue($vault->remove('foo')); $this->assertFalse($vault->remove('foo')); - unset($_SERVER['foo_SECRET'], $_ENV['foo_SECRET']); + unset($_SERVER['foo'], $_ENV['foo']); (new Dotenv(false))->load($this->envFile); - $this->assertSame([], $vault->list()); + $this->assertArrayNotHasKey('foo', $vault->list()); } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy