<?php
namespace App\EventListener;
use App\Controller\AccessDeniedController;
use App\Entity\User;
use App\Entity\WebService;
use App\Entity\WebServiceLog;
use App\Repository\DomainRepository;
use App\Repository\UserRepository;
use App\Service\Misc;
use App\Service\OrganisationService;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\NonUniqueResultException;
use GraphQL\Utils\AST;
use Overblog\GraphQLBundle\Request\Parser;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Security;
readonly class RequestListener
{
public function __construct(
private Security $security,
private UrlGeneratorInterface $router,
private Misc $misc,
private RequestStack $requestStack,
private UserRepository $userRepository,
private DomainRepository $domainRepository,
private EntityManagerInterface $entityManager,
private ContainerInterface $container,
private OrganisationService $organisationService
)
{
}
/**
* @throws NonUniqueResultException
*/
public function onKernelController(ControllerEvent $event): void
{
/**
* @var User $user
*/
$user = $this->security->getUser();
if ($user && $event->isMainRequest() && $user->isCustomer()) {
$controller = $event->getController();
if (in_array($controller[1], ['chooseOrganisation', 'choose'])) {
return;
}
$session = $this->requestStack->getSession();
// Verifier si l'utilisateur a plusieurs organisation
if (
$this->userRepository->getCountOrga($user) > 1 &&
$session->get('organisation', null) === null &&
!in_array($controller[1], ['chooseOrganisation', 'choose', 'endpointAction'])
) {
$event->stopPropagation();
$router = $this->router;
$event->setController(static function () use ($router) {
return new RedirectResponse($router->generate('choose_organisation'));
});
}
if ($controller[1] === 'endpointAction') {
$domainId = $this->requestStack->getCurrentRequest()?->get('id', 0);
$routeName = $this->requestStack->getCurrentRequest()?->attributes->get('name');
if (
$domainId === 0 &&
!$this->isAllowedCall()
) {
$event->stopPropagation();
$event->setController(static function () {
return new Response(
'Domain ID is required.',
Response::HTTP_FORBIDDEN
);
});
return;
}
$domain = $this->domainRepository->getDomainByUserAndId(
$user->getId(),
$domainId
);
$organisation = $domain?->getOrganisation();
$sub = $this->misc->getActifSubsription($organisation);
if (
($organisation === null || !$organisation->getIsAdded()) &&
!$this->isAllowedCall()
) {
$event->stopPropagation();
$event->setController(static function () {
return new Response(
'Requested resources didn\'t exist.',
Response::HTTP_UNAUTHORIZED
);
});
}else if (
($sub === null) &&
!$this->isAllowedCall()
) {
$event->stopPropagation();
$event->setController(static function () {
return new Response(
'Your subscription has expired.',
Response::HTTP_FORBIDDEN
);
});
}
if ($organisation && $routeName === 'publicEndpoint') {
$this->organisationService->setupConsumedQuota($organisation);
if ($organisation->getQuota() <= $organisation->getConsumedQuota()) {
$event->stopPropagation();
$event->setController(static function () {
return new Response(
'you have exceeded your quota.',
Response::HTTP_TOO_MANY_REQUESTS
);
});
}
}
} else {
$organisation = $this->misc->getUserOrganisation($user);
$sub = $this->misc->getActifSubsription($organisation);
if (
!($controller[0] instanceof AccessDeniedController) &&
($organisation === null || $sub === null || !$organisation->getIsAdded())
) {
$event->stopPropagation();
// Set your custom controller that produces a 403 response
$router = $this->router;
$event->setController(static function () use ($router) {
return new RedirectResponse($router->generate('access_denied'));
});
} elseif (
$controller[0] instanceof AccessDeniedController &&
$sub &&
$organisation->getIsAdded() &&
!in_array($controller[1], ['chooseOrganisation', 'choose'])
) {
$router = $this->router;
$event->setController(static function () use ($router) {
return new RedirectResponse($router->generate('home'));
});
}
}
}
}
public function onKernelResponse(ResponseEvent $event): void
{
try {
$response = $event->getResponse()->getContent();
$decodedResponse = json_decode($response, true, 512, JSON_THROW_ON_ERROR);
$routeName = $this->requestStack->getCurrentRequest()?->attributes->get('name');
if ($routeName === 'publicEndpoint') {
if (
!isset($decodedResponse['errors']) &&
$event->getResponse()->isSuccessful()
) {
$this->manageWebServiceStats();
}
}
} catch (\Exception $e) {
}
}
/**
* @return bool
*/
private function isAllowedCall(): bool
{
$functionNames = $this->getGraphQLFunctionName($this->requestStack->getCurrentRequest());
return count(
array_intersect(
[
'updateProfile', 'getProfile', 'enable2fAuth', 'getDomain', '__schema',
'recoverPassword', 'forgotPassword', 'upsertOrganisation', 'getIndustry'
],
$functionNames
)
) > 0;
}
/**
* @param Request $request
* @return array|string[]
*/
private function getGraphQLFunctionName(Request $request): array
{
try {
$requestParser = new Parser();
$parse = $requestParser->parse($request);
$parse2 = \GraphQL\Language\Parser::parse($parse['query']);
$array = AST::toArray($parse2);
$functions = [];
foreach ($array['definitions'] as $definition) {
foreach ($definition['selectionSet']['selections'] as $selection) {
if (!in_array($selection['name']['value'], $functions, true)) {
$functions[] = $selection['name']['value'];
}
}
}
return $functions;
} catch (\Exception $e) {
return ['Invalid request'];
}
}
/**
* @return void
*/
private function manageWebServiceStats(): void
{
$user = $this->security->getUser();
$functionNames = $this->getGraphQLFunctionName($this->requestStack->getCurrentRequest());
foreach ($functionNames as $functionName) {
if (!in_array($functionName, $this->getResolverAndMutation(), true)) {
continue;
}
$ws = $this->entityManager->getRepository(WebService::class)->findOneBy(['name' => $functionName]);
if (!$ws) {
$ws = new WebService();
$ws->setName($functionName);
$this->entityManager->persist($ws);
}
$ws->addWebServiceLog(
(new WebServiceLog())
->setCalledBy($user)
);
}
$this->entityManager->flush();
}
/**
* @return array
*/
private function getResolverAndMutation(): array
{
$services = ['overblog_graphql.query_resolver', 'overblog_graphql.mutation_resolver'];
$resolversAndMutations = [];
foreach ($services as $serviceId) {
$service = $this->container->get($serviceId);
array_push(
$resolversAndMutations,
...array_keys($service->getAliases())
);
}
return $resolversAndMutations;
}
}