11
11
12
12
namespace Symfony \Bundle \SecurityBundle \DependencyInjection \Security \Factory ;
13
13
14
+ use Symfony \Bridge \Doctrine \Security \RememberMe \DoctrineTokenProvider ;
15
+ use Symfony \Bundle \SecurityBundle \RememberMe \DecoratedRememberMeHandler ;
14
16
use Symfony \Component \Config \Definition \Builder \NodeDefinition ;
17
+ use Symfony \Component \Config \Definition \Exception \InvalidConfigurationException ;
18
+ use Symfony \Component \Config \FileLocator ;
15
19
use Symfony \Component \DependencyInjection \Argument \IteratorArgument ;
16
20
use Symfony \Component \DependencyInjection \ChildDefinition ;
17
21
use Symfony \Component \DependencyInjection \ContainerBuilder ;
18
22
use Symfony \Component \DependencyInjection \Definition ;
23
+ use Symfony \Component \DependencyInjection \Loader \PhpFileLoader ;
19
24
use Symfony \Component \DependencyInjection \Reference ;
20
25
use Symfony \Component \HttpFoundation \Cookie ;
21
26
use Symfony \Component \Security \Http \EventListener \RememberMeLogoutListener ;
@@ -94,31 +99,66 @@ public function create(ContainerBuilder $container, string $id, array $config, ?
94
99
95
100
public function createAuthenticator (ContainerBuilder $ container , string $ firewallName , array $ config , string $ userProviderId ): string
96
101
{
97
- $ templateId = $ this ->generateRememberMeServicesTemplateId ($ config , $ firewallName );
98
- $ rememberMeServicesId = $ templateId .'. ' .$ firewallName ;
102
+ if (!$ container ->hasDefinition ('security.authenticator.remember_me ' )) {
103
+ $ loader = new PhpFileLoader ($ container , new FileLocator (\dirname (__DIR__ ).'/../../Resources/config ' ));
104
+ $ loader ->load ('security_authenticator_remember_me.php ' );
105
+ }
106
+
107
+ // create remember me handler (which manage the remember-me cookies)
108
+ $ rememberMeHandlerId = 'security.authenticator.remember_me_handler. ' .$ firewallName ;
109
+ if (isset ($ config ['service ' ]) && isset ($ config ['token_provider ' ])) {
110
+ throw new InvalidConfigurationException (sprintf ('You cannot use both "service" and "token_provider" in "security.firewalls.%s.remember_me". ' , $ firewallName ));
111
+ }
112
+
113
+ if (isset ($ config ['service ' ])) {
114
+ $ container ->register ($ rememberMeHandlerId , DecoratedRememberMeHandler::class)
115
+ ->addArgument (new Reference ($ config ['service ' ]))
116
+ ->addTag ('security.remember_me_handler ' , ['firewall ' => $ firewallName ]);
117
+ } elseif (isset ($ config ['token_provider ' ])) {
118
+ $ tokenProviderId = $ this ->createTokenProvider ($ container , $ firewallName , $ config ['token_provider ' ]);
119
+ $ container ->setDefinition ($ rememberMeHandlerId , new ChildDefinition ('security.authenticator.persistent_remember_me_handler ' ))
120
+ ->replaceArgument (0 , new Reference ($ tokenProviderId ))
121
+ ->replaceArgument (2 , new Reference ($ userProviderId ))
122
+ ->replaceArgument (4 , $ config )
123
+ ->addTag ('security.remember_me_handler ' , ['firewall ' => $ firewallName ]);
124
+ } else {
125
+ $ signatureHasherId = 'security.authenticator.remember_me_signature_hasher. ' .$ firewallName ;
126
+ $ container ->setDefinition ($ signatureHasherId , new ChildDefinition ('security.authenticator.remember_me_signature_hasher ' ))
127
+ ->replaceArgument (1 , $ config ['signature_properties ' ])
128
+ ;
129
+
130
+ $ container ->setDefinition ($ rememberMeHandlerId , new ChildDefinition ('security.authenticator.signature_remember_me_handler ' ))
131
+ ->replaceArgument (0 , new Reference ($ signatureHasherId ))
132
+ ->replaceArgument (1 , new Reference ($ userProviderId ))
133
+ ->replaceArgument (3 , $ config )
134
+ ->addTag ('security.remember_me_handler ' , ['firewall ' => $ firewallName ]);
135
+ }
99
136
100
- // create remember me services (which manage the remember me cookies)
101
- $ this ->createRememberMeServices ($ container , $ firewallName , $ templateId , [new Reference ($ userProviderId )], $ config );
137
+ // create check remember me conditions listener (which checks if a remember-me cookie is supported and requested)
138
+ $ rememberMeConditionsListenerId = 'security.listener.check_remember_me_conditions. ' .$ firewallName ;
139
+ $ container ->setDefinition ($ rememberMeConditionsListenerId , new ChildDefinition ('security.listener.check_remember_me_conditions ' ))
140
+ ->replaceArgument (0 , array_intersect_key ($ config , ['always_remember_me ' => true , 'remember_me_parameter ' => true ]))
141
+ ->addTag ('kernel.event_subscriber ' , ['dispatcher ' => 'security.event_dispatcher. ' .$ firewallName ])
142
+ ;
102
143
103
144
// create remember me listener (which executes the remember me services for other authenticators and logout)
104
- $ this ->createRememberMeListener ($ container , $ firewallName , $ rememberMeServicesId );
145
+ $ rememberMeListenerId = 'security.listener.remember_me. ' .$ firewallName ;
146
+ $ container ->setDefinition ($ rememberMeListenerId , new ChildDefinition ('security.listener.remember_me ' ))
147
+ ->replaceArgument (0 , new Reference ($ rememberMeHandlerId ))
148
+ ->addTag ('kernel.event_subscriber ' , ['dispatcher ' => 'security.event_dispatcher. ' .$ firewallName ])
149
+ ;
105
150
106
- // create remember me authenticator (which re-authenticates the user based on the remember me cookie)
151
+ // create remember me authenticator (which re-authenticates the user based on the remember- me cookie)
107
152
$ authenticatorId = 'security.authenticator.remember_me. ' .$ firewallName ;
108
153
$ container
109
154
->setDefinition ($ authenticatorId , new ChildDefinition ('security.authenticator.remember_me ' ))
110
- ->replaceArgument (0 , new Reference ($ rememberMeServicesId ))
111
- ->replaceArgument (3 , $ container -> getDefinition ( $ rememberMeServicesId )-> getArgument ( 3 ) )
155
+ ->replaceArgument (0 , new Reference ($ rememberMeHandlerId ))
156
+ ->replaceArgument (3 , $ config [ ' name ' ] ?? $ this -> options [ ' name ' ] )
112
157
;
113
158
114
159
foreach ($ container ->findTaggedServiceIds ('security.remember_me_aware ' ) as $ serviceId => $ attributes ) {
115
160
// register ContextListener
116
161
if ('security.context_listener ' === substr ($ serviceId , 0 , 25 )) {
117
- $ container
118
- ->getDefinition ($ serviceId )
119
- ->addMethodCall ('setRememberMeServices ' , [new Reference ($ rememberMeServicesId )])
120
- ;
121
-
122
162
continue ;
123
163
}
124
164
@@ -148,15 +188,33 @@ public function addConfiguration(NodeDefinition $node)
148
188
$ builder
149
189
->scalarNode ('secret ' )->isRequired ()->cannotBeEmpty ()->end ()
150
190
->scalarNode ('service ' )->end ()
151
- ->scalarNode ('token_provider ' )->end ()
152
191
->arrayNode ('user_providers ' )
153
192
->beforeNormalization ()
154
193
->ifString ()->then (function ($ v ) { return [$ v ]; })
155
194
->end ()
156
195
->prototype ('scalar ' )->end ()
157
196
->end ()
158
197
->booleanNode ('catch_exceptions ' )->defaultTrue ()->end ()
159
- ;
198
+ ->arrayNode ('signature_properties ' )
199
+ ->prototype ('scalar ' )->end ()
200
+ ->requiresAtLeastOneElement ()
201
+ ->info ('An array of properties on your User that are used to sign the remember-me cookie. If any of these change, all existing cookies will become invalid. ' )
202
+ ->example (['email ' , 'password ' ])
203
+ ->end ()
204
+ ->arrayNode ('token_provider ' )
205
+ ->beforeNormalization ()
206
+ ->ifString ()->then (function ($ v ) { return ['service ' => $ v ]; })
207
+ ->end ()
208
+ ->children ()
209
+ ->scalarNode ('service ' )->info ('The service ID of a custom rememberme token provider. ' )->end ()
210
+ ->arrayNode ('doctrine ' )
211
+ ->canBeEnabled ()
212
+ ->children ()
213
+ ->scalarNode ('connection ' )->defaultNull ()->end ()
214
+ ->end ()
215
+ ->end ()
216
+ ->end ()
217
+ ->end ();
160
218
161
219
foreach ($ this ->options as $ name => $ value ) {
162
220
if ('secure ' === $ name ) {
@@ -195,9 +253,8 @@ private function createRememberMeServices(ContainerBuilder $container, string $i
195
253
$ rememberMeServices ->replaceArgument (2 , $ id );
196
254
197
255
if (isset ($ config ['token_provider ' ])) {
198
- $ rememberMeServices ->addMethodCall ('setTokenProvider ' , [
199
- new Reference ($ config ['token_provider ' ]),
200
- ]);
256
+ $ tokenProviderId = $ this ->createTokenProvider ($ container , $ id , $ config ['token_provider ' ]);
257
+ $ rememberMeServices ->addMethodCall ('setTokenProvider ' , [new Reference ($ tokenProviderId )]);
201
258
}
202
259
203
260
// remember-me options
@@ -222,17 +279,29 @@ private function createRememberMeServices(ContainerBuilder $container, string $i
222
279
$ rememberMeServices ->replaceArgument (0 , new IteratorArgument (array_unique ($ userProviders )));
223
280
}
224
281
225
- private function createRememberMeListener (ContainerBuilder $ container , string $ id , string $ rememberMeServicesId ): void
282
+ private function createTokenProvider (ContainerBuilder $ container , string $ firewallName , array $ config ): string
226
283
{
227
- $ container
228
- -> setDefinition ( ' security.listener.remember_me. ' . $ id , new ChildDefinition ( ' security.listener.remember_me ' ))
229
- -> addTag ( ' kernel.event_subscriber ' , [ ' dispatcher ' => ' security.event_dispatcher. ' . $ id ])
230
- -> replaceArgument ( 0 , new Reference ( $ rememberMeServicesId ))
231
- ;
284
+ $ tokenProviderId = $ config [ ' service ' ] ?? false ;
285
+ if ( $ config [ ' doctrine ' ][ ' enabled ' ] ?? false ) {
286
+ if (! class_exists (DoctrineTokenProvider::class)) {
287
+ throw new InvalidConfigurationException ( ' Cannot use the "doctrine" token provider for "remember_me" because the Doctrine Bridge is not installed. Try running "composer require symfony/doctrine-bridge". ' );
288
+ }
232
289
233
- $ container
234
- ->setDefinition ('security.logout.listener.remember_me. ' .$ id , new Definition (RememberMeLogoutListener::class))
235
- ->addTag ('kernel.event_subscriber ' , ['dispatcher ' => 'security.event_dispatcher. ' .$ id ])
236
- ->addArgument (new Reference ($ rememberMeServicesId ));
290
+ if (null === $ config ['doctrine ' ]['connection ' ]) {
291
+ $ connectionId = 'database_connection ' ;
292
+ } else {
293
+ $ connectionId = 'doctrine.dbal. ' .$ config ['doctrine ' ]['connection ' ].'_connection ' ;
294
+ }
295
+
296
+ $ tokenProviderId = 'security.remember_me.doctrine_token_provider. ' .$ firewallName ;
297
+ $ container ->register ($ tokenProviderId , DoctrineTokenProvider::class)
298
+ ->addArgument (new Reference ($ connectionId ));
299
+ }
300
+
301
+ if (!$ tokenProviderId ) {
302
+ throw new InvalidConfigurationException (sprintf ('No token provider was set for firewall "%s". Either configure a service ID or set "remember_me.token_provider.doctrine" to true. ' , $ firewallName ));
303
+ }
304
+
305
+ return $ tokenProviderId ;
237
306
}
238
307
}
0 commit comments