From d7915496db54e97361da0f580fe428aac4591afa Mon Sep 17 00:00:00 2001 From: David Maicher Date: Wed, 28 Feb 2018 20:42:00 +0100 Subject: [PATCH] [SecurityBundle] fallback to raw expressions to support custom functions --- .../DependencyInjection/SecurityExtension.php | 23 +++++--- .../SecurityExtensionTest.php | 54 +++++++++++++++++++ 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 3ffe2876e88d8..a2424c98f7eea 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -16,6 +16,7 @@ use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\ExpressionLanguage\SyntaxError; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -583,12 +584,22 @@ private function createExpression($container, $expression) return $this->expressions[$id]; } - $container - ->register($id, 'Symfony\Component\ExpressionLanguage\SerializedParsedExpression') - ->setPublic(false) - ->addArgument($expression) - ->addArgument(serialize($this->getExpressionLanguage()->parse($expression, array('token', 'user', 'object', 'roles', 'request', 'trust_resolver'))->getNodes())) - ; + try { + // due to the missing Cache component we try to parse the expression on Symfony < 3 as this will improve the performance. + // this can fail if custom functions are used inside the expression so we fallback to using a normal Expression instead + $container + ->register($id, 'Symfony\Component\ExpressionLanguage\SerializedParsedExpression') + ->setPublic(false) + ->addArgument($expression) + ->addArgument(serialize($this->getExpressionLanguage()->parse($expression, array('token', 'user', 'object', 'roles', 'request', 'trust_resolver'))->getNodes())) + ; + } catch (SyntaxError $error) { + $container + ->register($id, 'Symfony\Component\ExpressionLanguage\Expression') + ->setPublic(false) + ->addArgument($expression) + ; + } return $this->expressions[$id] = new Reference($id); } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php index 8eb2f70ea5de8..ca83d01e1e638 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php @@ -95,6 +95,60 @@ public function testFirewallWithInvalidUserProvider() $container->compile(); } + public function testRegisterRequestMatchersWithAllowIfExpression() + { + $container = $this->getRawContainer(); + + $rawExpression = "'foo' == 'bar' or 1 in [1, 3, 3]"; + $rawExpressionWithCustomFunction = "foo('bar')"; + + $container->loadFromExtension('security', array( + 'providers' => array( + 'default' => array('id' => 'foo'), + ), + + 'firewalls' => array( + 'some_firewall' => array( + 'pattern' => '/.*', + 'http_basic' => array(), + ), + ), + + 'access_control' => array( + array('path' => '/', 'allow_if' => $rawExpression), + array('path' => '/foo', 'allow_if' => $rawExpressionWithCustomFunction), + ), + )); + + $container->compile(); + + $accessMap = $container->getDefinition('security.access_map'); + $this->assertCount(2, $accessMap->getMethodCalls()); + + // first expression without any custom functions can be parsed already + $call = $accessMap->getMethodCalls()[0]; + $this->assertSame('add', $call[0]); + $args = $call[1]; + $this->assertCount(3, $args); + $expressionId = $args[1][0]; + $this->assertTrue($container->hasDefinition($expressionId)); + $expressionDef = $container->getDefinition($expressionId); + $this->assertSame('Symfony\Component\ExpressionLanguage\SerializedParsedExpression', $expressionDef->getClass()); + $this->assertSame($rawExpression, $expressionDef->getArgument(0)); + $this->assertInstanceOf('Symfony\Component\ExpressionLanguage\Node\BinaryNode', unserialize($expressionDef->getArgument(1))); + + // second expression cannot be parsed at compile-time so we use an Expression instead and let it be parsed at run-time + $call = $accessMap->getMethodCalls()[1]; + $this->assertSame('add', $call[0]); + $args = $call[1]; + $this->assertCount(3, $args); + $expressionId = $args[1][0]; + $this->assertTrue($container->hasDefinition($expressionId)); + $expressionDef = $container->getDefinition($expressionId); + $this->assertSame('Symfony\Component\ExpressionLanguage\Expression', $expressionDef->getClass()); + $this->assertSame($rawExpressionWithCustomFunction, $expressionDef->getArgument(0)); + } + protected function getRawContainer() { $container = new ContainerBuilder();