<?php
namespace App\EventListener;
use App\Enum\User\UserRestrictionsEnum;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpFoundation\RedirectResponse;
use App\Entity\User;
use App\Entity\Location;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Security;
use App\Service\DatabaseService;
use App\Service\UserAccessService;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
class RequestListener
{
/**
* @var RouterInterface
*/
private $router;
/**
* @var Security
*/
private $security;
/**
* @var DatabaseService
*/
private $databaseService;
/**
* @var UserAccessService
*/
private $userAccessService;
/**
* @var ParameterBagInterface
*/
private $parameterBag;
public function __construct(RouterInterface $router, Security $security, DatabaseService $databaseService, UserAccessService $userAccessService, ParameterBagInterface $parameterBag) {
$this->router = $router;
$this->security = $security;
$this->databaseService = $databaseService;
$this->userAccessService = $userAccessService;
$this->parameterBag = $parameterBag;
}
public function onKernelRequest(RequestEvent $event): void
{
$request = $event->getRequest();
$path = $request->getPathInfo();
$user = $this->security->getUser();
// Check for maintenance mode first (before any other processing)
$maintenanceResponse = $this->checkMaintenanceMode($path);
if ($maintenanceResponse) {
$event->setResponse($maintenanceResponse);
return;
}
if (!$user instanceof User) {
return;
}
if ($path === "/login" && $request->getSession()->isStarted()) {
/** @var User $user */
$user = $this->databaseService->getManager()->getRepository(User::class)->findOneBy(["username" =>$user->getUsername()]);
$activeLocations = $user->getActiveLocations();
if($activeLocations->isEmpty()) {
if ($user->isHatchAdministrator()) {
$event->setResponse(new RedirectResponse($this->router->generate('admin_index')));
return;
} else {
$event->getRequest()->getSession()->getFlashBag()->add('error', "No active locations for login");
return;
}
}
$defaultLocation = $user->getDefaultLocation();
if ( $defaultLocation instanceof Location && $activeLocations->contains($defaultLocation) ) {
$event->setResponse(new RedirectResponse($this->router->generate('client_index', ['locationId' =>$defaultLocation->getId()])));
return;
}
if($user->isHatchAdministrator()) {
$event->setResponse(new RedirectResponse($this->router->generate('admin_index')));
return;
} elseif ($activeLocations->count() === 1) {
$event->setResponse(new RedirectResponse($this->router->generate('client_index', ['locationId' =>$activeLocations->first()->getId()])));
return;
} else {
$event->setResponse(new RedirectResponse($this->router->generate('location_login_select')));
return;
}
}
if (str_starts_with($path, '/admin')) {
if ($user->isHatchAdministrator() && $user->hasRestrictions()) {
$restrictionsMap = UserRestrictionsEnum::getRestrictionsMap();
$userRestrictions = $user->getRestrictions();
$allowedPaths = [];
$redirectRoute = 'app_login';
foreach ($userRestrictions as $restriction) {
if (isset($restrictionsMap[$restriction])) {
$allowedPaths = array_merge($allowedPaths, $restrictionsMap[$restriction]['paths']);
if ($redirectRoute === 'app_login') {
$redirectRoute = $restrictionsMap[$restriction]['redirect_route'];
}
}
}
$isAllowed = false;
foreach ($allowedPaths as $allowedPath) {
// Check if the path contains regex pattern
if (strpos($allowedPath, '[') !== false) {
// Convert simple regex pattern to full regex
$pattern = '#^' . str_replace(['[', ']'], ['[', ']'], $allowedPath) . '$#';
if (preg_match($pattern, $path)) {
$isAllowed = true;
break;
}
} else {
// Use simple string comparison for non-regex paths
if (str_starts_with($path, $allowedPath)) {
$isAllowed = true;
break;
}
}
}
if (!$isAllowed) {
$event->setResponse(new RedirectResponse($this->router->generate($redirectRoute)));
return;
}
} elseif (!$user->isHatchAdministrator()) {
$event->setResponse(new RedirectResponse($this->router->generate('app_login')));
return;
}
}
if (str_starts_with($path, '/client')) {
$locationId = $request->attributes->get('locationId');
if ($locationId) {
$location = $this->databaseService->getManager()->getRepository(Location::class)->find($locationId);
if ($location instanceof Location && $location->getCompany() && $location->getCompany()->isBillingHold()) {
$firstValidLocation = $this->userAccessService->getFirstActiveLocationInFirstNonBillingHoldCompany($user);
$companyName = $location->getCompany()->getName();
$request->getSession()->getFlashBag()->add(
'error',
sprintf(
'Payment is Past Due for %s. Please submit payment to regain access. For any questions, please contact ComplianceBilling@KipuHealth.com.',
$companyName
)
);
if ($firstValidLocation) {
$event->setResponse(
new RedirectResponse(
$this->router->generate('client_index', ['locationId' => $firstValidLocation->getId()])
)
);
}
}
}
}
}
/**
* Check for maintenance mode and return appropriate redirect response
*/
private function checkMaintenanceMode(string $path): ?RedirectResponse
{
// Skip maintenance check for maintenance preview routes (for testing)
if (strpos($path, '/maintenance-preview') === 0) {
return null;
}
// Check maintenance environment variables in priority order
$maintenanceChecks = [
'is_maintenance_planned' => 'maintenance_planned',
'is_maintenance_unexpected' => 'maintenance_unexpected',
'is_maintenance_failure' => 'maintenance_failure',
'is_maintenance_suspended' => 'maintenance_suspended',
];
foreach ($maintenanceChecks as $parameter => $route) {
if ($this->parameterBag->has($parameter) && $this->parameterBag->get($parameter) === true) {
return new RedirectResponse($this->router->generate($route));
}
}
return null;
}
}