<?php
namespace App\EventSubscriber;
use App\Doctrine\HotelFilter;
use App\Entity\Hotel;
use App\Entity\User;
use App\Repository\ComplexRepository;
use App\Repository\HotelRepository;
use App\Service\HotelHelper;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Security;
use function Symfony\Component\String\u;
class HotelRequestSubscriber implements EventSubscriberInterface
{
private Security $security;
private ParameterBagInterface $parameterBag;
private UrlGeneratorInterface $router;
private EntityManagerInterface $em;
private ComplexRepository $complexRepository;
private HotelRepository $hotelRepository;
private HotelHelper $hotelHelper;
private string $baseHost;
private bool $saasCustomInstall;
public function __construct(Security $security, ParameterBagInterface $parameterBag, UrlGeneratorInterface $router, EntityManagerInterface $em, ComplexRepository $complexRepository, HotelRepository $hotelRepository, HotelHelper $hotelHelper, string $baseHost, bool $saasCustomInstall)
{
$this->security = $security;
$this->parameterBag = $parameterBag;
$this->router = $router;
$this->em = $em;
$this->complexRepository = $complexRepository;
$this->hotelRepository = $hotelRepository;
$this->hotelHelper = $hotelHelper;
$this->baseHost = $baseHost;
$this->saasCustomInstall = $saasCustomInstall;
}
public function onKernelRequest(RequestEvent $event)
{
$request = $event->getRequest();
$route = $request->attributes->get('_route');
// Go ahead if it's a profiler route in dev mode
$route = u($route);
$isDev = $this->parameterBag->get('kernel.environment') === 'dev';
$isProfilerRoute = $route->startsWith('_wdt') || $route->startsWith('_profiler') || $request->getPathInfo() === '/_fragment';
$isErrorRoute = $route->startsWith('_error');
if ($isDev && ($isProfilerRoute || $isErrorRoute)) {
return;
}
/** @var HotelFilter $filter */
$filter = $this->em->getFilters()->getFilter('hotelfilter');
// We have to disable filter to allow get hotel without interferences
$filter->disableForEntity(Hotel::class);
/** @var User $user */
$user = $this->security->getUser();
$host = $request->getHttpHost();
$isAdminRoute = $route->startsWith('admin_') && !$route->containsAny(['login', 'logout']);
$isGuestRoute = $route->startsWith('app_') && !$route->containsAny(['login', 'logout']);
$isAdminLoginOrLogoutRoute = $route->startsWith('admin_') && $route->containsAny(['login', 'logout']);
$isGuestLoginOrLogoutRoute = $route->startsWith('app_') && $route->containsAny(['login', 'logout']);
$subdomain = str_replace('.' . $this->baseHost, '', $host);
$complex = $this->complexRepository->findOneBy(['subdomain' => $subdomain]);
$hotel = $this->hotelRepository->findOneBy(['subdomain' => $subdomain]);
// Avoid infinite redirect loops
if ($isAdminLoginOrLogoutRoute) {
return;
}
// Redirect base route to admin if using complex subdomain
if ($isGuestLoginOrLogoutRoute && $complex) {
$event->setResponse(new RedirectResponse($this->router->generate('admin_login', [], UrlGeneratorInterface::ABSOLUTE_URL)));
return;
}
// Admin domain
if ($host === 'admin.' . $this->baseHost) {
if (!$this->saasCustomInstall && $isGuestRoute) {
throw new AccessDeniedException();
}
if (!$user || !$isAdminRoute) {
return;
}
if (!$this->security->isGranted('ROLE_SUPER_ADMIN')) {
throw new AccessDeniedException();
}
return;
}
// SaaS domain
if ($host === $this->baseHost) {
if (!$this->saasCustomInstall && $isGuestRoute) {
throw new AccessDeniedException();
}
if (!$user || !$isAdminRoute) {
return;
}
if (!$this->security->isGranted('ROLE_ADMIN')) {
throw new AccessDeniedException();
}
return;
}
// Complex and hotel subdomains
if ($complex) {
$hotel = $this->hotelHelper->getCurrentHotel($user, $complex);
if (!$complex->getHotels()->contains($hotel)) {
throw new AccessDeniedException();
}
if (!$hotel) {
$hotel = $complex->getHotels()->first();
if (!$hotel) {
throw new AccessDeniedException();
}
}
}
if (!$hotel) {
throw new NotFoundHttpException(sprintf('Subdomain %s does not exist in %s.', $subdomain, $this->baseHost));
}
if ($user && $isAdminRoute && !$this->hotelHelper->userCanAccessToHotel($user, $hotel)) {
throw new AccessDeniedException();
}
$this->hotelHelper->setCurrentHotel($hotel);
$filter->enableForEntity(Hotel::class);
}
public static function getSubscribedEvents(): array
{
return [
// Priority default is 0. This must be lower than 8 because this is the priority for get user in security service.
KernelEvents::REQUEST => [['onKernelRequest', 0]],
];
}
}