diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/authenticators.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/authenticators.xml index e4fa9008dd..f752f923ca 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/authenticators.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/authenticators.xml @@ -20,7 +20,7 @@ realm name user provider @@ -29,7 +29,7 @@ @@ -39,7 +39,7 @@ diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index 99d8550e1b..5e31b492f0 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -52,7 +52,7 @@ - + %security.authentication.manager.erase_credentials% diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/PreAuthenticationGuardToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/PreAuthenticationGuardToken.php new file mode 100644 index 0000000000..b19b82e066 --- /dev/null +++ b/src/Symfony/Component/Security/Core/Authentication/Token/PreAuthenticationGuardToken.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Authentication\Token; + +/** + * The token used by the guard auth system before authentication. + * + * The GuardAuthenticationListener creates this, which is then consumed + * immediately by the GuardAuthenticationProvider. If authentication is + * successful, a different authenticated token is returned + * + * @author Ryan Weaver + */ +class PreAuthenticationGuardToken extends AbstractToken +{ + private $credentials; + private $guardProviderKey; + private $providerKey; + + /** + * @param mixed $credentials + * @param string $guardProviderKey Unique key that bind this token to a specific AuthenticatorInterface + * @param string|null $providerKey The general provider key (when using with HTTP, this is the firewall name) + */ + public function __construct($credentials, string $guardProviderKey, ?string $providerKey = null) + { + $this->credentials = $credentials; + $this->guardProviderKey = $guardProviderKey; + $this->providerKey = $providerKey; + + parent::__construct([]); + + // never authenticated + parent::setAuthenticated(false); + } + + public function getProviderKey(): ?string + { + return $this->providerKey; + } + + public function getGuardProviderKey() + { + return $this->guardProviderKey; + } + + /** + * Returns the user credentials, which might be an array of anything you + * wanted to put in there (e.g. username, password, favoriteColor). + * + * @return mixed The user credentials + */ + public function getCredentials() + { + return $this->credentials; + } + + public function setAuthenticated(bool $authenticated) + { + throw new \LogicException('The PreAuthenticationGuardToken is *never* authenticated.'); + } +} diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Authenticator/HttpBasicAuthenticatorTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Authenticator/HttpBasicAuthenticatorTest.php index 9e923364ea..c0265cd55a 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Authenticator/HttpBasicAuthenticatorTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Authenticator/HttpBasicAuthenticatorTest.php @@ -5,7 +5,7 @@ namespace Symfony\Component\Security\Core\Tests\Authentication\Authenticator; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Security\Core\Authentication\Authenticator\HttpBasicAuthenticator; +use Symfony\Component\Security\Http\Authentication\Authenticator\HttpBasicAuthenticator; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface; use Symfony\Component\Security\Core\Exception\BadCredentialsException; diff --git a/src/Symfony/Component/Security/Core/composer.json b/src/Symfony/Component/Security/Core/composer.json index 83b082bdde..fc500b285f 100644 --- a/src/Symfony/Component/Security/Core/composer.json +++ b/src/Symfony/Component/Security/Core/composer.json @@ -20,7 +20,6 @@ "symfony/event-dispatcher-contracts": "^1.1|^2", "symfony/polyfill-php80": "^1.15", "symfony/service-contracts": "^1.1.6|^2", - "symfony/security-guard": "^4.4", "symfony/deprecation-contracts": "^2.1" }, "require-dev": { diff --git a/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php b/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php index d30a95fdd7..7ffad32454 100644 --- a/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php +++ b/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php @@ -15,9 +15,12 @@ use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; +use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticationGuardToken; use Symfony\Component\Security\Guard\AuthenticatorInterface; use Symfony\Component\Security\Guard\GuardAuthenticatorHandler; +use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken as GuardPreAuthenticationGuardToken; use Symfony\Component\Security\Http\Firewall\AbstractListener; +use Symfony\Component\Security\Http\Firewall\GuardManagerListenerTrait; use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; /** @@ -30,7 +33,7 @@ use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; */ class GuardAuthenticationListener extends AbstractListener { - use GuardAuthenticatorListenerTrait; + use GuardManagerListenerTrait; private $guardHandler; private $authenticationManager; @@ -101,6 +104,11 @@ class GuardAuthenticationListener extends AbstractListener $this->rememberMeServices = $rememberMeServices; } + protected function createPreAuthenticatedToken($credentials, string $uniqueGuardKey, string $providerKey): PreAuthenticationGuardToken + { + return new GuardPreAuthenticationGuardToken($credentials, $uniqueGuardKey, $providerKey); + } + protected function getGuardKey(string $key): string { // get a key that's unique to *this* guard authenticator diff --git a/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php index d2c0d298d2..2f16dfa140 100644 --- a/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php +++ b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php @@ -11,17 +11,7 @@ namespace Symfony\Component\Security\Guard; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Security\Core\Authentication\Authenticator\AuthenticatorInterface as CoreAuthenticatorInterface; -use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; -use Symfony\Component\Security\Http\SecurityEvents; -use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Security\Http\Authentication\GuardAuthenticatorHandler as CoreAuthenticatorHandlerAlias; /** * A utility class that does much of the *work* during the guard authentication process. @@ -33,116 +23,6 @@ use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; * * @final */ -class GuardAuthenticatorHandler +class GuardAuthenticatorHandler extends CoreAuthenticatorHandlerAlias { - private $tokenStorage; - private $dispatcher; - private $sessionStrategy; - private $statelessProviderKeys; - - /** - * @param array $statelessProviderKeys An array of provider/firewall keys that are "stateless" and so do not need the session migrated on success - */ - public function __construct(TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher = null, array $statelessProviderKeys = []) - { - $this->tokenStorage = $tokenStorage; - $this->dispatcher = $eventDispatcher; - $this->statelessProviderKeys = $statelessProviderKeys; - } - - /** - * Authenticates the given token in the system. - */ - public function authenticateWithToken(TokenInterface $token, Request $request, string $providerKey = null) - { - $this->migrateSession($request, $token, $providerKey); - $this->tokenStorage->setToken($token); - - if (null !== $this->dispatcher) { - $loginEvent = new InteractiveLoginEvent($request, $token); - $this->dispatcher->dispatch($loginEvent, SecurityEvents::INTERACTIVE_LOGIN); - } - } - - /** - * Returns the "on success" response for the given GuardAuthenticator. - * - * @param CoreAuthenticatorInterface|AuthenticatorInterface $guardAuthenticator - */ - public function handleAuthenticationSuccess(TokenInterface $token, Request $request, $guardAuthenticator, string $providerKey): ?Response - { - if (!$guardAuthenticator instanceof AuthenticatorInterface && !$guardAuthenticator instanceof CoreAuthenticatorInterface) { - throw new \UnexpectedValueException('Invalid guard authenticator passed to '.__METHOD__.'. Expected AuthenticatorInterface of either Security Core or Security Guard.'); - } - - $response = $guardAuthenticator->onAuthenticationSuccess($request, $token, $providerKey); - - // check that it's a Response or null - if ($response instanceof Response || null === $response) { - return $response; - } - - throw new \UnexpectedValueException(sprintf('The "%s::onAuthenticationSuccess()" method must return null or a Response object. You returned "%s".', \get_class($guardAuthenticator), get_debug_type($response))); - } - - /** - * Convenience method for authenticating the user and returning the - * Response *if any* for success. - * - * @param CoreAuthenticatorInterface|AuthenticatorInterface $authenticator - */ - public function authenticateUserAndHandleSuccess(UserInterface $user, Request $request, $authenticator, string $providerKey): ?Response - { - if (!$authenticator instanceof AuthenticatorInterface && !$authenticator instanceof CoreAuthenticatorInterface) { - throw new \UnexpectedValueException('Invalid guard authenticator passed to '.__METHOD__.'. Expected AuthenticatorInterface of either Security Core or Security Guard.'); - } - - // create an authenticated token for the User - $token = $authenticator->createAuthenticatedToken($user, $providerKey); - // authenticate this in the system - $this->authenticateWithToken($token, $request, $providerKey); - - // return the success metric - return $this->handleAuthenticationSuccess($token, $request, $authenticator, $providerKey); - } - - /** - * Handles an authentication failure and returns the Response for the - * GuardAuthenticator. - * - * @param CoreAuthenticatorInterface|AuthenticatorInterface $guardAuthenticator - */ - public function handleAuthenticationFailure(AuthenticationException $authenticationException, Request $request, $guardAuthenticator, string $providerKey): ?Response - { - if (!$guardAuthenticator instanceof AuthenticatorInterface && !$guardAuthenticator instanceof CoreAuthenticatorInterface) { - throw new \UnexpectedValueException('Invalid guard authenticator passed to '.__METHOD__.'. Expected AuthenticatorInterface of either Security Core or Security Guard.'); - } - - $response = $guardAuthenticator->onAuthenticationFailure($request, $authenticationException); - if ($response instanceof Response || null === $response) { - // returning null is ok, it means they want the request to continue - return $response; - } - - throw new \UnexpectedValueException(sprintf('The "%s::onAuthenticationFailure()" method must return null or a Response object. You returned "%s".', \get_class($guardAuthenticator), get_debug_type($response))); - } - - /** - * Call this method if your authentication token is stored to a session. - * - * @final - */ - public function setSessionAuthenticationStrategy(SessionAuthenticationStrategyInterface $sessionStrategy) - { - $this->sessionStrategy = $sessionStrategy; - } - - private function migrateSession(Request $request, TokenInterface $token, ?string $providerKey) - { - if (\in_array($providerKey, $this->statelessProviderKeys, true) || !$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession()) { - return; - } - - $this->sessionStrategy->onAuthentication($request, $token); - } } diff --git a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php index 04085aaa05..01f70e9b4e 100644 --- a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Security\Guard\Provider; +use Symfony\Component\Security\Http\Authentication\GuardAuthenticationManagerTrait; use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; @@ -30,7 +31,7 @@ use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; */ class GuardAuthenticationProvider implements AuthenticationProviderInterface { - use GuardAuthenticationProviderTrait; + use GuardAuthenticationManagerTrait; /** * @var AuthenticatorInterface[] diff --git a/src/Symfony/Component/Security/Guard/Token/PreAuthenticationGuardToken.php b/src/Symfony/Component/Security/Guard/Token/PreAuthenticationGuardToken.php index 460dcf9bda..69013599f3 100644 --- a/src/Symfony/Component/Security/Guard/Token/PreAuthenticationGuardToken.php +++ b/src/Symfony/Component/Security/Guard/Token/PreAuthenticationGuardToken.php @@ -11,7 +11,7 @@ namespace Symfony\Component\Security\Guard\Token; -use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; +use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticationGuardToken as CorePreAuthenticationGuardToken; /** * The token used by the guard auth system before authentication. @@ -22,52 +22,6 @@ use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; * * @author Ryan Weaver */ -class PreAuthenticationGuardToken extends AbstractToken implements GuardTokenInterface +class PreAuthenticationGuardToken extends CorePreAuthenticationGuardToken implements GuardTokenInterface { - private $credentials; - private $guardProviderKey; - private $providerKey; - - /** - * @param mixed $credentials - * @param string $guardProviderKey Unique key that bind this token to a specific AuthenticatorInterface - * @param string|null $providerKey The general provider key (when using with HTTP, this is the firewall name) - */ - public function __construct($credentials, string $guardProviderKey, ?string $providerKey = null) - { - $this->credentials = $credentials; - $this->guardProviderKey = $guardProviderKey; - $this->providerKey = $providerKey; - - parent::__construct([]); - - // never authenticated - parent::setAuthenticated(false); - } - - public function getProviderKey(): ?string - { - return $this->providerKey; - } - - public function getGuardProviderKey() - { - return $this->guardProviderKey; - } - - /** - * Returns the user credentials, which might be an array of anything you - * wanted to put in there (e.g. username, password, favoriteColor). - * - * @return mixed The user credentials - */ - public function getCredentials() - { - return $this->credentials; - } - - public function setAuthenticated(bool $authenticated) - { - throw new \LogicException('The PreAuthenticationGuardToken is *never* authenticated.'); - } } diff --git a/src/Symfony/Component/Security/Guard/composer.json b/src/Symfony/Component/Security/Guard/composer.json index 1b2337f829..f129233640 100644 --- a/src/Symfony/Component/Security/Guard/composer.json +++ b/src/Symfony/Component/Security/Guard/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": "^7.2.5", - "symfony/security-core": "^5.0", + "symfony/security-core": "^5.1", "symfony/security-http": "^4.4.1|^5.0.1", "symfony/polyfill-php80": "^1.15" }, diff --git a/src/Symfony/Component/Security/Core/Authentication/Authenticator/AbstractAuthenticator.php b/src/Symfony/Component/Security/Http/Authentication/Authenticator/AbstractAuthenticator.php similarity index 94% rename from src/Symfony/Component/Security/Core/Authentication/Authenticator/AbstractAuthenticator.php rename to src/Symfony/Component/Security/Http/Authentication/Authenticator/AbstractAuthenticator.php index 1127fb6781..ce22dce368 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Authenticator/AbstractAuthenticator.php +++ b/src/Symfony/Component/Security/Http/Authentication/Authenticator/AbstractAuthenticator.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Security\Core\Authentication\Authenticator; +namespace Symfony\Component\Security\Http\Authentication\Authenticator; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\User\UserInterface; diff --git a/src/Symfony/Component/Security/Core/Authentication/Authenticator/AbstractFormLoginAuthenticator.php b/src/Symfony/Component/Security/Http/Authentication/Authenticator/AbstractFormLoginAuthenticator.php similarity index 96% rename from src/Symfony/Component/Security/Core/Authentication/Authenticator/AbstractFormLoginAuthenticator.php rename to src/Symfony/Component/Security/Http/Authentication/Authenticator/AbstractFormLoginAuthenticator.php index 27df412d28..5cc2f95414 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Authenticator/AbstractFormLoginAuthenticator.php +++ b/src/Symfony/Component/Security/Http/Authentication/Authenticator/AbstractFormLoginAuthenticator.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Security\Core\Authentication\Authenticator; +namespace Symfony\Component\Security\Http\Authentication\Authenticator; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Symfony/Component/Security/Core/Authentication/Authenticator/AnonymousAuthenticator.php b/src/Symfony/Component/Security/Http/Authentication/Authenticator/AnonymousAuthenticator.php similarity index 97% rename from src/Symfony/Component/Security/Core/Authentication/Authenticator/AnonymousAuthenticator.php rename to src/Symfony/Component/Security/Http/Authentication/Authenticator/AnonymousAuthenticator.php index 26a7d3102b..bec859e7a7 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Authenticator/AnonymousAuthenticator.php +++ b/src/Symfony/Component/Security/Http/Authentication/Authenticator/AnonymousAuthenticator.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Security\Core\Authentication\Authenticator; +namespace Symfony\Component\Security\Http\Authentication\Authenticator; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; diff --git a/src/Symfony/Component/Security/Core/Authentication/Authenticator/AuthenticatorInterface.php b/src/Symfony/Component/Security/Http/Authentication/Authenticator/AuthenticatorInterface.php similarity index 98% rename from src/Symfony/Component/Security/Core/Authentication/Authenticator/AuthenticatorInterface.php rename to src/Symfony/Component/Security/Http/Authentication/Authenticator/AuthenticatorInterface.php index cf84ce1609..8bf38ac85a 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Authenticator/AuthenticatorInterface.php +++ b/src/Symfony/Component/Security/Http/Authentication/Authenticator/AuthenticatorInterface.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Security\Core\Authentication\Authenticator; +namespace Symfony\Component\Security\Http\Authentication\Authenticator; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; diff --git a/src/Symfony/Component/Security/Core/Authentication/Authenticator/FormLoginAuthenticator.php b/src/Symfony/Component/Security/Http/Authentication/Authenticator/FormLoginAuthenticator.php similarity index 98% rename from src/Symfony/Component/Security/Core/Authentication/Authenticator/FormLoginAuthenticator.php rename to src/Symfony/Component/Security/Http/Authentication/Authenticator/FormLoginAuthenticator.php index 19c5b69029..2ff37f987b 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Authenticator/FormLoginAuthenticator.php +++ b/src/Symfony/Component/Security/Http/Authentication/Authenticator/FormLoginAuthenticator.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Security\Core\Authentication\Authenticator; +namespace Symfony\Component\Security\Http\Authentication\Authenticator; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; diff --git a/src/Symfony/Component/Security/Core/Authentication/Authenticator/HttpBasicAuthenticator.php b/src/Symfony/Component/Security/Http/Authentication/Authenticator/HttpBasicAuthenticator.php similarity index 97% rename from src/Symfony/Component/Security/Core/Authentication/Authenticator/HttpBasicAuthenticator.php rename to src/Symfony/Component/Security/Http/Authentication/Authenticator/HttpBasicAuthenticator.php index 6ce74c6809..92cb130ec9 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Authenticator/HttpBasicAuthenticator.php +++ b/src/Symfony/Component/Security/Http/Authentication/Authenticator/HttpBasicAuthenticator.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Security\Core\Authentication\Authenticator; +namespace Symfony\Component\Security\Http\Authentication\Authenticator; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Symfony/Component/Security/Core/Authentication/Authenticator/UsernamePasswordTrait.php b/src/Symfony/Component/Security/Http/Authentication/Authenticator/UsernamePasswordTrait.php similarity index 96% rename from src/Symfony/Component/Security/Core/Authentication/Authenticator/UsernamePasswordTrait.php rename to src/Symfony/Component/Security/Http/Authentication/Authenticator/UsernamePasswordTrait.php index 292ec370f8..bbfbc5af02 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Authenticator/UsernamePasswordTrait.php +++ b/src/Symfony/Component/Security/Http/Authentication/Authenticator/UsernamePasswordTrait.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Security\Core\Authentication\Authenticator; +namespace Symfony\Component\Security\Http\Authentication\Authenticator; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; diff --git a/src/Symfony/Component/Security/Core/Authentication/GuardAuthenticationManager.php b/src/Symfony/Component/Security/Http/Authentication/GuardAuthenticationManager.php similarity index 88% rename from src/Symfony/Component/Security/Core/Authentication/GuardAuthenticationManager.php rename to src/Symfony/Component/Security/Http/Authentication/GuardAuthenticationManager.php index 8b4e2e6393..b62516168b 100644 --- a/src/Symfony/Component/Security/Core/Authentication/GuardAuthenticationManager.php +++ b/src/Symfony/Component/Security/Http/Authentication/GuardAuthenticationManager.php @@ -9,9 +9,11 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Security\Core\Authentication; +namespace Symfony\Component\Security\Http\Authentication; -use Symfony\Component\Security\Core\Authentication\Authenticator\AuthenticatorInterface; +use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; +use Symfony\Component\Security\Http\Authentication\Authenticator\AuthenticatorInterface; +use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticationGuardToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\AuthenticationEvents; use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent; @@ -20,9 +22,6 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationExpiredException; use Symfony\Component\Security\Core\Exception\ProviderNotFoundException; use Symfony\Component\Security\Core\User\UserCheckerInterface; -use Symfony\Component\Security\Guard\Provider\GuardAuthenticationProviderTrait; -use Symfony\Component\Security\Guard\Token\GuardTokenInterface; -use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** @@ -33,7 +32,7 @@ use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; */ class GuardAuthenticationManager implements AuthenticationManagerInterface { - use GuardAuthenticationProviderTrait; + use GuardAuthenticationManagerTrait; private $guardAuthenticators; private $userChecker; @@ -58,10 +57,6 @@ class GuardAuthenticationManager implements AuthenticationManagerInterface public function authenticate(TokenInterface $token) { - if (!$token instanceof GuardTokenInterface) { - throw new \InvalidArgumentException('GuardAuthenticationManager only supports GuardTokenInterface.'); - } - if (!$token instanceof PreAuthenticationGuardToken) { /* * The listener *only* passes PreAuthenticationGuardToken instances. diff --git a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProviderTrait.php b/src/Symfony/Component/Security/Http/Authentication/GuardAuthenticationManagerTrait.php similarity index 95% rename from src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProviderTrait.php rename to src/Symfony/Component/Security/Http/Authentication/GuardAuthenticationManagerTrait.php index 667c35d05e..7de91a75a3 100644 --- a/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProviderTrait.php +++ b/src/Symfony/Component/Security/Http/Authentication/GuardAuthenticationManagerTrait.php @@ -9,9 +9,10 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Security\Guard\Provider; +namespace Symfony\Component\Security\Http\Authentication; -use Symfony\Component\Security\Core\Authentication\Authenticator\AuthenticatorInterface as CoreAuthenticatorInterface; +use Symfony\Component\Security\Http\Authentication\Authenticator\AuthenticatorInterface as CoreAuthenticatorInterface; +use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticationGuardToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\LogicException; @@ -20,14 +21,13 @@ use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Guard\AuthenticatorInterface; use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface; -use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; /** * @author Ryan Weaver * * @internal */ -trait GuardAuthenticationProviderTrait +trait GuardAuthenticationManagerTrait { /** * @param CoreAuthenticatorInterface|AuthenticatorInterface $guardAuthenticator diff --git a/src/Symfony/Component/Security/Http/Authentication/GuardAuthenticatorHandler.php b/src/Symfony/Component/Security/Http/Authentication/GuardAuthenticatorHandler.php new file mode 100644 index 0000000000..d930df1896 --- /dev/null +++ b/src/Symfony/Component/Security/Http/Authentication/GuardAuthenticatorHandler.php @@ -0,0 +1,149 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Http\Authentication; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Http\Authentication\Authenticator\AuthenticatorInterface; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Guard\AuthenticatorInterface as GuardAuthenticatorInterface; +use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; +use Symfony\Component\Security\Http\SecurityEvents; +use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; + +/** + * A utility class that does much of the *work* during the guard authentication process. + * + * By having the logic here instead of the listener, more of the process + * can be called directly (e.g. for manual authentication) or overridden. + * + * @author Ryan Weaver + * + * @internal + */ +class GuardAuthenticatorHandler +{ + private $tokenStorage; + private $dispatcher; + private $sessionStrategy; + private $statelessProviderKeys; + + /** + * @param array $statelessProviderKeys An array of provider/firewall keys that are "stateless" and so do not need the session migrated on success + */ + public function __construct(TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher = null, array $statelessProviderKeys = []) + { + $this->tokenStorage = $tokenStorage; + $this->dispatcher = $eventDispatcher; + $this->statelessProviderKeys = $statelessProviderKeys; + } + + /** + * Authenticates the given token in the system. + */ + public function authenticateWithToken(TokenInterface $token, Request $request, string $providerKey = null) + { + $this->migrateSession($request, $token, $providerKey); + $this->tokenStorage->setToken($token); + + if (null !== $this->dispatcher) { + $loginEvent = new InteractiveLoginEvent($request, $token); + $this->dispatcher->dispatch($loginEvent, SecurityEvents::INTERACTIVE_LOGIN); + } + } + + /** + * Returns the "on success" response for the given GuardAuthenticator. + * + * @param AuthenticatorInterface|GuardAuthenticatorInterface $guardAuthenticator + */ + public function handleAuthenticationSuccess(TokenInterface $token, Request $request, $guardAuthenticator, string $providerKey): ?Response + { + if (!$guardAuthenticator instanceof AuthenticatorInterface && !$guardAuthenticator instanceof GuardAuthenticatorInterface) { + throw new \UnexpectedValueException('Invalid guard authenticator passed to '.__METHOD__.'. Expected AuthenticatorInterface of either Security Core or Security Guard.'); + } + + $response = $guardAuthenticator->onAuthenticationSuccess($request, $token, $providerKey); + + // check that it's a Response or null + if ($response instanceof Response || null === $response) { + return $response; + } + + throw new \UnexpectedValueException(sprintf('The "%s::onAuthenticationSuccess()" method must return null or a Response object. You returned "%s".', \get_class($guardAuthenticator), get_debug_type($response))); + } + + /** + * Convenience method for authenticating the user and returning the + * Response *if any* for success. + * + * @param AuthenticatorInterface|GuardAuthenticatorInterface $authenticator + */ + public function authenticateUserAndHandleSuccess(UserInterface $user, Request $request, $authenticator, string $providerKey): ?Response + { + if (!$authenticator instanceof AuthenticatorInterface && !$authenticator instanceof GuardAuthenticatorInterface) { + throw new \UnexpectedValueException('Invalid guard authenticator passed to '.__METHOD__.'. Expected AuthenticatorInterface of either Security Core or Security Guard.'); + } + + // create an authenticated token for the User + $token = $authenticator->createAuthenticatedToken($user, $providerKey); + // authenticate this in the system + $this->authenticateWithToken($token, $request, $providerKey); + + // return the success metric + return $this->handleAuthenticationSuccess($token, $request, $authenticator, $providerKey); + } + + /** + * Handles an authentication failure and returns the Response for the + * GuardAuthenticator. + * + * @param AuthenticatorInterface|GuardAuthenticatorInterface $guardAuthenticator + */ + public function handleAuthenticationFailure(AuthenticationException $authenticationException, Request $request, $guardAuthenticator, string $providerKey): ?Response + { + if (!$guardAuthenticator instanceof AuthenticatorInterface && !$guardAuthenticator instanceof GuardAuthenticatorInterface) { + throw new \UnexpectedValueException('Invalid guard authenticator passed to '.__METHOD__.'. Expected AuthenticatorInterface of either Security Core or Security Guard.'); + } + + $response = $guardAuthenticator->onAuthenticationFailure($request, $authenticationException); + if ($response instanceof Response || null === $response) { + // returning null is ok, it means they want the request to continue + return $response; + } + + throw new \UnexpectedValueException(sprintf('The "%s::onAuthenticationFailure()" method must return null or a Response object. You returned "%s".', \get_class($guardAuthenticator), get_debug_type($response))); + } + + /** + * Call this method if your authentication token is stored to a session. + * + * @final + */ + public function setSessionAuthenticationStrategy(SessionAuthenticationStrategyInterface $sessionStrategy) + { + $this->sessionStrategy = $sessionStrategy; + } + + private function migrateSession(Request $request, TokenInterface $token, ?string $providerKey) + { + if (\in_array($providerKey, $this->statelessProviderKeys, true) || !$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession()) { + return; + } + + $this->sessionStrategy->onAuthentication($request, $token); + } +} diff --git a/src/Symfony/Component/Security/Http/Firewall/GuardManagerListener.php b/src/Symfony/Component/Security/Http/Firewall/GuardManagerListener.php index 78681bd1e8..2367223657 100644 --- a/src/Symfony/Component/Security/Http/Firewall/GuardManagerListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/GuardManagerListener.php @@ -14,8 +14,8 @@ namespace Symfony\Component\Security\Http\Firewall; use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; -use Symfony\Component\Security\Core\Authentication\Authenticator\AuthenticatorInterface; -use Symfony\Component\Security\Guard\Firewall\GuardAuthenticatorListenerTrait; +use Symfony\Component\Security\Http\Authentication\Authenticator\AuthenticatorInterface; +use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticationGuardToken; use Symfony\Component\Security\Guard\GuardAuthenticatorHandler; use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; @@ -28,7 +28,7 @@ use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; */ class GuardManagerListener { - use GuardAuthenticatorListenerTrait; + use GuardManagerListenerTrait; private $authenticationManager; private $guardHandler; @@ -65,6 +65,11 @@ class GuardManagerListener $this->rememberMeServices = $rememberMeServices; } + protected function createPreAuthenticatedToken($credentials, string $uniqueGuardKey, string $providerKey): PreAuthenticationGuardToken + { + return new PreAuthenticationGuardToken($credentials, $uniqueGuardKey, $providerKey); + } + protected function getGuardKey(string $key): string { // Guard authenticators in the GuardManagerListener are already indexed diff --git a/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticatorListenerTrait.php b/src/Symfony/Component/Security/Http/Firewall/GuardManagerListenerTrait.php similarity index 94% rename from src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticatorListenerTrait.php rename to src/Symfony/Component/Security/Http/Firewall/GuardManagerListenerTrait.php index 245f02c906..794d1dd133 100644 --- a/src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticatorListenerTrait.php +++ b/src/Symfony/Component/Security/Http/Firewall/GuardManagerListenerTrait.php @@ -9,16 +9,16 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Security\Guard\Firewall; +namespace Symfony\Component\Security\Http\Firewall; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\RequestEvent; -use Symfony\Component\Security\Core\Authentication\Authenticator\AuthenticatorInterface as CoreAuthenticatorInterface; +use Symfony\Component\Security\Http\Authentication\Authenticator\AuthenticatorInterface as CoreAuthenticatorInterface; +use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticationGuardToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Guard\AuthenticatorInterface; -use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; /** * @author Ryan Weaver @@ -26,7 +26,7 @@ use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; * * @internal */ -trait GuardAuthenticatorListenerTrait +trait GuardManagerListenerTrait { protected function getSupportingGuardAuthenticators(Request $request): array { @@ -89,7 +89,7 @@ trait GuardAuthenticatorListenerTrait } // create a token with the unique key, so that the provider knows which authenticator to use - $token = new PreAuthenticationGuardToken($credentials, $uniqueGuardKey, $this->providerKey); + $token = $this->createPreAuthenticatedToken($credentials, $uniqueGuardKey, $this->providerKey); if (null !== $this->logger) { $this->logger->debug('Passing guard token information to the GuardAuthenticationProvider', ['firewall_key' => $this->providerKey, 'authenticator' => \get_class($guardAuthenticator)]); @@ -174,4 +174,6 @@ trait GuardAuthenticatorListenerTrait } abstract protected function getGuardKey(string $key): string; + + abstract protected function createPreAuthenticatedToken($credentials, string $uniqueGuardKey, string $providerKey): PreAuthenticationGuardToken; } diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json index 376ee410fa..77a16c50ce 100644 --- a/src/Symfony/Component/Security/Http/composer.json +++ b/src/Symfony/Component/Security/Http/composer.json @@ -18,7 +18,7 @@ "require": { "php": "^7.2.5", "symfony/deprecation-contracts": "^2.1", - "symfony/security-core": "^4.4.8|^5.0.8", + "symfony/security-core": "^5.1", "symfony/http-foundation": "^4.4.7|^5.0.7", "symfony/http-kernel": "^4.4|^5.0", "symfony/polyfill-php80": "^1.15",