diff --git a/UPGRADE-4.1.md b/UPGRADE-4.1.md index 8d96b710ed9ec..807c028e5d0b6 100644 --- a/UPGRADE-4.1.md +++ b/UPGRADE-4.1.md @@ -81,6 +81,7 @@ Security functionality, create a custom user-checker based on the `Symfony\Component\Security\Core\User\UserChecker`. * `AuthenticationUtils::getLastUsername()` now always returns a string. + * The `ExpressionVoter::addExpressionLanguageProvider()` method is deprecated. Register the provider directly on the injected ExpressionLanguage instance instead. SecurityBundle -------------- diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md index 2141dbb1df13c..d178fcdc0dc11 100644 --- a/UPGRADE-5.0.md +++ b/UPGRADE-5.0.md @@ -76,6 +76,7 @@ Security * The `ContextListener::setLogoutOnUserChange()` method has been removed. * The `Symfony\Component\Security\Core\User\AdvancedUserInterface` has been removed. + * The `ExpressionVoter::addExpressionLanguageProvider()` method has been removed. SecurityBundle -------------- diff --git a/src/Symfony/Bundle/SecurityBundle/CacheWarmer/ExpressionCacheWarmer.php b/src/Symfony/Bundle/SecurityBundle/CacheWarmer/ExpressionCacheWarmer.php new file mode 100644 index 0000000000000..de5a75b3b041f --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/CacheWarmer/ExpressionCacheWarmer.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\CacheWarmer; + +use Symfony\Component\ExpressionLanguage\Expression; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; +use Symfony\Component\Security\Core\Authorization\ExpressionLanguage; + +class ExpressionCacheWarmer implements CacheWarmerInterface +{ + private $expressions; + private $expressionLanguage; + + /** + * @param iterable|Expression[] $expressions + */ + public function __construct(iterable $expressions, ExpressionLanguage $expressionLanguage) + { + $this->expressions = $expressions; + $this->expressionLanguage = $expressionLanguage; + } + + public function isOptional() + { + return true; + } + + public function warmUp($cacheDir) + { + foreach ($this->expressions as $expression) { + $this->expressionLanguage->parse($expression, array('token', 'user', 'object', 'subject', 'roles', 'request', 'trust_resolver')); + } + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 3b56ac54dda35..88ab77577041e 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -26,7 +26,6 @@ use Symfony\Component\DependencyInjection\Parameter; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\Config\FileLocator; -use Symfony\Component\Security\Core\Authorization\ExpressionLanguage; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; use Symfony\Component\Security\Core\Encoder\Argon2iPasswordEncoder; use Symfony\Component\Security\Http\Controller\UserValueResolver; @@ -45,7 +44,6 @@ class SecurityExtension extends Extension private $listenerPositions = array('pre_auth', 'form', 'http', 'remember_me'); private $factories = array(); private $userProviderFactories = array(); - private $expressionLanguage; public function __construct() { @@ -136,10 +134,6 @@ private function createRoleHierarchy(array $config, ContainerBuilder $container) private function createAuthorization($config, ContainerBuilder $container) { - if (!$config['access_control']) { - return; - } - foreach ($config['access_control'] as $access) { $matcher = $this->createRequestMatcher( $container, @@ -157,6 +151,14 @@ private function createAuthorization($config, ContainerBuilder $container) $container->getDefinition('security.access_map') ->addMethodCall('add', array($matcher, $attributes, $access['requires_channel'])); } + + // allow cache warm-up for expressions + if (count($this->expressions)) { + $container->getDefinition('security.cache_warmer.expression') + ->replaceArgument(0, new IteratorArgument(array_values($this->expressions))); + } else { + $container->removeDefinition('security.cache_warmer.expression'); + } } private function createFirewalls($config, ContainerBuilder $container) @@ -636,11 +638,14 @@ private function createExpression($container, $expression) return $this->expressions[$id]; } + if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { + throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); + } + $container - ->register($id, 'Symfony\Component\ExpressionLanguage\SerializedParsedExpression') + ->register($id, 'Symfony\Component\ExpressionLanguage\Expression') ->setPublic(false) ->addArgument($expression) - ->addArgument(serialize($this->getExpressionLanguage()->parse($expression, array('token', 'user', 'object', 'roles', 'request', 'trust_resolver'))->getNodes())) ; return $this->expressions[$id] = new Reference($id); @@ -703,16 +708,4 @@ public function getConfiguration(array $config, ContainerBuilder $container) // first assemble the factories return new MainConfiguration($this->factories, $this->userProviderFactories); } - - private function getExpressionLanguage() - { - if (null === $this->expressionLanguage) { - if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { - throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); - } - $this->expressionLanguage = new ExpressionLanguage(); - } - - return $this->expressionLanguage; - } } diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index 26db528aed550..2659619d49e89 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -80,7 +80,9 @@ - + + + @@ -195,5 +197,17 @@ + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/CacheWarmer/ExpressionCacheWarmerTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/CacheWarmer/ExpressionCacheWarmerTest.php new file mode 100644 index 0000000000000..30ead76927a1b --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/CacheWarmer/ExpressionCacheWarmerTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\CacheWarmer; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\SecurityBundle\CacheWarmer\ExpressionCacheWarmer; +use Symfony\Component\ExpressionLanguage\Expression; +use Symfony\Component\Security\Core\Authorization\ExpressionLanguage; + +class ExpressionCacheWarmerTest extends TestCase +{ + public function testWarmUp() + { + $expressions = array(new Expression('A'), new Expression('B')); + + $expressionLang = $this->createMock(ExpressionLanguage::class); + $expressionLang->expects($this->exactly(2)) + ->method('parse') + ->withConsecutive( + array($expressions[0], array('token', 'user', 'object', 'subject', 'roles', 'request', 'trust_resolver')), + array($expressions[1], array('token', 'user', 'object', 'subject', 'roles', 'request', 'trust_resolver')) + ); + + (new ExpressionCacheWarmer($expressions, $expressionLang))->warmUp(''); + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php index 718c459f685b9..b1ff1c21250a8 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php @@ -15,7 +15,10 @@ use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension; use Symfony\Bundle\SecurityBundle\SecurityBundle; use Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Fixtures\UserProvider\DummyProvider; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\ExpressionLanguage\Expression; class SecurityExtensionTest extends TestCase { @@ -207,6 +210,66 @@ public function testPerListenerProviderWithRememberMe() $this->addToAssertionCount(1); } + public function testRegisterRequestMatchersWithAllowIfExpression() + { + $container = $this->getRawContainer(); + + $rawExpression = "'foo' == 'bar' or 1 in [1, 3, 3]"; + + $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), + ), + )); + + $container->compile(); + $accessMap = $container->getDefinition('security.access_map'); + $this->assertCount(1, $accessMap->getMethodCalls()); + $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(Expression::class, $expressionDef->getClass()); + $this->assertSame($rawExpression, $expressionDef->getArgument(0)); + + $this->assertTrue($container->hasDefinition('security.cache_warmer.expression')); + $this->assertEquals( + new IteratorArgument(array(new Reference($expressionId))), + $container->getDefinition('security.cache_warmer.expression')->getArgument(0) + ); + } + + public function testRemovesExpressionCacheWarmerDefinitionIfNoExpressions() + { + $container = $this->getRawContainer(); + $container->loadFromExtension('security', array( + 'providers' => array( + 'default' => array('id' => 'foo'), + ), + 'firewalls' => array( + 'some_firewall' => array( + 'pattern' => '/.*', + 'http_basic' => array(), + ), + ), + )); + $container->compile(); + + $this->assertFalse($container->hasDefinition('security.cache_warmer.expression')); + } + protected function getRawContainer() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php b/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php index 00633397d2d40..cbee938667789 100644 --- a/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/ExpressionVoter.php @@ -37,8 +37,13 @@ public function __construct(ExpressionLanguage $expressionLanguage, Authenticati $this->roleHierarchy = $roleHierarchy; } + /** + * @deprecated since Symfony 4.1, register the provider directly on the injected ExpressionLanguage instance instead. + */ public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider) { + @trigger_error(sprintf('The %s() method is deprecated since Symfony 4.1, register the provider directly on the injected ExpressionLanguage instance instead.', __METHOD__), E_USER_DEPRECATED); + $this->expressionLanguage->registerProvider($provider); } 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