vendor/nelmio/cors-bundle/EventListener/CorsListener.php line 50

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the NelmioCorsBundle.
  4.  *
  5.  * (c) Nelmio <hello@nelm.io>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Nelmio\CorsBundle\EventListener;
  11. use Symfony\Component\HttpKernel\HttpKernelInterface;
  12. use Symfony\Component\HttpFoundation\Request;
  13. use Symfony\Component\HttpFoundation\Response;
  14. use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
  15. use Symfony\Component\HttpKernel\Event\GetResponseEvent;
  16. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  17. use Nelmio\CorsBundle\Options\ResolverInterface;
  18. /**
  19.  * Adds CORS headers and handles pre-flight requests
  20.  *
  21.  * @author Jordi Boggiano <j.boggiano@seld.be>
  22.  */
  23. class CorsListener
  24. {
  25.     /**
  26.      * Simple headers as defined in the spec should always be accepted
  27.      */
  28.     protected static $simpleHeaders = array(
  29.         'accept',
  30.         'accept-language',
  31.         'content-language',
  32.         'origin',
  33.     );
  34.     protected $dispatcher;
  35.     /** @var ResolverInterface */
  36.     protected $configurationResolver;
  37.     public function __construct(EventDispatcherInterface $dispatcherResolverInterface $configurationResolver)
  38.     {
  39.         $this->dispatcher $dispatcher;
  40.         $this->configurationResolver $configurationResolver;
  41.     }
  42.     public function onKernelRequest(GetResponseEvent $event)
  43.     {
  44.         if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
  45.             return;
  46.         }
  47.         $request $event->getRequest();
  48.         if (!$options $this->configurationResolver->getOptions($request)) {
  49.             return;
  50.         }
  51.         // if the "forced_allow_origin_value" option is set, add a listener which will set or override the "Access-Control-Allow-Origin" header
  52.         if (!empty($options['forced_allow_origin_value'])) {
  53.             $this->dispatcher->addListener('kernel.response', array($this'forceAccessControlAllowOriginHeader'), -1);
  54.         }
  55.         // skip if not a CORS request
  56.         if (!$request->headers->has('Origin') || $request->headers->get('Origin') == $request->getSchemeAndHttpHost()) {
  57.             return;
  58.         }
  59.         // perform preflight checks
  60.         if ('OPTIONS' === $request->getMethod() && $request->headers->has('Access-Control-Request-Method')) {
  61.             $event->setResponse($this->getPreflightResponse($request$options));
  62.             return;
  63.         }
  64.         if (!$this->checkOrigin($request$options)) {
  65.             return;
  66.         }
  67.         $this->dispatcher->addListener('kernel.response', array($this'onKernelResponse'), 0);
  68.     }
  69.     public function onKernelResponse(FilterResponseEvent $event)
  70.     {
  71.         if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
  72.             return;
  73.         }
  74.         if (!$options $this->configurationResolver->getOptions($request $event->getRequest())) {
  75.             return;
  76.         }
  77.         $response $event->getResponse();
  78.         // add CORS response headers
  79.         $response->headers->set('Access-Control-Allow-Origin'$request->headers->get('Origin'));
  80.         if ($options['allow_credentials']) {
  81.             $response->headers->set('Access-Control-Allow-Credentials''true');
  82.         }
  83.         if ($options['expose_headers']) {
  84.             $response->headers->set('Access-Control-Expose-Headers'strtolower(implode(', '$options['expose_headers'])));
  85.         }
  86.     }
  87.     public function forceAccessControlAllowOriginHeader(FilterResponseEvent $event)
  88.     {
  89.         if (!$options $this->configurationResolver->getOptions($request $event->getRequest())) {
  90.             return;
  91.         }
  92.         $event->getResponse()->headers->set('Access-Control-Allow-Origin'$options['forced_allow_origin_value']);
  93.     }
  94.     protected function getPreflightResponse(Request $request, array $options)
  95.     {
  96.         $response = new Response();
  97.         $response->setVary(array('Origin'));
  98.         if ($options['allow_credentials']) {
  99.             $response->headers->set('Access-Control-Allow-Credentials''true');
  100.         }
  101.         if ($options['allow_methods']) {
  102.             $response->headers->set('Access-Control-Allow-Methods'implode(', '$options['allow_methods']));
  103.         }
  104.         if ($options['allow_headers']) {
  105.             $headers $options['allow_headers'] === true
  106.                 $request->headers->get('Access-Control-Request-Headers')
  107.                 : implode(', '$options['allow_headers']);
  108.             if ($headers) {
  109.                 $response->headers->set('Access-Control-Allow-Headers'$headers);
  110.             }
  111.         }
  112.         if ($options['max_age']) {
  113.             $response->headers->set('Access-Control-Max-Age'$options['max_age']);
  114.         }
  115.         if (!$this->checkOrigin($request$options)) {
  116.             $response->headers->set('Access-Control-Allow-Origin''null');
  117.             return $response;
  118.         }
  119.         $response->headers->set('Access-Control-Allow-Origin'$request->headers->get('Origin'));
  120.         // check request method
  121.         if (!in_array(strtoupper($request->headers->get('Access-Control-Request-Method')), $options['allow_methods'], true)) {
  122.             $response->setStatusCode(405);
  123.             return $response;
  124.         }
  125.         /**
  126.          * We have to allow the header in the case-set as we received it by the client.
  127.          * Firefox f.e. sends the LINK method as "Link", and we have to allow it like this or the browser will deny the
  128.          * request.
  129.          */
  130.         if (!in_array($request->headers->get('Access-Control-Request-Method'), $options['allow_methods'], true)) {
  131.             $options['allow_methods'][] = $request->headers->get('Access-Control-Request-Method');
  132.             $response->headers->set('Access-Control-Allow-Methods'implode(', '$options['allow_methods']));
  133.         }
  134.         // check request headers
  135.         $headers $request->headers->get('Access-Control-Request-Headers');
  136.         if ($options['allow_headers'] !== true && $headers) {
  137.             $headers trim(strtolower($headers));
  138.             foreach (preg_split('{, *}'$headers) as $header) {
  139.                 if (in_array($headerself::$simpleHeaderstrue)) {
  140.                     continue;
  141.                 }
  142.                 if (!in_array($header$options['allow_headers'], true)) {
  143.                     $response->setStatusCode(400);
  144.                     $response->setContent('Unauthorized header '.$header);
  145.                     break;
  146.                 }
  147.             }
  148.         }
  149.         return $response;
  150.     }
  151.     protected function checkOrigin(Request $request, array $options)
  152.     {
  153.         // check origin
  154.         $origin $request->headers->get('Origin');
  155.         if ($options['allow_origin'] === true) return true;
  156.         if ($options['origin_regex'] === true) {
  157.             // origin regex matching
  158.             foreach($options['allow_origin'] as $originRegexp) {
  159.                 if (preg_match('{'.$originRegexp.'}i'$origin)) {
  160.                     return true;
  161.                 }
  162.             }
  163.         } else {
  164.             // old origin matching
  165.             if (in_array($origin$options['allow_origin'])) {
  166.                 return true;
  167.             }
  168.         }
  169.         return false;
  170.     }
  171. }