<?php
declare(strict_types=1);
namespace App\Bundles\UserBundle\EventListener;
use App\Bundles\OrganizationBundle\Service\UserOrganization\UserOrganizationBinder;
use App\Bundles\UserBundle\Entity\UserInterface;
use App\Bundles\UserBundle\Entity\UserTrial;
use App\Bundles\UserBundle\Service\UserTrial\TrialPeriodProvider;
use App\Platform\Service\AuthenticationTokenProvider;
use App\Platform\Service\SessionProvider;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\Routing\RouterInterface;
#[AsEventListener(event: RequestEvent::class, method: 'onKernelRequest')]
class UserModifierListener
{
public const CHANGE_PASSWORD_PATH_NAME = 'app_password';
public const EMAIL_CONFIRM_PATH = 'app_email_confirm_form';
public const SELECT_ORGANIZATION_PATH = 'app_select_organization';
public const LOGIN_LINK_REDIRECT_PATH_INFO = '/login/redirect';
public function __construct(
private readonly RouterInterface $router,
private readonly SessionProvider $sessionProvider,
private readonly AuthenticationTokenProvider $tokenProvider,
private readonly TrialPeriodProvider $trialPeriodProvider,
) {
}
public function onKernelRequest(RequestEvent $event): void
{
if (!$token = $this->tokenProvider->getToken()) {
return;
}
if (!$user = $token->getUser()) {
return;
}
if ($this->checkLoginLinkRedirect($event->getRequest())) {
return;
}
/** @var UserInterface $user */
if ($user->hasExpiredPassword()) {
$this->changePassword($event);
return;
}
$trial = $this->trialPeriodProvider->provideByUserEmail($user->getEmail());
if (
$this->userHasNotTrialPeriodAndNotVerifiedEmail($user, $trial) ||
$this->isTrialPeriodExpiredAndNotVerifiedEmail($user, $trial)
) {
$this->confirmEmail($event, $trial);
return;
}
$this->bindOrganization($event);
}
private function userHasNotTrialPeriodAndNotVerifiedEmail(UserInterface $user, ?UserTrial $userTrial): bool
{
return !$userTrial && !$user->isVerifiedEmail();
}
private function isTrialPeriodExpiredAndNotVerifiedEmail(UserInterface $user, ?UserTrial $userTrial): bool
{
return ($userTrial && $userTrial->isExpired()) && !$user->isVerifiedEmail();
}
private function checkLoginLinkRedirect(Request $request): bool
{
return $request->getPathInfo() === self::LOGIN_LINK_REDIRECT_PATH_INFO;
}
private function bindOrganization(RequestEvent $event): void
{
$session = $this->sessionProvider->provide($event->getRequest());
if ($session->has(UserOrganizationBinder::SESSION_USER_ORGANIZATION_KEY)) {
return;
}
if ($this->validatePath($event->getRequest(), self::SELECT_ORGANIZATION_PATH)) {
return;
}
$this->setResponse($event, self::SELECT_ORGANIZATION_PATH);
}
private function confirmEmail(RequestEvent $event, ?UserTrial $trial): void
{
if ($this->validatePath($event->getRequest(), self::EMAIL_CONFIRM_PATH)) {
return;
}
if ($trial && !$trial->isExpired()) {
return;
}
$this->setResponse($event, self::EMAIL_CONFIRM_PATH);
}
private function changePassword(RequestEvent $event): void
{
if ($this->validatePath($event->getRequest(), self::CHANGE_PASSWORD_PATH_NAME)) {
return;
}
$this->setResponse($event, self::CHANGE_PASSWORD_PATH_NAME);
}
private function validatePath(Request $request, string $path): bool
{
return (
$path === $request->attributes->get('_route') || !$request->isMethod('GET')
);
}
private function setResponse(RequestEvent $event, string $path): void
{
$url = $this->router->generate($path);
$event->setResponse(new RedirectResponse($url));
}
}