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 ddf430f

Browse filesBrowse files
committed
Added remember me functionality
1 parent 1c810d5 commit ddf430f
Copy full SHA for ddf430f

15 files changed

+296
-105
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AnonymousFactory.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public function create(ContainerBuilder $container, $id, $config, $userProvider,
4242
return [$providerId, $listenerId, $defaultEntryPoint];
4343
}
4444

45-
public function createAuthenticator(ContainerBuilder $container, string $id, array $config, ?string $userProviderId): string
45+
public function createAuthenticator(ContainerBuilder $container, string $id, array $config, string $userProviderId): string
4646
{
4747
if (null === $config['secret']) {
4848
$config['secret'] = new Parameter('container.build_hash');

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AuthenticatorFactoryInterface.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ interface AuthenticatorFactoryInterface
2525
*
2626
* @return string|string[] The authenticator service ID(s) to be used by the firewall
2727
*/
28-
public function createAuthenticator(ContainerBuilder $container, string $id, array $config, ?string $userProviderId);
28+
public function createAuthenticator(ContainerBuilder $container, string $id, array $config, string $userProviderId);
2929
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public function createEntryPoint(ContainerBuilder $container, string $id, array
9797
return $entryPointId;
9898
}
9999

100-
public function createAuthenticator(ContainerBuilder $container, string $id, array $config, ?string $userProviderId): string
100+
public function createAuthenticator(ContainerBuilder $container, string $id, array $config, string $userProviderId): string
101101
{
102102
$authenticatorId = 'security.authenticator.form_login.'.$id;
103103
$defaultOptions = array_merge($this->defaultSuccessHandlerOptions, $this->options);

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/HttpBasicFactory.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public function create(ContainerBuilder $container, string $id, array $config, s
4646
return [$provider, $listenerId, $entryPointId];
4747
}
4848

49-
public function createAuthenticator(ContainerBuilder $container, string $id, array $config, ?string $userProviderId): string
49+
public function createAuthenticator(ContainerBuilder $container, string $id, array $config, string $userProviderId): string
5050
{
5151
$authenticatorId = 'security.authenticator.http_basic.'.$id;
5252
$container

‎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
+99-34Lines changed: 99 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
use Symfony\Component\HttpFoundation\Cookie;
2121
use Symfony\Component\Security\Http\EventListener\RememberMeLogoutListener;
2222

23-
class RememberMeFactory implements SecurityFactoryInterface
23+
class RememberMeFactory implements SecurityFactoryInterface, AuthenticatorFactoryInterface
2424
{
2525
protected $options = [
2626
'name' => 'REMEMBERME',
@@ -46,29 +46,8 @@ public function create(ContainerBuilder $container, string $id, array $config, ?
4646
;
4747

4848
// remember me services
49-
if (isset($config['service'])) {
50-
$templateId = $config['service'];
51-
$rememberMeServicesId = $templateId.'.'.$id;
52-
} elseif (isset($config['token_provider'])) {
53-
$templateId = 'security.authentication.rememberme.services.persistent';
54-
$rememberMeServicesId = $templateId.'.'.$id;
55-
} else {
56-
$templateId = 'security.authentication.rememberme.services.simplehash';
57-
$rememberMeServicesId = $templateId.'.'.$id;
58-
}
59-
60-
$rememberMeServices = $container->setDefinition($rememberMeServicesId, new ChildDefinition($templateId));
61-
$rememberMeServices->replaceArgument(1, $config['secret']);
62-
$rememberMeServices->replaceArgument(2, $id);
63-
64-
if (isset($config['token_provider'])) {
65-
$rememberMeServices->addMethodCall('setTokenProvider', [
66-
new Reference($config['token_provider']),
67-
]);
68-
}
69-
70-
// remember-me options
71-
$rememberMeServices->replaceArgument(3, array_intersect_key($config, $this->options));
49+
$templateId = $this->generateRememberMeServicesTemplateId($config, $id);
50+
$rememberMeServicesId = $templateId.'.'.$id;
7251

7352
// attach to remember-me aware listeners
7453
$userProviders = [];
@@ -93,17 +72,8 @@ public function create(ContainerBuilder $container, string $id, array $config, ?
9372
;
9473
}
9574
}
96-
if ($config['user_providers']) {
97-
$userProviders = [];
98-
foreach ($config['user_providers'] as $providerName) {
99-
$userProviders[] = new Reference('security.user.provider.concrete.'.$providerName);
100-
}
101-
}
102-
if (0 === \count($userProviders)) {
103-
throw new \RuntimeException('You must configure at least one remember-me aware listener (such as form-login) for each firewall that has remember-me enabled.');
104-
}
10575

106-
$rememberMeServices->replaceArgument(0, new IteratorArgument(array_unique($userProviders)));
76+
$this->createRememberMeServices($container, $id, $templateId, $userProviders, $config);
10777

10878
// remember-me listener
10979
$listenerId = 'security.authentication.listener.rememberme.'.$id;
@@ -119,6 +89,42 @@ public function create(ContainerBuilder $container, string $id, array $config, ?
11989
return [$authProviderId, $listenerId, $defaultEntryPoint];
12090
}
12191

92+
public function createAuthenticator(ContainerBuilder $container, string $id, array $config, string $userProviderId): string
93+
{
94+
$templateId = $this->generateRememberMeServicesTemplateId($config, $id);
95+
$rememberMeServicesId = $templateId.'.'.$id;
96+
97+
// create remember me services (which manage the remember me cookies)
98+
$this->createRememberMeServices($container, $id, $templateId, [new Reference($userProviderId)], $config);
99+
100+
// create remember me listener (which executes the remember me services for other authenticators and logout)
101+
$this->createRememberMeListener($container, $id, $rememberMeServicesId);
102+
103+
// create remember me authenticator (which re-authenticates the user based on the remember me cookie)
104+
$authenticatorId = 'security.authenticator.remember_me.'.$id;
105+
$container
106+
->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.remember_me'))
107+
->replaceArgument(0, new Reference($rememberMeServicesId))
108+
->replaceArgument(3, array_intersect_key($config, $this->options))
109+
;
110+
111+
foreach ($container->findTaggedServiceIds('security.remember_me_aware') as $serviceId => $attributes) {
112+
// register ContextListener
113+
if ('security.context_listener' === substr($serviceId, 0, 25)) {
114+
$container
115+
->getDefinition($serviceId)
116+
->addMethodCall('setRememberMeServices', [new Reference($rememberMeServicesId)])
117+
;
118+
119+
continue;
120+
}
121+
122+
throw new \LogicException(sprintf('Symfony Authenticator Security dropped support for the "security.remember_me_aware" tag, service "%s" will no longer work as expected.', $serviceId));
123+
}
124+
125+
return $authenticatorId;
126+
}
127+
122128
public function getPosition()
123129
{
124130
return 'remember_me';
@@ -163,4 +169,63 @@ public function addConfiguration(NodeDefinition $node)
163169
}
164170
}
165171
}
172+
173+
private function generateRememberMeServicesTemplateId(array $config, string $id): string
174+
{
175+
if (isset($config['service'])) {
176+
return $config['service'];
177+
}
178+
179+
if (isset($config['token_provider'])) {
180+
return 'security.authentication.rememberme.services.persistent';
181+
}
182+
183+
return 'security.authentication.rememberme.services.simplehash';
184+
}
185+
186+
private function createRememberMeServices(ContainerBuilder $container, string $id, string $templateId, array $userProviders, array $config): void
187+
{
188+
$rememberMeServicesId = $templateId.'.'.$id;
189+
190+
$rememberMeServices = $container->setDefinition($rememberMeServicesId, new ChildDefinition($templateId));
191+
$rememberMeServices->replaceArgument(1, $config['secret']);
192+
$rememberMeServices->replaceArgument(2, $id);
193+
194+
if (isset($config['token_provider'])) {
195+
$rememberMeServices->addMethodCall('setTokenProvider', [
196+
new Reference($config['token_provider']),
197+
]);
198+
}
199+
200+
// remember-me options
201+
$rememberMeServices->replaceArgument(3, array_intersect_key($config, $this->options));
202+
203+
if ($config['user_providers']) {
204+
$userProviders = [];
205+
foreach ($config['user_providers'] as $providerName) {
206+
$userProviders[] = new Reference('security.user.provider.concrete.'.$providerName);
207+
}
208+
}
209+
210+
if (0 === \count($userProviders)) {
211+
throw new \RuntimeException('You must configure at least one remember-me aware listener (such as form-login) for each firewall that has remember-me enabled.');
212+
}
213+
214+
$rememberMeServices->replaceArgument(0, new IteratorArgument(array_unique($userProviders)));
215+
}
216+
217+
private function createRememberMeListener(ContainerBuilder $container, string $id, string $rememberMeServicesId): void
218+
{
219+
$container
220+
->setDefinition('security.listener.remember_me.'.$id, new ChildDefinition('security.listener.remember_me'))
221+
->addTag('kernel.event_subscriber')
222+
->replaceArgument(0, new Reference($rememberMeServicesId))
223+
->replaceArgument(1, $id)
224+
;
225+
226+
$container
227+
->setDefinition('security.logout.listener.remember_me.'.$id, new Definition(RememberMeLogoutListener::class))
228+
->addTag('kernel.event_subscriber', ['dispatcher' => 'security.event_dispatcher.'.$id])
229+
->addArgument(new Reference($rememberMeServicesId));
230+
}
166231
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+17-19Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use Symfony\Component\DependencyInjection\ChildDefinition;
2727
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
2828
use Symfony\Component\DependencyInjection\ContainerBuilder;
29+
use Symfony\Component\DependencyInjection\Definition;
2930
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
3031
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
3132
use Symfony\Component\DependencyInjection\Reference;
@@ -34,6 +35,7 @@
3435
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
3536
use Symfony\Component\Security\Core\Encoder\NativePasswordEncoder;
3637
use Symfony\Component\Security\Core\Encoder\SodiumPasswordEncoder;
38+
use Symfony\Component\Security\Core\User\ChainUserProvider;
3739
use Symfony\Component\Security\Core\User\UserProviderInterface;
3840
use Symfony\Component\Security\Http\Controller\UserValueResolver;
3941
use Twig\Extension\AbstractExtension;
@@ -230,9 +232,16 @@ private function createFirewalls(array $config, ContainerBuilder $container)
230232
foreach ($providerIds as $userProviderId) {
231233
$userProviders[] = new Reference($userProviderId);
232234
}
233-
$arguments[1] = new IteratorArgument($userProviders);
235+
$arguments[1] = $userProviderIteratorsArgument = new IteratorArgument($userProviders);
234236
$contextListenerDefinition->setArguments($arguments);
235237

238+
if (\count($userProviders) > 1) {
239+
$container->setDefinition('security.user_providers', new Definition(ChainUserProvider::class, [$userProviderIteratorsArgument]))
240+
->setPublic(false);
241+
} else {
242+
$container->setAlias('security.user_providers', new Alias(current($providerIds)))->setPublic(false);
243+
}
244+
236245
if (1 === \count($providerIds)) {
237246
$container->setAlias(UserProviderInterface::class, current($providerIds));
238247
}
@@ -423,16 +432,6 @@ private function createFirewall(ContainerBuilder $container, string $id, array $
423432
// Determine default entry point
424433
$configuredEntryPoint = isset($firewall['entry_point']) ? $firewall['entry_point'] : null;
425434

426-
if ($this->authenticatorManagerEnabled) {
427-
// Remember me listener (must be before calling createAuthenticationListeners() to inject remember me services)
428-
$container
429-
->setDefinition('security.listener.remember_me.'.$id, new ChildDefinition('security.listener.remember_me'))
430-
->replaceArgument(0, $id)
431-
->addTag('kernel.event_subscriber')
432-
->addTag('security.remember_me_aware', ['id' => $id, 'provider' => 'none'])
433-
;
434-
}
435-
436435
// Authentication listeners
437436
$firewallAuthenticationProviders = [];
438437
list($authListeners, $defaultEntryPoint) = $this->createAuthenticationListeners($container, $id, $firewall, $firewallAuthenticationProviders, $defaultProvider, $providerIds, $configuredEntryPoint, $contextListenerId);
@@ -554,7 +553,7 @@ private function createAuthenticationListeners(ContainerBuilder $container, stri
554553
return [$listeners, $defaultEntryPoint];
555554
}
556555

557-
private function getUserProvider(ContainerBuilder $container, string $id, array $firewall, string $factoryKey, ?string $defaultProvider, array $providerIds, ?string $contextListenerId): ?string
556+
private function getUserProvider(ContainerBuilder $container, string $id, array $firewall, string $factoryKey, ?string $defaultProvider, array $providerIds, ?string $contextListenerId): string
558557
{
559558
if (isset($firewall[$factoryKey]['provider'])) {
560559
if (!isset($providerIds[$normalizedName = str_replace('-', '_', $firewall[$factoryKey]['provider'])])) {
@@ -564,13 +563,8 @@ private function getUserProvider(ContainerBuilder $container, string $id, array
564563
return $providerIds[$normalizedName];
565564
}
566565

567-
if ('remember_me' === $factoryKey || 'anonymous' === $factoryKey) {
568-
if ('remember_me' === $factoryKey && $contextListenerId) {
569-
$container->getDefinition($contextListenerId)->addTag('security.remember_me_aware', ['id' => $id, 'provider' => 'none']);
570-
}
571-
572-
// RememberMeFactory will use the firewall secret when created
573-
return null;
566+
if ('remember_me' === $factoryKey && $contextListenerId) {
567+
$container->getDefinition($contextListenerId)->addTag('security.remember_me_aware', ['id' => $id, 'provider' => 'none']);
574568
}
575569

576570
if ($defaultProvider) {
@@ -587,6 +581,10 @@ private function getUserProvider(ContainerBuilder $container, string $id, array
587581
return $userProvider;
588582
}
589583

584+
if ('remember_me' === $factoryKey || 'anonymous' === $factoryKey) {
585+
return 'security.user_providers';
586+
}
587+
590588
throw new InvalidConfigurationException(sprintf('Not configuring explicitly the provider for the "%s" listener on "%s" firewall is ambiguous as there is more than one registered provider.', $factoryKey, $id));
591589
}
592590

‎src/Symfony/Bundle/SecurityBundle/Resources/config/authenticators.xml

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/SecurityBundle/Resources/config/authenticators.xml
+12-1Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252
class="Symfony\Component\Security\Http\EventListener\RememberMeListener"
5353
abstract="true">
5454
<tag name="monolog.logger" channel="security" />
55-
<argument/> <!-- provider key -->
55+
<argument type="abstract">remember me services</argument>
56+
<argument type="abstract">provider key</argument>
5657
<argument type="service" id="logger" on-invalid="null" />
5758
</service>
5859

@@ -82,5 +83,15 @@
8283
<argument type="abstract">secret</argument>
8384
<argument type="service" id="security.untracked_token_storage" />
8485
</service>
86+
87+
<service id="security.authenticator.remember_me"
88+
class="Symfony\Component\Security\Http\Authenticator\RememberMeAuthenticator"
89+
abstract="true">
90+
<argument type="abstract">remember me services</argument>
91+
<argument>%kernel.secret%</argument>
92+
<argument type="service" id="security.token_storage" />
93+
<argument type="abstract">options</argument>
94+
<argument type="service" id="security.authentication.session_strategy" />
95+
</service>
8596
</services>
8697
</container>

‎src/Symfony/Component/Security/Http/Authenticator/AbstractLoginFormAuthenticator.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Http/Authenticator/AbstractLoginFormAuthenticator.php
+6-6Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
*
2626
* @experimental in 5.1
2727
*/
28-
abstract class AbstractLoginFormAuthenticator extends AbstractAuthenticator implements AuthenticationEntryPointInterface
28+
abstract class AbstractLoginFormAuthenticator extends AbstractAuthenticator implements AuthenticationEntryPointInterface, RememberMeAuthenticatorInterface
2929
{
3030
/**
3131
* Return the URL to the login page.
@@ -46,11 +46,6 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio
4646
return new RedirectResponse($url);
4747
}
4848

49-
public function supportsRememberMe(): bool
50-
{
51-
return true;
52-
}
53-
5449
/**
5550
* Override to control what happens when the user hits a secure page
5651
* but isn't logged in yet.
@@ -61,4 +56,9 @@ public function start(Request $request, AuthenticationException $authException =
6156

6257
return new RedirectResponse($url);
6358
}
59+
60+
public function supportsRememberMe(): bool
61+
{
62+
return true;
63+
}
6464
}

‎src/Symfony/Component/Security/Http/Authenticator/AnonymousAuthenticator.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Http/Authenticator/AnonymousAuthenticator.php
-5Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,4 @@ public function onAuthenticationSuccess(Request $request, TokenInterface $token,
7575
{
7676
return null;
7777
}
78-
79-
public function supportsRememberMe(): bool
80-
{
81-
return false;
82-
}
8378
}

‎src/Symfony/Component/Security/Http/Authenticator/AuthenticatorInterface.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Http/Authenticator/AuthenticatorInterface.php
-14Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -102,18 +102,4 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio
102102
* will be authenticated. This makes sense, for example, with an API.
103103
*/
104104
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey): ?Response;
105-
106-
/**
107-
* Does this method support remember me cookies?
108-
*
109-
* Remember me cookie will be set if *all* of the following are met:
110-
* A) This method returns true
111-
* B) The remember_me key under your firewall is configured
112-
* C) The "remember me" functionality is activated. This is usually
113-
* done by having a _remember_me checkbox in your form, but
114-
* can be configured by the "always_remember_me" and "remember_me_parameter"
115-
* parameters under the "remember_me" firewall key
116-
* D) The onAuthenticationSuccess method returns a Response object
117-
*/
118-
public function supportsRememberMe(): bool;
119105
}

‎src/Symfony/Component/Security/Http/Authenticator/HttpBasicAuthenticator.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Security/Http/Authenticator/HttpBasicAuthenticator.php
-5Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,4 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio
9494

9595
return $this->start($request, $exception);
9696
}
97-
98-
public function supportsRememberMe(): bool
99-
{
100-
return false;
101-
}
10297
}

0 commit comments

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