Skip to content

Commit efb19e4

Browse files
committed
create MakeFormLogin command
1 parent 24ae473 commit efb19e4

File tree

6 files changed

+288
-0
lines changed

6 files changed

+288
-0
lines changed

src/Maker/MakeLoginForm.php

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony MakerBundle package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\MakerBundle\Maker;
13+
14+
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
15+
use Symfony\Bundle\MakerBundle\ConsoleStyle;
16+
use Symfony\Bundle\MakerBundle\DependencyBuilder;
17+
use Symfony\Bundle\MakerBundle\FileManager;
18+
use Symfony\Bundle\MakerBundle\Generator;
19+
use Symfony\Bundle\MakerBundle\InputConfiguration;
20+
use Symfony\Bundle\MakerBundle\Security\SecurityConfigUpdater;
21+
use Symfony\Bundle\MakerBundle\Str;
22+
use Symfony\Bundle\SecurityBundle\SecurityBundle;
23+
use Symfony\Bundle\TwigBundle\TwigBundle;
24+
use Symfony\Component\Console\Command\Command;
25+
use Symfony\Component\Console\Input\InputInterface;
26+
27+
/**
28+
* @author Ryan Weaver <[email protected]>
29+
*/
30+
final class MakeLoginForm extends AbstractMaker
31+
{
32+
private $fileManager;
33+
34+
private $configUpdater;
35+
36+
public function __construct(FileManager $fileManager, SecurityConfigUpdater $configUpdater)
37+
{
38+
$this->fileManager = $fileManager;
39+
$this->configUpdater = $configUpdater;
40+
}
41+
42+
public static function getCommandName(): string
43+
{
44+
return 'make:login-form';
45+
}
46+
47+
public function configureCommand(Command $command, InputConfiguration $inputConfig)
48+
{
49+
$command
50+
->setDescription('Creates a login form and its controller and authenticator');
51+
}
52+
53+
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator)
54+
{
55+
$controllerClassNameDetails = $generator->createClassNameDetails(
56+
'SecurityController',
57+
'Controller\\',
58+
'Controller'
59+
);
60+
61+
$templateName = Str::asFilePath($controllerClassNameDetails->getRelativeNameWithoutSuffix()).'/login.html.twig';
62+
$controllerPath = $generator->generateClass(
63+
$controllerClassNameDetails->getFullName(),
64+
'login_form/SecurityController.tpl.php',
65+
[
66+
'parent_class_name' => \method_exists(AbstractController::class, 'getParameter') ? 'AbstractController' : 'Controller',
67+
]
68+
);
69+
70+
$generator->generateFile(
71+
'templates/'.$templateName,
72+
'login_form/login_form.tpl.php',
73+
[
74+
'controller_path' => $controllerPath,
75+
]
76+
);
77+
78+
$generator->writeChanges();
79+
80+
$this->writeSuccessMessage($io);
81+
}
82+
83+
public function configureDependencies(DependencyBuilder $dependencies)
84+
{
85+
$dependencies->addClassDependency(
86+
SecurityBundle::class,
87+
'security'
88+
);
89+
90+
$dependencies->addClassDependency(
91+
TwigBundle::class,
92+
'twig'
93+
);
94+
}
95+
}

src/Resources/config/makers.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@
4949
<tag name="maker.command" />
5050
</service>
5151

52+
<service id="maker.maker.make_login_form" class="Symfony\Bundle\MakerBundle\Maker\MakeLoginForm">
53+
<argument type="service" id="maker.file_manager" />
54+
<argument type="service" id="maker.security_config_updater" />
55+
<tag name="maker.command" />
56+
</service>
57+
5258
<service id="maker.maker.make_serializer_encoder" class="Symfony\Bundle\MakerBundle\Maker\MakeSerializerEncoder">
5359
<tag name="maker.command" />
5460
</service>
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?php
2+
3+
namespace App\Security;
4+
5+
use App\Entity\User;
6+
use Symfony\Component\HttpFoundation\RedirectResponse;
7+
use Symfony\Component\HttpFoundation\Request;
8+
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
9+
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
10+
use Symfony\Component\Security\Core\Exception\AuthenticationException;
11+
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
12+
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
13+
use Symfony\Component\Security\Core\Security;
14+
use Symfony\Component\Security\Core\User\UserInterface;
15+
use Symfony\Component\Security\Core\User\UserProviderInterface;
16+
use Symfony\Component\Security\Guard\AuthenticatorInterface;
17+
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
18+
19+
/**
20+
* Class AuthenticatorAuthenticator
21+
*/
22+
class Authenticator implements AuthenticatorInterface
23+
{
24+
/** @var EncoderFactoryInterface */
25+
private $encoderFactory;
26+
27+
/**
28+
* Default message for authentication failure.
29+
* @var string
30+
*/
31+
protected $failMessage = 'Invalid credentials';
32+
33+
/**
34+
* AdminAuthenticator constructor.
35+
*
36+
* @param EncoderFactoryInterface $encoderFactory
37+
*/
38+
public function __construct(EncoderFactoryInterface $encoderFactory) {
39+
$this->encoderFactory = $encoderFactory;
40+
}
41+
42+
public function supports(Request $request)
43+
{
44+
return $request->attributes->get('_route') == 'login' && $request->isMethod('POST');
45+
}
46+
47+
/**
48+
* {@inheritdoc}
49+
*/
50+
public function getCredentials(Request $request)
51+
{
52+
return [
53+
'username' => $request->get('_username'),
54+
'password' => $request->get('_password'),
55+
];
56+
}
57+
58+
/**
59+
* {@inheritdoc}
60+
*/
61+
public function getUser($credentials, UserProviderInterface $userProvider)
62+
{
63+
try {
64+
return $userProvider->loadUserByUsername($credentials['username']);
65+
} catch (UsernameNotFoundException $e) {
66+
throw new CustomUserMessageAuthenticationException($this->failMessage);
67+
}
68+
}
69+
70+
/**
71+
* @param mixed $credentials
72+
* @param UserInterface|User $user
73+
*
74+
* @return bool
75+
*/
76+
public function checkCredentials($credentials, UserInterface $user)
77+
{
78+
$encoder = $this->encoderFactory->getEncoder($user);
79+
80+
if ($user->getPassword() !== $encoder->encodePassword($credentials['password'], $user->getSalt())) {
81+
throw new CustomUserMessageAuthenticationException($this->failMessage);
82+
}
83+
84+
return true;
85+
}
86+
87+
/**
88+
* {@inheritdoc}
89+
*/
90+
public function createAuthenticatedToken(UserInterface $user, $providerKey)
91+
{
92+
return new PostAuthenticationGuardToken(
93+
$user,
94+
$providerKey,
95+
$user->getRoles()
96+
);
97+
}
98+
99+
/**
100+
* {@inheritdoc}
101+
*/
102+
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
103+
{
104+
// on success, let the request continue
105+
return null;
106+
}
107+
108+
/**
109+
* {@inheritdoc}
110+
*/
111+
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
112+
{
113+
return null;
114+
}
115+
116+
/**
117+
* {@inheritdoc}
118+
*/
119+
public function start(Request $request, AuthenticationException $authException = null)
120+
{
121+
$url = $this->router->generate('login');
122+
123+
return new RedirectResponse($url);
124+
}
125+
126+
/**
127+
* {@inheritdoc}
128+
*/
129+
public function supportsRememberMe()
130+
{
131+
return true;
132+
}
133+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?= "<?php\n" ?>
2+
3+
namespace <?= $namespace; ?>;
4+
5+
use Symfony\Bundle\FrameworkBundle\Controller\<?= $parent_class_name; ?>;
6+
use Symfony\Component\Routing\Annotation\Route;
7+
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
8+
9+
class SecurityController extends <?= $parent_class_name; ?><?= "\n" ?>
10+
{
11+
/**
12+
* @Route("/login", name="login")
13+
*/
14+
public function login(AuthenticationUtils $authenticationUtils)
15+
{
16+
// get the login error if there is one
17+
$error = $authenticationUtils->getLastAuthenticationError();
18+
19+
// last username entered by the user
20+
$lastUsername = $authenticationUtils->getLastUsername();
21+
22+
return $this->render('security/login.html.twig', [
23+
'last_username' => $lastUsername,
24+
'error' => $error,
25+
]);
26+
}
27+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?= $helper->getHeadPrintCode('Hello {{ controller_name }}!'); ?>
2+
3+
{% block body %}
4+
{% if error %}
5+
<div>{{ error.messageKey|trans(error.messageData, 'security') }}</div>
6+
{% endif %}
7+
8+
<form action="{{ path('login') }}" method="post">
9+
<label for="username">Username:</label>
10+
<input type="text" id="username" name="_username" value="{{ last_username }}"/>
11+
12+
<label for="password">Password:</label>
13+
<input type="password" id="password" name="_password"/>
14+
15+
<button type="submit">login</button>
16+
</form>
17+
{% endblock %}

tests/Maker/FunctionalTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Bundle\MakerBundle\Maker\MakeFixtures;
1717
use Symfony\Bundle\MakerBundle\Maker\MakeForm;
1818
use Symfony\Bundle\MakerBundle\Maker\MakeFunctionalTest;
19+
use Symfony\Bundle\MakerBundle\Maker\MakeLoginForm;
1920
use Symfony\Bundle\MakerBundle\Maker\MakeMigration;
2021
use Symfony\Bundle\MakerBundle\Maker\MakeSerializerEncoder;
2122
use Symfony\Bundle\MakerBundle\Maker\MakeSubscriber;
@@ -526,6 +527,15 @@ function (string $output, string $directory) {
526527
$this->assertContains('created: src/Form/SweetFoodType.php', $output);
527528
})
528529
];
530+
531+
yield 'login_form' => [
532+
MakerTestDetails::createTest(
533+
$this->getMakerInstance(MakeLoginForm::class),
534+
[]
535+
)
536+
->addExtraDependencies('security')
537+
->addExtraDependencies('twig'),
538+
];
529539
}
530540

531541
public function getCommandEntityTests()

0 commit comments

Comments
 (0)