<?php
declare(strict_types=1);
namespace App\EventSubscriber;
use App\Checker\RateLimiterChecker;
use App\Entity\Admin\SourceInterface;
use App\Entity\Device\DeviceInterface;
use App\Entity\Franchise\FranchiseInterface;
use App\Entity\Security\Manager;
use App\Entity\Security\ShopManager;
use App\Helper\Response\DeviceResponseInterface;
use App\Helper\Response\FranchiseResponseInterface;
use App\Helper\Response\ResponseInterface as CustomResponseInterface;
use App\Helper\Response\ShopResponseInterface;
use App\Repository\Admin\SourceRepository;
use App\Repository\Franchise\FranchiseRepository;
use App\Repository\Shop\DeviceRepository;
use App\Repository\Shop\ShopRepository;
use App\Verifier\Shop\LicenceVerifier;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Symfony\Component\Security\Core\Security;
class ApiRequestSubscriber implements EventSubscriberInterface
{
public const API_VERSION_200 = '2.0.0';
public const API_VERSIONS = [
self::API_VERSION_200,
];
private array $authorizedRoutes = [
'api_login_check',
'api_users_request_request_password_collection',
'api_users_check_password_reset_token_collection',
'api_users_reset_password_collection',
'api_entrypoint',
'api_doc',
'foxorders_api_',
'foxorders_api_doc',
];
public function __construct(
private $devicePinDev,
private Security $security,
private SourceRepository $sourceRepository,
private RateLimiterChecker $rateLimiterChecker,
private DeviceRepository $deviceRepository,
private FranchiseRepository $franchiseRepository,
private ShopRepository $shopRepository,
private LicenceVerifier $licenceVerifier,
) {
}
public static function getSubscribedEvents(): array
{
return [
RequestEvent::class => ['onKernelRequest', 6],
];
}
public function onKernelRequest(RequestEvent $event)
{
$request = $event->getRequest();
$route = $request->attributes->get('_route');
$source = $request->headers->get('source');
$version = $request->headers->get('version');
$franchiseToken = $request->headers->get('franchise-token');
$devicePinDev = $request->headers->get('device-pin-dev');
$shopToken = $request->headers->get('shop-token');
$identifier = $request->headers->get('device-token');
$isApiRoute = null !== $route && str_contains($route, 'api_') && false === str_starts_with($route, 'foxorders_documentation');
$user = $this->security->getUser();
if (false === $isApiRoute || true === \in_array($route, $this->authorizedRoutes, true)) {
return true;
}
$this->rateLimiterChecker->checkRate();
if (false === $this->security->getUser()) {
throw new AccessDeniedHttpException(Response::$statusTexts[Response::HTTP_FORBIDDEN]);
}
if (null === $source) {
throw new BadRequestHttpException(CustomResponseInterface::SOURCE_MANDATORY);
}
if (null === $version) {
throw new BadRequestHttpException(CustomResponseInterface::API_VERSION_MANDATORY);
}
if (false === \in_array($version, self::API_VERSIONS, true)) {
throw new BadRequestHttpException(CustomResponseInterface::API_VERSION_INVALID);
}
if (false === $this->sourceRepository->isApiSource($source)) {
throw new BadRequestHttpException(CustomResponseInterface::SOURCE_INVALID);
}
$franchise = $shop = $device = null;
if ($user instanceof Manager) {
$franchise = $user->getFranchise();
} elseif ($user instanceof ShopManager) {
$shop = $user->getShop();
$device = $user->getDevice();
$franchise = $shop->getFranchise();
$shopToken = $shop->getToken();
}
$franchiseToken = $franchise?->getToken() ?? $franchiseToken;
if (null === $franchiseToken && !\in_array($route, FranchiseInterface::ENDPOINTS_WITHOUT_TOKEN, true)) {
throw new BadRequestHttpException(FranchiseResponseInterface::FRANCHISE_TOKEN_REQUIRED);
}
if (null === $franchise && null !== $franchiseToken) {
$franchise = $this->franchiseRepository->findOneBy(['token' => $franchiseToken]);
if (null === $franchise) {
throw new BadRequestHttpException(FranchiseResponseInterface::FRANCHISE_TOKEN_INVALID);
}
}
if (null === $shop && null !== $shopToken && null !== $franchiseToken) {
if (false === $this->shopRepository->isMatchedShopTokenFranchiseToken($franchiseToken, $shopToken)) {
throw new BadRequestHttpException(ShopResponseInterface::SHOP_TOKEN_INVALID);
}
}
if (null !== $franchise && FranchiseInterface::STATUS_DISABLED === $franchise->getStatus()) {
throw new AccessDeniedHttpException(CustomResponseInterface::ACCOUNT_BLOCKED);
}
if (false === $this->licenceVerifier->verify()) {
throw new UnauthorizedHttpException('Unauthorized access', CustomResponseInterface::ACCESS_DENIED);
}
if (SourceInterface::SOURCE_FOXORDERS_FRONT === $source && null !== $shopToken) {
$device = $this->deviceRepository->defaultDevice($shopToken);
if (null === $device) {
throw new BadRequestHttpException(DeviceResponseInterface::DEVICE_TOKEN_INVALID);
}
$request->headers->set('device-token', $device->getToken());
return true;
}
if ($this->devicePinDev === $devicePinDev) {
return;
}
if (false === \in_array($source, SourceInterface::DEVICE_SOURCES, true) || true === \in_array($route, DeviceInterface::WHITELISTED_DEVICE_ENDPOINTS, true)) {
return true;
}
// if (null === $identifier || '' === trim($identifier)) {
// throw new UnauthorizedHttpException('Unauthorized access', DeviceResponseInterface::UNRECOGNIZED_DEVICE);
// }
// $device = $this->deviceRepository->findOneByFranchiseTokenAndIdentifier($franchiseToken, $identifier);
// if (null === $device) {
// throw new UnauthorizedHttpException('Unauthorized access', DeviceResponseInterface::UNRECOGNIZED_DEVICE);
// }
// $request->headers->set('device-token', $device->getToken());
if (null !== $identifier && '' !== trim($identifier)) {
$device = $this->deviceRepository->findOneByFranchiseTokenAndIdentifier($franchiseToken, $identifier);
if (null !== $device) {
$request->headers->set('device-token', $device->getToken());
}
}
}
}