diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 0d395d59c996e..24b6920e39a21 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -49,6 +49,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\EnvVarLoaderInterface; use Symfony\Component\DependencyInjection\EnvVarProcessorInterface; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\LogicException; @@ -376,6 +377,8 @@ public function load(array $configs, ContainerBuilder $container) ->addTag('console.command'); $container->registerForAutoconfiguration(ResourceCheckerInterface::class) ->addTag('config_cache.resource_checker'); + $container->registerForAutoconfiguration(EnvVarLoaderInterface::class) + ->addTag('container.env_var_loader'); $container->registerForAutoconfiguration(EnvVarProcessorInterface::class) ->addTag('container.env_var_processor'); $container->registerForAutoconfiguration(ServiceLocator::class) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/secrets.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/secrets.xml index d0ba0bcb34508..b587e897758e6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/secrets.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/secrets.xml @@ -6,6 +6,7 @@ + %kernel.project_dir%/config/secrets/%kernel.environment% @@ -31,11 +32,5 @@ %kernel.project_dir%/.env.local - - - - - - diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml index 02677f149f949..5d8508f97dbe9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml @@ -121,5 +121,11 @@ + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Secrets/DotenvVault.php b/src/Symfony/Bundle/FrameworkBundle/Secrets/DotenvVault.php index 93d8989502d07..a64a7449b2cae 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Secrets/DotenvVault.php +++ b/src/Symfony/Bundle/FrameworkBundle/Secrets/DotenvVault.php @@ -54,7 +54,7 @@ public function reveal(string $name): ?string { $this->lastMessage = null; $this->validateName($name); - $v = \is_string($_SERVER[$name] ?? null) ? $_SERVER[$name] : ($_ENV[$name] ?? null); + $v = \is_string($_SERVER[$name] ?? null) && 0 !== strpos($name, 'HTTP_') ? $_SERVER[$name] : ($_ENV[$name] ?? null); if (null === $v) { $this->lastMessage = sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath($this->dotenvFile)); diff --git a/src/Symfony/Bundle/FrameworkBundle/Secrets/SecretEnvVarProcessor.php b/src/Symfony/Bundle/FrameworkBundle/Secrets/SecretEnvVarProcessor.php deleted file mode 100644 index e3a15febe4722..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Secrets/SecretEnvVarProcessor.php +++ /dev/null @@ -1,59 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Secrets; - -use Symfony\Component\DependencyInjection\EnvVarProcessorInterface; -use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException; - -/** - * @author Tobias Schultze - * @author Nicolas Grekas - * - * @internal - */ -class SecretEnvVarProcessor implements EnvVarProcessorInterface -{ - private $vault; - private $localVault; - - public function __construct(AbstractVault $vault, AbstractVault $localVault = null) - { - $this->vault = $vault; - $this->localVault = $localVault; - } - - /** - * {@inheritdoc} - */ - public static function getProvidedTypes() - { - return [ - 'secret' => 'string', - ]; - } - - /** - * {@inheritdoc} - */ - public function getEnv($prefix, $name, \Closure $getEnv): string - { - if (null !== $this->localVault && null !== ($secret = $this->localVault->reveal($name)) && \array_key_exists($name, $this->vault->list())) { - return $secret; - } - - if (null !== $secret = $this->vault->reveal($name)) { - return $secret; - } - - throw new EnvNotFoundException($this->vault->getLastMessage() ?? sprintf('Secret "%s" not found or decryption key is missing.', $name)); - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php b/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php index cb6e9f527fdae..e6fcab506057d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php +++ b/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php @@ -11,6 +11,8 @@ namespace Symfony\Bundle\FrameworkBundle\Secrets; +use Symfony\Component\DependencyInjection\EnvVarLoaderInterface; + /** * @author Tobias Schultze * @author Jérémy Derussé @@ -18,7 +20,7 @@ * * @internal */ -class SodiumVault extends AbstractVault +class SodiumVault extends AbstractVault implements EnvVarLoaderInterface { private $encryptionKey; private $decryptionKey; @@ -56,8 +58,8 @@ public function generateKeys(bool $override = false): bool // ignore failures to load keys } - if ('' !== $this->decryptionKey && !file_exists($this->pathPrefix.'sodium.encrypt.public')) { - $this->export('sodium.encrypt.public', $this->encryptionKey); + if ('' !== $this->decryptionKey && !file_exists($this->pathPrefix.'encrypt.public.php')) { + $this->export('encrypt.public', $this->encryptionKey); } if (!$override && null !== $this->encryptionKey) { @@ -69,10 +71,10 @@ public function generateKeys(bool $override = false): bool $this->decryptionKey = sodium_crypto_box_keypair(); $this->encryptionKey = sodium_crypto_box_publickey($this->decryptionKey); - $this->export('sodium.encrypt.public', $this->encryptionKey); - $this->export('sodium.decrypt.private', $this->decryptionKey); + $this->export('encrypt.public', $this->encryptionKey); + $this->export('decrypt.private', $this->decryptionKey); - $this->lastMessage = sprintf('Sodium keys have been generated at "%s*.{public,private}".', $this->getPrettyPath($this->pathPrefix)); + $this->lastMessage = sprintf('Sodium keys have been generated at "%s*.public/private.php".', $this->getPrettyPath($this->pathPrefix)); return true; } @@ -82,12 +84,12 @@ public function seal(string $name, string $value): void $this->lastMessage = null; $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))); + $this->export($name.'.'.substr(md5($name), 0, 6), 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("pathPrefix.'list.php', sprintf("lastMessage = sprintf('Secret "%s" encrypted in "%s"; you can commit it.', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); } @@ -97,7 +99,7 @@ public function reveal(string $name): ?string $this->lastMessage = null; $this->validateName($name); - if (!file_exists($file = $this->pathPrefix.$name.'.'.substr_replace(md5($name), '.sodium', -26))) { + if (!file_exists($file = $this->pathPrefix.$name.'.'.substr_replace(md5($name), '.php', -26))) { $this->lastMessage = sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); return null; @@ -131,7 +133,7 @@ public function remove(string $name): bool $this->lastMessage = null; $this->validateName($name); - if (!file_exists($file = $this->pathPrefix.$name.'.'.substr_replace(md5($name), '.sodium', -26))) { + if (!file_exists($file = $this->pathPrefix.$name.'.'.substr_replace(md5($name), '.php', -26))) { $this->lastMessage = sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); return false; @@ -139,7 +141,7 @@ public function remove(string $name): bool $list = $this->list(); unset($list[$name]); - file_put_contents($this->pathPrefix.'sodium.list', sprintf("pathPrefix.'list.php', sprintf("lastMessage = sprintf('Secret "%s" removed from "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR)); @@ -150,7 +152,7 @@ public function list(bool $reveal = false): array { $this->lastMessage = null; - if (!file_exists($file = $this->pathPrefix.'sodium.list')) { + if (!file_exists($file = $this->pathPrefix.'list.php')) { return []; } @@ -167,6 +169,11 @@ public function list(bool $reveal = false): array return $secrets; } + public function loadEnvVars(): array + { + return $this->list(true); + } + private function loadKeys(): void { if (!\function_exists('sodium_crypto_box_seal')) { @@ -177,12 +184,12 @@ private function loadKeys(): void return; } - if (file_exists($this->pathPrefix.'sodium.decrypt.private')) { - $this->decryptionKey = (string) include $this->pathPrefix.'sodium.decrypt.private'; + if (file_exists($this->pathPrefix.'decrypt.private.php')) { + $this->decryptionKey = (string) include $this->pathPrefix.'decrypt.private.php'; } - if (file_exists($this->pathPrefix.'sodium.encrypt.public')) { - $this->encryptionKey = (string) include $this->pathPrefix.'sodium.encrypt.public'; + if (file_exists($this->pathPrefix.'encrypt.public.php')) { + $this->encryptionKey = (string) include $this->pathPrefix.'encrypt.public.php'; } elseif ('' !== $this->decryptionKey) { $this->encryptionKey = sodium_crypto_box_publickey($this->decryptionKey); } else { @@ -196,7 +203,7 @@ private function export(string $file, string $data): void $data = str_replace('%', '\x', rawurlencode($data)); $data = sprintf("pathPrefix.$file, $data, LOCK_EX)) { + if (false === file_put_contents($this->pathPrefix.$file.'.php', $data, LOCK_EX)) { $e = error_get_last(); throw new \ErrorException($e['message'] ?? 'Failed to write secrets data.', 0, $e['type'] ?? E_USER_WARNING); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Secrets/SodiumVaultTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Secrets/SodiumVaultTest.php index 2e25df902462b..a9b88b1763bd5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Secrets/SodiumVaultTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Secrets/SodiumVaultTest.php @@ -26,19 +26,19 @@ public function testGenerateKeys() $vault = new SodiumVault($this->secretsDir); $this->assertTrue($vault->generateKeys()); - $this->assertFileExists($this->secretsDir.'/test.sodium.encrypt.public'); - $this->assertFileExists($this->secretsDir.'/test.sodium.decrypt.private'); + $this->assertFileExists($this->secretsDir.'/test.encrypt.public.php'); + $this->assertFileExists($this->secretsDir.'/test.decrypt.private.php'); - $encKey = file_get_contents($this->secretsDir.'/test.sodium.encrypt.public'); - $decKey = file_get_contents($this->secretsDir.'/test.sodium.decrypt.private'); + $encKey = file_get_contents($this->secretsDir.'/test.encrypt.public.php'); + $decKey = file_get_contents($this->secretsDir.'/test.decrypt.private.php'); $this->assertFalse($vault->generateKeys()); - $this->assertStringEqualsFile($this->secretsDir.'/test.sodium.encrypt.public', $encKey); - $this->assertStringEqualsFile($this->secretsDir.'/test.sodium.decrypt.private', $decKey); + $this->assertStringEqualsFile($this->secretsDir.'/test.encrypt.public.php', $encKey); + $this->assertStringEqualsFile($this->secretsDir.'/test.decrypt.private.php', $decKey); $this->assertTrue($vault->generateKeys(true)); - $this->assertStringNotEqualsFile($this->secretsDir.'/test.sodium.encrypt.public', $encKey); - $this->assertStringNotEqualsFile($this->secretsDir.'/test.sodium.decrypt.private', $decKey); + $this->assertStringNotEqualsFile($this->secretsDir.'/test.encrypt.public.php', $encKey); + $this->assertStringNotEqualsFile($this->secretsDir.'/test.decrypt.private.php', $decKey); } public function testEncryptAndDecrypt() diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php index ea4a05cfd94d9..2147d53f1263d 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php @@ -188,7 +188,7 @@ private function checkType(Definition $checkedDefinition, $value, \ReflectionPar $checkFunction = sprintf('is_%s', $parameter->getType()->getName()); if (!$parameter->getType()->isBuiltin() || !$checkFunction($value)) { - throw new InvalidParameterTypeException($this->currentId, \gettype($value), $parameter); + throw new InvalidParameterTypeException($this->currentId, \is_object($value) ? \get_class($value) : \gettype($value), $parameter); } } } diff --git a/src/Symfony/Component/DependencyInjection/EnvVarLoaderInterface.php b/src/Symfony/Component/DependencyInjection/EnvVarLoaderInterface.php new file mode 100644 index 0000000000000..0c547f8a5fae2 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/EnvVarLoaderInterface.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +/** + * EnvVarLoaderInterface objects return key/value pairs that are added to the list of available env vars. + * + * @author Nicolas Grekas + */ +interface EnvVarLoaderInterface +{ + /** + * @return string[] Key/value pairs that can be accessed using the regular "%env()%" syntax + */ + public function loadEnvVars(): array; +} diff --git a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php index aef13a9ad1c63..6451ac9b0e9d3 100644 --- a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php +++ b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection; use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException; +use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; /** @@ -20,10 +21,17 @@ class EnvVarProcessor implements EnvVarProcessorInterface { private $container; + private $loaders; + private $loadedVars = []; - public function __construct(ContainerInterface $container) + /** + * @param EnvVarLoaderInterface[] $loaders + */ + public function __construct(ContainerInterface $container, \Traversable $loaders = null) { $this->container = $container; + $this->loaders = new \IteratorIterator($loaders ?? new \ArrayIterator()); + $this->loaders = $this->loaders->getInnerIterator(); } /** @@ -127,12 +135,31 @@ public function getEnv($prefix, $name, \Closure $getEnv) } elseif (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) { $env = $_SERVER[$name]; } elseif (false === ($env = getenv($name)) || null === $env) { // null is a possible value because of thread safety issues - if (!$this->container->hasParameter("env($name)")) { - throw new EnvNotFoundException(sprintf('Environment variable not found: "%s".', $name)); + foreach ($this->loadedVars as $vars) { + if (false !== $env = ($vars[$name] ?? false)) { + break; + } } - if (null === $env = $this->container->getParameter("env($name)")) { - return null; + try { + while ((false === $env || null === $env) && $this->loaders->valid()) { + $loader = $this->loaders->current(); + $this->loaders->next(); + $this->loadedVars[] = $vars = $loader->loadEnvVars(); + $env = $vars[$name] ?? false; + } + } catch (ParameterCircularReferenceException $e) { + // skip loaders that need an env var that is not defined + } + + if (false === $env || null === $env) { + if (!$this->container->hasParameter("env($name)")) { + throw new EnvNotFoundException(sprintf('Environment variable not found: "%s".', $name)); + } + + if (null === $env = $this->container->getParameter("env($name)")) { + return null; + } } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php b/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php index 12d852f4535c4..9970eb474f6be 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php @@ -5,6 +5,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\EnvVarLoaderInterface; use Symfony\Component\DependencyInjection\EnvVarProcessor; class EnvVarProcessorTest extends TestCase @@ -517,4 +518,39 @@ public function validCsv() [null, null], ]; } + + public function testEnvLoader() + { + $loaders = function () { + yield new class() implements EnvVarLoaderInterface { + public function loadEnvVars(): array + { + return [ + 'FOO_ENV_LOADER' => '123', + ]; + } + }; + + yield new class() implements EnvVarLoaderInterface { + public function loadEnvVars(): array + { + return [ + 'FOO_ENV_LOADER' => '234', + 'BAR_ENV_LOADER' => '456', + ]; + } + }; + }; + + $processor = new EnvVarProcessor(new Container(), $loaders()); + + $result = $processor->getEnv('string', 'FOO_ENV_LOADER', function () {}); + $this->assertSame('123', $result); + + $result = $processor->getEnv('string', 'BAR_ENV_LOADER', function () {}); + $this->assertSame('456', $result); + + $result = $processor->getEnv('string', 'FOO_ENV_LOADER', function () {}); + $this->assertSame('123', $result); // check twice + } } 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