Skip to content

Commit e81ff82

Browse files
committed
[SecurityBundle] Create a smooth upgrade path for security factories
1 parent b1e2527 commit e81ff82

20 files changed

+295
-93
lines changed

UPGRADE-5.4.md

+20
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,23 @@ HttpKernel
1111
----------
1212

1313
* Deprecate `AbstractTestSessionListener::getSession` inject a session in the request instead
14+
15+
SecurityBundle
16+
--------------
17+
18+
* Deprecate `SecurityFactoryInterface` and `SecurityExtension::addSecurityListenerFactory()` in favor of
19+
`AuthenticatorFactoryInterface` and `SecurityExtension::addAuthenticatorFactory()`
20+
21+
* Add `AuthenticatorFactoryInterface::getPriority()` which replaces `SecurityFactoryInterface::getPosition()`.
22+
Previous positions are mapped to the following priorities:
23+
24+
| Position | Constant | Priority |
25+
| ----------- | ----------------------------------------------------- | -------- |
26+
| pre_auth | `RemoteUserFactory::PRIORITY`/`X509Factory::PRIORITY` | -10 |
27+
| form | `FormLoginFactory::PRIORITY` | -30 |
28+
| http | `HttpBasicFactory::PRIORITY` | -50 |
29+
| remember_me | `RememberMeFactory::PRIORITY` | -60 |
30+
| anonymous | n/a | -70 |
31+
32+
* Deprecate passing an array of arrays as 1st argument to `MainConfiguration`, pass a sorted flat array of
33+
factories instead.

UPGRADE-6.0.md

+15
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,21 @@ Security
313313
SecurityBundle
314314
--------------
315315

316+
* Remove `SecurityFactoryInterface` and `SecurityExtension::addSecurityListenerFactory()` in favor of
317+
`AuthenticatorFactoryInterface` and `SecurityExtension::addAuthenticatorFactory()`
318+
* Add `AuthenticatorFactoryInterface::getPriority()` which replaces `SecurityFactoryInterface::getPosition()`.
319+
Previous positions are mapped to the following priorities:
320+
321+
| Position | Constant | Priority |
322+
| ----------- | ----------------------------------------------------- | -------- |
323+
| pre_auth | `RemoteUserFactory::PRIORITY`/`X509Factory::PRIORITY` | -10 |
324+
| form | `FormLoginFactory::PRIORITY` | -30 |
325+
| http | `HttpBasicFactory::PRIORITY` | -50 |
326+
| remember_me | `RememberMeFactory::PRIORITY` | -60 |
327+
| anonymous | n/a | -70 |
328+
329+
* Remove passing an array of arrays as 1st argument to `MainConfiguration`, pass a sorted flat array of
330+
factories instead.
316331
* Remove the `UserPasswordEncoderCommand` class and the corresponding `user:encode-password` command,
317332
use `UserPasswordHashCommand` and `user:hash-password` instead
318333
* Remove the `security.encoder_factory.generic` service, the `security.encoder_factory` and `Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface` aliases,

src/Symfony/Bundle/SecurityBundle/CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
CHANGELOG
22
=========
33

4+
5.4
5+
---
6+
7+
* Deprecate `SecurityFactoryInterface` and `SecurityExtension::addSecurityListenerFactory()` in favor of
8+
`AuthenticatorFactoryInterface` and `SecurityExtension::addAuthenticatorFactory()`
9+
* Add `AuthenticatorFactoryInterface::getPriority()` which replaces `SecurityFactoryInterface::getPosition()`
10+
* Deprecate passing an array of arrays as 1st argument to `MainConfiguration`, pass a sorted flat array of
11+
factories instead.
12+
413
5.3
514
---
615

src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php

+21-12
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
namespace Symfony\Bundle\SecurityBundle\DependencyInjection;
1313

1414
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AbstractFactory;
15+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AuthenticatorFactoryInterface;
16+
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
1517
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
1618
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
1719
use Symfony\Component\Config\Definition\ConfigurationInterface;
@@ -31,8 +33,17 @@ class MainConfiguration implements ConfigurationInterface
3133
private $factories;
3234
private $userProviderFactories;
3335

36+
/**
37+
* @param (SecurityFactoryInterface|AuthenticatorFactoryInterface)[]
38+
*/
3439
public function __construct(array $factories, array $userProviderFactories)
3540
{
41+
if (\is_array(current($factories))) {
42+
trigger_deprecation('symfony/security-bundle', '5.4', 'Passing an array of arrays as 1st argument to "%s" is deprecated, pass a sorted array of factories instead.', __METHOD__);
43+
44+
$factories = array_merge(...array_values($factories));
45+
}
46+
3647
$this->factories = $factories;
3748
$this->userProviderFactories = $userProviderFactories;
3849
}
@@ -294,19 +305,17 @@ private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $facto
294305
;
295306

296307
$abstractFactoryKeys = [];
297-
foreach ($factories as $factoriesAtPosition) {
298-
foreach ($factoriesAtPosition as $factory) {
299-
$name = str_replace('-', '_', $factory->getKey());
300-
$factoryNode = $firewallNodeBuilder->arrayNode($name)
301-
->canBeUnset()
302-
;
303-
304-
if ($factory instanceof AbstractFactory) {
305-
$abstractFactoryKeys[] = $name;
306-
}
307-
308-
$factory->addConfiguration($factoryNode);
308+
foreach ($factories as $factory) {
309+
$name = str_replace('-', '_', $factory->getKey());
310+
$factoryNode = $firewallNodeBuilder->arrayNode($name)
311+
->canBeUnset()
312+
;
313+
314+
if ($factory instanceof AbstractFactory) {
315+
$abstractFactoryKeys[] = $name;
309316
}
317+
318+
$factory->addConfiguration($factoryNode);
310319
}
311320

312321
// check for unreachable check paths

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AnonymousFactory.php

+5
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal
5252
throw new InvalidConfigurationException(sprintf('The authenticator manager no longer has "anonymous" security. Please remove this option under the "%s" firewall'.($config['lazy'] ? ' and add "lazy: true"' : '').'.', $firewallName));
5353
}
5454

55+
public function getPriority()
56+
{
57+
return -60;
58+
}
59+
5560
public function getPosition()
5661
{
5762
return 'anonymous';

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AuthenticatorFactoryInterface.php

+13
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@
1111

1212
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
1313

14+
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
1415
use Symfony\Component\DependencyInjection\ContainerBuilder;
1516

1617
/**
18+
* @method int getPriority defines the position at which the authenticator is called
19+
*
1720
* @author Wouter de Jong <wouter@wouterj.nl>
1821
*/
1922
interface AuthenticatorFactoryInterface
@@ -24,4 +27,14 @@ interface AuthenticatorFactoryInterface
2427
* @return string|string[] The authenticator service ID(s) to be used by the firewall
2528
*/
2629
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId);
30+
31+
/**
32+
* Defines the configuration key used to reference the authenticator
33+
* in the firewall configuration.
34+
*
35+
* @return string
36+
*/
37+
public function getKey();
38+
39+
public function addConfiguration(NodeDefinition $builder);
2740
}

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/CustomAuthenticatorFactory.php

+5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ public function create(ContainerBuilder $container, string $id, array $config, s
2727
throw new \LogicException('Custom authenticators are not supported when "security.enable_authenticator_manager" is not set to true.');
2828
}
2929

30+
public function getPriority(): int
31+
{
32+
return 0;
33+
}
34+
3035
public function getPosition(): string
3136
{
3237
return 'pre_auth';

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php

+7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
*/
2828
class FormLoginFactory extends AbstractFactory implements AuthenticatorFactoryInterface
2929
{
30+
public const PRIORITY = -30;
31+
3032
public function __construct()
3133
{
3234
$this->addOption('username_parameter', '_username');
@@ -37,6 +39,11 @@ public function __construct()
3739
$this->addOption('post_only', true);
3840
}
3941

42+
public function getPriority(): int
43+
{
44+
return self::PRIORITY;
45+
}
46+
4047
public function getPosition()
4148
{
4249
return 'form';

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/GuardAuthenticationFactory.php

+5
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ public function getPosition()
3434
return 'pre_auth';
3535
}
3636

37+
public function getPriority(): int
38+
{
39+
return 0;
40+
}
41+
3742
public function getKey()
3843
{
3944
return 'guard';

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php

+7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
*/
2626
class HttpBasicFactory implements SecurityFactoryInterface, AuthenticatorFactoryInterface
2727
{
28+
public const PRIORITY = -50;
29+
2830
public function create(ContainerBuilder $container, string $id, array $config, string $userProvider, ?string $defaultEntryPoint)
2931
{
3032
$provider = 'security.authentication.provider.dao.'.$id;
@@ -66,6 +68,11 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal
6668
return $authenticatorId;
6769
}
6870

71+
public function getPriority(): int
72+
{
73+
return self::PRIORITY;
74+
}
75+
6976
public function getPosition()
7077
{
7178
return 'http';

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginFactory.php

+7
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
*/
2525
class JsonLoginFactory extends AbstractFactory implements AuthenticatorFactoryInterface
2626
{
27+
public const PRIORITY = -40;
28+
2729
public function __construct()
2830
{
2931
$this->addOption('username_path', 'username');
@@ -32,6 +34,11 @@ public function __construct()
3234
$this->defaultSuccessHandlerOptions = [];
3335
}
3436

37+
public function getPriority(): int
38+
{
39+
return self::PRIORITY;
40+
}
41+
3542
/**
3643
* {@inheritdoc}
3744
*/

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginLinkFactory.php

+7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
*/
2828
class LoginLinkFactory extends AbstractFactory implements AuthenticatorFactoryInterface
2929
{
30+
public const PRIORITY = -20;
31+
3032
public function addConfiguration(NodeDefinition $node)
3133
{
3234
/** @var NodeBuilder $builder */
@@ -147,6 +149,11 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal
147149
return $authenticatorId;
148150
}
149151

152+
public function getPriority(): int
153+
{
154+
return self::PRIORITY;
155+
}
156+
150157
public function getPosition()
151158
{
152159
return 'form';

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php

+6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ public function create(ContainerBuilder $container, string $id, array $config, s
3434
throw new \LogicException('Login throttling is not supported when "security.enable_authenticator_manager" is not set to true.');
3535
}
3636

37+
public function getPriority(): int
38+
{
39+
// this factory doesn't register any authenticators, this priority doesn't matter
40+
return 0;
41+
}
42+
3743
public function getPosition(): string
3844
{
3945
// this factory doesn't register any authenticators, this position doesn't matter

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php

+35-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Symfony\Component\DependencyInjection\ContainerBuilder;
2222
use Symfony\Component\DependencyInjection\ContainerInterface;
2323
use Symfony\Component\DependencyInjection\Definition;
24+
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
2425
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
2526
use Symfony\Component\DependencyInjection\Reference;
2627
use Symfony\Component\HttpFoundation\Cookie;
@@ -30,8 +31,10 @@
3031
/**
3132
* @internal
3233
*/
33-
class RememberMeFactory implements SecurityFactoryInterface, AuthenticatorFactoryInterface
34+
class RememberMeFactory implements SecurityFactoryInterface, AuthenticatorFactoryInterface, PrependExtensionInterface
3435
{
36+
public const PRIORITY = -50;
37+
3538
protected $options = [
3639
'name' => 'REMEMBERME',
3740
'lifetime' => 31536000,
@@ -181,6 +184,14 @@ public function getPosition()
181184
return 'remember_me';
182185
}
183186

187+
/**
188+
* {@inheritDoc}
189+
*/
190+
public function getPriority(): int
191+
{
192+
return self::PRIORITY;
193+
}
194+
184195
public function getKey()
185196
{
186197
return 'remember-me';
@@ -331,4 +342,27 @@ private function createTokenVerifier(ContainerBuilder $container, string $firewa
331342

332343
return new Reference($tokenVerifierId, ContainerInterface::NULL_ON_INVALID_REFERENCE);
333344
}
345+
346+
/**
347+
* {@inheritdoc}
348+
*/
349+
public function prepend(ContainerBuilder $container)
350+
{
351+
$rememberMeSecureDefault = false;
352+
$rememberMeSameSiteDefault = null;
353+
354+
if (!isset($container->getExtensions()['framework'])) {
355+
return;
356+
}
357+
358+
foreach ($container->getExtensionConfig('framework') as $config) {
359+
if (isset($config['session']) && \is_array($config['session'])) {
360+
$rememberMeSecureDefault = $config['session']['cookie_secure'] ?? $rememberMeSecureDefault;
361+
$rememberMeSameSiteDefault = \array_key_exists('cookie_samesite', $config['session']) ? $config['session']['cookie_samesite'] : $rememberMeSameSiteDefault;
362+
}
363+
}
364+
365+
$this->options['secure'] = $rememberMeSecureDefault;
366+
$this->options['samesite'] = $rememberMeSameSiteDefault;
367+
}
334368
}

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RemoteUserFactory.php

+7
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
*/
2727
class RemoteUserFactory implements SecurityFactoryInterface, AuthenticatorFactoryInterface
2828
{
29+
public const PRIORITY = -10;
30+
2931
public function create(ContainerBuilder $container, string $id, array $config, string $userProvider, ?string $defaultEntryPoint)
3032
{
3133
$providerId = 'security.authentication.provider.pre_authenticated.'.$id;
@@ -58,6 +60,11 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal
5860
return $authenticatorId;
5961
}
6062

63+
public function getPriority(): int
64+
{
65+
return self::PRIORITY;
66+
}
67+
6168
public function getPosition()
6269
{
6370
return 'pre_auth';

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/SecurityFactoryInterface.php

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
* SecurityFactoryInterface is the interface for all security authentication listener.
1919
*
2020
* @author Fabien Potencier <fabien@symfony.com>
21+
*
22+
* @deprecated since Symfony 5.3, use AuthenticatorFactoryInterface instead.
2123
*/
2224
interface SecurityFactoryInterface
2325
{

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/X509Factory.php

+7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
*/
2626
class X509Factory implements SecurityFactoryInterface, AuthenticatorFactoryInterface
2727
{
28+
public const PRIORITY = -10;
29+
2830
public function create(ContainerBuilder $container, string $id, array $config, string $userProvider, ?string $defaultEntryPoint)
2931
{
3032
$providerId = 'security.authentication.provider.pre_authenticated.'.$id;
@@ -60,6 +62,11 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal
6062
return $authenticatorId;
6163
}
6264

65+
public function getPriority(): int
66+
{
67+
return self::PRIORITY;
68+
}
69+
6370
public function getPosition()
6471
{
6572
return 'pre_auth';

0 commit comments

Comments
 (0)