I'm working on adding Audit logging to a Symfony2 project which logs all page loads and post requests into a custom audit table. The project uses the default logout route for Symfony2 (visiting /logout) which destroys the session then re-directs to the /login route.
There is an event listener set up for onKernelRequest which then writes the correct data into the table. In the security.yml file I have the following listed for the logout route.
security:
firewalls:
main:
logout:
path: /logout
target: /login
The Audit logging is working fine for all pages except the logout event. After logging out I have tried visiting the profiler then selecting the '/logout' action from the "Last 10" option in the sidebar. When clicking on "Events" this lists the default Symfony events for kernel.request such as the DebugHandler and the ProfileListener however my custom callback is showing under the "Not Called Listeners" tab.
There is a success_handler that can be added to the security.yml file however I would need a method that can run my event listener before the session is destroyed. Is there a way to make the existing listener also record the logout event prior the Symfony actioning the logout?
Edit
<?php
// src/AuditBundle/EventListener/AuditListener.php
namespace AuditBundle\EventListener;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpFoundation\RequestStack;
use AuditBundle\Entity\AuditLog;
class AuditListener
{
protected $requestStack;
protected $em;
protected $tokenStorage;
protected $authorizationChecker;
public function __construct(RequestStack $requestStack, \Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage $tokenStorage, $authorizationChecker, \Doctrine\ORM\EntityManager $em = NULL)
{
$this->requestStack = $requestStack;
$this->em = $em;
$this->tokenStorage = $tokenStorage;
$this->authorizationChecker = $authorizationChecker;
}
public function onKernelRequest(GetResponseEvent $response)
{
$request = $response->getRequest();
if ( strpos($request->getRequestUri(), 'fonts') !== false)
return;
if ( strpos($request->getRequestUri(), 'css') !== false)
return;
if ( strpos($request->getRequestUri(), '_wdt') !== false)
return;
if ( strpos($request->getRequestUri(), 'js') !== false)
return;
if ( strpos($request->getRequestUri(), '_profiler') !== false)
return;
$this->log('Request', $request);
}
public function postUpdate(\Doctrine\ORM\Event\LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$this->em = $args->getEntityManager();
$className = $this->em->getClassMetadata(get_class($entity))->getName();
if ($entity instanceof \AuditBundle\Entity\AuditLog)
return;
$this->log($className.' Updated', $this->requestStack->getCurrentRequest(), $entity);
}
public function postPersist(\Doctrine\ORM\Event\LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$this->em = $args->getEntityManager();
$className = $this->em->getClassMetadata(get_class($entity))->getName();
if ($entity instanceof \AuditBundle\Entity\AuditLog)
return;
$this->log($className.' Created', $this->requestStack->getCurrentRequest(), $entity);
}
public function postDelete(\Doctrine\ORM\Event\LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$this->em = $args->getEntityManager();
$className = $this->em->getClassMetadata(get_class($entity))->getName();
if ($entity instanceof \AuditBundle\Entity\AuditLog)
return;
$this->log($className.' Deleted', $this->requestStack->getCurrentRequest());
}
protected function log($message, $request, $entity = NULL)
{
$log = new AuditLog();
$log->setType($request->getRealMethod());
if ($this->authorizationChecker->isGranted('IS_AUTHENTICATED_FULLY'))
{
$log->setUser($this->tokenStorage->getToken()->getUser());
}
if ($entity)
{
$log->setEntityId($entity->getId());
}
$log->setUriString($request->getRequestUri());
$log->setMessage($message);
$log->setDatetime(new \DateTime());
$log->setIp($request->getClientIp());
$log->setBrowser(isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '');
$this->em->persist($log);
$this->em->flush();
}
}
services.yml
services:
audits.audit_listener:
class: AuditBundle\EventListener\AuditListener
arguments: [@request_stack, @security.token_storage, @security.authorization_checker, @doctrine.orm.entity_manager]
tags:
- { name: kernel.event_listener, event: kernel.request }