Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c321f4d

Browse files
committed
Created GuardAuthenticationManager to make Guard first-class Security
1 parent e464954 commit c321f4d

File tree

4 files changed

+211
-59
lines changed

4 files changed

+211
-59
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Component\Security\Core\Authentication;
13+
14+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
15+
use Symfony\Component\Security\Core\AuthenticationEvents;
16+
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
17+
use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent;
18+
use Symfony\Component\Security\Core\Exception\AuthenticationException;
19+
use Symfony\Component\Security\Core\Exception\AuthenticationExpiredException;
20+
use Symfony\Component\Security\Core\Exception\ProviderNotFoundException;
21+
use Symfony\Component\Security\Core\User\UserCheckerInterface;
22+
use Symfony\Component\Security\Guard\AuthenticatorInterface;
23+
use Symfony\Component\Security\Guard\Provider\GuardAuthenticationProviderTrait;
24+
use Symfony\Component\Security\Guard\Token\GuardTokenInterface;
25+
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
26+
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
27+
28+
class GuardAuthenticationManager implements AuthenticationManagerInterface
29+
{
30+
use GuardAuthenticationProviderTrait;
31+
32+
private $guardAuthenticators;
33+
private $userChecker;
34+
private $eraseCredentials;
35+
/** @var EventDispatcherInterface */
36+
private $eventDispatcher;
37+
38+
/**
39+
* @param iterable|AuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationListener
40+
*/
41+
public function __construct($guardAuthenticators, UserCheckerInterface $userChecker, bool $eraseCredentials = true)
42+
{
43+
$this->guardAuthenticators = $guardAuthenticators;
44+
$this->userChecker = $userChecker;
45+
$this->eraseCredentials = $eraseCredentials;
46+
}
47+
48+
public function setEventDispatcher(EventDispatcherInterface $dispatcher)
49+
{
50+
$this->eventDispatcher = $dispatcher;
51+
}
52+
53+
public function authenticate(TokenInterface $token)
54+
{
55+
if (!$token instanceof GuardTokenInterface) {
56+
throw new \InvalidArgumentException('GuardAuthenticationManager only supports GuardTokenInterface.');
57+
}
58+
59+
if (!$token instanceof PreAuthenticationGuardToken) {
60+
/*
61+
* The listener *only* passes PreAuthenticationGuardToken instances.
62+
* This means that an authenticated token (e.g. PostAuthenticationGuardToken)
63+
* is being passed here, which happens if that token becomes
64+
* "not authenticated" (e.g. happens if the user changes between
65+
* requests). In this case, the user should be logged out.
66+
*/
67+
68+
// this should never happen - but technically, the token is
69+
// authenticated... so it could just be returned
70+
if ($token->isAuthenticated()) {
71+
return $token;
72+
}
73+
74+
// this AccountStatusException causes the user to be logged out
75+
throw new AuthenticationExpiredException();
76+
}
77+
78+
$guard = $this->findOriginatingAuthenticator($token);
79+
if (null === $guard) {
80+
$this->handleFailure(new ProviderNotFoundException(sprintf('Token with provider key "%s" did not originate from any of the guard authenticators.', $token->getGuardProviderKey())), $token);
81+
}
82+
83+
try {
84+
$result = $this->authenticateViaGuard($guard, $token);
85+
} catch (AuthenticationException $exception) {
86+
$this->handleFailure($exception, $token);
87+
}
88+
89+
if (true === $this->eraseCredentials) {
90+
$result->eraseCredentials();
91+
}
92+
93+
if (null !== $this->eventDispatcher) {
94+
$this->eventDispatcher->dispatch(new AuthenticationSuccessEvent($result), AuthenticationEvents::AUTHENTICATION_SUCCESS);
95+
}
96+
97+
return $result;
98+
}
99+
100+
private function handleFailure(AuthenticationException $exception, TokenInterface $token)
101+
{
102+
if (null !== $this->eventDispatcher) {
103+
$this->eventDispatcher->dispatch(new AuthenticationFailureEvent($token, $exception), AuthenticationEvents::AUTHENTICATION_FAILURE);
104+
}
105+
106+
$exception->setToken($token);
107+
108+
throw $exception;
109+
}
110+
111+
protected function getGuardKey(string $key): string
112+
{
113+
// Guard authenticators in the GuardAuthenticationManager are already indexed
114+
// by an unique key
115+
return $key;
116+
}
117+
}

src/Symfony/Component/Security/Core/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"symfony/event-dispatcher-contracts": "^1.1|^2",
2121
"symfony/polyfill-php80": "^1.15",
2222
"symfony/service-contracts": "^1.1.6|^2",
23+
"symfony/security-guard": "^4.4",
2324
"symfony/deprecation-contracts": "^2.1"
2425
},
2526
"require-dev": {

src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php

Lines changed: 7 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,9 @@
1616
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
1717
use Symfony\Component\Security\Core\Exception\AuthenticationException;
1818
use Symfony\Component\Security\Core\Exception\AuthenticationExpiredException;
19-
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
20-
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
21-
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
2219
use Symfony\Component\Security\Core\User\UserCheckerInterface;
23-
use Symfony\Component\Security\Core\User\UserInterface;
2420
use Symfony\Component\Security\Core\User\UserProviderInterface;
2521
use Symfony\Component\Security\Guard\AuthenticatorInterface;
26-
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
2722
use Symfony\Component\Security\Guard\Token\GuardTokenInterface;
2823
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
2924

@@ -35,6 +30,8 @@
3530
*/
3631
class GuardAuthenticationProvider implements AuthenticationProviderInterface
3732
{
33+
use GuardAuthenticationProviderTrait;
34+
3835
/**
3936
* @var AuthenticatorInterface[]
4037
*/
@@ -99,60 +96,6 @@ public function authenticate(TokenInterface $token)
9996
return $this->authenticateViaGuard($guardAuthenticator, $token);
10097
}
10198

102-
private function authenticateViaGuard(AuthenticatorInterface $guardAuthenticator, PreAuthenticationGuardToken $token): GuardTokenInterface
103-
{
104-
// get the user from the GuardAuthenticator
105-
$user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider);
106-
107-
if (null === $user) {
108-
throw new UsernameNotFoundException(sprintf('Null returned from "%s::getUser()".', get_debug_type($guardAuthenticator)));
109-
}
110-
111-
if (!$user instanceof UserInterface) {
112-
throw new \UnexpectedValueException(sprintf('The "%s::getUser()" method must return a UserInterface. You returned "%s".', get_debug_type($guardAuthenticator), get_debug_type($user)));
113-
}
114-
115-
$this->userChecker->checkPreAuth($user);
116-
if (true !== $checkCredentialsResult = $guardAuthenticator->checkCredentials($token->getCredentials(), $user)) {
117-
if (false !== $checkCredentialsResult) {
118-
throw new \TypeError(sprintf('"%s::checkCredentials()" must return a boolean value.', get_debug_type($guardAuthenticator)));
119-
}
120-
121-
throw new BadCredentialsException(sprintf('Authentication failed because "%s::checkCredentials()" did not return true.', get_debug_type($guardAuthenticator)));
122-
}
123-
if ($this->userProvider instanceof PasswordUpgraderInterface && $guardAuthenticator instanceof PasswordAuthenticatedInterface && null !== $this->passwordEncoder && (null !== $password = $guardAuthenticator->getPassword($token->getCredentials())) && method_exists($this->passwordEncoder, 'needsRehash') && $this->passwordEncoder->needsRehash($user)) {
124-
$this->userProvider->upgradePassword($user, $this->passwordEncoder->encodePassword($user, $password));
125-
}
126-
$this->userChecker->checkPostAuth($user);
127-
128-
// turn the UserInterface into a TokenInterface
129-
$authenticatedToken = $guardAuthenticator->createAuthenticatedToken($user, $this->providerKey);
130-
if (!$authenticatedToken instanceof TokenInterface) {
131-
throw new \UnexpectedValueException(sprintf('The "%s::createAuthenticatedToken()" method must return a TokenInterface. You returned "%s".', get_debug_type($guardAuthenticator), get_debug_type($authenticatedToken)));
132-
}
133-
134-
return $authenticatedToken;
135-
}
136-
137-
private function findOriginatingAuthenticator(PreAuthenticationGuardToken $token): ?AuthenticatorInterface
138-
{
139-
// find the *one* GuardAuthenticator that this token originated from
140-
foreach ($this->guardAuthenticators as $key => $guardAuthenticator) {
141-
// get a key that's unique to *this* guard authenticator
142-
// this MUST be the same as GuardAuthenticationListener
143-
$uniqueGuardKey = $this->providerKey.'_'.$key;
144-
145-
if ($uniqueGuardKey === $token->getGuardProviderKey()) {
146-
return $guardAuthenticator;
147-
}
148-
}
149-
150-
// no matching authenticator found - but there will be multiple GuardAuthenticationProvider
151-
// instances that will be checked if you have multiple firewalls.
152-
153-
return null;
154-
}
155-
15699
public function supports(TokenInterface $token)
157100
{
158101
if ($token instanceof PreAuthenticationGuardToken) {
@@ -161,4 +104,9 @@ public function supports(TokenInterface $token)
161104

162105
return $token instanceof GuardTokenInterface;
163106
}
107+
108+
protected function getGuardKey(string $key): string
109+
{
110+
return $this->providerKey.'_'.$key;
111+
}
164112
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Component\Security\Guard\Provider;
13+
14+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
15+
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
16+
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
17+
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
18+
use Symfony\Component\Security\Core\User\UserInterface;
19+
use Symfony\Component\Security\Guard\AuthenticatorInterface;
20+
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
21+
use Symfony\Component\Security\Guard\Token\GuardTokenInterface;
22+
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
23+
24+
/**
25+
* @author Ryan Weaver <[email protected]>
26+
*
27+
* @internal
28+
*/
29+
trait GuardAuthenticationProviderTrait
30+
{
31+
private function authenticateViaGuard(AuthenticatorInterface $guardAuthenticator, PreAuthenticationGuardToken $token): GuardTokenInterface
32+
{
33+
// get the user from the GuardAuthenticator
34+
$user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider);
35+
36+
if (null === $user) {
37+
throw new UsernameNotFoundException(sprintf('Null returned from "%s::getUser()".', get_debug_type($guardAuthenticator)));
38+
}
39+
40+
if (!$user instanceof UserInterface) {
41+
throw new \UnexpectedValueException(sprintf('The "%s::getUser()" method must return a UserInterface. You returned "%s".', get_debug_type($guardAuthenticator), get_debug_type($user)));
42+
}
43+
44+
$this->userChecker->checkPreAuth($user);
45+
if (true !== $checkCredentialsResult = $guardAuthenticator->checkCredentials($token->getCredentials(), $user)) {
46+
if (false !== $checkCredentialsResult) {
47+
throw new \TypeError(sprintf('"%s::checkCredentials()" must return a boolean value.', get_debug_type($guardAuthenticator)));
48+
}
49+
50+
throw new BadCredentialsException(sprintf('Authentication failed because "%s::checkCredentials()" did not return true.', get_debug_type($guardAuthenticator)));
51+
}
52+
if ($this->userProvider instanceof PasswordUpgraderInterface && $guardAuthenticator instanceof PasswordAuthenticatedInterface && null !== $this->passwordEncoder && (null !== $password = $guardAuthenticator->getPassword($token->getCredentials())) && method_exists($this->passwordEncoder, 'needsRehash') && $this->passwordEncoder->needsRehash($user)) {
53+
$this->userProvider->upgradePassword($user, $this->passwordEncoder->encodePassword($user, $password));
54+
}
55+
$this->userChecker->checkPostAuth($user);
56+
57+
// turn the UserInterface into a TokenInterface
58+
$authenticatedToken = $guardAuthenticator->createAuthenticatedToken($user, $this->providerKey);
59+
if (!$authenticatedToken instanceof TokenInterface) {
60+
throw new \UnexpectedValueException(sprintf('The "%s::createAuthenticatedToken()" method must return a TokenInterface. You returned "%s".', get_debug_type($guardAuthenticator), get_debug_type($authenticatedToken)));
61+
}
62+
63+
return $authenticatedToken;
64+
}
65+
66+
private function findOriginatingAuthenticator(PreAuthenticationGuardToken $token): ?AuthenticatorInterface
67+
{
68+
// find the *one* GuardAuthenticator that this token originated from
69+
foreach ($this->guardAuthenticators as $key => $guardAuthenticator) {
70+
// get a key that's unique to *this* guard authenticator
71+
// this MUST be the same as GuardAuthenticationListener
72+
$uniqueGuardKey = $this->getGuardKey($key);
73+
74+
if ($uniqueGuardKey === $token->getGuardProviderKey()) {
75+
return $guardAuthenticator;
76+
}
77+
}
78+
79+
// no matching authenticator found - but there will be multiple GuardAuthenticationProvider
80+
// instances that will be checked if you have multiple firewalls.
81+
82+
return null;
83+
}
84+
85+
abstract protected function getGuardKey(string $key): string;
86+
}

0 commit comments

Comments
 (0)