<?php
namespace App\Controller;
use App\Entity\Data\User;
use App\EventSubscriber\UserSecurityCheckSubscriber;
use App\Form\LoginSecurityCheckType;
use App\Form\RecoverPasswordNewType;
use App\Form\RecoverPasswordRequestType;
use App\Form\RegisterSecondType;
use App\Form\RegisterThirdType;
use App\Form\RegisterType;
use App\Service\ActionAttemptManager;
use App\Service\Emailer;
use App\Service\Helper;
use App\Service\SmsManager;
use App\Service\UserManager;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Contracts\Translation\TranslatorInterface;
class AuthController extends AbstractController
{
/**
* @Route("/login", name="login")
*/
public function login(AuthenticationUtils $authenticationUtils, Request $request, ActionAttemptManager $actionAttempt): Response
{
$actionAllowed = $actionAttempt->isActionAllowed(ActionAttemptManager::ACTION_LOGIN);
$captchaRequired = $actionAllowed === 'captcha';
if ($captchaRequired) {
$request->getSession()->set('captcha_whitelist_key', ['login']);
}
$error = $authenticationUtils->getLastAuthenticationError();
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('auth/login.html.twig', [
'last_username' => $lastUsername,
'error' => $error,
'captcha_key' => $captchaRequired ? 'login' : null,
'login_blocked' => $actionAllowed === false
]);
}
/**
* @Route("/login/security/check", name="login_security_check")
*/
public function loginSecurityCheck(Request $request, ActionAttemptManager $actionAttempt): Response
{
$actionAllowed = $actionAttempt->isActionAllowed(ActionAttemptManager::ACTION_LOGIN);
$user = $this->getUser();
$form = $this->createForm(LoginSecurityCheckType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$request->getSession()->set(UserSecurityCheckSubscriber::SESSION_KEY, true);
return $this->redirectToRoute('home');
}
return $this->render('auth/loginSecurityCheck.html.twig', [
'form' => $form->createView(),
'login_blocked' => $actionAllowed === false
]);
}
/**
* @Route("/register/email/resend/{email}/{code}", name="register_email_resend")
*/
public function registerEmailResend(Request $request, ActionAttemptManager $actionAttempt, Emailer $emailer, string $email, string $code): Response
{
$actionAllowed = $actionAttempt->isActionAllowed(ActionAttemptManager::ACTION_LOGIN);
$em = $this->getDoctrine()->getManager();
$email = User::urlEmailDecode($email);
/** @var User $user */
$user = $em->getRepository(User::class)->findOneBy([
'email' => $email,
'emailResendRequestCode' => $code,
'isEmailConfirmed' => false
]);
$datNow = new \DateTime();
if ($user === null || $datNow > $user->getEmailResendRequestCodeExpiryOn()) {
$this->addFlash('error', 'recover_password.token_wrong');
return $this->redirectToRoute('login');
}
$user->prepareEmailConfirmation();
$user->setEmailResendRequestCode(null);
$user->setEmailResendRequestCodeExpiryOn(null);
$em->flush();
$this->sendConfirmationEmail($user, $emailer);
$this->addFlash('notice', 'registration.complete.1.msg.1');
$actionAttempt->resetAction(ActionAttemptManager::ACTION_LOGIN);
return $this->redirectToRoute('login');
}
/**
* @Route("/register", name="register")
*/
public function register(Request $request, ActionAttemptManager $actionAttempt, UserManager $userManager, UserPasswordEncoderInterface $passwordEncoder): Response
{
// $this->addFlash('notice', 'Testing colors');
$actionAllowed = $actionAttempt->isActionAllowed(ActionAttemptManager::ACTION_REGISTER);
$user = new User();
$form = $this->createForm(RegisterType::class, $user);
$form->handleRequest($request);
// $this->addFlash('notice', 'registration.complete.1.msg.1');
if ($form->isSubmitted() && $form->isValid() && $actionAllowed) {
$em = $this->getDoctrine()->getManager();
// check if PRE_USER exists and delete it
$userDb = $em->getRepository(User::class)->findOneByEmail($user->getEmail());
if ($userDb !== null && $userDb->hasRole('PRE_USER')) {
$em->remove($userDb);
$em->flush();
}
// encode password
$user->setPassword($passwordEncoder->encodePassword($user, $user->getNewPassword()));
$user->setEmail(strtolower($user->getEmail()));
// generate email confirmation code
$user->prepareEmailConfirmation();
// add PRE_USER role
$user->addRole('ROLE_PRE_USER');
// save user in db
$em->persist($user);
$em->flush();
$userManager->sendUserConfirmationEmail($user);
$this->addFlash('notice', 'registration.complete.1.msg.1');
$request->getSession()->set('registerOk', true);
return $this->redirectToRoute('register');
}
return $this->render('auth/register.html.twig', [
'action_blocked' => !$actionAllowed,
'form' => $form->createView()
]);
}
/**
* @Route("/register/activate/{email}/{code}", name="register_activate")
*/
public function registerActivate(Request $request, ActionAttemptManager $actionAttempt, UserPasswordEncoderInterface $passwordEncoder, TranslatorInterface $translator, SmsManager $smsManager, $email, $code): Response
{
$actionAllowed = $actionAttempt->isActionAllowed(ActionAttemptManager::ACTION_LOGIN);
$email = User::urlEmailDecode($email);
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository(User::class)->findOneBy(['email' => $email, 'emailConfirmationCode' => $code]);
$datNow = new \DateTime();
if ($user === null || $user->getIsEmailConfirmed() || $user->getEmailConfirmationExpiryOn() < $datNow) {
throw $this->createNotFoundException();
}
if ($user->hasRole('PRE_USER')) {
$user->setLocale($request->getLocale());
$form = $this->createForm(RegisterSecondType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid() && $actionAllowed) {
$user->setSecurityAnswer1($passwordEncoder->encodePassword($user, strtolower($user->getNewSecurityAnswer1())));
$user->setSecurityAnswer2($passwordEncoder->encodePassword($user, strtolower($user->getNewSecurityAnswer2())));
$user->setEmailAlternative(strtolower($user->getEmailAlternative()));
$otp = Helper::randomNumber();
$user->setOtp($otp);
$em->flush();
// send sms
$smsMsg = $translator->trans('otp.registration') . ' ' . $user->getOtp();
$smsManager->sendOtp($user->getMobile(), $smsMsg, $user->getOtp());
return $this->redirectToRoute('register_activate2', ['email' => $user->getUrlEmail(), 'code' => $code]);
}
return $this->render('auth/register2.html.twig', [
'action_blocked' => !$actionAllowed,
'form' => $form->createView()
]);
}
$user->setIsEmailConfirmed(true);
$em->flush();
$this->addFlash('notice', 'registration.complete.2.msg.1');
return $this->redirectToRoute('login');
}
/**
* @Route("/register/activate/{email}/{code}/2", name="register_activate2")
*/
public function registerActivate2(Request $request, ActionAttemptManager $actionAttempt, UserPasswordEncoderInterface $passwordEncoder, $email, $code): Response
{
$actionAllowed = $actionAttempt->isActionAllowed(ActionAttemptManager::ACTION_LOGIN);
$email = User::urlEmailDecode($email);
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository(User::class)->findOneBy(['email' => $email, 'emailConfirmationCode' => $code]);
if ($user === null) {
throw $this->createNotFoundException();
}
$datNow = new \DateTime();
if ($user->getEmailConfirmationExpiryOn() < $datNow || !$user->hasRole('PRE_USER')) {
throw $this->createNotFoundException();
}
$user->setLocale($request->getLocale());
$form = $this->createForm(RegisterThirdType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid() && $actionAllowed) {
$user->removeRole('PRE_USER');
$user->addRole('FULL_USER');
$user->setIsEmailConfirmed(true);
$em->flush();
$this->addFlash('notice', 'registration.complete.2.msg.1');
return $this->redirectToRoute('login');
}
return $this->render('auth/register3.html.twig', [
'action_blocked' => !$actionAllowed,
'form' => $form->createView()
]);
}
/**
* @Route("/recover/password/request", name="password_recover")
*/
public function passwordRecoverRequest(Request $request, ActionAttemptManager $actionAttempt, Emailer $emailer, UrlGeneratorInterface $urlGenerator): Response
{
$actionAllowed = $actionAttempt->isActionAllowed(ActionAttemptManager::ACTION_RECOVER_PASSWORD);
$form = $this->createForm(RecoverPasswordRequestType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$email = strtolower($form->get('email')->getData());
$user = $em->getRepository(User::class)->findOneByEmail($email);
$isAltEmail = false;
if ($user === null) {
$user = $em->getRepository(User::class)->findOneByEmailAlternative($email);
$isAltEmail = true;
}
if ($user !== null) {
$user->setRecoverPasswordCode(Helper::generateString());
$datNow = new \DateTime();
$datNow->add(new \DateInterval('PT4H'));
$user->setRecoverPasswordCodeExpiryOn($datNow);
$em->flush();
$url = $urlGenerator->generate('password_recover_process', [ 'code' => $user->getRecoverPasswordCode(), 'email' => $user->getUrlEmail() ], UrlGeneratorInterface::ABSOLUTE_URL);
$emailer->sendRecoverPassword($user, $url, $isAltEmail);
}
$this->addFlash('notice', 'recover_password.request_completed');
return $this->redirectToRoute('password_recover');
}
return $this->render('auth/recoverPasswordRequest.html.twig', [
'action_blocked' => !$actionAllowed,
'form' => $form->createView()
]);
}
/**
* @Route("/recover/password/process/{email}/{code}", name="password_recover_process")
*/
public function passwordRecoverNew(Request $request, ActionAttemptManager $actionAttempt, UserPasswordEncoderInterface $passwordEncoder, $email, $code): Response
{
$actionAllowed = $actionAttempt->isActionAllowed(ActionAttemptManager::ACTION_RECOVER_PASSWORD);
$email = User::urlEmailDecode($email);
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository(User::class)->findOneBy(['email' => $email, 'recoverPasswordCode' => $code]);
if ($user === null) {
$user = $em->getRepository(User::class)->findOneBy(['emailAlternative' => $email, 'recoverPasswordCode' => $code]);
}
$datNow = new \DateTime();
if ($user === null || $user->getRecoverPasswordCodeExpiryOn() === null || $user->getRecoverPasswordCodeExpiryOn() < $datNow) {
$this->addFlash('notice', 'recover_password.token_wrong');
return $this->redirectToRoute('password_recover');
}
$form = $this->createForm(RecoverPasswordNewType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($user->getNewPassword() !== null && $passwordEncoder->isPasswordValid($user, $user->getNewPassword())) {
$form->get('newPassword')->get('first')->addError(new FormError('validate.is_previous_password'));
}
}
if ($form->isSubmitted() && $form->isValid()) {
$encPassword = $passwordEncoder->encodePassword($user, $user->getNewPassword());
$user->setPassword($encPassword);
$user->setRecoverPasswordCodeExpiryOn(null);
$user->setRecoverPasswordCode(null);
$em->flush();
$this->addFlash('notice', 'recover_password.completed');
return $this->redirectToRoute('login');
}
return $this->render('auth/recoverPasswordNew.html.twig', [
'action_blocked' => !$actionAllowed,
'form' => $form->createView()
]);
}
/**
* @Route("/logout", name="logout")
*/
public function logout()
{
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}
}