<?php
namespace App\Controller;
use App\Entity\User;
use App\Form\Security\ForgotPasswordForm;
use App\Form\Security\ResetPasswordForm;
use App\Form\Security\SignInForm;
use App\Service\TokenGenerator;
use Carbon\Carbon;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\NonUniqueResultException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
/**
* Class SecurityController.
*
* @Route("", name="security")
*/
class SecurityController extends AbstractController
{
/**
* @var int
*/
private $lockoutPolicy;
public function __construct(int $lockoutPolicy)
{
$this->lockoutPolicy = $lockoutPolicy;
}
/**
* @Route("/sign-in", name=".sign-in")
*/
public function index(AuthenticationUtils $authenticationUtils): Response
{
$utils = $authenticationUtils;
$error = $utils->getLastAuthenticationError();
$last = $utils->getLastUsername();
$attempts = 0;
$user = $this->getDoctrine()->getRepository(User::class)->findUser($last);
if ($user instanceof User) {
$attempts = $this->lockoutPolicy - $user->getFailures();
if (4 === $attempts) {
$attempts = null;
}
if (0 === $attempts) {
$error = new CustomUserMessageAuthenticationException(
'account.locked'
);
}
}
$form = $this->createForm(SignInForm::class, [
'_username' => $last,
]);
return $this->render('security/sign-in.html.twig', [
'form' => $form->createView(),
'error' => $error,
'attempts' => $attempts,
]);
}
/**
* @Route("/forgot-password", name=".forgot-password")
*/
public function forgotPasswordAction(Request $request, EntityManagerInterface $em, TokenGenerator $generator)
{
$form = $this->createForm(ForgotPasswordForm::class);
$form->handleRequest($request);
if ($form->isSubmitted()) {
$email = $form->get('email')->getData();
$this->addFlash('success', 'Please check your mailbox to continue.');
$user = $em->getRepository(User::class)->findUser($email);
if ($user instanceof User && !$user->isRemoved() && $user->getFailures() < $this->lockoutPolicy) {
$generator->generate($user);
}
return $this->redirectToRoute('security.sign-in');
}
return $this->render('security/forgot-password.html.twig', [
'form' => $form->createView(),
]);
}
/**
* @Route("/{token}/{email}/reset-password", name=".reset-password")
*
* @return Response
*
* @throws NonUniqueResultException
*/
public function resetPasswordAction(Request $request, EntityManagerInterface $em, TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher, string $email, string $token)
{
$now = Carbon::now();
/** @var User $user */
$user = $em->createQueryBuilder()
->from(User::class, 'user')
->select('user')
->where('user.email = :email')
->andWhere('user.token.string = :token')
->andWhere('user.token.expires >= :now')
->andWhere('user.removed = FALSE')
->andWhere('user.failures < :lockout')
->setParameters(['email' => $email, 'token' => $token, 'now' => $now, 'lockout' => $this->lockoutPolicy])
->setMaxResults(1)
->getQuery()->getOneOrNullResult();
if (!$user instanceof User) {
throw $this->createNotFoundException();
}
$form = $this->createForm(ResetPasswordForm::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$password = $form->get('password')->getData();
$user->setPlainPassword($password);
$user->getToken()->reset();
$em->flush();
$token = new UsernamePasswordToken($user, $password, 'main', $user->getRoles());
$tokenStorage->setToken($token);
$event = new InteractiveLoginEvent($request, $token);
$eventDispatcher->dispatch($event);
$this->addFlash('success', 'Your password was successfully updated.');
return $this->redirectToRoute('dashboard');
}
return $this->render('security/reset-password.html.twig', [
'form' => $form->createView(),
'email' => $email,
'token' => $token,
]);
}
/**
* @Route("/sign-out", name=".sign-out")
*/
public function signOutAction()
{
throw new \Exception('');
}
}