initial commit
This commit is contained in:
50
vendor/symfony/maker-bundle/templates/resetPassword/ChangePasswordFormType.tpl.php
vendored
Normal file
50
vendor/symfony/maker-bundle/templates/resetPassword/ChangePasswordFormType.tpl.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?= "<?php\n" ?>
|
||||
|
||||
namespace <?= $namespace ?>;
|
||||
|
||||
<?= $use_statements ?>
|
||||
|
||||
class <?= $class_name ?> extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('plainPassword', RepeatedType::class, [
|
||||
'type' => PasswordType::class,
|
||||
'options' => [
|
||||
'attr' => [
|
||||
'autocomplete' => 'new-password',
|
||||
],
|
||||
],
|
||||
'first_options' => [
|
||||
'constraints' => [
|
||||
new NotBlank([
|
||||
'message' => 'Please enter a password',
|
||||
]),
|
||||
new Length([
|
||||
'min' => 12,
|
||||
'minMessage' => 'Your password should be at least {{ limit }} characters',
|
||||
// max length allowed by Symfony for security reasons
|
||||
'max' => 4096,
|
||||
]),
|
||||
new PasswordStrength(),
|
||||
new NotCompromisedPassword(),
|
||||
],
|
||||
'label' => 'New password',
|
||||
],
|
||||
'second_options' => [
|
||||
'label' => 'Repeat Password',
|
||||
],
|
||||
'invalid_message' => 'The password fields must match.',
|
||||
// Instead of being set onto the object directly,
|
||||
// this is read and encoded in the controller
|
||||
'mapped' => false,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([]);
|
||||
}
|
||||
}
|
||||
158
vendor/symfony/maker-bundle/templates/resetPassword/ResetPasswordController.tpl.php
vendored
Normal file
158
vendor/symfony/maker-bundle/templates/resetPassword/ResetPasswordController.tpl.php
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
<?= "<?php\n" ?>
|
||||
|
||||
namespace <?= $namespace ?>;
|
||||
|
||||
<?= $use_statements; ?>
|
||||
|
||||
#[Route('/reset-password')]
|
||||
class <?= $class_name ?> extends AbstractController
|
||||
{
|
||||
use ResetPasswordControllerTrait;
|
||||
|
||||
public function __construct(
|
||||
private ResetPasswordHelperInterface $resetPasswordHelper,
|
||||
private EntityManagerInterface $entityManager
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Display & process form to request a password reset.
|
||||
*/
|
||||
#[Route('', name: 'app_forgot_password_request')]
|
||||
public function request(Request $request, MailerInterface $mailer<?php if ($translator_available): ?>, TranslatorInterface $translator<?php endif ?>): Response
|
||||
{
|
||||
$form = $this->createForm(<?= $request_form_type_class_name ?>::class);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
/** @var string $email */
|
||||
$email = $form->get('<?= $email_field ?>')->getData();
|
||||
|
||||
return $this->processSendingPasswordResetEmail($email, $mailer<?php if ($translator_available): ?>, $translator<?php endif ?><?= "\n" ?>);
|
||||
}
|
||||
|
||||
return $this->render('reset_password/request.html.twig', [
|
||||
'requestForm' => $form,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation page after a user has requested a password reset.
|
||||
*/
|
||||
#[Route('/check-email', name: 'app_check_email')]
|
||||
public function checkEmail(): Response
|
||||
{
|
||||
// Generate a fake token if the user does not exist or someone hit this page directly.
|
||||
// This prevents exposing whether or not a user was found with the given email address or not
|
||||
if (null === ($resetToken = $this->getTokenObjectFromSession())) {
|
||||
$resetToken = $this->resetPasswordHelper->generateFakeResetToken();
|
||||
}
|
||||
|
||||
return $this->render('reset_password/check_email.html.twig', [
|
||||
'resetToken' => $resetToken,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and process the reset URL that the user clicked in their email.
|
||||
*/
|
||||
#[Route('/reset/{token}', name: 'app_reset_password')]
|
||||
public function reset(Request $request, UserPasswordHasherInterface $passwordHasher<?php if ($translator_available): ?>, TranslatorInterface $translator<?php endif ?>, string $token = null): Response
|
||||
{
|
||||
if ($token) {
|
||||
// We store the token in session and remove it from the URL, to avoid the URL being
|
||||
// loaded in a browser and potentially leaking the token to 3rd party JavaScript.
|
||||
$this->storeTokenInSession($token);
|
||||
|
||||
return $this->redirectToRoute('app_reset_password');
|
||||
}
|
||||
|
||||
$token = $this->getTokenFromSession();
|
||||
if (null === $token) {
|
||||
throw $this->createNotFoundException('No reset password token found in the URL or in the session.');
|
||||
}
|
||||
|
||||
try {
|
||||
/** @var <?= $user_class_name ?> $user */
|
||||
$user = $this->resetPasswordHelper->validateTokenAndFetchUser($token);
|
||||
} catch (ResetPasswordExceptionInterface $e) {
|
||||
$this->addFlash('reset_password_error', sprintf(
|
||||
'%s - %s',
|
||||
<?php if ($translator_available): ?>$translator->trans(<?= $problem_validate_message_or_constant ?>, [], 'ResetPasswordBundle')<?php else: ?><?= $problem_validate_message_or_constant ?><?php endif ?>,
|
||||
<?php if ($translator_available): ?>$translator->trans($e->getReason(), [], 'ResetPasswordBundle')<?php else: ?>$e->getReason()<?php endif ?><?= "\n" ?>
|
||||
));
|
||||
|
||||
return $this->redirectToRoute('app_forgot_password_request');
|
||||
}
|
||||
|
||||
// The token is valid; allow the user to change their password.
|
||||
$form = $this->createForm(<?= $reset_form_type_class_name ?>::class);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
// A password reset token should be used only once, remove it.
|
||||
$this->resetPasswordHelper->removeResetRequest($token);
|
||||
|
||||
/** @var string $plainPassword */
|
||||
$plainPassword = $form->get('plainPassword')->getData();
|
||||
|
||||
// Encode(hash) the plain password, and set it.
|
||||
$user-><?= $password_setter ?>($passwordHasher->hashPassword($user, $plainPassword));
|
||||
$this->entityManager->flush();
|
||||
|
||||
// The session is cleaned up after the password has been changed.
|
||||
$this->cleanSessionAfterReset();
|
||||
|
||||
return $this->redirectToRoute('<?= $success_redirect_route ?>');
|
||||
}
|
||||
|
||||
return $this->render('reset_password/reset.html.twig', [
|
||||
'resetForm' => $form,
|
||||
]);
|
||||
}
|
||||
|
||||
private function processSendingPasswordResetEmail(string $emailFormData, MailerInterface $mailer<?php if ($translator_available): ?>, TranslatorInterface $translator<?php endif ?>): RedirectResponse
|
||||
{
|
||||
$user = $this->entityManager->getRepository(<?= $user_class_name ?>::class)->findOneBy([
|
||||
'<?= $email_field ?>' => $emailFormData,
|
||||
]);
|
||||
|
||||
// Do not reveal whether a user account was found or not.
|
||||
if (!$user) {
|
||||
return $this->redirectToRoute('app_check_email');
|
||||
}
|
||||
|
||||
try {
|
||||
$resetToken = $this->resetPasswordHelper->generateResetToken($user);
|
||||
} catch (ResetPasswordExceptionInterface $e) {
|
||||
// If you want to tell the user why a reset email was not sent, uncomment
|
||||
// the lines below and change the redirect to 'app_forgot_password_request'.
|
||||
// Caution: This may reveal if a user is registered or not.
|
||||
//
|
||||
// $this->addFlash('reset_password_error', sprintf(
|
||||
// '%s - %s',
|
||||
// <?php if ($translator_available): ?>$translator->trans(<?= $problem_handle_message_or_constant ?>, [], 'ResetPasswordBundle')<?php else: ?><?= $problem_handle_message_or_constant ?><?php endif ?>,
|
||||
// <?php if ($translator_available): ?>$translator->trans($e->getReason(), [], 'ResetPasswordBundle')<?php else: ?>$e->getReason()<?php endif ?><?= "\n" ?>
|
||||
// ));
|
||||
|
||||
return $this->redirectToRoute('app_check_email');
|
||||
}
|
||||
|
||||
$email = (new TemplatedEmail())
|
||||
->from(new Address('<?= $from_email ?>', '<?= $from_email_name ?>'))
|
||||
->to((string) $user-><?= $email_getter ?>())
|
||||
->subject('Your password reset request')
|
||||
->htmlTemplate('reset_password/email.html.twig')
|
||||
->context([
|
||||
'resetToken' => $resetToken,
|
||||
])
|
||||
;
|
||||
|
||||
$mailer->send($email);
|
||||
|
||||
// Store the token object in session for retrieval in check-email route.
|
||||
$this->setTokenObjectInSession($resetToken);
|
||||
|
||||
return $this->redirectToRoute('app_check_email');
|
||||
}
|
||||
}
|
||||
27
vendor/symfony/maker-bundle/templates/resetPassword/ResetPasswordRequestFormType.tpl.php
vendored
Normal file
27
vendor/symfony/maker-bundle/templates/resetPassword/ResetPasswordRequestFormType.tpl.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?= "<?php\n" ?>
|
||||
|
||||
namespace <?= $namespace ?>;
|
||||
|
||||
<?= $use_statements ?>
|
||||
|
||||
class <?= $class_name ?> extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('<?= $email_field ?>', EmailType::class, [
|
||||
'attr' => ['autocomplete' => 'email'],
|
||||
'constraints' => [
|
||||
new NotBlank([
|
||||
'message' => 'Please enter your email',
|
||||
]),
|
||||
],
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([]);
|
||||
}
|
||||
}
|
||||
98
vendor/symfony/maker-bundle/templates/resetPassword/Test.ResetPasswordController.tpl.php
vendored
Normal file
98
vendor/symfony/maker-bundle/templates/resetPassword/Test.ResetPasswordController.tpl.php
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
<?= "<?php\n" ?>
|
||||
namespace App\Tests;
|
||||
|
||||
<?= $use_statements ?>
|
||||
|
||||
class ResetPasswordControllerTest extends WebTestCase
|
||||
{
|
||||
private KernelBrowser $client;
|
||||
private EntityManagerInterface $em;
|
||||
private <?= $user_repo_short_name ?> $userRepository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->client = static::createClient();
|
||||
|
||||
// Ensure we have a clean database
|
||||
$container = static::getContainer();
|
||||
|
||||
/** @var EntityManagerInterface $em */
|
||||
$em = $container->get('doctrine')->getManager();
|
||||
$this->em = $em;
|
||||
|
||||
$this->userRepository = $container->get(<?= $user_repo_short_name ?>::class);
|
||||
|
||||
foreach ($this->userRepository->findAll() as $user) {
|
||||
$this->em->remove($user);
|
||||
}
|
||||
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
public function testResetPasswordController(): void
|
||||
{
|
||||
// Create a test user
|
||||
$user = (new <?= $user_short_name ?>())
|
||||
->setEmail('me@example.com')
|
||||
->setPassword('a-test-password-that-will-be-changed-later')
|
||||
;
|
||||
$this->em->persist($user);
|
||||
$this->em->flush();
|
||||
|
||||
// Test Request reset password page
|
||||
$this->client->request('GET', '/reset-password');
|
||||
|
||||
self::assertResponseIsSuccessful();
|
||||
self::assertPageTitleContains('Reset your password');
|
||||
|
||||
// Submit the reset password form and test email message is queued / sent
|
||||
$this->client->submitForm('Send password reset email', [
|
||||
'reset_password_request_form[email]' => 'me@example.com',
|
||||
]);
|
||||
|
||||
// Ensure the reset password email was sent
|
||||
// Use either assertQueuedEmailCount() || assertEmailCount() depending on your mailer setup
|
||||
// self::assertQueuedEmailCount(1);
|
||||
self::assertEmailCount(1);
|
||||
|
||||
self::assertCount(1, $messages = $this->getMailerMessages());
|
||||
|
||||
self::assertEmailAddressContains($messages[0], 'from', '<?= $from_email ?>');
|
||||
self::assertEmailAddressContains($messages[0], 'to', 'me@example.com');
|
||||
self::assertEmailTextBodyContains($messages[0], 'This link will expire in 1 hour.');
|
||||
|
||||
self::assertResponseRedirects('/reset-password/check-email');
|
||||
|
||||
// Test check email landing page shows correct "expires at" time
|
||||
$crawler = $this->client->followRedirect();
|
||||
|
||||
self::assertPageTitleContains('Password Reset Email Sent');
|
||||
self::assertStringContainsString('This link will expire in 1 hour', $crawler->html());
|
||||
|
||||
// Test the link sent in the email is valid
|
||||
$email = $messages[0]->toString();
|
||||
preg_match('#(/reset-password/reset/[a-zA-Z0-9]+)#', $email, $resetLink);
|
||||
|
||||
$this->client->request('GET', $resetLink[1]);
|
||||
|
||||
self::assertResponseRedirects('/reset-password/reset');
|
||||
|
||||
$this->client->followRedirect();
|
||||
|
||||
// Test we can set a new password
|
||||
$this->client->submitForm('Reset password', [
|
||||
'change_password_form[plainPassword][first]' => 'newStrongPassword',
|
||||
'change_password_form[plainPassword][second]' => 'newStrongPassword',
|
||||
]);
|
||||
|
||||
self::assertResponseRedirects('<?= $success_route_path ?>');
|
||||
|
||||
$user = $this->userRepository->findOneBy(['email' => 'me@example.com']);
|
||||
|
||||
self::assertInstanceOf(<?= $user_short_name ?>::class, $user);
|
||||
|
||||
/** @var UserPasswordHasherInterface $passwordHasher */
|
||||
$passwordHasher = static::getContainer()->get(UserPasswordHasherInterface::class);
|
||||
self::assertTrue($passwordHasher->isPasswordValid($user, 'newStrongPassword'));
|
||||
}
|
||||
}
|
||||
11
vendor/symfony/maker-bundle/templates/resetPassword/twig_check_email.tpl.php
vendored
Normal file
11
vendor/symfony/maker-bundle/templates/resetPassword/twig_check_email.tpl.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Password Reset Email Sent{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<p>
|
||||
If an account matching your email exists, then an email was just sent that contains a link that you can use to reset your password.
|
||||
This link will expire in {{ resetToken.expirationMessageKey|trans(resetToken.expirationMessageData, 'ResetPasswordBundle') }}.
|
||||
</p>
|
||||
<p>If you don't receive an email please check your spam folder or <a href="{{ path('app_forgot_password_request') }}">try again</a>.</p>
|
||||
{% endblock %}
|
||||
9
vendor/symfony/maker-bundle/templates/resetPassword/twig_email.tpl.php
vendored
Normal file
9
vendor/symfony/maker-bundle/templates/resetPassword/twig_email.tpl.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<h1>Hi!</h1>
|
||||
|
||||
<p>To reset your password, please visit the following link</p>
|
||||
|
||||
<a href="{{ url('app_reset_password', {token: resetToken.token}) }}">{{ url('app_reset_password', {token: resetToken.token}) }}</a>
|
||||
|
||||
<p>This link will expire in {{ resetToken.expirationMessageKey|trans(resetToken.expirationMessageData, 'ResetPasswordBundle') }}.</p>
|
||||
|
||||
<p>Cheers!</p>
|
||||
22
vendor/symfony/maker-bundle/templates/resetPassword/twig_request.tpl.php
vendored
Normal file
22
vendor/symfony/maker-bundle/templates/resetPassword/twig_request.tpl.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Reset your password{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% for flash_error in app.flashes('reset_password_error') %}
|
||||
<div class="alert alert-danger" role="alert">{{ flash_error }}</div>
|
||||
{% endfor %}
|
||||
<h1>Reset your password</h1>
|
||||
|
||||
{{ form_start(requestForm) }}
|
||||
{{ form_row(requestForm.<?= $email_field ?>) }}
|
||||
<div>
|
||||
<small>
|
||||
Enter your email address, and we will send you a
|
||||
link to reset your password.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary">Send password reset email</button>
|
||||
{{ form_end(requestForm) }}
|
||||
{% endblock %}
|
||||
12
vendor/symfony/maker-bundle/templates/resetPassword/twig_reset.tpl.php
vendored
Normal file
12
vendor/symfony/maker-bundle/templates/resetPassword/twig_reset.tpl.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Reset your password{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>Reset your password</h1>
|
||||
|
||||
{{ form_start(resetForm) }}
|
||||
{{ form_row(resetForm.plainPassword) }}
|
||||
<button class="btn btn-primary">Reset password</button>
|
||||
{{ form_end(resetForm) }}
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user