Skip to content

Commit 731020d

Browse files
Merge branch '4.4' into 5.0
* 4.4: [DI] auto-register singly implemented interfaces by default [DI] fix overriding existing services with aliases for singly-implemented interfaces remove service when base class is missing do not depend on the QueryBuilder from the ORM [Security/Http] call auth listeners/guards eagerly when they "support" the request [Messenger] add tests to FailedMessagesShowCommand Fix the translation commands when a template contains a syntax error [Security] Fix clearing remember-me cookie after deauthentication [Validator] Update Slovenian translations [HttpClient] remove conflict rule with HttpKernel that prevents using the component in Symfony 3.4 [Config][ReflectionClassResource] Handle parameters with undefined constant as their default values fix dumping number-like string parameters Fix CI [Console] Fix autocomplete multibyte input support [Config] don't break on virtual stack frames in ClassExistenceResource more robust initialization from request Changing the multipart form-data behavior to use the form name as an array, which makes it recognizable as an array by PHP on the $_POST globals once it is coming from the HttpClient component
2 parents 169d2e4 + 0ce1d81 commit 731020d

16 files changed

+268
-184
lines changed

Firewall/AbstractAuthenticationListener.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
* @author Fabien Potencier <[email protected]>
4949
* @author Johannes M. Schmitt <[email protected]>
5050
*/
51-
abstract class AbstractAuthenticationListener
51+
abstract class AbstractAuthenticationListener extends AbstractListener
5252
{
5353
protected $options;
5454
protected $logger;
@@ -102,20 +102,24 @@ public function setRememberMeServices(RememberMeServicesInterface $rememberMeSer
102102
$this->rememberMeServices = $rememberMeServices;
103103
}
104104

105+
/**
106+
* {@inheritdoc}
107+
*/
108+
public function supports(Request $request): ?bool
109+
{
110+
return $this->requiresAuthentication($request);
111+
}
112+
105113
/**
106114
* Handles form based authentication.
107115
*
108116
* @throws \RuntimeException
109117
* @throws SessionUnavailableException
110118
*/
111-
public function __invoke(RequestEvent $event)
119+
public function authenticate(RequestEvent $event)
112120
{
113121
$request = $event->getRequest();
114122

115-
if (!$this->requiresAuthentication($request)) {
116-
return;
117-
}
118-
119123
if (!$request->hasSession()) {
120124
throw new \RuntimeException('This authentication method requires a session.');
121125
}

Firewall/AbstractListener.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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\Http\Firewall;
13+
14+
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\HttpKernel\Event\RequestEvent;
16+
17+
/**
18+
* A base class for listeners that can tell whether they should authenticate incoming requests.
19+
*
20+
* @author Nicolas Grekas <[email protected]>
21+
*/
22+
abstract class AbstractListener
23+
{
24+
final public function __invoke(RequestEvent $event)
25+
{
26+
if (false !== $this->supports($event->getRequest())) {
27+
$this->authenticate($event);
28+
}
29+
}
30+
31+
/**
32+
* Tells whether the authenticate() method should be called or not depending on the incoming request.
33+
*
34+
* Returning null means authenticate() can be called lazily when accessing the token storage.
35+
*/
36+
abstract public function supports(Request $request): ?bool;
37+
38+
/**
39+
* Does whatever is required to authenticate the request, typically calling $event->setResponse() internally.
40+
*/
41+
abstract public function authenticate(RequestEvent $event);
42+
}

Firewall/AbstractPreAuthenticatedListener.php

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
*
3535
* @internal
3636
*/
37-
abstract class AbstractPreAuthenticatedListener
38-
{
37+
abstract class AbstractPreAuthenticatedListener extends AbstractListener
38+
3939
protected $logger;
4040
private $tokenStorage;
4141
private $authenticationManager;
@@ -53,20 +53,31 @@ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationM
5353
}
5454

5555
/**
56-
* Handles pre-authentication.
56+
* {@inheritdoc}
5757
*/
58-
public function __invoke(RequestEvent $event)
58+
public function supports(Request $request): ?bool
5959
{
60-
$request = $event->getRequest();
61-
6260
try {
63-
list($user, $credentials) = $this->getPreAuthenticatedData($request);
61+
$request->attributes->set('_pre_authenticated_data', $this->getPreAuthenticatedData($request));
6462
} catch (BadCredentialsException $e) {
6563
$this->clearToken($e);
6664

67-
return;
65+
return false;
6866
}
6967

68+
return true;
69+
}
70+
71+
/**
72+
* Handles pre-authentication.
73+
*/
74+
public function authenticate(RequestEvent $event)
75+
{
76+
$request = $event->getRequest();
77+
78+
[$user, $credentials] = $request->attributes->get('_pre_authenticated_data');
79+
$request->attributes->remove('_pre_authenticated_data');
80+
7081
if (null !== $this->logger) {
7182
$this->logger->debug('Checking current security token.', ['token' => (string) $this->tokenStorage->getToken()]);
7283
}

Firewall/AccessListener.php

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111

1212
namespace Symfony\Component\Security\Http\Firewall;
1313

14+
use Symfony\Component\HttpFoundation\Request;
1415
use Symfony\Component\HttpKernel\Event\RequestEvent;
1516
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
1617
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
1718
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
19+
use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter;
1820
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
1921
use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException;
2022
use Symfony\Component\Security\Http\AccessMapInterface;
@@ -27,7 +29,7 @@
2729
*
2830
* @final
2931
*/
30-
class AccessListener
32+
class AccessListener extends AbstractListener
3133
{
3234
private $tokenStorage;
3335
private $accessDecisionManager;
@@ -42,23 +44,35 @@ public function __construct(TokenStorageInterface $tokenStorage, AccessDecisionM
4244
$this->authManager = $authManager;
4345
}
4446

47+
/**
48+
* {@inheritdoc}
49+
*/
50+
public function supports(Request $request): ?bool
51+
{
52+
[$attributes] = $this->map->getPatterns($request);
53+
$request->attributes->set('_access_control_attributes', $attributes);
54+
55+
return $attributes && [AuthenticatedVoter::IS_AUTHENTICATED_ANONYMOUSLY] !== $attributes ? true : null;
56+
}
57+
4558
/**
4659
* Handles access authorization.
4760
*
4861
* @throws AccessDeniedException
4962
* @throws AuthenticationCredentialsNotFoundException
5063
*/
51-
public function __invoke(RequestEvent $event)
64+
public function authenticate(RequestEvent $event)
5265
{
5366
if (!$event instanceof LazyResponseEvent && null === $token = $this->tokenStorage->getToken()) {
5467
throw new AuthenticationCredentialsNotFoundException('A Token was not found in the TokenStorage.');
5568
}
5669

5770
$request = $event->getRequest();
5871

59-
list($attributes) = $this->map->getPatterns($request);
72+
$attributes = $request->attributes->get('_access_control_attributes');
73+
$request->attributes->remove('_access_control_attributes');
6074

61-
if (!$attributes) {
75+
if (!$attributes || ([AuthenticatedVoter::IS_AUTHENTICATED_ANONYMOUSLY] === $attributes && $event instanceof LazyResponseEvent)) {
6276
return;
6377
}
6478

Firewall/AnonymousAuthenticationListener.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Security\Http\Firewall;
1313

1414
use Psr\Log\LoggerInterface;
15+
use Symfony\Component\HttpFoundation\Request;
1516
use Symfony\Component\HttpKernel\Event\RequestEvent;
1617
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
1718
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
@@ -26,7 +27,7 @@
2627
*
2728
* @final
2829
*/
29-
class AnonymousAuthenticationListener
30+
class AnonymousAuthenticationListener extends AbstractListener
3031
{
3132
private $tokenStorage;
3233
private $secret;
@@ -41,10 +42,18 @@ public function __construct(TokenStorageInterface $tokenStorage, string $secret,
4142
$this->logger = $logger;
4243
}
4344

45+
/**
46+
* {@inheritdoc}
47+
*/
48+
public function supports(Request $request): ?bool
49+
{
50+
return null; // always run authenticate() lazily with lazy firewalls
51+
}
52+
4453
/**
4554
* Handles anonymous authentication.
4655
*/
47-
public function __invoke(RequestEvent $event)
56+
public function authenticate(RequestEvent $event)
4857
{
4958
if (null !== $this->tokenStorage->getToken()) {
5059
return;

Firewall/BasicAuthenticationListener.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
*
3030
* @final
3131
*/
32-
class BasicAuthenticationListener
32+
class BasicAuthenticationListener extends AbstractListener
3333
{
3434
private $tokenStorage;
3535
private $authenticationManager;
@@ -53,10 +53,18 @@ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationM
5353
$this->ignoreFailure = false;
5454
}
5555

56+
/**
57+
* {@inheritdoc}
58+
*/
59+
public function supports(Request $request): ?bool
60+
{
61+
return null !== $request->headers->get('PHP_AUTH_USER');
62+
}
63+
5664
/**
5765
* Handles basic authentication.
5866
*/
59-
public function __invoke(RequestEvent $event)
67+
public function authenticate(RequestEvent $event)
6068
{
6169
$request = $event->getRequest();
6270

Firewall/ChannelListener.php

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Security\Http\Firewall;
1313

1414
use Psr\Log\LoggerInterface;
15+
use Symfony\Component\HttpFoundation\Request;
1516
use Symfony\Component\HttpKernel\Event\RequestEvent;
1617
use Symfony\Component\Security\Http\AccessMapInterface;
1718
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
@@ -24,7 +25,7 @@
2425
*
2526
* @final
2627
*/
27-
class ChannelListener
28+
class ChannelListener extends AbstractListener
2829
{
2930
private $map;
3031
private $authenticationEntryPoint;
@@ -40,10 +41,8 @@ public function __construct(AccessMapInterface $map, AuthenticationEntryPointInt
4041
/**
4142
* Handles channel management.
4243
*/
43-
public function __invoke(RequestEvent $event)
44+
public function supports(Request $request): ?bool
4445
{
45-
$request = $event->getRequest();
46-
4746
list(, $channel) = $this->map->getPatterns($request);
4847

4948
if ('https' === $channel && !$request->isSecure()) {
@@ -57,21 +56,26 @@ public function __invoke(RequestEvent $event)
5756
}
5857
}
5958

60-
$response = $this->authenticationEntryPoint->start($request);
61-
62-
$event->setResponse($response);
63-
64-
return;
59+
return true;
6560
}
6661

6762
if ('http' === $channel && $request->isSecure()) {
6863
if (null !== $this->logger) {
6964
$this->logger->info('Redirecting to HTTP.');
7065
}
7166

72-
$response = $this->authenticationEntryPoint->start($request);
73-
74-
$event->setResponse($response);
67+
return true;
7568
}
69+
70+
return false;
71+
}
72+
73+
public function authenticate(RequestEvent $event)
74+
{
75+
$request = $event->getRequest();
76+
77+
$response = $this->authenticationEntryPoint->start($request);
78+
79+
$event->setResponse($response);
7680
}
7781
}

Firewall/ContextListener.php

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use Symfony\Component\Security\Core\User\UserInterface;
3030
use Symfony\Component\Security\Core\User\UserProviderInterface;
3131
use Symfony\Component\Security\Http\Event\DeauthenticatedEvent;
32+
use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface;
3233
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
3334

3435
/**
@@ -39,7 +40,7 @@
3940
*
4041
* @final
4142
*/
42-
class ContextListener
43+
class ContextListener extends AbstractListener
4344
{
4445
private $tokenStorage;
4546
private $sessionKey;
@@ -48,6 +49,7 @@ class ContextListener
4849
private $dispatcher;
4950
private $registered;
5051
private $trustResolver;
52+
private $rememberMeServices;
5153
private $sessionTrackerEnabler;
5254

5355
/**
@@ -68,10 +70,18 @@ public function __construct(TokenStorageInterface $tokenStorage, iterable $userP
6870
$this->sessionTrackerEnabler = $sessionTrackerEnabler;
6971
}
7072

73+
/**
74+
* {@inheritdoc}
75+
*/
76+
public function supports(Request $request): ?bool
77+
{
78+
return null; // always run authenticate() lazily with lazy firewalls
79+
}
80+
7181
/**
7282
* Reads the Security Token from the session.
7383
*/
74-
public function __invoke(RequestEvent $event)
84+
public function authenticate(RequestEvent $event)
7585
{
7686
if (!$this->registered && null !== $this->dispatcher && $event->isMasterRequest()) {
7787
$this->dispatcher->addListener(KernelEvents::RESPONSE, [$this, 'onKernelResponse']);
@@ -112,6 +122,10 @@ public function __invoke(RequestEvent $event)
112122

113123
if ($token instanceof TokenInterface) {
114124
$token = $this->refreshUser($token);
125+
126+
if (!$token && $this->rememberMeServices) {
127+
$this->rememberMeServices->loginFail($request);
128+
}
115129
} elseif (null !== $token) {
116130
if (null !== $this->logger) {
117131
$this->logger->warning('Expected a security token from the session, got something else.', ['key' => $this->sessionKey, 'received' => $token]);
@@ -282,4 +296,9 @@ public static function handleUnserializeCallback($class)
282296
{
283297
throw new \ErrorException('Class not found: '.$class, 0x37313bc);
284298
}
299+
300+
public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices)
301+
{
302+
$this->rememberMeServices = $rememberMeServices;
303+
}
285304
}

0 commit comments

Comments
 (0)