Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e45b25f

Browse filesBrowse files
committed
[Security] Replace RememberMeServices with RememberMeHandlers
* Add TokenDeauthenticatedEvent to reset remember me cookies in RememberMeListener * Add SignatureRememberMeHandler (replacing TokenBasedRememberMeServices) * Add PersistentRememberMeHandler (replacing PersistentTokenBasedRememberMeServices)
1 parent 89a87aa commit e45b25f
Copy full SHA for e45b25f

29 files changed

+1203
-192
lines changed

‎src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterGlobalSecurityEventListenersPass.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterGlobalSecurityEventListenersPass.php
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Symfony\Component\Security\Http\Event\LoginFailureEvent;
2222
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
2323
use Symfony\Component\Security\Http\Event\LogoutEvent;
24+
use Symfony\Component\Security\Http\Event\TokenDeauthenticatedEvent;
2425
use Symfony\Component\Security\Http\SecurityEvents;
2526

2627
/**
@@ -44,6 +45,7 @@ class RegisterGlobalSecurityEventListenersPass implements CompilerPassInterface
4445
AuthenticationTokenCreatedEvent::class,
4546
AuthenticationSuccessEvent::class,
4647
InteractiveLoginEvent::class,
48+
TokenDeauthenticatedEvent::class,
4749

4850
// When events are registered by their name
4951
AuthenticationEvents::AUTHENTICATION_SUCCESS,

‎src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginLinkFactory.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginLinkFactory.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal
111111

112112
$signatureHasherId = 'security.authenticator.login_link_signature_hasher.'.$firewallName;
113113
$container
114-
->setDefinition($signatureHasherId, new ChildDefinition('security.authenticator.abstract_signature_hasher'))
114+
->setDefinition($signatureHasherId, new ChildDefinition('security.authenticator.abstract_login_link_signature_hasher'))
115115
->replaceArgument(1, $config['signature_properties'])
116116
->replaceArgument(3, $expiredStorageId ? new Reference($expiredStorageId) : null)
117117
->replaceArgument(4, $config['max_uses'] ?? null)

‎src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php
+45-26Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
1313

1414
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
15+
use Symfony\Component\Config\FileLocator;
1516
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
1617
use Symfony\Component\DependencyInjection\ChildDefinition;
1718
use Symfony\Component\DependencyInjection\ContainerBuilder;
1819
use Symfony\Component\DependencyInjection\Definition;
20+
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
1921
use Symfony\Component\DependencyInjection\Reference;
2022
use Symfony\Component\HttpFoundation\Cookie;
2123
use Symfony\Component\Security\Http\EventListener\RememberMeLogoutListener;
@@ -94,31 +96,56 @@ public function create(ContainerBuilder $container, string $id, array $config, ?
9496

9597
public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string
9698
{
97-
$templateId = $this->generateRememberMeServicesTemplateId($config, $firewallName);
98-
$rememberMeServicesId = $templateId.'.'.$firewallName;
99+
if (!$container->hasDefinition('security.authenticator.remember_me')) {
100+
$loader = new PhpFileLoader($container, new FileLocator(\dirname(__DIR__).'/../../Resources/config'));
101+
$loader->load('security_authenticator_remember_me.php');
102+
}
99103

100-
// create remember me services (which manage the remember me cookies)
101-
$this->createRememberMeServices($container, $firewallName, $templateId, [new Reference($userProviderId)], $config);
104+
$options = $config + $this->options;
105+
106+
// create remember me handler (which manage the remember me cookies)
107+
$rememberMeHandlerId = 'security.authenticator.remember_me_handler.'.$firewallName;
108+
if (isset($options['service'])) {
109+
$container->setDefinition($rememberMeHandlerId, $container->getDefinition($options['service']))
110+
->addTag('security.remember_me_handler', ['firewall' => $firewallName]);
111+
} elseif (isset($options['token_provider'])) {
112+
$container->setDefinition($rememberMeHandlerId, new ChildDefinition('security.authenticator.persistent_remember_me_handler'))
113+
->replaceArgument(0, new Reference($options['token_provider']))
114+
->replaceArgument(2, new Reference($userProviderId))
115+
->replaceArgument(4, $options)
116+
->addTag('security.remember_me_handler', ['firewall' => $firewallName]);
117+
} else {
118+
$signatureHasherId = 'security.authenticator.remember_me_signature_hasher.'.$firewallName;
119+
$container->setDefinition($signatureHasherId, new ChildDefinition('security.authenticator.remember_me_signature_hasher'))
120+
->replaceArgument(1, $options['signature_properties'])
121+
;
122+
123+
$container->setDefinition($rememberMeHandlerId, new ChildDefinition('security.authenticator.signature_remember_me_handler'))
124+
->replaceArgument(0, new Reference($signatureHasherId))
125+
->replaceArgument(1, new Reference($userProviderId))
126+
->replaceArgument(3, $options)
127+
->addTag('security.remember_me_handler', ['firewall' => $firewallName]);
128+
}
102129

103130
// create remember me listener (which executes the remember me services for other authenticators and logout)
104-
$this->createRememberMeListener($container, $firewallName, $rememberMeServicesId);
131+
$rememberMeListenerId = 'security.listener.remember_me.'.$firewallName;
132+
$container->setDefinition($rememberMeListenerId, new ChildDefinition('security.listener.remember_me'))
133+
->replaceArgument(0, new Reference($rememberMeHandlerId))
134+
->replaceArgument(1, array_intersect_key($options, ['always_remember_me' => true, 'remember_me_parameter' => true]))
135+
->addTag('kernel.event_subscriber', ['dispatcher' => 'security.event_dispatcher.'.$firewallName])
136+
;
105137

106138
// create remember me authenticator (which re-authenticates the user based on the remember me cookie)
107139
$authenticatorId = 'security.authenticator.remember_me.'.$firewallName;
108140
$container
109141
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.remember_me'))
110-
->replaceArgument(0, new Reference($rememberMeServicesId))
111-
->replaceArgument(3, array_intersect_key($config, $this->options))
142+
->replaceArgument(0, new Reference($rememberMeHandlerId))
143+
->replaceArgument(3, $config['name'] ?? $this->options['name'])
112144
;
113145

114146
foreach ($container->findTaggedServiceIds('security.remember_me_aware') as $serviceId => $attributes) {
115147
// register ContextListener
116148
if ('security.context_listener' === substr($serviceId, 0, 25)) {
117-
$container
118-
->getDefinition($serviceId)
119-
->addMethodCall('setRememberMeServices', [new Reference($rememberMeServicesId)])
120-
;
121-
122149
continue;
123150
}
124151

@@ -156,6 +183,12 @@ public function addConfiguration(NodeDefinition $node)
156183
->prototype('scalar')->end()
157184
->end()
158185
->booleanNode('catch_exceptions')->defaultTrue()->end()
186+
->arrayNode('signature_properties')
187+
->prototype('scalar')->end()
188+
->requiresAtLeastOneElement()
189+
->info('An array of properties on your User that are used to sign the rememberme cookie. If any of these change, all existing cookies will become invalid.')
190+
->example(['email', 'password'])
191+
->end()
159192
;
160193

161194
foreach ($this->options as $name => $value) {
@@ -216,18 +249,4 @@ private function createRememberMeServices(ContainerBuilder $container, string $i
216249

217250
$rememberMeServices->replaceArgument(0, new IteratorArgument(array_unique($userProviders)));
218251
}
219-
220-
private function createRememberMeListener(ContainerBuilder $container, string $id, string $rememberMeServicesId): void
221-
{
222-
$container
223-
->setDefinition('security.listener.remember_me.'.$id, new ChildDefinition('security.listener.remember_me'))
224-
->addTag('kernel.event_subscriber', ['dispatcher' => 'security.event_dispatcher.'.$id])
225-
->replaceArgument(0, new Reference($rememberMeServicesId))
226-
;
227-
228-
$container
229-
->setDefinition('security.logout.listener.remember_me.'.$id, new Definition(RememberMeLogoutListener::class))
230-
->addTag('kernel.event_subscriber', ['dispatcher' => 'security.event_dispatcher.'.$id])
231-
->addArgument(new Reference($rememberMeServicesId));
232-
}
233252
}

‎src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+3-2Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
381381
// Context serializer listener
382382
if (false === $firewall['stateless']) {
383383
$contextKey = $firewall['context'] ?? $id;
384-
$listeners[] = new Reference($contextListenerId = $this->createContextListener($container, $contextKey));
384+
$listeners[] = new Reference($contextListenerId = $this->createContextListener($container, $contextKey, $firewallEventDispatcherId));
385385
$sessionStrategyId = 'security.authentication.session_strategy';
386386

387387
if ($this->authenticatorManagerEnabled) {
@@ -541,7 +541,7 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
541541
return [$matcher, $listeners, $exceptionListener, null !== $logoutListenerId ? new Reference($logoutListenerId) : null];
542542
}
543543

544-
private function createContextListener(ContainerBuilder $container, string $contextKey)
544+
private function createContextListener(ContainerBuilder $container, string $contextKey, string $firewallEventDispatcherId)
545545
{
546546
if (isset($this->contextListeners[$contextKey])) {
547547
return $this->contextListeners[$contextKey];
@@ -550,6 +550,7 @@ private function createContextListener(ContainerBuilder $container, string $cont
550550
$listenerId = 'security.context_listener.'.\count($this->contextListeners);
551551
$listener = $container->setDefinition($listenerId, new ChildDefinition('security.context_listener'));
552552
$listener->replaceArgument(2, $contextKey);
553+
$listener->addArgument(new Reference($firewallEventDispatcherId));
553554

554555
return $this->contextListeners[$contextKey] = $listenerId;
555556
}

‎src/Symfony/Bundle/SecurityBundle/LoginLink/FirewallAwareLoginLinkHandler.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/LoginLink/FirewallAwareLoginLinkHandler.php
+7-25Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Bundle\SecurityBundle\LoginLink;
1313

1414
use Psr\Container\ContainerInterface;
15+
use Symfony\Bundle\SecurityBundle\Security\FirewallAwareTrait;
1516
use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
1617
use Symfony\Component\HttpFoundation\Request;
1718
use Symfony\Component\HttpFoundation\RequestStack;
@@ -26,43 +27,24 @@
2627
*/
2728
class FirewallAwareLoginLinkHandler implements LoginLinkHandlerInterface
2829
{
29-
private $firewallMap;
30-
private $loginLinkHandlerLocator;
31-
private $requestStack;
30+
private const FIREWALL_OPTION = 'login_link';
31+
32+
use FirewallAwareTrait;
3233

3334
public function __construct(FirewallMap $firewallMap, ContainerInterface $loginLinkHandlerLocator, RequestStack $requestStack)
3435
{
3536
$this->firewallMap = $firewallMap;
36-
$this->loginLinkHandlerLocator = $loginLinkHandlerLocator;
37+
$this->locator = $loginLinkHandlerLocator;
3738
$this->requestStack = $requestStack;
3839
}
3940

4041
public function createLoginLink(UserInterface $user): LoginLinkDetails
4142
{
42-
return $this->getLoginLinkHandler()->createLoginLink($user);
43+
return $this->getForFirewall()->createLoginLink($user);
4344
}
4445

4546
public function consumeLoginLink(Request $request): UserInterface
4647
{
47-
return $this->getLoginLinkHandler()->consumeLoginLink($request);
48-
}
49-
50-
private function getLoginLinkHandler(): LoginLinkHandlerInterface
51-
{
52-
if (null === $request = $this->requestStack->getCurrentRequest()) {
53-
throw new \LogicException('Cannot determine the correct LoginLinkHandler to use: there is no active Request and so, the firewall cannot be determined. Try using the specific login link handler service.');
54-
}
55-
56-
$firewall = $this->firewallMap->getFirewallConfig($request);
57-
if (!$firewall) {
58-
throw new \LogicException('No login link handler found as the current route is not covered by a firewall.');
59-
}
60-
61-
$firewallName = $firewall->getName();
62-
if (!$this->loginLinkHandlerLocator->has($firewallName)) {
63-
throw new \LogicException(sprintf('No login link handler found. Did you add a login_link key under your "%s" firewall?', $firewallName));
64-
}
65-
66-
return $this->loginLinkHandlerLocator->get($firewallName);
48+
return $this->getForFirewall()->consumeLoginLink($request);
6749
}
6850
}
+56Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Bundle\SecurityBundle\RememberMe;
13+
14+
use Psr\Container\ContainerInterface;
15+
use Symfony\Bundle\SecurityBundle\Security\FirewallAwareTrait;
16+
use Symfony\Bundle\SecurityBundle\Security\FirewallMap;
17+
use Symfony\Component\HttpFoundation\RequestStack;
18+
use Symfony\Component\Security\Core\User\UserInterface;
19+
use Symfony\Component\Security\Http\RememberMe\RememberMeDetails;
20+
use Symfony\Component\Security\Http\RememberMe\RememberMeHandlerInterface;
21+
22+
/**
23+
* Decorates {@see RememberMeHandlerInterface} for the current firewall.
24+
*
25+
* @author Wouter de Jong <wouter@wouterj.nl>
26+
*
27+
* @experimental in 5.3
28+
*/
29+
final class FirewallAwareRememberMeHandler implements RememberMeHandlerInterface
30+
{
31+
private const FIREWALL_OPTION = 'remember_me';
32+
33+
use FirewallAwareTrait;
34+
35+
public function __construct(FirewallMap $firewallMap, ContainerInterface $rememberMeHandlerLocator, RequestStack $requestStack)
36+
{
37+
$this->firewallMap = $firewallMap;
38+
$this->locator = $rememberMeHandlerLocator;
39+
$this->requestStack = $requestStack;
40+
}
41+
42+
public function createRememberMeCookie(UserInterface $user): void
43+
{
44+
$this->getForFirewall()->createRememberMeCookie($user);
45+
}
46+
47+
public function consumeRememberMeCookie(RememberMeDetails $rememberMeDetails): UserInterface
48+
{
49+
return $this->getForFirewall()->consumeRememberMeCookie($rememberMeDetails);
50+
}
51+
52+
public function clearRememberMeCookie(): void
53+
{
54+
$this->getForFirewall()->clearRememberMeCookie();
55+
}
56+
}

‎src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator.php
-20Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,12 @@
2020
use Symfony\Component\Security\Http\Authenticator\FormLoginAuthenticator;
2121
use Symfony\Component\Security\Http\Authenticator\HttpBasicAuthenticator;
2222
use Symfony\Component\Security\Http\Authenticator\JsonLoginAuthenticator;
23-
use Symfony\Component\Security\Http\Authenticator\RememberMeAuthenticator;
2423
use Symfony\Component\Security\Http\Authenticator\RemoteUserAuthenticator;
2524
use Symfony\Component\Security\Http\Authenticator\X509Authenticator;
2625
use Symfony\Component\Security\Http\Event\CheckPassportEvent;
2726
use Symfony\Component\Security\Http\EventListener\CheckCredentialsListener;
2827
use Symfony\Component\Security\Http\EventListener\LoginThrottlingListener;
2928
use Symfony\Component\Security\Http\EventListener\PasswordMigratingListener;
30-
use Symfony\Component\Security\Http\EventListener\RememberMeListener;
3129
use Symfony\Component\Security\Http\EventListener\SessionStrategyListener;
3230
use Symfony\Component\Security\Http\EventListener\UserCheckerListener;
3331
use Symfony\Component\Security\Http\EventListener\UserProviderListener;
@@ -106,14 +104,6 @@
106104
service('security.authentication.session_strategy'),
107105
])
108106

109-
->set('security.listener.remember_me', RememberMeListener::class)
110-
->abstract()
111-
->args([
112-
abstract_arg('remember me services'),
113-
service('logger')->nullOnInvalid(),
114-
])
115-
->tag('monolog.logger', ['channel' => 'security'])
116-
117107
->set('security.listener.login_throttling', LoginThrottlingListener::class)
118108
->abstract()
119109
->args([
@@ -153,16 +143,6 @@
153143
])
154144
->call('setTranslator', [service('translator')->ignoreOnInvalid()])
155145

156-
->set('security.authenticator.remember_me', RememberMeAuthenticator::class)
157-
->abstract()
158-
->args([
159-
abstract_arg('remember me services'),
160-
param('kernel.secret'),
161-
service('security.token_storage'),
162-
abstract_arg('options'),
163-
service('security.authentication.session_strategy'),
164-
])
165-
166146
->set('security.authenticator.x509', X509Authenticator::class)
167147
->abstract()
168148
->args([

‎src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_login_link.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_login_link.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
abstract_arg('options'),
4040
])
4141

42-
->set('security.authenticator.abstract_signature_hasher', SignatureHasher::class)
42+
->set('security.authenticator.abstract_login_link_signature_hasher', SignatureHasher::class)
4343
->args([
4444
service('property_accessor'),
4545
abstract_arg('signature properties'),

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.