Skip to content

Commit ca84bcc

Browse files
committed
bug symfony#57493 [SecurityBundle] Make security schema deterministic (MatTheCat)
This PR was squashed before being merged into the 5.4 branch. Discussion ---------- [SecurityBundle] Make security schema deterministic | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Issues | Fix symfony#57463 | License | MIT Currently you cannot put a namespace on custom providers/authenticators because the `XmlFileLoader` will ignore every element whose namespace is not its parent’s. This means the only way to configure e.g. [SymfonyConnect](https://github.com/symfonycorp/connect)’s provider is ```xml <provider name="connect_memory"> <connect_memory> <user username="whatever">ROLE_ADMIN</user> </connect_memory> </provider> ``` This requires providers and authenticators `xsd:any`’s `namespace` to be `##any`, which is no longer possible with libxml ≥ 2.12 (already available on Alpine 3.20 and Arch Linux e.g.) because it will report their content model as non-determinist. > Unable to parse file "/srv/backend/config/packages/security.xml": [ERROR 3070] complex type 'provider': The content model is not determinist. (in file:////srv/backend/vendor/symfony/security-bundle/DependencyInjection/../Resources/config/schema//security-1.0.xsd - line 88, column 0) [ERROR 3070] complex type 'firewall': The content model is not determinist. (in file:////srv/backend/vendor/symfony/security-bundle/DependencyInjection/../Resources/config/schema//security-1.0.xsd - line 134, column 0) in /srv/backend/config/packages/security.xml (which is being imported from "/srv/backend/src/Kernel.php"). As a result, any XML security config will fail to be loaded once libxml is updated. Fixing this issue requires to change `xsd:any`s’ `namespace` to `##other` so that content models become deterministic. A side-effect is that any custom providers/authenticators XML configuration will become invalid. > Unable to parse file "/srv/backend/config/packages/security.xml": [ERROR 1871] Element '{http://symfony.com/schema/dic/security}connect_memory': This element is not expected. Expected is one of ( {http://symfony.com/schema/dic/security}chain, {http://symfony.com/schema/dic/security}memory, {http://symfony.com/schema/dic/security}ldap, ##other{http://symfony.com/schema/dic/security}* ). (in /srv/backend/public/ - line 13, column 0) in /srv/backend/config/packages/security.xml (which is being imported from "/srv/backend/src/Kernel.php"). To avoid a BC break this PR allows such errors to occur. A deprecation will have to be triggered on 7.2 for users to namespace their custom providers/authenticators elements, e.g. ```xml <provider name="symfony_connect"> <connect_memory xmlns="whatever"> <user username="whatever">ROLE_ADMIN</custom:user> </connect_memory> </provider> ``` Because custom providers/authenticators’ `processContents` is `lax` there won’t be any validation if the namespace has no schema. If it has, it will be better to provide it: ```diff <srv:container xmlns="http://symfony.com/schema/dic/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" + xmlns:custom="whatever" xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd http://symfony.com/schema/dic/security https://symfony.com/schema/dic/security/security-1.0.xsd + whatever the/schema.xsd"> <config> <provider name="symfony_connect"> - <connect_memory xmlns="whatever"> - <user username="whatever">ROLE_ADMIN</custom:user> - </connect_memory> + <custom:connect_memory> + <custom:user username="whatever">ROLE_ADMIN</custom:user> + </custom:connect_memory> </provider> </srv:container> ``` Commits ------- ce030e8 [SecurityBundle] Make security schema deterministic
2 parents 4e9167e + ce030e8 commit ca84bcc

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",

0 commit comments

Comments
 (0)