<?php
namespace App\Controller;
use App\Event\AppEvents;
use App\Event\UserResetPasswordEvent;
use App\Model\Form\ChangePasswordForm;
use App\Model\Form\ResetPasswordForm;
use App\Repository\UserRepository;
use DateTime;
use App\Entity\User;
use App\ValueObject\SearchCriteria\UserSearchCriteria;
use Doctrine\ORM\EntityManagerInterface;
use Ecommerce121\UtilBundle\Controller\ControllerBase;
use Ecommerce121\UtilBundle\Controller\ControllerUtil;
use Exception;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
/**
* Class with version related actions.
*/
class SecurityController extends ControllerBase
{
/**
* @var AuthenticationUtils
*/
private $authenticationUtils;
/**
* @var PasswordHasherFactoryInterface
*/
private $encoderFactory;
private EntityManagerInterface $entityManager;
private UserRepository $userRepository;
/**
* Constructor.
*
* @param ControllerUtil $controllerUtil
* @param AuthenticationUtils $authenticationUtils
* @param PasswordHasherFactoryInterface $encoderFactory
*/
public function __construct(
ControllerUtil $controllerUtil,
AuthenticationUtils $authenticationUtils,
PasswordHasherFactoryInterface $encoderFactory,
EntityManagerInterface $entityManager,
UserRepository $userRepository
) {
parent::__construct($controllerUtil);
$this->authenticationUtils = $authenticationUtils;
$this->encoderFactory = $encoderFactory;
$this->entityManager = $entityManager;
$this->userRepository = $userRepository;
}
/**
* @Route("/login", name="app_login")
* @Template("Security/login.html.twig")
*/
public function loginAction()
{
if ($this->getUser() instanceof User) {
$dashboardPath = $this->generateUrl('app_home');
return $this->redirect($dashboardPath);
}
return [
// last username entered by the user (if any)
'last_username' => $this->authenticationUtils->getLastUsername(),
// last authentication error (if any)
'error' => $this->authenticationUtils->getLastAuthenticationError(),
];
}
/**
* @Route("/loginCheck", name="app_login_check")
*
* @throws \Exception
*/
public function loginCheckAction()
{
throw new \RuntimeException('This should never be reached!');
}
/**
* @Route("/logout", name="app_logout")
*
* @throws \Exception
*/
public function logoutAction()
{
throw new \RuntimeException('This should never be reached!');
}
/**
* @Route("/resetPassword", name="app_reset_password")
* @Template("Security/resetPassword.html.twig")
*
* @param Request $request
*
* @return array
*/
public function resetPasswordAction(Request $request)
{
$form = $this->createForm(ResetPasswordForm::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$email = $form->get('email')->getData();
/** @var User $user */
$user = $this->getEntityManager()->getRepository(User::class)->findOneBy(['email' => $email]);
if ($user instanceof User) {
$user->setForgetPasswordValidUntil(new DateTime('+7 days'));
$user->setForgetPasswordCode(uniqid());
$this->getEntityManager()->persist($user);
$this->getEntityManager()->flush();
$this->dispatchEvent(AppEvents::USER_RESET_PASSWORD, new UserResetPasswordEvent($user));
}
}
return [
'form' => $form->createView(),
'formIsValid' => $form->isSubmitted() && $form->isValid(),
];
}
/** use common method */
/**
* @Route("/changePassword/{code}", name="app_change_password")
* @Template("Security/changePassword.html.twig")
*/
public function changePasswordAction($code, Request $request)
{
return $this->handlePasswordChange($code, $request, 'resetPassword');
}
/**
* @Route("/update-password/{code}", name="app_update_user_password")
* @Template("Security/changePassword.html.twig")
*/
public function updateUserPassword($code, Request $request)
{
return $this->handlePasswordChange($code, $request, 'updatePassword');
}
private function handlePasswordChange($code, Request $request, string $passwordAction) {
$this->forward404Unless($code);
/** @var User $user */
$user = $this->userRepository->findOneBy(['forgetPasswordCode' => $code]);
$oldPassword = $user->getPassword();
$homeUrl = $this->generateUrl('app_home');
$updatePassUrl = $this->generateUrl('app_update_user_password', ['code' => $code]);
if (!$user instanceof User) {
/** @var Session $session */
$session = $request->getSession();
$session->getFlashBag()->add(
'message_password',
'You code have been already used or have expired.'
);
return $this->redirect($homeUrl);
}
$form = $this->createForm(ChangePasswordForm::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$encoder = $this->encoderFactory->getPasswordHasher(User::class);
$encodedPassword = $encoder->hash($form['newPassword']->getData(), $user->getSalt());
$user->changePassword($encodedPassword);
$user->setForgetPasswordCode(null);
$user->setForgetPasswordValidUntil(null);
$user->setPasswordValidUntil(new \DateTime('+90 days'));
$session = $request->getSession();
$newPassword = $form['newPassword']->getData();
$sameAsOldPassword = $encoder->verify($oldPassword, $newPassword);
if ($sameAsOldPassword) {
$session->getFlashBag()->add('message_password', 'Please use different password. Old password and new password are same.');
return $this->redirect($updatePassUrl);
}
$this->entityManager->persist($user);
$this->entityManager->flush();
$session = $request->getSession();
$session->getFlashBag()->add('message_password', 'Your password has been updated.');
return $this->redirect($homeUrl);
}
return [
'form' => $form->createView(),
'code' => $code,
'user' => $user,
'updatePassword' => ($passwordAction === 'updatePassword')
];
}
}