<?php
namespace App\Controller;
use App\Constants\MailEventConstants;
use App\Constants\PageTypeConstant;
use App\Constants\UserIpConstants;
use App\Entity\ResetPasswordRequest;
use App\Entity\Tariff;
use App\Entity\User;
use App\Service\KyivstarService;
use App\Service\MailService;
use App\Service\UserIpService;
use App\Repository\BlacklistedDomainRepository;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Http\Authentication\UserAuthenticatorInterface;
use Symfony\Component\Security\Http\Authenticator\FormLoginAuthenticator;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class RegistrationController extends AbstractController
{
public function __construct(
private FormLoginAuthenticator $authenticator
) {
}
#[Route(path: '/register', name: 'registration_page', methods: ["get", "post"])]
public function registerAction(Request $request, ManagerRegistry $managerRegistry, ValidatorInterface $validator, MailService $mailService, UserAuthenticatorInterface $userAuthenticator, UserIpService $userIpService, KyivstarService $kyivstarService, TranslatorInterface $translator): Response
{
if ($this->getUser()) {
return $this->redirectToRoute('index_page');
}
$lifecellSelected = $request->cookies->get('lifecellSelected');
$tariffId = $request->cookies->get('tariffSelected');
if (!$tariffId && !$lifecellSelected) {
return $this->redirectToRoute('index_page');
}
$tariff = null;
if (!$lifecellSelected) {
$tariff = $managerRegistry->getRepository(Tariff::class)->getOneByCompany($tariffId, $request->get('companyInner'));
if (!$tariff) {
$response = $this->redirectToRoute('index_page');
$response->headers->clearCookie('tariffSelected');
return $response;
}
}
$submittedToken = $request->request->get('_token');
$company = $request->get('companyInner');
$errors = [];
if ($request->getMethod() == 'POST' && $this->isCsrfTokenValid('registration', $submittedToken)) {
$email = $request->get('email');
$now = new \DateTime();
$user = $managerRegistry->getRepository(User::class)->findOneBy(['email'=>$email]);
$newUser = false;
$userIpService->createUserIpData(UserIpConstants::REGISTER, $request, ['email' => $email]);
if ($user && $user->hasCompany($company)) {
return $this->render('pages/register/register.html.twig', [
'errors' => $errors,
'page' => PageTypeConstant::REGISTRATION,
'showLoginPopup' => true,
'tariff' => $tariff,
'email' => $request->get('email')
]);
}
if (!$user) {
$newUser = true;
$user = new User();
$user->setEmail($email);
$user->setUsername($email);
$code = rand(10000, 99999);
$user->setVerifyCode($code);
$user->setCodeSentAt($now);
$user->addRole('ROLE_FRONT_USER');
$password = bin2hex(openssl_random_pseudo_bytes(4));
$user->setPlainPassword($password);
$user->setEnabled(true);
}
$user->addCompany($company);
$validatorErrors = $validator->validate($user);
if (count($validatorErrors) == 0) {
if ($newUser) {
$domain = explode('@', $email);
if (count($domain) > 1) {
$domain = end($domain);
$forbiddenDomains = $kyivstarService->getForbiddenDomains();
if (!empty($forbiddenDomains)) {
if (in_array($domain, $forbiddenDomains)) {
$errors['email'] = $translator->trans('email_is_forbidden', [], 'validators');
$this->addFlash('error', $translator->trans('email_is_forbidden', [], 'validators'));
return $this->render('pages/register/register.html.twig', [
'errors' => $errors,
'page' => PageTypeConstant::REGISTRATION,
'tariff' => $tariff,
'email' => $request->get('email')
]);
}
}
$domainRepository = $managerRegistry->getRepository(\App\Entity\BlacklistedDomain::class);
if ($domainRepository->isDomainBlacklisted($domain)) {
$mailService->sendEmailToUserByCompany(MailEventConstants::REGISTRATION_BLACKLISTED, $user, null, ['http_host' => $request->getSchemeAndHttpHost()]);
$errors['email'] = $translator->trans('email_is_forbidden', [], 'validators');
$this->addFlash('error', $translator->trans('email_is_forbidden', [], 'validators'));
return $this->render('pages/register/register.html.twig', [
'errors' => $errors,
'page' => PageTypeConstant::REGISTRATION,
'tariff' => $tariff,
'email' => $request->get('email')
]);
}
}
}
$manager = $managerRegistry->getManager();
$manager->persist($user);
$manager->flush($user);
$userAuthenticator->authenticateUser(
$user,
$this->authenticator,
$request
);
if (!$user->getEmailVerifiedAt()) {
if ($newUser) {
$mailService->sendEmailToUserByCompany(MailEventConstants::REGISTRATION, $user, null, ['http_host' => $request->getSchemeAndHttpHost(), 'user_password' => $password]);
}
$response = $this->redirectToRoute('email_verification_page');
return $response;
}
if ($lifecellSelected) {
$response = $this->redirectToRoute('lifecell_phones_page', ['slug' => $lifecellSelected]);
} else {
if (!$tariffId) {
return $this->redirectToRoute('index_page');
}
$response = $this->redirectToRoute('tariff_phones_page', ['tariff' => $tariffId]);
}
$response->headers->clearCookie('tariffSelected');
$response->headers->clearCookie('lifecellSelected');
return $response;
} else {
foreach ($validatorErrors as $error) {
$errors[$error->getPropertyPath()] = $error->getMessage();
}
}
}
return $this->render('pages/register/register.html.twig', [
'errors' => $errors,
'page' => PageTypeConstant::REGISTRATION,
'tariff' => $tariff,
'email' => $request->get('email')
]);
}
#[Route(path: '/password/reset', name: 'password_reset_page', methods: ["get", "post"])]
public function passwordResetLinkAction(Request $request, ManagerRegistry $managerRegistry, MailService $mailService, TranslatorInterface $translator): Response
{
$submittedToken = $request->request->get('_token');
$errors = [];
if ($request->getMethod() == 'POST' && $this->isCsrfTokenValid('password-reset', $submittedToken)) {
$email = $request->get('email');
$manager = $managerRegistry->getManager();
$user = $manager->getRepository(User::class)->getOneByCompany($email, $request->get('companyInner'));
if ($user) {
$resetUser = $managerRegistry->getRepository(ResetPasswordRequest::class)->findOneBy(['email'=>$email]);
$now = new \DateTime();
if ($resetUser) {
$lastResetRequest = $resetUser->getCreatedAt();
$resendPeriod = PageTypeConstant::PASSWORD_RESET_PERIOD;
$lastResetRequest->modify("+{$resendPeriod} minutes");
if ($now < $lastResetRequest) {
$dateDiff = $now->diff($lastResetRequest);
$minutes = $dateDiff->days * 24 * 60;
$minutes += $dateDiff->h * 60;
$minutes += $dateDiff->i;
$seconds = $dateDiff->s;
$seconds = sprintf("%02d", $seconds);
$errors['email'] = $translator->trans('password_can_be_reset_after', ['%time%' => $minutes.':'.$seconds], 'validators');
return $this->render('pages/password_reset/password_reset.html.twig', [
'page' => PageTypeConstant::PASSWORD_RESET_PAGE,
'errors' => $errors,
'email' => $request->get('email')
]);
}
} else {
$resetUser = new ResetPasswordRequest();
$resetUser->setEmail($email);
}
$resetUser->setIp($request->getClientIp());
$resetUser->setCreatedAt($now);
$manager->persist($resetUser);
$manager->flush($resetUser);
$token = bin2hex(openssl_random_pseudo_bytes(16));
$user->setResetHash($token);
$manager->flush($user);
$mailService->sendEmailToUserByCompany(MailEventConstants::PASSWORD_RESET_LINK, $user, null, ['http_host' => $request->getSchemeAndHttpHost()]);
$this->addFlash('password_reset_link_success', $user->getEmail());
return $this->redirectToRoute('index_page');
} else {
$errors['email'] = $translator->trans('password_reset_user_not_found', [], 'validators');
}
}
$emailFromLogin = $request->cookies->get('emailToReset');
$clearCookie = false;
if ($emailFromLogin) {
$email = $emailFromLogin;
$clearCookie = true;
} else {
$email = $request->get('email');
}
$response = $this->render('pages/password_reset/password_reset.html.twig', [
'page' => PageTypeConstant::PASSWORD_RESET_PAGE,
'errors' => $errors,
'email' => $email
]);
if ($clearCookie) {
$response->headers->clearCookie('emailToReset');
}
return $response;
}
#[Route(path: '/password/reset/{hash}', name: 'password_reset_link_page', methods: ["get"])]
public function passwordResetAction(Request $request, ManagerRegistry $managerRegistry, MailService $mailService, TranslatorInterface $translator): Response
{
$manager = $managerRegistry->getManager();
$user = $manager->getRepository(User::class)->getOneByCompanyHash($request->get('hash'), $request->get('companyInner'));
if ($user) {
$password = bin2hex(openssl_random_pseudo_bytes(4));
$user->setPlainPassword($password);
$user->setResetHash(null);
$now = new \DateTime();
$user->setEmailVerifiedAt($now);
$user->setVerifyCode(null);
$manager->flush($user);
$mailService->sendEmailToUserByCompany(MailEventConstants::PASSWORD_RESET, $user, null, ['http_host' => $request->getSchemeAndHttpHost(), 'user_password' => $password]);
$this->addFlash('password_reset_success', $user->getEmail());
return $this->redirectToRoute('index_page');
}
throw $this->createNotFoundException();
}
#[Route(path: '/code/resend', name: 'code_resend_page', methods: ["post"])]
public function codeResendAction(Request $request, ManagerRegistry $managerRegistry, MailService $mailService, TranslatorInterface $translator): Response
{
$user = $this->getUser();
if (!$user) {
throw new NotFoundHttpException();
}
$manager = $managerRegistry->getManager();
$now = new \DateTime();
$submittedToken = $request->request->get('_token');
// TODO add verify code to service|helper
if (($codeSentAt = $user->getCodeSentAt()) && $this->isCsrfTokenValid('code-resend', $submittedToken)) {
$resendPeriod = PageTypeConstant::EMAIL_CODE_PERIOD;
$codeSentAt->modify("+{$resendPeriod} minutes");
if ($now < $codeSentAt || $user->getEmailVerifiedAt()) {
$this->addFlash('code_resend_error', $user->getEmail());
} else {
$code = rand(10000, 99999);
$user->setVerifyCode($code);
$user->setCodeSentAt($now);
$manager->flush($user);
$mailService->sendEmailToUserByCompany(MailEventConstants::CODE_RESEND, $user, null, ['http_host' => $request->getSchemeAndHttpHost()]);
$this->addFlash('code_resend_success', $user->getEmail());
}
}
return $this->redirectToRoute('email_verification_page');
}
#[Route(path: '/email-verification', name: 'email_verification_page', methods: ["get", "post"])]
public function emailVerificationAction(Request $request, ManagerRegistry $managerRegistry, TranslatorInterface $translator): Response
{
/* @var $user null|User */
$user = $this->getUser();
if (!$user) {
throw new NotFoundHttpException();
}
$lifecellSelected = $request->cookies->get('lifecellSelected');
$tariffId = $request->cookies->get('tariffSelected');
if ($user->getEmailVerifiedAt()) {
if ($lifecellSelected) {
$response = $this->redirectToRoute('lifecell_phones_page', ['slug' => $lifecellSelected]);
$response->headers->clearCookie('lifecellSelected');
$response->headers->clearCookie('tariffSelected');
} else {
if (!$tariffId) {
return $this->redirectToRoute('index_page');
}
$response = $this->redirectToRoute('tariff_phones_page', ['tariff'=>$tariffId]);
$response->headers->clearCookie('tariffSelected');
}
}
$submittedToken = $request->request->get('_token');
$errors = [];
$now = new \DateTime();
if ($request->getMethod() == 'POST' && $this->isCsrfTokenValid('email-verification', $submittedToken)) {
$code = str_replace('-', '', $request->get('code'));
if ($code == $user->getVerifyCode()) {
$manger = $managerRegistry->getManager();
$user->setEmailVerifiedAt($now);
$user->setVerifyCode(null);
$manger->flush($user);
if ($lifecellSelected) {
$response = $this->redirectToRoute('lifecell_phones_page', ['slug' => $lifecellSelected]);
$response->headers->clearCookie('lifecellSelected');
$response->headers->clearCookie('tariffSelected');
} else {
if (!$tariffId) {
return $this->redirectToRoute('index_page');
}
$response = $this->redirectToRoute('tariff_phones_page', ['tariff' => $tariffId]);
$response->headers->clearCookie('tariffSelected');
}
return $response;
} else {
$errors['code'] = $translator->trans('user_email_code_not_valid', [], 'validators');
}
}
$canRequestCodeAt = null;
if ($codeSentAt = $user->getCodeSentAt()) {
$resendPeriod = PageTypeConstant::EMAIL_CODE_PERIOD;
$codeSentAt->modify("+{$resendPeriod} minutes");
if ($now < $codeSentAt) {
$dateDiff = $now->diff($codeSentAt);
$minutes = $dateDiff->days * 24 * 60;
$minutes += $dateDiff->h * 60;
$minutes += $dateDiff->i;
$canRequestCodeAt = ['minutes' => $minutes, 'seconds' => $dateDiff->s];
}
}
$tariff = null;
if ($tariffId) {
$tariff = $managerRegistry->getRepository(Tariff::class)->find($tariffId);
}
return $this->render('pages/email_verification/email_verification.html.twig', [
'user' => $user,
'tariff' => $tariff,
'canRequestCodeAt' => $canRequestCodeAt,
'errors' => $errors,
'page' => PageTypeConstant::EMAIL_VERIFICATION
]);
}
#[Route(path: '/change-email', name: 'change_email_page')]
public function changeEmailAction(Request $request, ManagerRegistry $managerRegistry, TokenStorageInterface $tokenStorage): Response
{
$user = $this->getUser();
if (!$user) {
$this->redirectToRoute('registration_page');
}
$lifecellSelected = $request->cookies->get('lifecellSelected');
$tariffId = $request->cookies->get('tariffSelected');
if ($lifecellSelected) {
$tokenStorage->setToken();
$response = $this->redirectToRoute('registration_page');
$response->headers->setCookie(new Cookie('lifecellSelected', $lifecellSelected, time() + (365 * 24 * 60 * 60)));
return $response;
} elseif ($tariffId) {
$tokenStorage->setToken();
$response = $this->redirectToRoute('registration_page');
$response->headers->setCookie(new Cookie('tariffSelected', $tariffId, time() + (365 * 24 * 60 * 60)));
return $response;
}
return $this->redirectToRoute('index_page');
}
}