Merge branch '4.4'
* 4.4: [FrameworkBundle] Remove suffix convention when using env vars to override secrets from the vault
This commit is contained in:
commit
6c3924c80a
@ -45,7 +45,7 @@ final class SecretsDecryptToLocalCommand extends Command
|
|||||||
->setDescription('Decrypts all secrets and stores them in the local vault.')
|
->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')
|
->addOption('force', 'f', InputOption::VALUE_NONE, 'Forces overriding of secrets that already exist in the local vault')
|
||||||
->setHelp(<<<'EOF'
|
->setHelp(<<<'EOF'
|
||||||
The <info>%command.name%</info> command list decrypts all secrets and stores them in the local vault..
|
The <info>%command.name%</info> command decrypts all secrets and copies them in the local vault.
|
||||||
|
|
||||||
<info>%command.full_name%</info>
|
<info>%command.full_name%</info>
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ namespace Symfony\Bundle\FrameworkBundle\Command;
|
|||||||
use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault;
|
use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
|
||||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
@ -43,15 +42,10 @@ final class SecretsEncryptFromLocalCommand extends Command
|
|||||||
{
|
{
|
||||||
$this
|
$this
|
||||||
->setDescription('Encrypts all local secrets to the vault.')
|
->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'
|
->setHelp(<<<'EOF'
|
||||||
The <info>%command.name%</info> command list encrypts all local secrets and stores them in the vault..
|
The <info>%command.name%</info> command encrypts all locally overridden secrets to the vault.
|
||||||
|
|
||||||
<info>%command.full_name%</info>
|
<info>%command.full_name%</info>
|
||||||
|
|
||||||
When the option <info>--force</info> is provided, secrets that already exist in the vault are overriden.
|
|
||||||
|
|
||||||
<info>%command.full_name% --force</info>
|
|
||||||
EOF
|
EOF
|
||||||
)
|
)
|
||||||
;
|
;
|
||||||
@ -67,22 +61,16 @@ EOF
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
$secrets = $this->localVault->list(true);
|
foreach ($this->vault->list(true) as $name => $value) {
|
||||||
|
$localValue = $this->localVault->reveal($name);
|
||||||
|
|
||||||
if (!$input->getOption('force')) {
|
if (null !== $localValue && $value !== $localValue) {
|
||||||
foreach ($this->vault->list() as $k => $v) {
|
$this->vault->seal($name, $localValue);
|
||||||
unset($secrets[$k]);
|
} elseif (null !== $message = $this->localVault->getLastMessage()) {
|
||||||
}
|
$io->error($message);
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($secrets as $k => $v) {
|
|
||||||
if (null === $v) {
|
|
||||||
$io->error($this->localVault->getLastMessage());
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->vault->seal($k, $v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -89,10 +89,10 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($localSecrets ?? [] as $name => $value) {
|
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()) {
|
if (null !== $this->localVault && null !== $message = $this->localVault->getLastMessage()) {
|
||||||
$io->comment($message);
|
$io->comment($message);
|
||||||
|
@ -86,6 +86,12 @@ EOF
|
|||||||
return 1;
|
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) {
|
if (0 < $random = $input->getOption('random') ?? 16) {
|
||||||
$value = strtr(substr(base64_encode(random_bytes($random)), 0, $random), '+/', '-_');
|
$value = strtr(substr(base64_encode(random_bytes($random)), 0, $random), '+/', '-_');
|
||||||
} elseif (!$file = $input->getArgument('file')) {
|
} elseif (!$file = $input->getArgument('file')) {
|
||||||
|
@ -36,14 +36,13 @@ class DotenvVault extends AbstractVault
|
|||||||
{
|
{
|
||||||
$this->lastMessage = null;
|
$this->lastMessage = null;
|
||||||
$this->validateName($name);
|
$this->validateName($name);
|
||||||
$k = $name.'_SECRET';
|
|
||||||
$v = str_replace("'", "'\\''", $value);
|
$v = str_replace("'", "'\\''", $value);
|
||||||
|
|
||||||
$content = file_exists($this->dotenvFile) ? file_get_contents($this->dotenvFile) : '';
|
$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) {
|
if (!$count) {
|
||||||
$content .= "$k='$v'\n";
|
$content .= "$name='$v'\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
file_put_contents($this->dotenvFile, $content);
|
file_put_contents($this->dotenvFile, $content);
|
||||||
@ -55,8 +54,7 @@ class DotenvVault extends AbstractVault
|
|||||||
{
|
{
|
||||||
$this->lastMessage = null;
|
$this->lastMessage = null;
|
||||||
$this->validateName($name);
|
$this->validateName($name);
|
||||||
$k = $name.'_SECRET';
|
$v = \is_string($_SERVER[$name] ?? null) ? $_SERVER[$name] : ($_ENV[$name] ?? null);
|
||||||
$v = \is_string($_SERVER[$k] ?? null) ? $_SERVER[$k] : ($_ENV[$k] ?? null);
|
|
||||||
|
|
||||||
if (null === $v) {
|
if (null === $v) {
|
||||||
$this->lastMessage = sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath($this->dotenvFile));
|
$this->lastMessage = sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath($this->dotenvFile));
|
||||||
@ -71,10 +69,9 @@ class DotenvVault extends AbstractVault
|
|||||||
{
|
{
|
||||||
$this->lastMessage = null;
|
$this->lastMessage = null;
|
||||||
$this->validateName($name);
|
$this->validateName($name);
|
||||||
$k = $name.'_SECRET';
|
|
||||||
|
|
||||||
$content = file_exists($this->dotenvFile) ? file_get_contents($this->dotenvFile) : '';
|
$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) {
|
if ($count) {
|
||||||
file_put_contents($this->dotenvFile, $content);
|
file_put_contents($this->dotenvFile, $content);
|
||||||
@ -94,14 +91,14 @@ class DotenvVault extends AbstractVault
|
|||||||
$secrets = [];
|
$secrets = [];
|
||||||
|
|
||||||
foreach ($_ENV as $k => $v) {
|
foreach ($_ENV as $k => $v) {
|
||||||
if (preg_match('/^(\w+)_SECRET$/D', $k, $m)) {
|
if (preg_match('/^\w+$/D', $k)) {
|
||||||
$secrets[$m[1]] = $reveal ? $v : null;
|
$secrets[$k] = $reveal ? $v : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($_SERVER as $k => $v) {
|
foreach ($_SERVER as $k => $v) {
|
||||||
if (\is_string($v) && preg_match('/^(\w+)_SECRET$/D', $k, $m)) {
|
if (\is_string($v) && preg_match('/^\w+$/D', $k)) {
|
||||||
$secrets[$m[1]] = $reveal ? $v : null;
|
$secrets[$k] = $reveal ? $v : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,9 +44,9 @@ class SecretEnvVarProcessor implements EnvVarProcessorInterface
|
|||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@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;
|
return $secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +87,12 @@ class SodiumVault extends AbstractVault
|
|||||||
$this->validateName($name);
|
$this->validateName($name);
|
||||||
$this->loadKeys();
|
$this->loadKeys();
|
||||||
$this->export($name.'.'.substr_replace(md5($name), '.sodium', -26), sodium_crypto_box_seal($value, $this->encryptionKey ?? sodium_crypto_box_publickey($this->decryptionKey)));
|
$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("<?php\n\nreturn %s;\n", var_export($list, true), LOCK_EX));
|
||||||
|
|
||||||
$this->lastMessage = sprintf('Secret "%s" encrypted in "%s"; you can commit it.', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR));
|
$this->lastMessage = sprintf('Secret "%s" encrypted in "%s"; you can commit it.', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,6 +129,10 @@ class SodiumVault extends AbstractVault
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$list = $this->list();
|
||||||
|
unset($list[$name]);
|
||||||
|
file_put_contents($this->pathPrefix.'sodium.list', sprintf("<?php\n\nreturn %s;\n", var_export($list, true), LOCK_EX));
|
||||||
|
|
||||||
$this->lastMessage = sprintf('Secret "%s" removed from "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR));
|
$this->lastMessage = sprintf('Secret "%s" removed from "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR));
|
||||||
|
|
||||||
return @unlink($file) || !file_exists($file);
|
return @unlink($file) || !file_exists($file);
|
||||||
@ -131,13 +141,19 @@ class SodiumVault extends AbstractVault
|
|||||||
public function list(bool $reveal = false): array
|
public function list(bool $reveal = false): array
|
||||||
{
|
{
|
||||||
$this->lastMessage = null;
|
$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 (!file_exists($file = $this->pathPrefix.'sodium.list')) {
|
||||||
if (preg_match($regexp, $name, $m)) {
|
return [];
|
||||||
$secrets[$m[1]] = $reveal ? $this->reveal($m[1]) : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$secrets = include $file;
|
||||||
|
|
||||||
|
if (!$reveal) {
|
||||||
|
return $secrets;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($secrets as $name => $value) {
|
||||||
|
$secrets[$name] = $this->reveal($name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $secrets;
|
return $secrets;
|
||||||
|
@ -37,21 +37,21 @@ class DotenvVaultTest extends TestCase
|
|||||||
|
|
||||||
$vault->seal('foo', $plain);
|
$vault->seal('foo', $plain);
|
||||||
|
|
||||||
unset($_SERVER['foo_SECRET'], $_ENV['foo_SECRET']);
|
unset($_SERVER['foo'], $_ENV['foo']);
|
||||||
(new Dotenv(false))->load($this->envFile);
|
(new Dotenv(false))->load($this->envFile);
|
||||||
|
|
||||||
$decrypted = $vault->reveal('foo');
|
$decrypted = $vault->reveal('foo');
|
||||||
$this->assertSame($plain, $decrypted);
|
$this->assertSame($plain, $decrypted);
|
||||||
|
|
||||||
$this->assertSame(['foo' => null], $vault->list());
|
$this->assertSame(['foo' => null], array_intersect_key($vault->list(), ['foo' => 123]));
|
||||||
$this->assertSame(['foo' => $plain], $vault->list(true));
|
$this->assertSame(['foo' => $plain], array_intersect_key($vault->list(true), ['foo' => 123]));
|
||||||
|
|
||||||
$this->assertTrue($vault->remove('foo'));
|
$this->assertTrue($vault->remove('foo'));
|
||||||
$this->assertFalse($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);
|
(new Dotenv(false))->load($this->envFile);
|
||||||
|
|
||||||
$this->assertSame([], $vault->list());
|
$this->assertArrayNotHasKey('foo', $vault->list());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user