2121use Symfony \Bundle \MakerBundle \InputConfiguration ;
2222use Symfony \Bundle \MakerBundle \Security \InteractiveSecurityHelper ;
2323use Symfony \Bundle \MakerBundle \Security \SecurityConfigUpdater ;
24+ use Symfony \Bundle \MakerBundle \Security \SecurityControllerBuilder ;
2425use Symfony \Bundle \MakerBundle \Str ;
26+ use Symfony \Bundle \MakerBundle \Util \ClassSourceManipulator ;
2527use Symfony \Bundle \MakerBundle \Util \YamlManipulationFailedException ;
2628use Symfony \Bundle \MakerBundle \Util \YamlSourceManipulator ;
29+ use Symfony \Bundle \MakerBundle \Validator ;
2730use Symfony \Bundle \SecurityBundle \SecurityBundle ;
2831use Symfony \Component \Console \Command \Command ;
2932use Symfony \Component \Console \Input \InputArgument ;
3033use Symfony \Component \Console \Input \InputInterface ;
3134use Symfony \Component \Console \Input \InputOption ;
32- use Symfony \Component \Security \ Core \ User \ UserInterface ;
35+ use Symfony \Component \Console \ Question \ Question ;
3336use Symfony \Component \Yaml \Yaml ;
3437
3538/**
@@ -72,6 +75,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf
7275
7376 public function interact (InputInterface $ input , ConsoleStyle $ io , Command $ command )
7477 {
78+ // authenticator type
7579 $ authenticatorTypeValues = [
7680 'Empty authenticator ' => self ::AUTH_TYPE_EMPTY_AUTHENTICATOR ,
7781 'Form login ' => self ::AUTH_TYPE_FORM_LOGIN ,
@@ -87,97 +91,67 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
8791 $ authenticatorTypeValues [$ authenticatorType ]
8892 );
8993
90- $ command ->addArgument ('authenticator-class ' , InputArgument::REQUIRED );
91- $ input ->setArgument (
92- 'authenticator-class ' ,
93- $ io ->ask ('The class name of the authenticator to create (e.g. <fg=yellow>AppCustomAuthenticator</>) ' )
94- );
95-
96- // TODO : validate class name
97-
98- if (null === $ input ->getArgument ('authenticator-class ' )) {
99- throw new RuntimeCommandException ('The authenticator class could not be empty! ' );
100- }
94+ $ manipulator = new YamlSourceManipulator ($ this ->fileManager ->getFileContents ('config/packages/security.yaml ' ));
95+ $ securityData = $ manipulator ->getData ();
10196
102- if (!$ this ->fileManager ->fileExists ($ path = 'config/packages/security.yaml ' )) {
103- return ;
97+ if (self ::AUTH_TYPE_FORM_LOGIN === $ input ->getArgument ('authenticator-type ' )
98+ && !isset ($ securityData ['security ' ]['providers ' ]) || !$ securityData ['security ' ]['providers ' ]) {
99+ throw new RuntimeCommandException ('You need to have at least one provider defined in security.yaml ' );
104100 }
105101
106- $ manipulator = new YamlSourceManipulator ($ this ->fileManager ->getFileContents ($ path ));
107- $ securityData = $ manipulator ->getData ();
108-
109- $ interactiveSecurityHelper = new InteractiveSecurityHelper ();
102+ // authenticator class
103+ $ command ->addArgument ('authenticator-class ' , InputArgument::REQUIRED );
104+ $ questionAuthenticatorClass = new Question ('The class name of the authenticator to create (e.g. <fg=yellow>AppCustomAuthenticator</>) ' );
105+ $ questionAuthenticatorClass ->setValidator (
106+ function ($ answer ) {
107+ Validator::notBlank ($ answer );
108+ return Validator::validateClassDoesNotExist (
109+ $ this ->generator ->createClassNameDetails (
110+ $ answer ,
111+ 'Security \\'
112+ )->getFullName ()
113+ );
114+ }
115+ );
116+ $ input ->setArgument ('authenticator-class ' , $ io ->askQuestion ($ questionAuthenticatorClass ));
110117
111118 $ command ->addOption ('firewall-name ' , null , InputOption::VALUE_OPTIONAL );
112- $ input ->setOption ('firewall-name ' , $ firewallName = $ interactiveSecurityHelper -> guessFirewallName ($ io , $ securityData ));
119+ $ input ->setOption ('firewall-name ' , $ firewallName = InteractiveSecurityHelper:: guessFirewallName ($ io , $ securityData ));
113120
114121 $ command ->addOption ('entry-point ' , null , InputOption::VALUE_OPTIONAL );
115-
116- $ authenticatorClassNameDetails = $ this ->generator ->createClassNameDetails (
117- $ input ->getArgument ('authenticator-class ' ),
118- 'Security \\'
119- );
120-
121122 $ input ->setOption (
122123 'entry-point ' ,
123- $ interactiveSecurityHelper -> guessEntryPoint ($ io , $ securityData , $ authenticatorClassNameDetails -> getFullName ( ), $ firewallName )
124+ InteractiveSecurityHelper:: guessEntryPoint ($ io , $ securityData , $ input -> getArgument ( ' authenticator-class ' ), $ firewallName )
124125 );
125126
126127 if (self ::AUTH_TYPE_FORM_LOGIN === $ input ->getArgument ('authenticator-type ' )) {
127- $ command ->addArgument ('controller-class ' , InputArgument::OPTIONAL , 'Choose a name for the controller class (e.g. <fg=yellow>SecurityController</>) ' , 'SecurityController ' );
128-
129- $ controllerClass = $ io ->ask (
130- $ command ->getDefinition ()->getArgument ('controller-class ' )->getDescription (),
131- $ command ->getDefinition ()->getArgument ('controller-class ' )->getDefault ()
128+ $ command ->addArgument ('controller-class ' , InputArgument::OPTIONAL );
129+ $ input ->setArgument (
130+ 'controller-class ' ,
131+ $ io ->ask (
132+ 'Choose a name for the controller class (e.g. <fg=yellow>SecurityController</>) ' ,
133+ 'SecurityController ' ,
134+ [Validator::class, 'validateClassName ' ]
135+ )
132136 );
133- // TODO : validate class name
134- $ input ->setArgument ('controller-class ' , $ controllerClass );
135-
136- if (!isset ($ securityData ['security ' ]['providers ' ]) || !$ securityData ['security ' ]['providers ' ]) {
137- throw new RuntimeCommandException ('You need to have at least one provider defined in security.yaml ' );
138- }
139137
140138 $ command ->addArgument ('user-class ' , InputArgument::OPTIONAL );
141- if (1 === \count ($ securityData ['security ' ]['providers ' ]) && isset (current ($ securityData ['security ' ]['providers ' ])['entity ' ])) {
142- $ entityProvider = current ($ securityData ['security ' ]['providers ' ]);
143- $ userClass = $ entityProvider ['entity ' ]['class ' ];
144- } else {
145- $ userClass = $ io ->ask (
146- 'Enter the User class you want to authenticate (e.g. <fg=yellow>App \\Entity \\User</>)
147- (It has to be handled by one of the firewall \'s providers) ' ,
148- class_exists ('App \\Entity \\User ' ) && isset (class_implements ('App \\Entity \\User ' )[UserInterface::class]) ? 'App \\Entity \\User '
149- : class_exists ('App \\Security \\User ' ) && isset (class_implements ('App \\Security \\User ' )[UserInterface::class]) ? 'App \\Security \\User ' : null
150- );
151-
152- if (!class_exists ($ userClass )) {
153- throw new RuntimeCommandException (sprintf ('The class "%s" does not exist ' , $ userClass ));
154- }
155-
156- if (!isset (class_implements ($ userClass )[UserInterface::class])) {
157- throw new RuntimeCommandException (sprintf ('The class "%s" doesn \'t implement "%s" ' , $ userClass , UserInterface::class));
158- }
159- }
160- $ input ->setArgument ('user-class ' , $ userClass );
139+ $ input ->setArgument ('user-class ' , InteractiveSecurityHelper::guessUserClass ($ io , $ securityData ));
161140 }
162141 }
163142
164143 public function generate (InputInterface $ input , ConsoleStyle $ io , Generator $ generator )
165144 {
166- $ classNameDetails = $ generator ->createClassNameDetails (
167- $ input ->getArgument ('authenticator-class ' ),
168- 'Security \\'
169- );
170-
171- // TODO update LoginFormNotEntityAuthenticator
172-
145+ // generate authenticator class
173146 $ generator ->generateClass (
174- $ classNameDetails -> getFullName ( ),
147+ $ input -> getArgument ( ' authenticator-class ' ),
175148 self ::AUTH_TYPE_FORM_LOGIN === $ input ->getArgument ('authenticator-type ' ) ?
176149 ($ this ->doctrineHelper ->isClassAMappedEntity ($ input ->getArgument ('user-class ' )) ? 'authenticator/LoginFormEntityAuthenticator.tpl.php ' : 'authenticator/LoginFormNotEntityAuthenticator.tpl.php ' )
177150 : 'authenticator/Empty.tpl.php ' ,
178151 []
179152 );
180153
154+ // update security.yaml with guard config
181155 $ securityYamlUpdated = false ;
182156 $ path = 'config/packages/security.yaml ' ;
183157 if ($ this ->fileManager ->fileExists ($ path )) {
@@ -186,7 +160,7 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
186160 $ this ->fileManager ->getFileContents ($ path ),
187161 $ input ->getOption ('firewall-name ' ),
188162 $ input ->getOption ('entry-point ' ),
189- $ classNameDetails -> getFullName ( )
163+ $ input -> getArgument ( ' authenticator-class ' )
190164 );
191165 $ generator ->dumpFile ($ path , $ newYaml );
192166 $ securityYamlUpdated = true ;
@@ -195,25 +169,41 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
195169 }
196170
197171 if (self ::AUTH_TYPE_FORM_LOGIN === $ input ->getArgument ('authenticator-type ' )) {
198- // TODO check if SecurityController exists
199172 $ controllerClassNameDetails = $ generator ->createClassNameDetails (
200173 $ input ->getArgument ('controller-class ' ),
201174 'Controller \\' ,
202175 'Controller '
203176 );
204177
205- $ controllerPath = $ generator ->generateClass (
206- $ controllerClassNameDetails ->getFullName (),
207- 'login_form/SecurityController.tpl.php ' ,
208- [
209- 'parent_class_name ' => \method_exists (AbstractController::class, 'getParameter ' ) ? 'AbstractController ' : 'Controller ' ,
210- ]
211- );
178+ if (class_exists ($ controllerClassNameDetails ->getFullName ())) {
179+ // If provided security controller class exist, add login() method
180+ if (method_exists ($ controllerClassNameDetails ->getFullName (), 'login ' )) {
181+ throw new RuntimeCommandException (sprintf ('Method "login" already exists on class %s ' , $ controllerClassNameDetails ->getFullName ()));
182+ }
183+
184+ $ manipulator = new ClassSourceManipulator (
185+ $ this ->fileManager ->getFileContents ($ controllerPath = $ this ->fileManager ->getRelativePathForFutureClass ($ controllerClassNameDetails ->getFullName ())),
186+ true
187+ );
188+ $ securityControllerBuilder = new SecurityControllerBuilder ();
189+ $ securityControllerBuilder ->addLoginMethod ($ manipulator );
190+ $ this ->generator ->dumpFile ($ controllerPath , $ manipulator ->getSourceCode ());
191+ } else {
192+ // otherwise, create security controller
193+ $ controllerPath = $ generator ->generateClass (
194+ $ controllerClassNameDetails ->getFullName (),
195+ 'authenticator/SecurityController.tpl.php ' ,
196+ [
197+ 'parent_class_name ' => \method_exists (AbstractController::class, 'getParameter ' ) ? 'AbstractController ' : 'Controller ' ,
198+ ]
199+ );
200+ }
212201
202+ // create login form template
213203 $ templateName = Str::asFilePath ($ controllerClassNameDetails ->getRelativeNameWithoutSuffix ()).'/login.html.twig ' ;
214204 $ generator ->generateFile (
215205 'templates/ ' .$ templateName ,
216- 'login_form /login_form.tpl.php ' ,
206+ 'authenticator /login_form.tpl.php ' ,
217207 [
218208 'controller_path ' => $ controllerPath ,
219209 ]
@@ -230,7 +220,7 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
230220 'security: {} ' ,
231221 'main ' ,
232222 null ,
233- $ classNameDetails -> getFullName ( )
223+ $ input -> getArgument ( ' authenticator-class ' )
234224 );
235225 $ text [] = "Your <info>security.yaml</info> could not be updated automatically. You'll need to add the following config manually: \n\n" .$ yamlExample ;
236226 }
0 commit comments