diff options
Diffstat (limited to 'Guard/Provider/GuardAuthenticationProvider.php')
-rw-r--r-- | Guard/Provider/GuardAuthenticationProvider.php | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/Guard/Provider/GuardAuthenticationProvider.php b/Guard/Provider/GuardAuthenticationProvider.php new file mode 100644 index 0000000..4347e02 --- /dev/null +++ b/Guard/Provider/GuardAuthenticationProvider.php @@ -0,0 +1,148 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Guard\Provider; + +use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; +use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; +use Symfony\Component\Security\Core\Exception\BadCredentialsException; +use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Guard\GuardAuthenticatorInterface; +use Symfony\Component\Security\Guard\Token\GuardTokenInterface; +use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; +use Symfony\Component\Security\Core\User\UserCheckerInterface; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\User\UserProviderInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationExpiredException; + +/** + * Responsible for accepting the PreAuthenticationGuardToken and calling + * the correct authenticator to retrieve the authenticated token. + * + * @author Ryan Weaver <ryan@knpuniversity.com> + */ +class GuardAuthenticationProvider implements AuthenticationProviderInterface +{ + /** + * @var GuardAuthenticatorInterface[] + */ + private $guardAuthenticators; + private $userProvider; + private $providerKey; + private $userChecker; + + /** + * @param GuardAuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationListener + * @param UserProviderInterface $userProvider The user provider + * @param string $providerKey The provider (i.e. firewall) key + * @param UserCheckerInterface $userChecker + */ + public function __construct(array $guardAuthenticators, UserProviderInterface $userProvider, $providerKey, UserCheckerInterface $userChecker) + { + $this->guardAuthenticators = $guardAuthenticators; + $this->userProvider = $userProvider; + $this->providerKey = $providerKey; + $this->userChecker = $userChecker; + } + + /** + * Finds the correct authenticator for the token and calls it. + * + * @param GuardTokenInterface $token + * + * @return TokenInterface + */ + public function authenticate(TokenInterface $token) + { + if (!$this->supports($token)) { + throw new \InvalidArgumentException('GuardAuthenticationProvider only supports GuardTokenInterface.'); + } + + if (!$token instanceof PreAuthenticationGuardToken) { + /* + * The listener *only* passes PreAuthenticationGuardToken instances. + * This means that an authenticated token (e.g. PostAuthenticationGuardToken) + * is being passed here, which happens if that token becomes + * "not authenticated" (e.g. happens if the user changes between + * requests). In this case, the user should be logged out, so + * we will return an AnonymousToken to accomplish that. + */ + + // this should never happen - but technically, the token is + // authenticated... so it could just be returned + if ($token->isAuthenticated()) { + return $token; + } + + // this AccountStatusException causes the user to be logged out + throw new AuthenticationExpiredException(); + } + + // find the *one* GuardAuthenticator that this token originated from + foreach ($this->guardAuthenticators as $key => $guardAuthenticator) { + // get a key that's unique to *this* guard authenticator + // this MUST be the same as GuardAuthenticationListener + $uniqueGuardKey = $this->providerKey.'_'.$key; + + if ($uniqueGuardKey == $token->getGuardProviderKey()) { + return $this->authenticateViaGuard($guardAuthenticator, $token); + } + } + + // no matching authenticator found - but there will be multiple GuardAuthenticationProvider + // instances that will be checked if you have multiple firewalls. + } + + private function authenticateViaGuard(GuardAuthenticatorInterface $guardAuthenticator, PreAuthenticationGuardToken $token) + { + // get the user from the GuardAuthenticator + $user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider); + + if (null === $user) { + throw new UsernameNotFoundException(sprintf( + 'Null returned from %s::getUser()', + get_class($guardAuthenticator) + )); + } + + if (!$user instanceof UserInterface) { + throw new \UnexpectedValueException(sprintf( + 'The %s::getUser() method must return a UserInterface. You returned %s.', + get_class($guardAuthenticator), + is_object($user) ? get_class($user) : gettype($user) + )); + } + + $this->userChecker->checkPreAuth($user); + if (true !== $guardAuthenticator->checkCredentials($token->getCredentials(), $user)) { + throw new BadCredentialsException(sprintf('Authentication failed because %s::checkCredentials() did not return true.', get_class($guardAuthenticator))); + } + $this->userChecker->checkPostAuth($user); + + // turn the UserInterface into a TokenInterface + $authenticatedToken = $guardAuthenticator->createAuthenticatedToken($user, $this->providerKey); + if (!$authenticatedToken instanceof TokenInterface) { + throw new \UnexpectedValueException(sprintf( + 'The %s::createAuthenticatedToken() method must return a TokenInterface. You returned %s.', + get_class($guardAuthenticator), + is_object($authenticatedToken) ? get_class($authenticatedToken) : gettype($authenticatedToken) + )); + } + + return $authenticatedToken; + } + + public function supports(TokenInterface $token) + { + return $token instanceof GuardTokenInterface; + } +} |