Remove randomness from dumped containers

This commit is contained in:
Nicolas Grekas 2018-01-03 12:52:04 +01:00
parent a483d37eaf
commit 14dd5d1dbd
11 changed files with 75 additions and 19 deletions

View File

@ -36,6 +36,7 @@ use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
@ -1626,7 +1627,7 @@ class FrameworkExtension extends Extension
private function registerCacheConfiguration(array $config, ContainerBuilder $container)
{
$version = substr(str_replace('/', '-', base64_encode(hash('sha256', uniqid(mt_rand(), true), true))), 0, 22);
$version = new Parameter('container.build_id');
$container->getDefinition('cache.adapter.apcu')->replaceArgument(2, $version);
$container->getDefinition('cache.adapter.system')->replaceArgument(2, $version);
$container->getDefinition('cache.adapter.filesystem')->replaceArgument(2, $config['directory']);

View File

@ -1045,6 +1045,9 @@ abstract class FrameworkExtensionTest extends TestCase
'kernel.name' => 'kernel',
'kernel.root_dir' => __DIR__,
'kernel.container_class' => 'testContainer',
'container.build_hash' => 'Abc1234',
'container.build_id' => hash('crc32', 'Abc123423456789'),
'container.build_time' => 23456789,
), $data)));
}

View File

@ -20,7 +20,7 @@
"ext-xml": "*",
"symfony/cache": "~3.4|~4.0",
"symfony/class-loader": "~3.2",
"symfony/dependency-injection": "~3.4|~4.0",
"symfony/dependency-injection": "^3.4.3|^4.0.3",
"symfony/config": "~3.4|~4.0",
"symfony/event-dispatcher": "~3.4|~4.0",
"symfony/http-foundation": "^3.3.11|~4.0",

View File

@ -289,7 +289,7 @@ class MainConfiguration implements ConfigurationInterface
->arrayNode('anonymous')
->canBeUnset()
->children()
->scalarNode('secret')->defaultValue(uniqid('', true))->end()
->scalarNode('secret')->defaultNull()->end()
->end()
->end()
->arrayNode('switch_user')

View File

@ -14,6 +14,7 @@ namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvide
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Reference;
/**
@ -27,13 +28,14 @@ class InMemoryFactory implements UserProviderFactoryInterface
public function create(ContainerBuilder $container, $id, $config)
{
$definition = $container->setDefinition($id, new ChildDefinition('security.user.provider.in_memory'));
$defaultPassword = new Parameter('container.build_id');
foreach ($config['users'] as $username => $user) {
$userId = $id.'_'.$username;
$container
->setDefinition($userId, new ChildDefinition('security.user.provider.in_memory.user'))
->setArguments(array($username, (string) $user['password'], $user['roles']))
->setArguments(array($username, null !== $user['password'] ? (string) $user['password'] : $defaultPassword, $user['roles']))
;
$definition->addMethodCall('createUser', array(new Reference($userId)));
@ -55,7 +57,7 @@ class InMemoryFactory implements UserProviderFactoryInterface
->normalizeKeys(false)
->prototype('array')
->children()
->scalarNode('password')->defaultValue(uniqid('', true))->end()
->scalarNode('password')->defaultNull()->end()
->arrayNode('roles')
->beforeNormalization()->ifString()->then(function ($v) { return preg_split('/\s*,\s*/', $v); })->end()
->prototype('scalar')->end()

View File

@ -22,6 +22,7 @@ use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Security\Core\Authorization\ExpressionLanguage;
@ -529,6 +530,10 @@ class SecurityExtension extends Extension
// Anonymous
if (isset($firewall['anonymous'])) {
if (null === $firewall['anonymous']['secret']) {
$firewall['anonymous']['secret'] = new Parameter('container.build_hash');
}
$listenerId = 'security.authentication.listener.anonymous.'.$id;
$container
->setDefinition($listenerId, new ChildDefinition('security.authentication.listener.anonymous'))

View File

@ -19,8 +19,8 @@
"php": "^5.5.9|>=7.0.8",
"ext-xml": "*",
"symfony/security": "~3.4|~4.0",
"symfony/dependency-injection": "~3.4|~4.0",
"symfony/http-kernel": "~3.3|~4.0",
"symfony/dependency-injection": "^3.4.3|^4.0.3",
"symfony/http-kernel": "~3.4|~4.0",
"symfony/polyfill-php70": "~1.0"
},
"require-dev": {

View File

@ -1239,6 +1239,8 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
$value = $this->doGet((string) $value, $value->getInvalidBehavior(), $inlineServices);
} elseif ($value instanceof Definition) {
$value = $this->createService($value, $inlineServices);
} elseif ($value instanceof Parameter) {
$value = $this->getParameter((string) $value);
} elseif ($value instanceof Expression) {
$value = $this->getExpressionLanguage()->evaluate($value, array('container' => $this));
}

View File

@ -211,6 +211,8 @@ EOF;
array_pop($code);
$code["Container{$hash}/{$options['class']}.php"] = substr_replace($files[$options['class'].'.php'], "<?php\n\nnamespace Container{$hash};\n", 0, 6);
$namespaceLine = $this->namespace ? "\nnamespace {$this->namespace};\n" : '';
$time = time();
$id = hash('crc32', $hash.$time);
$code[$options['class'].'.php'] = <<<EOF
<?php
@ -229,7 +231,11 @@ if (!\\class_exists({$options['class']}::class, false)) {
\\class_alias(\\Container{$hash}\\{$options['class']}::class, {$options['class']}::class, false);
}
return new \\Container{$hash}\\{$options['class']}();
return new \\Container{$hash}\\{$options['class']}(array(
'container.build_hash' => '$hash',
'container.build_id' => '$id',
'container.build_time' => $time,
));
EOF;
} else {
@ -564,7 +570,7 @@ EOTXT;
}
foreach ($definition->getArguments() as $arg) {
if (!$arg) {
if (!$arg || $arg instanceof Parameter) {
continue;
}
if (is_array($arg) && 3 >= count($arg)) {
@ -572,7 +578,7 @@ EOTXT;
if ($this->dumpValue($k) !== $this->dumpValue($k, false)) {
return false;
}
if (!$v) {
if (!$v || $v instanceof Parameter) {
continue;
}
if ($v instanceof Reference && $this->container->has($id = (string) $v) && $this->container->findDefinition($id)->isSynthetic()) {
@ -892,10 +898,10 @@ EOF;
}
if (0 === strpos($class, 'new ')) {
return $return.sprintf("(%s)->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : '');
return $return.sprintf("(%s)->%s(%s);\n", $class, $callable[1], $arguments ? implode(', ', $arguments) : '');
}
return $return.sprintf("\\call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? ', '.implode(', ', $arguments) : '');
return $return.sprintf("\\call_user_func(array(%s, '%s')%s);\n", $class, $callable[1], $arguments ? ', '.implode(', ', $arguments) : '');
}
return $return.sprintf("%s(%s);\n", $this->dumpLiteralClass($this->dumpValue($callable)), $arguments ? implode(', ', $arguments) : '');
@ -957,6 +963,11 @@ EOF;
EOF;
}
if ($this->asFiles) {
$code = str_replace('$parameters', "\$buildParameters;\n private \$parameters", $code);
$code = str_replace('__construct()', '__construct(array $buildParameters = array())', $code);
$code .= " \$this->buildParameters = \$buildParameters;\n";
}
if ($this->container->isCompiled()) {
if ($this->container->getParameterBag()->all()) {
@ -1283,6 +1294,9 @@ EOF;
public function getParameter($name)
{
if (isset($this->buildParameters[$name])) {
return $this->buildParameters[$name];
}
if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
$name = $this->normalizeParameterName($name);
@ -1299,6 +1313,9 @@ EOF;
public function hasParameter($name)
{
if (isset($this->buildParameters[$name])) {
return true;
}
$name = $this->normalizeParameterName($name);
return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
@ -1316,6 +1333,9 @@ EOF;
foreach ($this->loadedDynamicParameters as $name => $loaded) {
$parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name);
}
foreach ($this->buildParameters as $name => $value) {
$parameters[$name] = $value;
}
$this->parameterBag = new FrozenParameterBag($parameters);
}
@ -1323,6 +1343,9 @@ EOF;
}
EOF;
if (!$this->asFiles) {
$code = preg_replace('/^.*buildParameters.*\n.*\n.*\n/m', '', $code);
}
if ($dynamicPhp) {
$loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, count($dynamicPhp), false)), '', 8);
@ -1717,16 +1740,21 @@ EOF;
throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $factory[1] ?: 'n/a'));
}
$class = $this->dumpValue($factory[0]);
if (is_string($factory[0])) {
return sprintf('%s::%s(%s)', $this->dumpLiteralClass($this->dumpValue($factory[0])), $factory[1], implode(', ', $arguments));
return sprintf('%s::%s(%s)', $this->dumpLiteralClass($class), $factory[1], implode(', ', $arguments));
}
if ($factory[0] instanceof Definition) {
return sprintf("\\call_user_func(array(%s, '%s')%s)", $this->dumpValue($factory[0]), $factory[1], count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
if (0 === strpos($class, 'new ')) {
return sprintf('(%s)->%s(%s)', $class, $factory[1], implode(', ', $arguments));
}
return sprintf("\\call_user_func(array(%s, '%s')%s)", $class, $factory[1], count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
}
if ($factory[0] instanceof Reference) {
return sprintf('%s->%s(%s)', $this->dumpValue($factory[0]), $factory[1], implode(', ', $arguments));
return sprintf('%s->%s(%s)', $class, $factory[1], implode(', ', $arguments));
}
}

View File

@ -276,15 +276,17 @@ use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
*/
class ProjectServiceContainer extends Container
{
private $buildParameters;
private $parameters;
private $targetDirs = array();
public function __construct()
public function __construct(array $buildParameters = array())
{
$dir = $this->targetDirs[0] = \dirname(__DIR__);
for ($i = 1; $i <= 5; ++$i) {
$this->targetDirs[$i] = $dir = \dirname($dir);
}
$this->buildParameters = $buildParameters;
$this->parameters = $this->getDefaultParameters();
$this->services = array();
@ -382,6 +384,9 @@ class ProjectServiceContainer extends Container
public function getParameter($name)
{
if (isset($this->buildParameters[$name])) {
return $this->buildParameters[$name];
}
if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
$name = $this->normalizeParameterName($name);
@ -398,6 +403,9 @@ class ProjectServiceContainer extends Container
public function hasParameter($name)
{
if (isset($this->buildParameters[$name])) {
return true;
}
$name = $this->normalizeParameterName($name);
return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
@ -415,6 +423,9 @@ class ProjectServiceContainer extends Container
foreach ($this->loadedDynamicParameters as $name => $loaded) {
$parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name);
}
foreach ($this->buildParameters as $name => $value) {
$parameters[$name] = $value;
}
$this->parameterBag = new FrozenParameterBag($parameters);
}
@ -485,6 +496,10 @@ if (!\class_exists(ProjectServiceContainer::class, false)) {
\class_alias(\Container%s\ProjectServiceContainer::class, ProjectServiceContainer::class, false);
}
return new \Container%s\ProjectServiceContainer();
return new \Container%s\ProjectServiceContainer(array(
'container.build_hash' => '%s',
'container.build_id' => '%s',
'container.build_time' => %d,
));
)

View File

@ -83,7 +83,7 @@ class ProjectServiceContainer extends Container
*/
protected function getFooServiceService()
{
return $this->services['foo_service'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber(\call_user_func(array(new \Symfony\Component\DependencyInjection\ServiceLocator(array('Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => function () {
return $this->services['foo_service'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber((new \Symfony\Component\DependencyInjection\ServiceLocator(array('Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => function () {
$f = function (\Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition $v = null) { return $v; }; return $f(${($_ = isset($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition']) ? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] : $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition()) && false ?: '_'});
}, 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => function () {
$f = function (\Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber $v) { return $v; }; return $f(${($_ = isset($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber']) ? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] : $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber()) && false ?: '_'});
@ -91,7 +91,7 @@ class ProjectServiceContainer extends Container
$f = function (\Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition $v) { return $v; }; return $f(${($_ = isset($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber']) ? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] : $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber()) && false ?: '_'});
}, 'baz' => function () {
$f = function (\Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition $v = null) { return $v; }; return $f(${($_ = isset($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition']) ? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] : $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition()) && false ?: '_'});
})), 'withContext'), 'foo_service', $this));
})))->withContext('foo_service', $this));
}
/**