<?php
namespace App\Controller\Main;
use App\Entity\Main\User;
use App\Events\Main\Registration\SecurityEvent;
use App\EventSubscribers\Main\SecuritySubscriber;
use App\Services\CartManager;
use App\Form\Main\UserLoginType;
use App\Services\ReferralProgramManager;
use App\Services\Setup;
use App\Twig\GoogleTagManagerExtension;
use phpDocumentor\Reflection\Types\This;
use Predis\Client;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
/**
* @Route(
* condition="not (context.getHost() matches '%coaching_domain.host.regexp%')"
* )
*/
class SecurityController extends AbstractController
{
use BrulafineControllerTrait;
const
TEMPLATE_NAME_BASE = 'templateBase',
TEMPLATE_NAME_CART = 'templateCart',
ROUTE_OVER = 'over',
ROUTE_FAILED_PAYMENT = 'failed-payment',
ROUTE_OFFER_PREUPSELL = CartManager::OFFER_PREUPSELL,
ROUTE_OFFER_UPSELL = CartManager::OFFER_UPSELL
;
const
AUTOLOGIN_DESTINATION_ROUTES = [
"home" => 'brulafine_home',
"pack" => 'brulafine_show_packs',
"packs" => 'brulafine_show_packs',
"account" => 'brulafine_user_compte',
"order" => 'brulafine_user_commandes',
"orders" => 'brulafine_user_commandes',
"password" => 'user_change_password',
"cyrielle" => 'brulafine_temoignage_interactive',
"testimonies" => 'brulafine_temoignages_no_page',
"testimony" => 'brulafine_temoignages_no_page',
"contact" => "brulafine_contact",
"ingredients" => "brulafine_ingredients",
"ingredient" => "brulafine_ingredients",
"cgv" => "brulafine_cgv",
"avis" => 'brulafine_avis_no_page',
"video" => 'brulafine_temoignage_videos',
"videos" => 'brulafine_temoignage_videos',
self::ROUTE_OVER => 'brulafine_over',
self::ROUTE_FAILED_PAYMENT => 'brulafine_payment',
self::ROUTE_OFFER_PREUPSELL => 'brulafine_offers',
self::ROUTE_OFFER_UPSELL => 'brulafine_offers',
];
const
DESTINATION_ROUTES_USER_REQUIRED = [
"account" => 'brulafine_user_compte',
"order" => 'brulafine_user_commandes',
"orders" => 'brulafine_user_commandes',
"password" => 'user_change_password',
self::ROUTE_OVER => 'brulafine_over',
self::ROUTE_FAILED_PAYMENT => 'brulafine_payment',
self::ROUTE_OFFER_PREUPSELL => 'brulafine_offers',
self::ROUTE_OFFER_UPSELL => 'brulafine_offers',
];
public function __construct(private AuthenticationUtils $authenticationUtils)
{
}
/**
* @param Request $request
*
* @param ReferralProgramManager $referralProgramManager
* @return Response
*/
public function loginAction(Request $request, ReferralProgramManager $referralProgramManager, AuthorizationCheckerInterface $authorizationChecker, AuthenticationUtils $authenticationUtils)
{
/** @var $session \Symfony\Component\HttpFoundation\Session\Session */
$session = $request->getSession();
$setup = $this->getSetup();
if ($authorizationChecker->isGranted('ROLE_USER')) {
if ($referralProgramManager->isReferralProgramEnabled($setup->getUser(), $setup->getSite())) {
return $this->redirectToRoute('brulafine_user_referral_program');
}
return $this->redirectToRoute('brulafine_user_compte');
}
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
$authErrorKey = Security::AUTHENTICATION_ERROR;
// get the error if any (works with forward and redirect -- see below)
if ($request->attributes->has($authErrorKey)) {
$error = $request->attributes->get($authErrorKey);
} elseif (null !== $session && $session->has($authErrorKey)) {
$error = $session->get($authErrorKey);
$session->remove($authErrorKey);
$session->set(GoogleTagManagerExtension::GTM_SESSION_LOGIN_EVENT_KEY, $error->getMessage());
} else {
$error = null;
}
$form = $this->createForm(UserLoginType::class, [
'_username' => $lastUsername,
]);
return $this->render(
$this->getTemplatesDir() . '/Security/views/Login/login.html.twig',
array(
'error' => $error,
'form' => $form->createView(),
'tracking' => $setup->getTracking(),
'site' => $setup->getSite(),
)
);
}
/**
* @param Request $request
* @param $templateName
* @return Response
*/
public function loginPartialAction(Request $request, $templateName = null)
{
/** @var $session \Symfony\Component\HttpFoundation\Session\Session */
$session = $request->getSession();
$authErrorKey = Security::AUTHENTICATION_ERROR;
// get the error if any (works with forward and redirect -- see below)
if ($request->attributes->has($authErrorKey)) {
$error = $request->attributes->get($authErrorKey);
} elseif (null !== $session && $session->has($authErrorKey)) {
$error = $session->get($authErrorKey);
$session->remove($authErrorKey);
$session->set(GoogleTagManagerExtension::GTM_SESSION_LOGIN_EVENT_KEY, $error->getMessage());
} else {
$error = null;
}
if (!$error instanceof AuthenticationException) {
$error = null; // The value does not come from the security component.
}
// last username entered by the user
$lastUsername = $this->authenticationUtils->getLastUsername();
$form = $this->createForm(UserLoginType::class, [
'_username' => $lastUsername,
]);
$setup = $this->getSetup();
return $this->renderLoginPartial([
'error' => $error,
'tracking' => $setup->getTracking(),
'site' => $setup->getSite(),
'form' => $form->createView(),
'templateName' => $templateName
]);
}
/**
* Renders the login template with the given parameters. Overwrite this function in
* an extended controller to provide additional data for the login template.
*
* @param array $data
*
* @return Response
*/
protected function renderLoginPartial(array $data)
{
return $this->render($this->getTemplatesDir() . '/Security/views/Login/login_partial.html.twig', $data);
}
/**
* Renders the login template with the given parameters. Overwrite this function in
* an extended controller to provide additional data for the login template.
*
* @param array $data
*
* @return Response
*/
protected function renderLogin(array $data)
{
return $this->render($this->getTemplatesDir() . '/Security/views/Login/login.html.twig', $data);
}
/**
* @Route(
* "/security/token/login/{loginId}/{loginToken}/{dest}",
* name="brulafine_token_login",
* defaults={"dest": "home", "data"= 0},
* requirements={
* "dest": "home|pack|packs|account|order|orders|password|cyrielle|testimonies|testimony|contact|ingredients|ingredient|cgv|over|failed-payment|offer-preupsell|offer-upsell|video|videos",
* "loginId": "[0-9a-zA-Z]+",
* "loginToken": "[0-9a-zA-Z]+"
* }
* )
* @param Request $request
* @param $loginId
* @param $loginToken
* @param $dest
* @param RouterInterface $router
* @param LoggerInterface $logger
* @param Session $session
* @return Response
*/
public function tokenLoginAction(Request $request, $loginId, $loginToken, $dest, RouterInterface $router, LoggerInterface $logger, Session $session, $redisClient, EventDispatcherInterface $eventDispatcher)
{
//https://brulafine8up.dev.wcc-bg.com/security/token/login/2ba805dbd290855491f49edebfbb8d047f1bed37/9f3d4b64d188883156e08642d37d60caa71eb878e889d/failed-payment
// already logged in users can't access this place
if ($this->getUser() instanceof User) {
$routeToRedirect = self::AUTOLOGIN_DESTINATION_ROUTES[$dest];
if (self::ROUTE_OVER === $dest && $request->query->get('data', null)) {
return $this->redirectToRoute(self::AUTOLOGIN_DESTINATION_ROUTES[$dest], ['shortId' => $request->query->get('data')]);
}
if (self::ROUTE_FAILED_PAYMENT == $dest) {
$request->getSession()->set(CartManager::CHECK_FAILED_PURCHASE_KEY, true);
}
if (in_array($dest, [self::ROUTE_OFFER_PREUPSELL, self::ROUTE_OFFER_UPSELL])) {
$routeToRedirect = $router->generate(self::AUTOLOGIN_DESTINATION_ROUTES[$dest], ['offer' => $dest]);
return new RedirectResponse($routeToRedirect);
}
return $this->redirectToRoute($routeToRedirect);
}
$environment = $this->getParameter("kernel.environment");
$redisKey = ResettingController::AUTOLOGIN_ATTEMPTS_REDIS_KEY . $request->getClientIp();
// prevent brut force attacks by limiting to 50 calls on this address by the same IP.
$redisClient->setex($redisKey, 3600, ((int) $redisClient->get($redisKey) + 1));
if ((int) $redisClient->get($redisKey) > 50 && 'test' != $environment) {
$logger->alert("Someone is trying to bruteforce the autologin url, IP {$request->getClientIp()}, number of tries so far : {$redisClient->get($redisKey)}");
$request->getSession()->set(GoogleTagManagerExtension::GTM_SESSION_LOGIN_EVENT_KEY, "Too many autologin attempts");
throw new NotFoundHttpException();
}
$usersToLogin = $this->getDoctrine()->getRepository(User::class)->getUserFromAutologinToken($loginToken);
if (!count($usersToLogin)) {
$request->getSession()->set(GoogleTagManagerExtension::GTM_SESSION_LOGIN_EVENT_KEY, "autologin token invalid");
throw new NotFoundHttpException();
}
$userToLogin = null;
foreach ($usersToLogin as $loopUser) {
if (!$loopUser instanceof User) {
continue;
}
if ($loopUser->isValidAutoLoginToken($loginId, $loginToken, 45)) {
$userToLogin = $loopUser;
break;
}
}
if (!$userToLogin instanceof User) {
$request->getSession()->set(GoogleTagManagerExtension::GTM_SESSION_LOGIN_EVENT_KEY, "autologin token invalid");
throw new NotFoundHttpException();
}
// check if the user is anonymized. If so he can't login.
if ($userToLogin->isAnonymized()) {
$userToLogin->eraseAutoLoginToken();
$this->getDoctrine()->flush();
throw new NotFoundHttpException();
}
// For Guest users we set the user to the session and don`t log them in.
if ($userToLogin->hasRole(User::ROLE_GUEST)) {
if (key_exists($dest, self::DESTINATION_ROUTES_USER_REQUIRED)) {
$session->set(User::GUEST_USER_SESSION_KEY, $userToLogin->getId());
}
if (self::ROUTE_OVER === $dest && $request->query->get('data', null)) {
$routeToRedirect = $router->generate(self::AUTOLOGIN_DESTINATION_ROUTES[$dest], ['shortId' => $request->query->get('data')]);
} else {
$routeToRedirect = $router->generate(self::AUTOLOGIN_DESTINATION_ROUTES[$dest]);
}
return new RedirectResponse($routeToRedirect);
}
$eventDispatcher->dispatch(
new SecurityEvent($userToLogin, $request),
SecuritySubscriber::USER_LOGGED_IN
);
// 1. new special case, the route over requires the cart id.
// 2. new special case, we redirect to payment page users with failed purchases and allow them PayPal payment option with no limit.
if (self::ROUTE_OVER === $dest && $request->query->get('data', null)) {
$routeToRedirect = $router->generate(self::AUTOLOGIN_DESTINATION_ROUTES[$dest], ['shortId' => $request->query->get('data')]);
} elseif (self::ROUTE_FAILED_PAYMENT === $dest) {
$request->getSession()->set(CartManager::CHECK_FAILED_PURCHASE_KEY, true);
$routeToRedirect = $router->generate(self::AUTOLOGIN_DESTINATION_ROUTES[$dest]);
} elseif (in_array($dest, [self::ROUTE_OFFER_PREUPSELL, self::ROUTE_OFFER_UPSELL])) {
$routeToRedirect = $router->generate(self::AUTOLOGIN_DESTINATION_ROUTES[$dest], ['offer' => $dest]);
} else {
$routeToRedirect = $router->generate(self::AUTOLOGIN_DESTINATION_ROUTES[$dest]);
}
return new RedirectResponse($routeToRedirect);
}
}