8000 [SecurityBundle] Make security schema deterministic · symfony/symfony@ce030e8 · GitHub
[go: up one dir, main page]

Skip to content

Commit ce030e8

Browse files
MatTheCatfabpot
authored andcommitted
[SecurityBundle] Make security schema deterministic
1 parent 9348c49 commit ce030e8

File tree

11 files changed

+261
-5
lines changed

11 files changed

+261
-5
lines changed

src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
<xsd:element name="memory" type="memory" />
118118
<xsd:element name="ldap" type="ldap" />
119119
<!-- allow factories to use dynamic elements -->
120-
<xsd:any processContents="lax" />
120+
<xsd:any processContents="lax" namespace="##other" />
121121
</xsd:choice>
122122
<xsd:attribute name="name" type="xsd:string" use="required" />
123123
<xsd:attribute name="id" type="xsd:string" />
@@ -176,7 +176,7 @@
176176
<xsd:element name="x509" type="x509" minOccurs="0" maxOccurs="1" />
177177
<xsd:element name="required-badge" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
178178
<!-- allow factories to use dynamic elements -->
179-
<xsd:any processContents="lax" minOccurs="0" maxOccurs="unbounded" />
179+
<xsd:any processContents="lax" minOccurs="0" maxOccurs="unbounded" namespace="##other" />
180180
</xsd:choice>
181181
<xsd:attribute name="name" type="xsd:string" use="required" />
182182
<xsd:attribute name="pattern" type="xsd:string" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Fixtures\Authenticator;
4+
5+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AuthenticatorFactoryInterface;
6+
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
7+
use Symfony\Component\DependencyInjection\ContainerBuilder;
8+
9+
class CustomAuthenticator implements AuthenticatorFactoryInterface
10+
{
11+
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string
12+
{
13+
return 'security.authenticator.custom.'.$firewallName;
14+
}
15+
16+
public function getKey(): string
17+
{
18+
return 'custom';
19+
}
20+
21+
public function addConfiguration(NodeDefinition $builder)
22+
{
23+
}
24+
25+
public function getPriority(): int
26+
{
27+
return 0;
28+
}
29+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Fixtures\UserProvider;
4+
5+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\UserProvider\UserProviderFactoryInterface;
6+
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
7+
use Symfony\Component\DependencyInjection\ContainerBuilder;
8+
9+
class CustomProvider implements UserProviderFactoryInterface
10+
{
11+
public function create(ContainerBuilder $container, string $id, array $config)
12+
{
13+
}
14+
15+
public function getKey(): string
16+
{
17+
return 'custom';
18+
}
19+
20+
public function addConfiguration(NodeDefinition $builder)
21+
{
22+
$builder
23+
->children()
24+
->scalarNode('foo')->defaultValue('bar')->end()
25+
->end()
26+
;
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns:sec="http://symfony.com/schema/dic/security"
6+
xsi:schemaLocation="http://symfony.com/schema/dic/services
7+
https://symfony.com/schema/dic/services/services-1.0.xsd
8+
http://symfony.com/schema/dic/security
9+
https://symfony.com/schema/dic/security/security-1.0.xsd">
10+
11+
<sec:config enable-authenticator-manager="true">
12+
<sec:firewall name="main">
13+
<custom xmlns="http://example.com/schema" />
14+
</sec:firewall>
15+
</sec:config>
16+
17+
</container>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns:sec="http://symfony.com/schema/dic/security"
6+
xsi:schemaLocation="http://symfony.com/schema/dic/services
7+
https://symfony.com/schema/dic/services/services-1.0.xsd
8+
http://symfony.com/schema/dic/security
9+
https://symfony.com/schema/dic/security/security-1.0.xsd">
10+
11+
<sec:config enable-authenticator-manager="true">
12+
<sec:firewall name="main">
13+
<sec:custom />
14+
</sec:firewall>
15+
</sec:config>
16+
17+
</container>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns:sec="http://symfony.com/schema/dic/security"
6+
xsi:schemaLocation="http://symfony.com/schema/dic/services
7+
https://symfony.com/schema/dic/services/services-1.0.xsd
8+
http://symfony.com/schema/dic/security
9+
https://symfony.com/schema/dic/security/security-1.0.xsd">
10+
11+
<sec:config enable-authenticator-manager="true">
12+
<sec:provider name="foo">
13+
<custom xmlns="http://example.com/schema" />
14+
</sec:provider>
15+
16+
<sec:firewall name="main" provider="foo" />
17+
</sec:config>
18+
19+
</container>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns:sec="http://symfony.com/schema/dic/security"
6+
xsi:schemaLocation="http://symfony.com/schema/dic/services
7+
https://symfony.com/schema/dic/services/services-1.0.xsd
8+
http://symfony.com/schema/dic/security
9+
https://symfony.com/schema/dic/security/security-1.0.xsd">
10+
11+
<sec:config enable-authenticator-manager="true">
12+
<sec:provider name="foo">
13+
<sec:custom />
14+
</sec:provider>
15+
16+
<sec:firewall name="main" provider="foo" />
17+
</sec:config>
18+
19+
</container>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension;
16+
use Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Fixtures\Authenticator\CustomAuthenticator;
17+
use Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Fixtures\UserProvider\CustomProvider;
18+
use Symfony\Component\Config\FileLocator;
19+
use Symfony\Component\DependencyInjection\ContainerBuilder;
20+
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
21+
22+
class XmlCustomAuthenticatorTest extends TestCase
23+
{
24+
/**
25+
* @dataProvider provideXmlConfigurationFile
26+
*/
27+
public function testCustomProviderElement(string $configurationFile)
28+
{
29+
$container = new ContainerBuilder();
30+
$container->setParameter('kernel.debug', false);
31+
$container->register('cache.system', \stdClass::class);
32+
33+
$security = new SecurityExtension();
34+
$security->addAuthenticatorFactory(new CustomAuthenticator());
35+
$container->registerExtension($security);
36+
37+
(new XmlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/xml')))->load($configurationFile);
38+
39+
$container->getCompilerPassConfig()->setRemovingPasses([]);
40+
$container->getCompilerPassConfig()->setAfterRemovingPasses([]);
41+
$container->compile();
42+
43+
$this->addToAssertionCount(1);
44+
}
45+
46+
public static function provideXmlConfigurationFile(): iterable
47+
{
48+
yield 'Custom authenticator element under SecurityBundle’s namespace' => ['custom_authenticator_under_security_namespace.xml'];
49+
yield 'Custom authenticator element under its own namespace' => ['custom_authenticator_under_own_namespace.xml'];
50+
}
51+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\SecurityBundle\Tests\DependencyInjection;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension;
16+
use Symfony\Bundle\SecurityBundle\Tests\DependencyInjection\Fixtures\UserProvider\CustomProvider;
17+
use Symfony\Component\Config\FileLocator;
18+
use Symfony\Component\DependencyInjection\ContainerBuilder;
19+
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
20+
21+
class XmlCustomProviderTest extends TestCase
22+
{
23+
/**
24+
* @dataProvider provideXmlConfigurationFile
25+
*/
26+
public function testCustomProviderElement(string $configurationFile)
27+
{
28+
$container = new ContainerBuilder();
29+
$container->setParameter('kernel.debug', false);
30+
$container->register('cache.system', \stdClass::class);
31+
32+
$security = new SecurityExtension();
33+
$security->addUserProviderFactory(new CustomProvider());
34+
$container->registerExtension($security);
35+
36+
(new XmlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/xml')))->load($configurationFile);
37+
38+
$container->getCompilerPassConfig()->setRemovingPasses([]);
39+
$container->getCompilerPassConfig()->setAfterRemovingPasses([]);
40+
$container->compile();
41+
42+
$this->addToAssertionCount(1);
43+
}
44+
45+
public static function provideXmlConfigurationFile(): iterable
46+
{
47+
yield 'Custom provider element under SecurityBundle’s namespace' => ['custom_provider_under_security_namespace.xml'];
48+
yield 'Custom provider element under its own namespace' => ['custom_provider_under_own_namespace.xml'];
49+
}
50+
}

src/Symfony/Bundle/SecurityBundle/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"php": ">=7.2.5",
2020
"ext-xml": "*",
2121
"symfony/config": "^4.4|^5.0|^6.0",
22-
"symfony/dependency-injection": "^5.3|^6.0",
22+
"symfony/dependency-injection": "^5.4.43|^6.4.11",
2323
"symfony/deprecation-contracts": "^2.1|^3",
2424
"symfony/event-dispatcher": "^5.1|^6.0",
2525
"symfony/http-kernel": "^5.3|^6.0",

src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,33 @@ private function parseFileToDOM(string $file): \DOMDocument
404404
try {
405405
$dom = XmlUtils::loadFile($file, [$this, 'validateSchema']);
406406
} catch (\InvalidArgumentException $e) {
407-
throw new InvalidArgumentException(sprintf('Unable to parse file "%s": ', $file).$e->getMessage(), $e->getCode(), $e);
407+
$invalidSecurityElements = [];
408+
$errors = explode("\n", $e->getMessage());
409+
foreach ($errors as $i => $error) {
410+
if (preg_match("#^\[ERROR 1871] Element '\{http://symfony\.com/schema/dic/security}([^']+)'#", $error, $matches)) {
411+
$invalidSecurityElements[$i] = $matches[1];
412+
}
413+
}
414+
if ($invalidSecurityElements) {
415+
$dom = XmlUtils::loadFile($file);
416+
417+
foreach ($invalidSecurityElements as $errorIndex => $tagName) {
418+
foreach ($dom->getElementsByTagNameNS('http://symfony.com/schema/dic/security', $tagName) as $element) {
419+
if (!$parent = $element->parentNode) {
420+
continue;
421+
}
422+
if ('http://symfony.com/schema/dic/security' !== $parent->namespaceURI) {
423+
continue;
424+
}
425+
if ('provider' === $parent->localName || 'firewall' === $parent->localName) {
426+
unset($errors[$errorIndex]);
427+
}
428+
}
429+
}
430+
}
431+
if ($errors) {
432+
throw new InvalidArgumentException(sprintf('Unable to parse file "%s": ', $file).implode("/n", $errors), $e->getCode(), $e);
433+
}
408434
}
409435

410436
$this->validateExtensions($dom, $file);
@@ -777,6 +803,6 @@ private function loadFromExtensions(\DOMDocument $xml)
777803
*/
778804
public static function convertDomElementToArray(\DOMElement $element)
779805
{
780-
return XmlUtils::convertDomElementToArray($element);
806+
return XmlUtils::convertDomElementToArray($element, false);
781807
}
782808
}

0 commit comments

Comments
 (0)
0