diff --git a/UPGRADE-5.1.md b/UPGRADE-5.1.md index 456b1d0bf1..d580cc7a79 100644 --- a/UPGRADE-5.1.md +++ b/UPGRADE-5.1.md @@ -109,6 +109,14 @@ Routing * Added argument `$priority` to `RouteCollection::add()` * Deprecated the `RouteCompiler::REGEX_DELIMITER` constant +SecurityBundle +-------------- + + * Marked the `AbstractFactory`, `AnonymousFactory`, `FormLoginFactory`, `FormLoginLdapFactory`, `GuardAuthenticationFactory`, + `HttpBasicFactory`, `HttpBasicLdapFactory`, `JsonLoginFactory`, `JsonLoginLdapFactory`, `RememberMeFactory`, `RemoteUserFactory` + and `X509Factory` as `@internal`. Instead of extending these classes, create your own implementation based on + `SecurityFactoryInterface`. + Security -------- diff --git a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md index 5995cb1893..615aceb7dc 100644 --- a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md @@ -6,6 +6,8 @@ CHANGELOG * Added XSD for configuration * Added security configuration for priority-based access decision strategy + * Marked the `AbstractFactory`, `AnonymousFactory`, `FormLoginFactory`, `FormLoginLdapFactory`, `GuardAuthenticationFactory`, `HttpBasicFactory`, `HttpBasicLdapFactory`, `JsonLoginFactory`, `JsonLoginLdapFactory`, `RememberMeFactory`, `RemoteUserFactory` and `X509Factory` as `@internal` + * Renamed method `AbstractFactory#createEntryPoint()` to `AbstractFactory#createDefaultEntryPoint()` 5.0.0 ----- diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php index a5d6f7e45e..c31e08ba7a 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php @@ -23,6 +23,8 @@ use Symfony\Component\DependencyInjection\Reference; * @author Fabien Potencier * @author Lukas Kahwe Smith * @author Johannes M. Schmitt + * + * @internal */ abstract class AbstractFactory implements SecurityFactoryInterface { @@ -65,7 +67,7 @@ abstract class AbstractFactory implements SecurityFactoryInterface } // create entry point if applicable (optional) - $entryPointId = $this->createEntryPoint($container, $id, $config, $defaultEntryPointId); + $entryPointId = $this->createDefaultEntryPoint($container, $id, $config, $defaultEntryPointId); return [$authProviderId, $listenerId, $entryPointId]; } @@ -126,7 +128,7 @@ abstract class AbstractFactory implements SecurityFactoryInterface * * @return string|null the entry point id */ - protected function createEntryPoint(ContainerBuilder $container, string $id, array $config, ?string $defaultEntryPointId) + protected function createDefaultEntryPoint(ContainerBuilder $container, string $id, array $config, ?string $defaultEntryPointId) { return $defaultEntryPointId; } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AnonymousFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AnonymousFactory.php index 53a6b503a1..7caff9fa05 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AnonymousFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AnonymousFactory.php @@ -18,6 +18,8 @@ use Symfony\Component\DependencyInjection\Parameter; /** * @author Wouter de Jong + * + * @internal */ class AnonymousFactory implements SecurityFactoryInterface, AuthenticatorFactoryInterface { diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/CustomAuthenticatorFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/CustomAuthenticatorFactory.php index 95fa3c050f..35984ca8be 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/CustomAuthenticatorFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/CustomAuthenticatorFactory.php @@ -15,6 +15,12 @@ use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\NodeDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; +/** + * @author Wouter de Jong + * + * @internal + * @experimental in Symfony 5.1 + */ class CustomAuthenticatorFactory implements AuthenticatorFactoryInterface, SecurityFactoryInterface { public function create(ContainerBuilder $container, string $id, array $config, string $userProvider, ?string $defaultEntryPoint) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/EntryPointFactoryInterface.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/EntryPointFactoryInterface.php index bf0e625f0a..0b56e309d5 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/EntryPointFactoryInterface.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/EntryPointFactoryInterface.php @@ -23,5 +23,5 @@ interface EntryPointFactoryInterface /** * Creates the entry point and returns the service ID. */ - public function createEntryPoint(ContainerBuilder $container, string $id, array $config, ?string $defaultEntryPointId): string; + public function createEntryPoint(ContainerBuilder $container, string $id, array $config): ?string; } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php index c5f247c307..92ce50527d 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php @@ -22,6 +22,8 @@ use Symfony\Component\DependencyInjection\Reference; * * @author Fabien Potencier * @author Johannes M. Schmitt + * + * @internal */ class FormLoginFactory extends AbstractFactory implements AuthenticatorFactoryInterface, EntryPointFactoryInterface { @@ -90,7 +92,12 @@ class FormLoginFactory extends AbstractFactory implements AuthenticatorFactoryIn return $listenerId; } - public function createEntryPoint(ContainerBuilder $container, string $id, array $config, ?string $defaultEntryPoint): string + protected function createDefaultEntryPoint(ContainerBuilder $container, string $id, array $config, ?string $defaultEntryPointId) + { + return $this->createEntryPoint($container, $id, $config); + } + + public function createEntryPoint(ContainerBuilder $container, string $id, array $config): string { $entryPointId = 'security.authentication.form_entry_point.'.$id; $container diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginLdapFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginLdapFactory.php index b2136c5056..3d6d119b8c 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginLdapFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginLdapFactory.php @@ -22,6 +22,8 @@ use Symfony\Component\Security\Core\Exception\LogicException; * * @author Grégoire Pineau * @author Charles Sarrazin + * + * @internal */ class FormLoginLdapFactory extends FormLoginFactory { diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/GuardAuthenticationFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/GuardAuthenticationFactory.php index a18dfefa3d..283da74373 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/GuardAuthenticationFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/GuardAuthenticationFactory.php @@ -23,6 +23,8 @@ use Symfony\Component\Security\Guard\Authenticator\GuardBridgeAuthenticator; * Configures the "guard" authentication provider key under a firewall. * * @author Ryan Weaver + * + * @internal */ class GuardAuthenticationFactory implements SecurityFactoryInterface, AuthenticatorFactoryInterface, EntryPointFactoryInterface { @@ -111,9 +113,15 @@ class GuardAuthenticationFactory implements SecurityFactoryInterface, Authentica return $authenticatorIds; } - public function createEntryPoint(ContainerBuilder $container, string $id, array $config, ?string $defaultEntryPointId): string + public function createEntryPoint(ContainerBuilder $container, string $id, array $config): ?string { - return $this->determineEntryPoint($defaultEntryPointId, $config); + try { + return $this->determineEntryPoint(null, $config); + } catch (\LogicException $e) { + // ignore the exception, the new system prefers setting "entry_point" over "guard.entry_point" + } + + return null; } private function determineEntryPoint(?string $defaultEntryPointId, array $config): string diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php index a698d2a1d1..5dfe0747d1 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php @@ -20,8 +20,10 @@ use Symfony\Component\DependencyInjection\Reference; * HttpBasicFactory creates services for HTTP basic authentication. * * @author Fabien Potencier + * + * @internal */ -class HttpBasicFactory implements SecurityFactoryInterface, AuthenticatorFactoryInterface +class HttpBasicFactory implements SecurityFactoryInterface, AuthenticatorFactoryInterface, EntryPointFactoryInterface { public function create(ContainerBuilder $container, string $id, array $config, string $userProvider, ?string $defaultEntryPoint) { @@ -34,7 +36,10 @@ class HttpBasicFactory implements SecurityFactoryInterface, AuthenticatorFactory ; // entry point - $entryPointId = $this->createEntryPoint($container, $id, $config, $defaultEntryPoint); + $entryPointId = $defaultEntryPoint; + if (null === $entryPointId) { + $entryPointId = $this->createEntryPoint($container, $id, $config); + } // listener $listenerId = 'security.authentication.listener.basic.'.$id; @@ -77,12 +82,8 @@ class HttpBasicFactory implements SecurityFactoryInterface, AuthenticatorFactory ; } - protected function createEntryPoint(ContainerBuilder $container, string $id, array $config, ?string $defaultEntryPoint) + public function createEntryPoint(ContainerBuilder $container, string $id, array $config): string { - if (null !== $defaultEntryPoint) { - return $defaultEntryPoint; - } - $entryPointId = 'security.authentication.basic_entry_point.'.$id; $container ->setDefinition($entryPointId, new ChildDefinition('security.authentication.basic_entry_point')) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicLdapFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicLdapFactory.php index 630e0b75b7..3e0bf5b0e3 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicLdapFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicLdapFactory.php @@ -23,6 +23,8 @@ use Symfony\Component\Security\Core\Exception\LogicException; * @author Fabien Potencier * @author Grégoire Pineau * @author Charles Sarrazin + * + * @internal */ class HttpBasicLdapFactory extends HttpBasicFactory { diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginFactory.php index 7aa9040579..393c553907 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginFactory.php @@ -19,6 +19,8 @@ use Symfony\Component\DependencyInjection\Reference; * JsonLoginFactory creates services for JSON login authentication. * * @author Kévin Dunglas + * + * @internal */ class JsonLoginFactory extends AbstractFactory implements AuthenticatorFactoryInterface { diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginLdapFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginLdapFactory.php index 6428f61c23..ba0d713664 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginLdapFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginLdapFactory.php @@ -19,6 +19,8 @@ use Symfony\Component\Security\Core\Exception\LogicException; /** * JsonLoginLdapFactory creates services for json login ldap authentication. + * + * @internal */ class JsonLoginLdapFactory extends JsonLoginFactory { diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php index 4b29db1a03..884c7b5721 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php @@ -20,6 +20,9 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpFoundation\Cookie; use Symfony\Component\Security\Http\EventListener\RememberMeLogoutListener; +/** + * @internal + */ class RememberMeFactory implements SecurityFactoryInterface, AuthenticatorFactoryInterface { protected $options = [ diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RemoteUserFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RemoteUserFactory.php index e25c3c7d07..fc2e49f6f0 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RemoteUserFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RemoteUserFactory.php @@ -21,6 +21,8 @@ use Symfony\Component\DependencyInjection\Reference; * * @author Fabien Potencier * @author Maxime Douailin + * + * @internal */ class RemoteUserFactory implements SecurityFactoryInterface, AuthenticatorFactoryInterface { diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/X509Factory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/X509Factory.php index f966302a1d..56a25653af 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/X509Factory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/X509Factory.php @@ -20,6 +20,8 @@ use Symfony\Component\DependencyInjection\Reference; * X509Factory creates services for X509 certificate authentication. * * @author Fabien Potencier + * + * @internal */ class X509Factory implements SecurityFactoryInterface, AuthenticatorFactoryInterface { diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index ac089d1eb2..5d65aea643 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -39,6 +39,7 @@ use Symfony\Component\Security\Core\Encoder\SodiumPasswordEncoder; use Symfony\Component\Security\Core\User\ChainUserProvider; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Controller\UserValueResolver; +use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; use Twig\Extension\AbstractExtension; /** @@ -519,6 +520,7 @@ class SecurityExtension extends Extension implements PrependExtensionInterface { $listeners = []; $hasListeners = false; + $entryPoints = []; foreach ($this->listenerPositions as $position) { foreach ($this->factories[$position] as $factory) { @@ -541,8 +543,8 @@ class SecurityExtension extends Extension implements PrependExtensionInterface $authenticationProviders[] = $authenticators; } - if ($factory instanceof EntryPointFactoryInterface) { - $defaultEntryPoint = $factory->createEntryPoint($container, $id, $firewall[$key], $defaultEntryPoint); + if ($factory instanceof EntryPointFactoryInterface && ($entryPoint = $factory->createEntryPoint($container, $id, $firewall[$key], null))) { + $entryPoints[$key] = $entryPoint; } } else { list($provider, $listenerId, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint); @@ -555,6 +557,19 @@ class SecurityExtension extends Extension implements PrependExtensionInterface } } + if ($entryPoints) { + // we can be sure the authenticator system is enabled + if (null !== $defaultEntryPoint) { + return $entryPoints[$defaultEntryPoint] ?? $defaultEntryPoint; + } + + if (1 === \count($entryPoints)) { + return current($entryPoints); + } + + throw new InvalidConfigurationException(sprintf('Because you have multiple authenticators in firewall "%s", you need to set the "entry_point" key to one of your authenticators (%s) or a service ID implementing "%s". The "entry_point" determines what should happen (e.g. redirect to "/login") when an anonymous user tries to access a protected page.', $id, implode(', ', $entryPoints), AuthenticationEntryPointInterface::class)); + } + if (false === $hasListeners) { throw new InvalidConfigurationException(sprintf('No authentication listener registered for firewall "%s".', $id)); }