Skip to content

Commit 02b77f5

Browse files
committed
feature symfony#16835 [Security] Add Guard authenticator <supports> method (Amo, chalasr)
This PR was merged into the 3.4 branch. Discussion ---------- [Security] Add Guard authenticator <supports> method This method will be called before starting an authentication against a guard authenticator. The authentication will be tried only if the supports method returned `true` This improves understanding of code, increase consistency and removes responsability for `getCredentials` method to decide if the current request should be supported or not. | Q | A | | --- | --- | | Bug fix? | no | | New feature? | yes | | BC breaks? | yes | | Deprecations? | yes | | Tests pass? | yes | | Fixed tickets | none | | License | MIT | | Doc PR | | Commits ------- 78eecba Fix BC layer a7a6f8a [Security] Add Guard authenticator <supports> method
2 parents 86684f1 + 78eecba commit 02b77f5

13 files changed

+381
-41
lines changed

UPGRADE-3.4.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,9 @@ Security
305305
* Deprecated the HTTP digest authentication: `NonceExpiredException`,
306306
`DigestAuthenticationListener` and `DigestAuthenticationEntryPoint` will be
307307
removed in 4.0. Use another authentication system like `http_basic` instead.
308+
309+
* The `GuardAuthenticatorInterface` has been deprecated and will be removed in 4.0.
310+
Use `AuthenticatorInterface` instead.
308311

309312
SecurityBundle
310313
--------------

UPGRADE-4.0.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,9 @@ Security
679679
`DigestAuthenticationListener` and `DigestAuthenticationEntryPoint` classes
680680
have been removed. Use another authentication system like `http_basic` instead.
681681

682+
* The `GuardAuthenticatorInterface` interface has been removed.
683+
Use `AuthenticatorInterface` instead.
684+
682685
SecurityBundle
683686
--------------
684687

src/Symfony/Component/Security/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ CHANGELOG
1515
requests.
1616
* deprecated HTTP digest authentication
1717
* Added a new password encoder for the Argon2i hashing algorithm
18+
* deprecated `GuardAuthenticatorInterface` in favor of `AuthenticatorInterface`
1819

1920
3.3.0
2021
-----

src/Symfony/Component/Security/Guard/AbstractGuardAuthenticator.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Security\Guard;
1313

14+
use Symfony\Component\HttpFoundation\Request;
1415
use Symfony\Component\Security\Core\User\UserInterface;
1516
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
1617

@@ -19,8 +20,20 @@
1920
*
2021
* @author Ryan Weaver <[email protected]>
2122
*/
22-
abstract class AbstractGuardAuthenticator implements GuardAuthenticatorInterface
23+
abstract class AbstractGuardAuthenticator implements AuthenticatorInterface
2324
{
25+
/**
26+
* {@inheritdoc}
27+
*
28+
* @deprecated since version 3.4, to be removed in 4.0
29+
*/
30+
public function supports(Request $request)
31+
{
32+
@trigger_error(sprintf('The "%s()" method is deprecated since version 3.4 and will be removed in 4.0. Implement the "%s::supports()" method in class "%s" instead.', __METHOD__, AuthenticatorInterface::class, get_class($this)), E_USER_DEPRECATED);
33+
34+
return true;
35+
}
36+
2437
/**
2538
* Shortcut to create a PostAuthenticationGuardToken for you, if you don't really
2639
* care about which authenticated token you're using.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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;
13+
14+
use Symfony\Component\HttpFoundation\Request;
15+
16+
/**
17+
* The interface for all "guard" authenticators.
18+
*
19+
* The methods on this interface are called throughout the guard authentication
20+
* process to give you the power to control most parts of the process from
21+
* one location.
22+
*
23+
* @author Ryan Weaver <[email protected]>
24+
* @author Amaury Leroux de Lens <[email protected]>
25+
*/
26+
interface AuthenticatorInterface extends GuardAuthenticatorInterface
27+
{
28+
/**
29+
* Does the authenticator support the given Request?
30+
*
31+
* If this returns false, the authenticator will be skipped.
32+
*
33+
* @param Request $request
34+
*
35+
* @return bool
36+
*/
37+
public function supports(Request $request);
38+
39+
/**
40+
* Get the authentication credentials from the request and return them
41+
* as any type (e.g. an associate array).
42+
*
43+
* Whatever value you return here will be passed to getUser() and checkCredentials()
44+
*
45+
* For example, for a form login, you might:
46+
*
47+
* return array(
48+
* 'username' => $request->request->get('_username'),
49+
* 'password' => $request->request->get('_password'),
50+
* );
51+
*
52+
* Or for an API token that's on a header, you might use:
53+
*
54+
* return array('api_key' => $request->headers->get('X-API-TOKEN'));
55+
*
56+
* @param Request $request
57+
*
58+
* @return mixed Any non-null value
59+
*
60+
* @throws \UnexpectedValueException If null is returned
61+
*/
62+
public function getCredentials(Request $request);
63+
}

src/Symfony/Component/Security/Guard/Firewall/GuardAuthenticationListener.php

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
use Symfony\Component\HttpFoundation\Response;
1616
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
1717
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
18+
use Symfony\Component\Security\Guard\GuardAuthenticatorInterface;
1819
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
1920
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
20-
use Symfony\Component\Security\Guard\GuardAuthenticatorInterface;
21+
use Symfony\Component\Security\Guard\AuthenticatorInterface;
2122
use Psr\Log\LoggerInterface;
2223
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
2324
use Symfony\Component\Security\Core\Exception\AuthenticationException;
@@ -28,6 +29,7 @@
2829
* Authentication listener for the "guard" system.
2930
*
3031
* @author Ryan Weaver <[email protected]>
32+
* @author Amaury Leroux de Lens <[email protected]>
3133
*/
3234
class GuardAuthenticationListener implements ListenerInterface
3335
{
@@ -39,11 +41,11 @@ class GuardAuthenticationListener implements ListenerInterface
3941
private $rememberMeServices;
4042

4143
/**
42-
* @param GuardAuthenticatorHandler $guardHandler The Guard handler
43-
* @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance
44-
* @param string $providerKey The provider (i.e. firewall) key
45-
* @param iterable|GuardAuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider
46-
* @param LoggerInterface $logger A LoggerInterface instance
44+
* @param GuardAuthenticatorHandler $guardHandler The Guard handler
45+
* @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance
46+
* @param string $providerKey The provider (i.e. firewall) key
47+
* @param iterable|AuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider
48+
* @param LoggerInterface $logger A LoggerInterface instance
4749
*/
4850
public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, $providerKey, $guardAuthenticators, LoggerInterface $logger = null)
4951
{
@@ -100,12 +102,29 @@ private function executeGuardAuthenticator($uniqueGuardKey, GuardAuthenticatorIn
100102
$this->logger->debug('Calling getCredentials() on guard configurator.', array('firewall_key' => $this->providerKey, 'authenticator' => get_class($guardAuthenticator)));
101103
}
102104

105+
// abort the execution of the authenticator if it doesn't support the request
106+
if ($guardAuthenticator instanceof AuthenticatorInterface) {
107+
if (!$guardAuthenticator->supports($request)) {
108+
return;
109+
}
110+
// as there was a support for given request,
111+
// authenticator is expected to give not-null credentials.
112+
$credentialsCanBeNull = false;
113+
} else {
114+
// deprecated since version 3.4, to be removed in 4.0
115+
$credentialsCanBeNull = true;
116+
}
117+
103118
// allow the authenticator to fetch authentication info from the request
104119
$credentials = $guardAuthenticator->getCredentials($request);
105120

106-
// allow null to be returned to skip authentication
107121
if (null === $credentials) {
108-
return;
122+
// deprecated since version 3.4, to be removed in 4.0
123+
if ($credentialsCanBeNull) {
124+
return;
125+
}
126+
127+
throw new \UnexpectedValueException(sprintf('The return value of "%s::getCredentials()" must not be null. Return false from "%s::supports()" instead.', get_class($guardAuthenticator), get_class($guardAuthenticator)));
109128
}
110129

111130
// create a token with the unique key, so that the provider knows which authenticator to use
@@ -172,10 +191,10 @@ public function setRememberMeServices(RememberMeServicesInterface $rememberMeSer
172191
* Checks to see if remember me is supported in the authenticator and
173192
* on the firewall. If it is, the RememberMeServicesInterface is notified.
174193
*
175-
* @param GuardAuthenticatorInterface $guardAuthenticator
176-
* @param Request $request
177-
* @param TokenInterface $token
178-
* @param Response $response
194+
* @param AuthenticatorInterface $guardAuthenticator
195+
* @param Request $request
196+
* @param TokenInterface $token
197+
* @param Response $response
179198
*/
180199
private function triggerRememberMe(GuardAuthenticatorInterface $guardAuthenticator, Request $request, TokenInterface $token, Response $response = null)
181200
{

src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
* can be called directly (e.g. for manual authentication) or overridden.
3030
*
3131
* @author Ryan Weaver <[email protected]>
32+
*
33+
* @final since version 3.4
3234
*/
3335
class GuardAuthenticatorHandler
3436
{
@@ -61,10 +63,10 @@ public function authenticateWithToken(TokenInterface $token, Request $request)
6163
/**
6264
* Returns the "on success" response for the given GuardAuthenticator.
6365
*
64-
* @param TokenInterface $token
65-
* @param Request $request
66-
* @param GuardAuthenticatorInterface $guardAuthenticator
67-
* @param string $providerKey The provider (i.e. firewall) key
66+
* @param TokenInterface $token
67+
* @param Request $request
68+
* @param AuthenticatorInterface $guardAuthenticator
69+
* @param string $providerKey The provider (i.e. firewall) key
6870
*
6971
* @return null|Response
7072
*/
@@ -88,10 +90,10 @@ public function handleAuthenticationSuccess(TokenInterface $token, Request $requ
8890
* Convenience method for authenticating the user and returning the
8991
* Response *if any* for success.
9092
*
91-
* @param UserInterface $user
92-
* @param Request $request
93-
* @param GuardAuthenticatorInterface $authenticator
94-
* @param string $providerKey The provider (i.e. firewall) key
93+
* @param UserInterface $user
94+
* @param Request $request
95+
* @param AuthenticatorInterface $authenticator
96+
* @param string $providerKey The provider (i.e. firewall) key
9597
*
9698
* @return Response|null
9799
*/
@@ -110,10 +112,10 @@ public function authenticateUserAndHandleSuccess(UserInterface $user, Request $r
110112
* Handles an authentication failure and returns the Response for the
111113
* GuardAuthenticator.
112114
*
113-
* @param AuthenticationException $authenticationException
114-
* @param Request $request
115-
* @param GuardAuthenticatorInterface $guardAuthenticator
116-
* @param string $providerKey The key of the firewall
115+
* @param AuthenticationException $authenticationException
116+
* @param Request $request
117+
* @param AuthenticatorInterface $guardAuthenticator
118+
* @param string $providerKey The key of the firewall
117119
*
118120
* @return null|Response
119121
*/

src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
* one location.
2929
*
3030
* @author Ryan Weaver <[email protected]>
31+
*
32+
* @deprecated since version 3.4, to be removed in 4.0. Use AuthenticatorInterface instead
3133
*/
3234
interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface
3335
{

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
1515
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
1616
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
17-
use Symfony\Component\Security\Guard\GuardAuthenticatorInterface;
17+
use Symfony\Component\Security\Guard\AuthenticatorInterface;
1818
use Symfony\Component\Security\Guard\Token\GuardTokenInterface;
1919
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
2020
use Symfony\Component\Security\Core\User\UserCheckerInterface;
@@ -32,18 +32,18 @@
3232
class GuardAuthenticationProvider implements AuthenticationProviderInterface
3333
{
3434
/**
35-
* @var GuardAuthenticatorInterface[]
35+
* @var AuthenticatorInterface[]
3636
*/
3737
private $guardAuthenticators;
3838
private $userProvider;
3939
private $providerKey;
4040
private $userChecker;
4141

4242
/**
43-
* @param iterable|GuardAuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationListener
44-
* @param UserProviderInterface $userProvider The user provider
45-
* @param string $providerKey The provider (i.e. firewall) key
46-
* @param UserCheckerInterface $userChecker
43+
* @param iterable|AuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationListener
44+
* @param UserProviderInterface $userProvider The user provider
45+
* @param string $providerKey The provider (i.e. firewall) key
46+
* @param UserCheckerInterface $userChecker
4747
*/
4848
public function __construct($guardAuthenticators, UserProviderInterface $userProvider, $providerKey, UserCheckerInterface $userChecker)
4949
{
@@ -101,7 +101,7 @@ public function authenticate(TokenInterface $token)
101101
// instances that will be checked if you have multiple firewalls.
102102
}
103103

104-
private function authenticateViaGuard(GuardAuthenticatorInterface $guardAuthenticator, PreAuthenticationGuardToken $token)
104+
private function authenticateViaGuard($guardAuthenticator, PreAuthenticationGuardToken $token)
105105
{
106106
// get the user from the GuardAuthenticator
107107
$user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider);

0 commit comments

Comments
 (0)