<?php
namespace PaperKite\Common\EventSubscriber;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
class AccessLogSubscriber implements EventSubscriberInterface
{
public function __construct(
private LoggerInterface $auditLogger,
) {
}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => 'onKernelRequest',
KernelEvents::EXCEPTION => ['onKernelException', 2],
];
}
public function onKernelRequest(RequestEvent $event): void
{
if (!$event->isMainRequest()) {
return;
}
$mainRequest = $event->getRequest();
$this->buildLogBasedOnRequest('Access requested.', $mainRequest);
}
public function onKernelException(ExceptionEvent $event): void
{
if (!$event->isMainRequest()) {
return;
}
$exception = $event->getThrowable();
if (!$exception instanceof AccessDeniedException) {
return;
}
$mainRequest = $event->getRequest();
$this->buildLogBasedOnRequest('Access refused.', $mainRequest);
}
private function buildLogBasedOnRequest(string $message, Request $request): void
{
$routeName = $request->attributes->get('_route');
if (false === $this->isListenedRoute($routeName)) {
return;
}
$context = [
'uri' => $request->getRequestUri(),
'method' => $request->getMethod(),
];
$relatedObjects = $this->getRelatedObjectsFromRequest($request);
if (count($relatedObjects) > 0) {
$context[] = $relatedObjects;
}
$this->auditLogger->info($message, $context);
}
private function isListenedRoute(string $routeName): bool
{
// Use symfony console debug:router command to get full routes list.
if (
str_starts_with($routeName, 'mycmcm_')
||
str_starts_with($routeName, 'public_')
||
str_starts_with($routeName, '_profiler')
||
true === in_array($routeName, [
'_preview_error',
'_wdt',
'admin_dashboard',
'company_get_api_version',
'company_show_apidoc',
'health_mutual_get_api_version',
'health_mutual_show_apidoc',
'employee_get_api_version',
'employee_get_companies',
'employee_get_departments',
'employee_get_inquiry_statuses',
'employee_get_health_insurances',
'employee_get_health_mutuals',
'employee_get_dossier_types',
'employee_get_dossier_statuses',
'employee_get_api_version',
'employee_list_subscription_plans',
'employee_show_apidoc',
'employee_support_predefined_templates',
])
) {
return false;
}
return true;
}
private function getRelatedObjectsFromRequest(Request $request): array
{
$relatedObject = array_merge($request->attributes->get('_route_params'), $request->query->all());
unset($relatedObject['schemes']);
unset($relatedObject['_locale']);
// To log information from post parameter/content - use $request->getContent()
$relatedObject = array_merge($relatedObject, ['Request Content' => json_decode($request->getContent(), true)]);
return $relatedObject;
}
}