AjaxListener.php 2.34 KB
Newer Older
Emma's avatar
Emma committed
1 2
<?php

Emma's avatar
Emma committed
3
namespace App\EventListener;
Emma's avatar
Emma committed
4 5 6 7 8 9 10

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
Emma's avatar
Emma committed
11
use Symfony\Component\Serializer\Serializer;
Emma's avatar
Emma committed
12 13 14 15

/**
 * Suppresses redirection when a controller throws a 403 exception during an XHR
 * request.
16 17 18 19 20 21 22 23 24 25 26
 *
 * This is necessary because Symfony by default has the shitty, non-configurable
 * behaviour of redirecting to the login page whenever `AccessDeniedException`
 * or `AuthenticationException` objects are thrown. Making things worse,
 * XMLHttpRequest is meant to follow redirects silently, making it impossible to
 * determine if a request was truly successful.
 *
 * This listener fixes this issue by intercepting the offending exceptions and
 * sending an actual 403 response if the request happens through XHR.
 *
 * @see \Symfony\Component\Security\Http\Firewall\ExceptionListener
Emma's avatar
Emma committed
27 28
 */
final class AjaxListener implements EventSubscriberInterface {
29
    /**
Emma's avatar
Emma committed
30
     * @var Serializer
31 32 33
     */
    private $serializer;

Emma's avatar
Emma committed
34
    public function __construct(Serializer $serializer) {
35 36 37
        $this->serializer = $serializer;
    }

Emma's avatar
Emma committed
38 39 40 41
    /**
     * @param GetResponseForExceptionEvent $event
     */
    public function onKernelException(GetResponseForExceptionEvent $event) {
42
        $e = $event->getException();
Emma's avatar
Emma committed
43 44
        $request = $event->getRequest();

45 46
        if (!$e instanceof AuthenticationException && !$e instanceof AccessDeniedException) {
            return;
Emma's avatar
Emma committed
47
        }
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

        if (!$request->isXmlHttpRequest()) {
            return;
        }

        $format = $request->getRequestFormat();

        if ($this->serializer->supportsEncoding($format)) {
            $data = ['error' => $e->getMessage()];
            $responseBody = $this->serializer->serialize($data, $format);
        } else {
            // html and such
            $responseBody = $e->getMessage();
        }

        $event->setResponse(new Response($responseBody, 403));
Emma's avatar
Emma committed
64 65 66 67 68 69 70 71 72 73 74
    }

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents() {
        return [
            KernelEvents::EXCEPTION => ['onKernelException', 1000],
        ];
    }
}