<?php
/**
 * Created by PhpStorm.
 * User: damien
 * Date: 10/03/2018
 * Time: 21:15
 */

namespace Actigraph\ActipageBaseBundle\EventListener;

use Actigraph\ActipageBaseBundle\Services\ContentSecurityPolicyNonceService;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class ContentSecurityPolicyResponseSubscriber implements EventSubscriberInterface
{

    private $allowIframe,$extraFontHosts,$extraImgHosts,$extraJsHosts,$extraFrameHosts,$extraStyleHosts,$extraConnectHosts,$contentSecurityPolicyNonceService,$customRefererPolicy,$csp3Enabled=false;

    public function __construct($params,ContentSecurityPolicyNonceService $contentSecurityPolicyNonceService)
    {
        $this->contentSecurityPolicyNonceService = $contentSecurityPolicyNonceService;
        if(isset($params["csp3"])){
            if(isset($params["csp3"]["extraFontHosts"]))
                $this->extraFontHosts = $params["csp3"]["extraFontHosts"];
            if(isset($params["csp3"]["extraImgHosts"]))
                $this->extraImgHosts = $params["csp3"]["extraImgHosts"];
            if(isset($params["csp3"]["extraJsHosts"]))
                $this->extraJsHosts = $params["csp3"]["extraJsHosts"];
            if(isset($params["csp3"]["extraStyleHosts"]))
                $this->extraStyleHosts = $params["csp3"]["extraStyleHosts"];
            if(isset($params["csp3"]["extraFrameHosts"]))
                $this->extraFrameHosts = $params["csp3"]["extraFrameHosts"];
            if(isset($params["csp3"]["extraConnectHosts"]))
                $this->extraConnectHosts = $params["csp3"]["extraConnectHosts"];
            if(isset($params["csp3"]["enabled"]))
                $this->csp3Enabled = $params["csp3"]["enabled"];
            if(isset($params["csp3"]["customRefererPolicy"]) && strlen($params["csp3"]["customRefererPolicy"])>0)
                $this->customRefererPolicy = $params["csp3"]["customRefererPolicy"];
            if(isset($params['allowIframe']))
                $this->allowIframe = true;
        }

    }

    /**
     * Returns an array of event names this subscriber wants to listen to.
     *
     * The array keys are event names and the value can be:
     *
     *  * The method name to call (priority defaults to 0)
     *  * An array composed of the method name to call and the priority
     *  * An array of arrays composed of the method names to call and respective
     *    priorities, or 0 if unset
     *
     * For instance:
     *
     *  * array('eventName' => 'methodName')
     *  * array('eventName' => array('methodName', $priority))
     *  * array('eventName' => array(array('methodName1', $priority), array('methodName2')))
     *
     * @return array The event names to listen to
     */
    public static function getSubscribedEvents()
    {
        return [
            KernelEvents::RESPONSE => 'onResponse'
        ];
    }

    /**
     * Callback function for event subscriber
     * @param FilterResponseEvent $event
     */
    public function onResponse(FilterResponseEvent $event)
    {
        $response = $event->getResponse();

        // Nonce frais par requête. On l'injecte à la place du placeholder figé
        // (émis par ContentSecurityPolicyNonceService), afin que le nonce reste
        // cohérent entre le header et le HTML même lorsque la page ou des
        // fragments sont servis depuis le cache.
        $nonce = base64_encode(random_bytes(16));
        $content = $response->getContent();
        if (is_string($content) && strpos($content, ContentSecurityPolicyNonceService::PLACEHOLDER) !== false) {
            $response->setContent(str_replace(ContentSecurityPolicyNonceService::PLACEHOLDER, $nonce, $content));
        }

        if($this->csp3Enabled){
            $policy = "default-src 'self';"
                . "script-src 'self' 'strict-dynamic' 'unsafe-inline' 'nonce-".$nonce."' www.google.com *.gstatic.com www.google-analytics.com *.hcaptcha.com *.prosopo.io stats.g.doubleclick.net *.vimeo.com *.vimeocdn.com *.newrelic.com *.nr-data.net platform.twitter.com syndication.twitter.com cdn.syndication.twimg.com ".$this->extraJsHosts.";"
                . "style-src 'self' 'unsafe-inline' fonts.googleapis.com *.vimeocdn.com ".$this->extraStyleHosts.";"
                . "font-src 'self' fonts.gstatic.com ".$this->extraFontHosts.";"
                . "img-src 'self' data: *.cloud.ovh.net www.google-analytics.com s3.cloud.actigraph.com stats.g.doubleclick.net stats.normandie.cloud-ed.fr ".$this->extraImgHosts.";"
                . "child-src 'self' www.google.com www.youtube.com *.vimeo.com *.vimeocdn.com *.hcaptcha.com *.prosopo.io ".$this->extraFrameHosts.";"
                . "base-uri 'self';"
                . "connect-src 'self' *.hcaptcha.com *.prosopo.io stats.normandie.cloud-ed.fr ".$this->extraConnectHosts.";"
                . "report-uri /csp-report;";

            $response->headers->set("Content-Security-Policy", $policy);
        }

        $response->headers->set("X-XSS-Protection", "1; mode=block");
        if ($this->allowIframe) {
            // Pas de X frame option
        } else {
            $response->headers->set("X-Frame-Options", "SAMEORIGIN");
        }
        if($this->customRefererPolicy != "")
            $response->headers->set("Referrer-Policy", $this->customRefererPolicy);
        else
            $response->headers->set("Referrer-Policy", "same-origin");
        $response->headers->set("X-Content-Type-Options", "nosniff");
    }
}
