parent
a7e977fa94
commit
e813480de5
@ -1,177 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace App\Controller; |
|
||||||
|
|
||||||
use App\Entity\User; |
|
||||||
use App\Form\ChangePasswordFormType; |
|
||||||
use App\Form\ResetPasswordRequestFormType; |
|
||||||
use Symfony\Bridge\Twig\Mime\TemplatedEmail; |
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
|
||||||
use Symfony\Component\HttpFoundation\RedirectResponse; |
|
||||||
use Symfony\Component\HttpFoundation\Request; |
|
||||||
use Symfony\Component\HttpFoundation\Response; |
|
||||||
use Symfony\Component\Mailer\MailerInterface; |
|
||||||
use Symfony\Component\Mime\Address; |
|
||||||
use Symfony\Component\Routing\Annotation\Route; |
|
||||||
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; |
|
||||||
use SymfonyCasts\Bundle\ResetPassword\Controller\ResetPasswordControllerTrait; |
|
||||||
use SymfonyCasts\Bundle\ResetPassword\Exception\ResetPasswordExceptionInterface; |
|
||||||
use SymfonyCasts\Bundle\ResetPassword\ResetPasswordHelperInterface; |
|
||||||
|
|
||||||
/** |
|
||||||
* @Route("/reset-password") |
|
||||||
*/ |
|
||||||
class ResetPasswordController extends AbstractController |
|
||||||
{ |
|
||||||
use ResetPasswordControllerTrait; |
|
||||||
|
|
||||||
private $resetPasswordHelper; |
|
||||||
|
|
||||||
public function __construct(ResetPasswordHelperInterface $resetPasswordHelper) |
|
||||||
{ |
|
||||||
$this->resetPasswordHelper = $resetPasswordHelper; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Display & process form to request a password reset. |
|
||||||
* |
|
||||||
* @Route("", name="app_forgot_password_request") |
|
||||||
*/ |
|
||||||
public function request(Request $request, MailerInterface $mailer): Response |
|
||||||
{ |
|
||||||
$form = $this->createForm(ResetPasswordRequestFormType::class); |
|
||||||
$form->handleRequest($request); |
|
||||||
|
|
||||||
if ($form->isSubmitted() && $form->isValid()) { |
|
||||||
return $this->processSendingPasswordResetEmail( |
|
||||||
$form->get('email')->getData(), |
|
||||||
$mailer |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
return $this->render('reset_password/request.html.twig', [ |
|
||||||
'requestForm' => $form->createView(), |
|
||||||
]); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 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, UserPasswordEncoderInterface $passwordEncoder, 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 { |
|
||||||
$user = $this->resetPasswordHelper->validateTokenAndFetchUser($token); |
|
||||||
} catch (ResetPasswordExceptionInterface $e) { |
|
||||||
$this->addFlash('reset_password_error', sprintf( |
|
||||||
'There was a problem validating your reset request - %s', |
|
||||||
$e->getReason() |
|
||||||
)); |
|
||||||
|
|
||||||
return $this->redirectToRoute('app_forgot_password_request'); |
|
||||||
} |
|
||||||
|
|
||||||
// The token is valid; allow the user to change their password. |
|
||||||
$form = $this->createForm(ChangePasswordFormType::class); |
|
||||||
$form->handleRequest($request); |
|
||||||
|
|
||||||
if ($form->isSubmitted() && $form->isValid()) { |
|
||||||
// A password reset token should be used only once, remove it. |
|
||||||
$this->resetPasswordHelper->removeResetRequest($token); |
|
||||||
|
|
||||||
// Encode the plain password, and set it. |
|
||||||
$encodedPassword = $passwordEncoder->encodePassword( |
|
||||||
$user, |
|
||||||
$form->get('plainPassword')->getData() |
|
||||||
); |
|
||||||
|
|
||||||
$user->setPassword($encodedPassword); |
|
||||||
$this->getDoctrine()->getManager()->flush(); |
|
||||||
|
|
||||||
// The session is cleaned up after the password has been changed. |
|
||||||
$this->cleanSessionAfterReset(); |
|
||||||
|
|
||||||
return $this->redirectToRoute('app_home'); |
|
||||||
} |
|
||||||
|
|
||||||
return $this->render('reset_password/reset.html.twig', [ |
|
||||||
'resetForm' => $form->createView(), |
|
||||||
]); |
|
||||||
} |
|
||||||
|
|
||||||
private function processSendingPasswordResetEmail(string $emailFormData, MailerInterface $mailer): RedirectResponse |
|
||||||
{ |
|
||||||
$user = $this->getDoctrine()->getRepository(User::class)->findOneBy([ |
|
||||||
'email' => $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( |
|
||||||
// 'There was a problem handling your password reset request - %s', |
|
||||||
// $e->getReason() |
|
||||||
// )); |
|
||||||
|
|
||||||
return $this->redirectToRoute('app_check_email'); |
|
||||||
} |
|
||||||
|
|
||||||
$email = (new TemplatedEmail()) |
|
||||||
->from(new Address('k.jouini@tyr.local', 'SfyCAS Mail Bot')) |
|
||||||
->to($user->getEmail()) |
|
||||||
->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'); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,19 +1,86 @@ |
|||||||
<!DOCTYPE html> |
<!doctype html> |
||||||
<html> |
<html lang="en"> |
||||||
<head> |
<head> |
||||||
<meta charset="UTF-8"> |
<meta charset="utf-8"> |
||||||
<title>{% block title %}Welcome!{% endblock %}</title> |
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
||||||
{# Run `composer require symfony/webpack-encore-bundle` |
|
||||||
and uncomment the following Encore helpers to start using Symfony UX #} |
<title>{% block title %}Welcome!{% endblock %}</title> |
||||||
{% block stylesheets %} |
|
||||||
{{ encore_entry_link_tags('app') }} |
{% block stylesheets %} |
||||||
{% endblock %} |
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,500,500i,600,600i&display=swap" rel="stylesheet" /> |
||||||
|
{{ encore_entry_link_tags('app') }} |
||||||
{% block javascripts %} |
{% endblock %} |
||||||
{{ encore_entry_script_tags('app') }} |
</head> |
||||||
{% endblock %} |
<body> |
||||||
</head> |
<header class="header"> |
||||||
<body> |
<h1 class="sr-only"> |
||||||
{% block body %}{% endblock %} |
Symfony sandbox |
||||||
</body> |
</h1> |
||||||
|
|
||||||
|
<nav class="navbar navbar-expand-xl navbar-light bg-light"> |
||||||
|
<div class="container mt-4 mb-3"> |
||||||
|
<a class="navbar-brand mr-4 pr-2" href="{{ path('app_home') }}"> |
||||||
|
📙 Home |
||||||
|
</a> |
||||||
|
|
||||||
|
<button class="navbar-toggler border-0" type="button" data-toggle="collapse" data-target="#header-menu" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Show/Hide navigation"> |
||||||
|
<span class="navbar-toggler-icon"></span> |
||||||
|
</button> |
||||||
|
|
||||||
|
<div class="collapse navbar-collapse" id="header-menu"> |
||||||
|
<ul class="navbar-nav ml-auto"> |
||||||
|
<li class="nav-item mr-3"> |
||||||
|
<a class="nav-link" href="{{ path('app_home') }}"> |
||||||
|
Import |
||||||
|
</a> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="collapse navbar-collapse" id="header-menu"> |
||||||
|
<ul class="navbar-nav ml-auto"> |
||||||
|
<li class="nav-item mr-3"> |
||||||
|
<a class="nav-link" href=""> |
||||||
|
Export |
||||||
|
</a> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="collapse navbar-collapse" id="header-menu"> |
||||||
|
<ul class="navbar-nav ml-auto"> |
||||||
|
<li class="nav-item mr-3"> |
||||||
|
<a class="nav-link" href="{{ path('app_home') }}"> |
||||||
|
Admin |
||||||
|
</a> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</nav> |
||||||
|
|
||||||
|
<nav class="bg-light border-bottom"> |
||||||
|
<div class="container"> |
||||||
|
{# render_esi(path('conference_header')) #} |
||||||
|
</div> |
||||||
|
</nav> |
||||||
|
</header> |
||||||
|
|
||||||
|
<main role="main" class="container mt-5"> |
||||||
|
{% block body %}{% endblock %} |
||||||
|
</main> |
||||||
|
|
||||||
|
<footer class="mt-7 px-3 py-5 text-center text-muted"> |
||||||
|
<p> |
||||||
|
Symfony sandbox |
||||||
|
</p> |
||||||
|
<p> |
||||||
|
<a href="#" class="text-white">Back to top</a> |
||||||
|
</p> |
||||||
|
</footer> |
||||||
|
|
||||||
|
{% block javascripts %} |
||||||
|
{{ encore_entry_script_tags('app') }} |
||||||
|
{% endblock %} |
||||||
|
</body> |
||||||
</html> |
</html> |
||||||
|
|||||||
Loading…
Reference in new issue