From ce030e801a492961a5e87e766132b29cde586ccb Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Sat, 22 Jun 2024 10:50:22 +0200 Subject: [PATCH] [SecurityBundle] Make security schema deterministic --- .../Resources/config/schema/security-1.0.xsd | 4 +- .../Authenticator/CustomAuthenticator.php | 29 +++++++++++ .../Fixtures/UserProvider/CustomProvider.php | 28 ++++++++++ ...stom_authenticator_under_own_namespace.xml | 17 +++++++ ...authenticator_under_security_namespace.xml | 17 +++++++ .../custom_provider_under_own_namespace.xml | 19 +++++++ ...stom_provider_under_security_namespace.xml | 19 +++++++ .../XmlCustomAuthenticatorTest.php | 51 +++++++++++++++++++ .../XmlCustomProviderTest.php | 50 ++++++++++++++++++ .../Bundle/SecurityBundle/composer.json | 2 +- .../Loader/XmlFileLoader.php | 30 ++++++++++- 11 files changed, 261 insertions(+), 5 deletions(-) create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/Authenticator/CustomAuthenticator.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/UserProvider/CustomProvider.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_authenticator_under_own_namespace.xml create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_authenticator_under_security_namespace.xml create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_provider_under_own_namespace.xml create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_provider_under_security_namespace.xml create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCustomAuthenticatorTest.php create mode 100644 src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCustomProviderTest.php diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd index 1a367b8397213..3b13833629102 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd @@ -117,7 +117,7 @@ - + @@ -176,7 +176,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/Authenticator/CustomAuthenticator.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/Authenticator/CustomAuthenticator.php new file mode 100644 index 0000000000000..89019e7be41e2 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/Authenticator/CustomAuthenticator.php @@ -0,0 +1,29 @@ +children() + ->scalarNode('foo')->defaultValue('bar')->end() + ->end() + ; + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_authenticator_under_own_namespace.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_authenticator_under_own_namespace.xml new file mode 100644 index 0000000000000..177cb88f59e6b --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_authenticator_under_own_namespace.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_authenticator_under_security_namespace.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_authenticator_under_security_namespace.xml new file mode 100644 index 0000000000000..1dbbc9d9a8901 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_authenticator_under_security_namespace.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_provider_under_own_namespace.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_provider_under_own_namespace.xml new file mode 100644 index 0000000000000..45d6602516a69 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_provider_under_own_namespace.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_provider_under_security_namespace.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_provider_under_security_namespace.xml new file mode 100644 index 0000000000000..00890b2d66be0 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/custom_provider_under_security_namespace.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCustomAuthenticatorTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCustomAuthenticatorTest.php new file mode 100644 index 0000000000000..de3db233a2060 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCustomAuthenticatorTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension; +use Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Fixtures\Authenticator\CustomAuthenticator; +use Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Fixtures\UserProvider\CustomProvider; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; + +class XmlCustomAuthenticatorTest extends TestCase +{ + /** + * @dataProvider provideXmlConfigurationFile + */ + public function testCustomProviderElement(string $configurationFile) + { + $container = new ContainerBuilder(); + $container->setParameter('kernel.debug', false); + $container->register('cache.system', \stdClass::class); + + $security = new SecurityExtension(); + $security->addAuthenticatorFactory(new CustomAuthenticator()); + $container->registerExtension($security); + + (new XmlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/xml')))->load($configurationFile); + + $container->getCompilerPassConfig()->setRemovingPasses([]); + $container->getCompilerPassConfig()->setAfterRemovingPasses([]); + $container->compile(); + + $this->addToAssertionCount(1); + } + + public static function provideXmlConfigurationFile(): iterable + { + yield 'Custom authenticator element under SecurityBundle’s namespace' => ['custom_authenticator_under_security_namespace.xml']; + yield 'Custom authenticator element under its own namespace' => ['custom_authenticator_under_own_namespace.xml']; + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCustomProviderTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCustomProviderTest.php new file mode 100644 index 0000000000000..a3f59fc299a24 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCustomProviderTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension; +use Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Fixtures\UserProvider\CustomProvider; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; + +class XmlCustomProviderTest extends TestCase +{ + /** + * @dataProvider provideXmlConfigurationFile + */ + public function testCustomProviderElement(string $configurationFile) + { + $container = new ContainerBuilder(); + $container->setParameter('kernel.debug', false); + $container->register('cache.system', \stdClass::class); + + $security = new SecurityExtension(); + $security->addUserProviderFactory(new CustomProvider()); + $container->registerExtension($security); + + (new XmlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/xml')))->load($configurationFile); + + $container->getCompilerPassConfig()->setRemovingPasses([]); + $container->getCompilerPassConfig()->setAfterRemovingPasses([]); + $container->compile(); + + $this->addToAssertionCount(1); + } + + public static function provideXmlConfigurationFile(): iterable + { + yield 'Custom provider element under SecurityBundle’s namespace' => ['custom_provider_under_security_namespace.xml']; + yield 'Custom provider element under its own namespace' => ['custom_provider_under_own_namespace.xml']; + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 097031baffb6d..2ffc3df2e5f12 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -19,7 +19,7 @@ "php": ">=7.2.5", "ext-xml": "*", "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^5.3|^6.0", + "symfony/dependency-injection": "^5.4.43|^6.4.11", "symfony/deprecation-contracts": "^2.1|^3", "symfony/event-dispatcher": "^5.1|^6.0", "symfony/http-kernel": "^5.3|^6.0", diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php index 7d52958809f62..c8ecaec19affd 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php @@ -404,7 +404,33 @@ private function parseFileToDOM(string $file): \DOMDocument try { $dom = XmlUtils::loadFile($file, [$this, 'validateSchema']); } catch (\InvalidArgumentException $e) { - throw new InvalidArgumentException(sprintf('Unable to parse file "%s": ', $file).$e->getMessage(), $e->getCode(), $e); + $invalidSecurityElements = []; + $errors = explode("\n", $e->getMessage()); + foreach ($errors as $i => $error) { + if (preg_match("#^\[ERROR 1871] Element '\{http://symfony\.com/schema/dic/security}([^']+)'#", $error, $matches)) { + $invalidSecurityElements[$i] = $matches[1]; + } + } + if ($invalidSecurityElements) { + $dom = XmlUtils::loadFile($file); + + foreach ($invalidSecurityElements as $errorIndex => $tagName) { + foreach ($dom->getElementsByTagNameNS('http://symfony.com/schema/dic/security', $tagName) as $element) { + if (!$parent = $element->parentNode) { + continue; + } + if ('http://symfony.com/schema/dic/security' !== $parent->namespaceURI) { + continue; + } + if ('provider' === $parent->localName || 'firewall' === $parent->localName) { + unset($errors[$errorIndex]); + } + } + } + } + if ($errors) { + throw new InvalidArgumentException(sprintf('Unable to parse file "%s": ', $file).implode("/n", $errors), $e->getCode(), $e); + } } $this->validateExtensions($dom, $file); @@ -777,6 +803,6 @@ private function loadFromExtensions(\DOMDocument $xml) */ public static function convertDomElementToArray(\DOMElement $element) { - return XmlUtils::convertDomElementToArray($element); + return XmlUtils::convertDomElementToArray($element, false); } }