11
11
12
12
namespace Symfony \Bundle \MakerBundle \Maker ;
13
13
14
+ use Symfony \Bundle \FrameworkBundle \Controller \AbstractController ;
14
15
use Symfony \Bundle \MakerBundle \ConsoleStyle ;
15
16
use Symfony \Bundle \MakerBundle \DependencyBuilder ;
16
17
use Symfony \Bundle \MakerBundle \Doctrine \DoctrineHelper ;
23
24
use Symfony \Bundle \MakerBundle \Security \SecurityControllerBuilder ;
24
25
use Symfony \Bundle \MakerBundle \Str ;
25
26
use Symfony \Bundle \MakerBundle \Util \ClassSourceManipulator ;
27
+ use Symfony \Bundle \MakerBundle \Util \TemplateComponentGenerator ;
26
28
use Symfony \Bundle \MakerBundle \Util \YamlManipulationFailedException ;
27
29
use Symfony \Bundle \MakerBundle \Util \YamlSourceManipulator ;
28
30
use Symfony \Bundle \MakerBundle \Validator ;
33
35
use Symfony \Component \Console \Input \InputInterface ;
34
36
use Symfony \Component \Console \Input \InputOption ;
35
37
use Symfony \Component \Console \Question \Question ;
36
- use Symfony \Component \Form \Form ;
37
- use Symfony \Component \HttpKernel \Kernel ;
38
- use Symfony \Component \Security \Guard \Authenticator \AbstractFormLoginAuthenticator ;
39
- use Symfony \Component \Security \Http \Authenticator \AuthenticatorInterface ;
38
+ use Symfony \Component \HttpFoundation \RedirectResponse ;
39
+ use Symfony \Component \HttpFoundation \Request ;
40
+ use Symfony \Component \HttpFoundation \Response ;
41
+ use Symfony \Component \Routing \Annotation \Route ;
42
+ use Symfony \Component \Routing \Generator \UrlGeneratorInterface ;
43
+ use Symfony \Component \Security \Core \Authentication \Token \TokenInterface ;
44
+ use Symfony \Component \Security \Core \Exception \AuthenticationException ;
45
+ use Symfony \Component \Security \Core \Security ;
46
+ use Symfony \Component \Security \Http \Authentication \AuthenticationUtils ;
47
+ use Symfony \Component \Security \Http \Authenticator \AbstractAuthenticator ;
48
+ use Symfony \Component \Security \Http \Authenticator \AbstractLoginFormAuthenticator ;
49
+ use Symfony \Component \Security \Http \Authenticator \Passport \Badge \CsrfTokenBadge ;
40
50
use Symfony \Component \Security \Http \Authenticator \Passport \Badge \UserBadge ;
41
- use Symfony \Component \Security \Http \Authenticator \Passport \PassportInterface ;
51
+ use Symfony \Component \Security \Http \Authenticator \Passport \Credentials \PasswordCredentials ;
52
+ use Symfony \Component \Security \Http \Authenticator \Passport \Passport ;
53
+ use Symfony \Component \Security \Http \Util \TargetPathTrait ;
42
54
use Symfony \Component \Yaml \Yaml ;
43
55
44
56
/**
@@ -53,17 +65,11 @@ final class MakeAuthenticator extends AbstractMaker
53
65
private const AUTH_TYPE_FORM_LOGIN = 'form-login ' ;
54
66
55
67
private $ fileManager ;
56
-
57
68
private $ configUpdater ;
58
-
59
69
private $ generator ;
60
-
61
70
private $ doctrineHelper ;
62
-
63
71
private $ securityControllerBuilder ;
64
72
65
- private $ useSecurity52 = false ;
66
-
67
73
public function __construct (FileManager $ fileManager , SecurityConfigUpdater $ configUpdater , Generator $ generator , DoctrineHelper $ doctrineHelper , SecurityControllerBuilder $ securityControllerBuilder )
68
74
{
69
75
$ this ->fileManager = $ fileManager ;
@@ -83,27 +89,22 @@ public static function getCommandDescription(): string
83
89
return 'Creates a Guard authenticator of different flavors ' ;
84
90
}
85
91
86
- public function configureCommand (Command $ command , InputConfiguration $ inputConfig )
92
+ public function configureCommand (Command $ command , InputConfiguration $ inputConfig ): void
87
93
{
88
94
$ command
89
95
->setHelp (file_get_contents (__DIR__ .'/../Resources/help/MakeAuth.txt ' ));
90
96
}
91
97
92
- public function interact (InputInterface $ input , ConsoleStyle $ io , Command $ command )
98
+ public function interact (InputInterface $ input , ConsoleStyle $ io , Command $ command ): void
93
99
{
94
100
if (!$ this ->fileManager ->fileExists ($ path = 'config/packages/security.yaml ' )) {
95
101
throw new RuntimeCommandException ('The file "config/packages/security.yaml" does not exist. This command requires that file to exist so that it can be updated. ' );
96
102
}
97
103
$ manipulator = new YamlSourceManipulator ($ this ->fileManager ->getFileContents ($ path ));
98
104
$ securityData = $ manipulator ->getData ();
99
105
100
- // Determine if we should use new security features introduced in Symfony 5.2
101
- if ($ securityData ['security ' ]['enable_authenticator_manager ' ] ?? false ) {
102
- $ this ->useSecurity52 = true ;
103
- }
104
-
105
- if ($ this ->useSecurity52 && !class_exists (UserBadge::class)) {
106
- throw new RuntimeCommandException ('MakerBundle does not support generating authenticators using the new authenticator system before symfony/security-bundle 5.2. Please upgrade to 5.2 and try again. ' );
106
+ if (!($ securityData ['security ' ]['enable_authenticator_manager ' ] ?? false )) {
107
+ throw new RuntimeCommandException ('MakerBundle only supports the new authenticator based security system. See https://symfony.com/doc/current/security.html ' );
107
108
}
108
109
109
110
// authenticator type
@@ -124,14 +125,8 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
124
125
125
126
if (self ::AUTH_TYPE_FORM_LOGIN === $ input ->getArgument ('authenticator-type ' )) {
126
127
$ neededDependencies = [TwigBundle::class => 'twig ' ];
127
- $ missingPackagesMessage = 'Twig must be installed to display the login form. ' ;
128
-
129
- if (Kernel::VERSION_ID < 40100 ) {
130
- $ neededDependencies [Form::class] = 'symfony/form ' ;
131
- $ missingPackagesMessage = 'Twig and symfony/form must be installed to display the login form ' ;
132
- }
128
+ $ missingPackagesMessage = $ this ->addDependencies ($ neededDependencies , 'Twig must be installed to display the login form. ' );
133
129
134
- $ missingPackagesMessage = $ this ->addDependencies ($ neededDependencies , $ missingPackagesMessage );
135
130
if ($ missingPackagesMessage ) {
136
131
throw new RuntimeCommandException ($ missingPackagesMessage );
137
132
}
@@ -161,13 +156,6 @@ function ($answer) {
161
156
162
157
$ command ->addOption ('entry-point ' , null , InputOption::VALUE_OPTIONAL );
163
158
164
- if (!$ this ->useSecurity52 ) {
165
- $ input ->setOption (
166
- 'entry-point ' ,
167
- $ interactiveSecurityHelper ->guessEntryPoint ($ io , $ securityData , $ input ->getArgument ('authenticator-class ' ), $ firewallName )
168
- );
169
- }
170
-
171
159
if (self ::AUTH_TYPE_FORM_LOGIN === $ input ->getArgument ('authenticator-type ' )) {
172
160
$ command ->addArgument ('controller-class ' , InputArgument::REQUIRED );
173
161
$ input ->setArgument (
@@ -202,7 +190,7 @@ function ($answer) {
202
190
}
203
191
}
204
192
205
- public function generate (InputInterface $ input , ConsoleStyle $ io , Generator $ generator )
193
+ public function generate (InputInterface $ input , ConsoleStyle $ io , Generator $ generator ): void
206
194
{
207
195
$ manipulator = new YamlSourceManipulator ($ this ->fileManager ->getFileContents ('config/packages/security.yaml ' ));
208
196
$ securityData = $ manipulator ->getData ();
@@ -220,7 +208,7 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
220
208
221
209
$ entryPoint = $ input ->getOption ('entry-point ' );
222
210
223
- if ($ this -> useSecurity52 && self ::AUTH_TYPE_FORM_LOGIN !== $ input ->getArgument ('authenticator-type ' )) {
211
+ if (self ::AUTH_TYPE_FORM_LOGIN !== $ input ->getArgument ('authenticator-type ' )) {
224
212
$ entryPoint = false ;
225
213
}
226
214
@@ -230,8 +218,7 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
230
218
$ input ->getOption ('firewall-name ' ),
231
219
$ entryPoint ,
232
220
$ input ->getArgument ('authenticator-class ' ),
233
- $ input ->hasArgument ('logout-setup ' ) ? $ input ->getArgument ('logout-setup ' ) : false ,
234
- $ this ->useSecurity52
221
+ $ input ->hasArgument ('logout-setup ' ) ? $ input ->getArgument ('logout-setup ' ) : false
235
222
);
236
223
$ generator ->dumpFile ($ path , $ newYaml );
237
224
$ securityYamlUpdated = true ;
@@ -262,45 +249,64 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
262
249
);
263
250
}
264
251
265
- private function generateAuthenticatorClass (array $ securityData , string $ authenticatorType , string $ authenticatorClass , $ userClass , $ userNameField )
252
+ private function generateAuthenticatorClass (array $ securityData , string $ authenticatorType , string $ authenticatorClass , $ userClass , $ userNameField ): void
266
253
{
254
+ $ useStatements = [
255
+ Request::class,
256
+ Response::class,
257
+ TokenInterface::class,
258
+ Passport::class,
259
+ ];
260
+
267
261
// generate authenticator class
268
262
if (self ::AUTH_TYPE_EMPTY_AUTHENTICATOR === $ authenticatorType ) {
263
+ $ emptyAuthUseStatements = array_merge ($ useStatements , [
264
+ AuthenticationException::class,
265
+ AbstractAuthenticator::class,
266
+ ]);
267
+
269
268
$ this ->generator ->generateClass (
270
269
$ authenticatorClass ,
271
- sprintf ('authenticator/%sEmptyAuthenticator.tpl.php ' , $ this ->useSecurity52 ? 'Security52 ' : '' ),
272
- [
273
- 'provider_key_type_hint ' => $ this ->getGuardProviderKeyTypeHint (),
274
- 'use_legacy_passport_interface ' => $ this ->shouldUseLegacyPassportInterface (),
275
- ]
270
+ 'authenticator/EmptyAuthenticator.tpl.php ' ,
271
+ ['use_statements ' => TemplateComponentGenerator::generateUseStatements ($ emptyAuthUseStatements )]
276
272
);
277
273
278
274
return ;
279
275
}
280
276
277
+ $ useStatements = array_merge ($ useStatements , [
278
+ RedirectResponse::class,
279
+ UrlGeneratorInterface::class,
280
+ Security::class,
281
+ AbstractLoginFormAuthenticator::class,
282
+ CsrfTokenBadge::class,
283
+ UserBadge::class,
284
+ PasswordCredentials::class,
285
+ TargetPathTrait::class,
286
+ ]);
287
+
281
288
$ userClassNameDetails = $ this ->generator ->createClassNameDetails (
282
289
'\\' .$ userClass ,
283
290
'Entity \\'
284
291
);
285
292
286
293
$ this ->generator ->generateClass (
287
294
$ authenticatorClass ,
288
- sprintf ( 'authenticator/%sLoginFormAuthenticator .tpl.php ' , $ this -> useSecurity52 ? ' Security52 ' : '' ) ,
295
+ 'authenticator/LoginFormAuthenticator .tpl.php ' ,
289
296
[
297
+ 'use_statements ' => TemplateComponentGenerator::generateUseStatements ($ useStatements ),
290
298
'user_fully_qualified_class_name ' => trim ($ userClassNameDetails ->getFullName (), '\\' ),
291
299
'user_class_name ' => $ userClassNameDetails ->getShortName (),
292
300
'username_field ' => $ userNameField ,
293
301
'username_field_label ' => Str::asHumanWords ($ userNameField ),
294
302
'username_field_var ' => Str::asLowerCamelCase ($ userNameField ),
295
303
'user_needs_encoder ' => $ this ->userClassHasEncoder ($ securityData , $ userClass ),
296
304
'user_is_entity ' => $ this ->doctrineHelper ->isClassAMappedEntity ($ userClass ),
297
- 'provider_key_type_hint ' => $ this ->getGuardProviderKeyTypeHint (),
298
- 'use_legacy_passport_interface ' => $ this ->shouldUseLegacyPassportInterface (),
299
305
]
300
306
);
301
307
}
302
308
303
- private function generateFormLoginFiles (string $ controllerClass , string $ userNameField , bool $ logoutSetup )
309
+ private function generateFormLoginFiles (string $ controllerClass , string $ userNameField , bool $ logoutSetup ): void
304
310
{
305
311
$ controllerClassNameDetails = $ this ->generator ->createClassNameDetails (
306
312
$ controllerClass ,
@@ -309,9 +315,16 @@ private function generateFormLoginFiles(string $controllerClass, string $userNam
309
315
);
310
316
311
317
if (!class_exists ($ controllerClassNameDetails ->getFullName ())) {
318
+ $ useStatements = [
319
+ AbstractController::class,
320
+ Route::class,
321
+ AuthenticationUtils::class,
322
+ ];
323
+
312
324
$ controllerPath = $ this ->generator ->generateController (
313
325
$ controllerClassNameDetails ->getFullName (),
314
- 'authenticator/EmptySecurityController.tpl.php '
326
+ 'authenticator/EmptySecurityController.tpl.php ' ,
327
+ ['use_statements ' => TemplateComponentGenerator::generateUseStatements ($ useStatements )]
315
328
);
316
329
317
330
$ controllerSourceCode = $ this ->generator ->getFileContentsForPendingOperation ($ controllerPath );
@@ -358,8 +371,7 @@ private function generateNextMessage(bool $securityYamlUpdated, string $authenti
358
371
'main ' ,
359
372
null ,
360
373
$ authenticatorClass ,
361
- $ logoutSetup ,
362
- $ this ->useSecurity52
374
+ $ logoutSetup
363
375
);
364
376
$ nextTexts [] = "- Your <info>security.yaml</info> could not be updated automatically. You'll need to add the following config manually: \n\n" .$ yamlExample ;
365
377
}
@@ -371,11 +383,6 @@ private function generateNextMessage(bool $securityYamlUpdated, string $authenti
371
383
$ nextTexts [] = sprintf ('- Review <info>%s::getUser()</info> to make sure it matches your needs. ' , $ authenticatorClass );
372
384
}
373
385
374
- // this only applies to Guard authentication AND if the user does not have a hasher configured
375
- if (!$ this ->useSecurity52 && !$ this ->userClassHasEncoder ($ securityData , $ userClass )) {
376
- $ nextTexts [] = sprintf ('- Check the user \'s password in <info>%s::checkCredentials()</info>. ' , $ authenticatorClass );
377
- }
378
-
379
386
$ nextTexts [] = '- Review & adapt the login template: <info> ' .$ this ->fileManager ->getPathForTemplate ('security/login.html.twig ' ).'</info>. ' ;
380
387
}
381
388
@@ -396,7 +403,7 @@ private function userClassHasEncoder(array $securityData, string $userClass): bo
396
403
return $ userNeedsEncoder ;
397
404
}
398
405
399
- public function configureDependencies (DependencyBuilder $ dependencies , InputInterface $ input = null )
406
+ public function configureDependencies (DependencyBuilder $ dependencies , InputInterface $ input = null ): void
400
407
{
401
408
$ dependencies ->addClassDependency (
402
409
SecurityBundle::class,
@@ -409,43 +416,4 @@ public function configureDependencies(DependencyBuilder $dependencies, InputInte
409
416
'yaml '
410
417
);
411
418
}
412
-
413
- /**
414
- * Calculates the type-hint used for the $provider argument (string or nothing) for Guard.
415
- */
416
- private function getGuardProviderKeyTypeHint (): string
417
- {
418
- // doesn't matter: this only applies to non-Guard authenticators
419
- if (!class_exists (AbstractFormLoginAuthenticator::class)) {
420
- return '' ;
421
- }
422
-
423
- $ reflectionMethod = new \ReflectionMethod (AbstractFormLoginAuthenticator::class, 'onAuthenticationSuccess ' );
424
- $ type = $ reflectionMethod ->getParameters ()[2 ]->getType ();
425
-
426
- if (!$ type instanceof \ReflectionNamedType) {
427
- return '' ;
428
- }
429
-
430
- return sprintf ('%s ' , $ type ->getName ());
431
- }
432
-
433
- private function shouldUseLegacyPassportInterface (): bool
434
- {
435
- // only applies to new authenticator security
436
- if (!$ this ->useSecurity52 ) {
437
- return false ;
438
- }
439
-
440
- // legacy: checking for Symfony 5.2 & 5.3 before PassportInterface deprecation
441
- $ class = new \ReflectionClass (AuthenticatorInterface::class);
442
- $ method = $ class ->getMethod ('authenticate ' );
443
-
444
- // 5.4 where return type is temporarily removed
445
- if (!$ method ->getReturnType ()) {
446
- return false ;
447
- }
448
-
449
- return PassportInterface::class === $ method ->getReturnType ()->getName ();
450
- }
451
419
}
0 commit comments