-
-
Notifications
You must be signed in to change notification settings - Fork 439
[make:security:json-login] WIP #1246
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 1.x
Are you sure you want to change the base?
Changes from all commits
4be8648
c38789d
eed3c3a
9076e71
ae32f23
c8e960d
053bcb5
7844f78
cf68b9e
16d0b39
0811ef8
a5c0657
789d245
df01550
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| <?php | ||
|
|
||
| /* | ||
| * This file is part of the Symfony MakerBundle package. | ||
| * | ||
| * (c) Fabien Potencier <fabien@symfony.com> | ||
| * | ||
| * For the full copyright and license information, please view the LICENSE | ||
| * file that was distributed with this source code. | ||
| */ | ||
|
|
||
| namespace Symfony\Bundle\MakerBundle\Maker\Security; | ||
|
|
||
| use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; | ||
| use Symfony\Bundle\MakerBundle\ConsoleStyle; | ||
| use Symfony\Bundle\MakerBundle\DependencyBuilder; | ||
| use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException; | ||
| use Symfony\Bundle\MakerBundle\FileManager; | ||
| use Symfony\Bundle\MakerBundle\Maker\AbstractMaker; | ||
| use Symfony\Bundle\MakerBundle\Security\InteractiveSecurityHelper; | ||
| use Symfony\Bundle\MakerBundle\Security\SecurityConfigUpdater; | ||
| use Symfony\Bundle\MakerBundle\Security\SecurityControllerBuilder; | ||
| use Symfony\Bundle\MakerBundle\Util\YamlSourceManipulator; | ||
| use Symfony\Bundle\MakerBundle\Validator; | ||
| use Symfony\Bundle\SecurityBundle\SecurityBundle; | ||
| use Symfony\Component\Console\Command\Command; | ||
| use Symfony\Component\Console\Input\InputInterface; | ||
| use Symfony\Component\Process\Process; | ||
| use Symfony\Component\Yaml\Yaml; | ||
|
|
||
| /** | ||
| * @author Jesse Rushlow <jr@rushlow.dev> | ||
| * | ||
| * @internal | ||
| */ | ||
| abstract class AbstractSecurityMaker extends AbstractMaker | ||
| { | ||
| protected const SECURITY_CONFIG_PATH = 'config/packages/security.yaml'; | ||
|
|
||
| protected YamlSourceManipulator $ysm; | ||
| protected string $securityControllerName; | ||
| protected string $firewallToUpdate; | ||
| protected string $userClass; | ||
| protected string $userNameField; | ||
| protected bool $willLogout; | ||
|
|
||
| public function __construct( | ||
| protected FileManager $fileManager, | ||
| protected SecurityConfigUpdater $securityConfigUpdater, | ||
| protected SecurityControllerBuilder $securityControllerBuilder, | ||
| ) { | ||
| } | ||
|
|
||
| public function configureDependencies(DependencyBuilder $dependencies): void | ||
| { | ||
| $dependencies->addClassDependency(SecurityBundle::class, 'security'); | ||
| $dependencies->addClassDependency(Process::class, 'process'); | ||
| $dependencies->addClassDependency(Yaml::class, 'yaml'); | ||
| $dependencies->addClassDependency(DoctrineBundle::class, 'orm'); | ||
| } | ||
|
|
||
| public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void | ||
| { | ||
| if (!$this->fileManager->fileExists(self::SECURITY_CONFIG_PATH)) { | ||
| throw new RuntimeCommandException(sprintf('The file "%s" does not exist. PHP & XML configuration formats are currently not supported.', self::SECURITY_CONFIG_PATH)); | ||
| } | ||
|
|
||
| $this->securityControllerName = $io->ask( | ||
| 'Choose a name for the controller class (e.g. <fg=yellow>ApiLoginController</>)', | ||
| 'ApiLoginController', | ||
| [Validator::class, 'validateClassName'] | ||
| ); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This question and the suggestion of |
||
|
|
||
| $this->ysm = new YamlSourceManipulator($this->fileManager->getFileContents(self::SECURITY_CONFIG_PATH)); | ||
| $securityData = $this->ysm->getData(); | ||
|
|
||
| $securityHelper = new InteractiveSecurityHelper(); | ||
| $this->firewallToUpdate = $securityHelper->guessFirewallName($io, $securityData); | ||
| $this->userClass = $securityHelper->guessUserClass($io, $securityData['security']['providers']); | ||
| $this->userNameField = $securityHelper->guessUserNameField($io, $this->userClass, $securityData['security']['providers']); | ||
| $this->willLogout = $io->confirm('Do you want to generate a \'/logout\' URL?'); | ||
| } | ||
| } | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could probably find more repeated code between |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| <?php | ||
|
|
||
| /* | ||
| * This file is part of the Symfony MakerBundle package. | ||
| * | ||
| * (c) Fabien Potencier <fabien@symfony.com> | ||
| * | ||
| * For the full copyright and license information, please view the LICENSE | ||
| * file that was distributed with this source code. | ||
| */ | ||
|
|
||
| namespace Symfony\Bundle\MakerBundle\Maker\Security; | ||
|
|
||
| use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||
| use Symfony\Bundle\MakerBundle\ConsoleStyle; | ||
| use Symfony\Bundle\MakerBundle\Generator; | ||
| use Symfony\Bundle\MakerBundle\InputConfiguration; | ||
| use Symfony\Bundle\MakerBundle\Util\ClassNameDetails; | ||
| use Symfony\Bundle\MakerBundle\Util\ClassSourceManipulator; | ||
| use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator; | ||
| use Symfony\Component\Console\Command\Command; | ||
| use Symfony\Component\Console\Input\InputInterface; | ||
| use Symfony\Component\HttpFoundation\JsonResponse; | ||
| use Symfony\Component\HttpFoundation\Response; | ||
| use Symfony\Component\Routing\Annotation\Route; | ||
| use Symfony\Component\Security\Http\Attribute\CurrentUser; | ||
|
|
||
| /** | ||
| * Generate Form Login Security using SecurityBundle's Authenticator. | ||
| * | ||
| * @see https://symfony.com/doc/current/security.html#form-login | ||
| * | ||
| * @author Jesse Rushlow <jr@rushlow.dev> | ||
| * | ||
| * @internal | ||
| */ | ||
| final class MakeJsonLogin extends AbstractSecurityMaker | ||
| { | ||
| public static function getCommandName(): string | ||
| { | ||
| return 'make:security:json-login'; | ||
| } | ||
|
|
||
| public function configureCommand(Command $command, InputConfiguration $inputConfig): void | ||
| { | ||
| $command->setHelp(file_get_contents(\dirname(__DIR__, 2).'/Resources/help/security/MakeJsonLogin.txt')); | ||
| } | ||
|
|
||
| public static function getCommandDescription(): string | ||
| { | ||
| return 'Generate the code needed for the json_login authenticator'; | ||
| } | ||
|
|
||
| public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void | ||
| { | ||
| $userClassDetails = new ClassNameDetails($this->userClass, ''); | ||
|
|
||
| $useStatements = new UseStatementGenerator([ | ||
| $userClassDetails->getFullName(), | ||
| AbstractController::class, | ||
| JsonResponse::class, | ||
| Response::class, | ||
| Route::class, | ||
| CurrentUser::class, | ||
| ]); | ||
|
|
||
| $controllerNameDetails = $generator->createClassNameDetails($this->securityControllerName, 'Controller\\', 'Controller'); | ||
|
|
||
| $controllerPath = $this->fileManager->getRelativePathForFutureClass($controllerNameDetails->getFullName()); | ||
|
|
||
| $controllerExists = $this->fileManager->fileExists($controllerPath); | ||
|
|
||
| if (!$controllerExists) { | ||
| $generator->generateController( | ||
| $controllerNameDetails->getFullName(), | ||
| 'EmptyController.tpl.php', | ||
| [ | ||
| 'use_statements' => $useStatements, | ||
| 'controller_name' => $controllerNameDetails->getShortName(), | ||
| ] | ||
| ); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be a new |
||
| } | ||
|
|
||
| $controllerSource = $controllerExists ? file_get_contents($controllerPath) : $generator->getFileContentsForPendingOperation($controllerPath); | ||
|
|
||
| $manipulator = new ClassSourceManipulator($controllerSource); | ||
|
|
||
| $this->securityControllerBuilder->addJsonLoginMethod($manipulator, $userClassDetails); | ||
|
|
||
| $securityData = $this->securityConfigUpdater->updateForJsonLogin($this->ysm->getContents(), $this->firewallToUpdate, 'app_api_login'); | ||
|
|
||
| if ($this->willLogout) { | ||
| $this->securityControllerBuilder->addLogoutMethod($manipulator); | ||
|
|
||
| $securityData = $this->securityConfigUpdater->updateForLogout($securityData, $this->firewallToUpdate); | ||
| } | ||
|
|
||
| $generator->dumpFile(self::SECURITY_CONFIG_PATH, $securityData); | ||
| $generator->dumpFile($controllerPath, $manipulator->getSourceCode()); | ||
|
|
||
| $generator->writeChanges(); | ||
|
|
||
| $this->writeSuccessMessage($io); | ||
|
|
||
| $io->text([ | ||
| 'Next: Make a <info>POST</info> request to <info>/api/login</info> with a <info>username</info> and <info>password</info> to login.', | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The user also needs to set |
||
| 'Then: The security system intercepts the requests and authenticates the user.', | ||
| sprintf('And Finally: The <info>%s::apiLogin</info> method creates and returns a JsonResponse.', $controllerNameDetails->getShortName()), | ||
| ]); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| The <info>%command.name%</info> command generates a controller to allow users to | ||
| login using the json_login authenticator. | ||
|
|
||
| The controller name, and logout ability can be customized by answering the | ||
| questions asked when running <info>%command.name%</info>. | ||
|
|
||
| This will also update your <info>security.yaml</info> for the new authenticator. | ||
|
|
||
| <info>php %command.full_name%</info> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| <?= "<?php\n" ?> | ||
|
|
||
| namespace <?= $namespace; ?>; | ||
|
|
||
| <?= $use_statements; ?> | ||
|
|
||
| class <?= $controller_name ?> extends AbstractController | ||
| { | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this actually needed?