# frozen_string_literal: true

require "spec_helper"

module Decidim
  module Headers
    describe "ContentSecurityPolicy" do
      let!(:organization) { create(:organization) }
      let!(:user) { create(:user, :confirmed, organization:) }
      let!(:additional_content_security_policies) do
        {

          "default-src" => "https://example.org",
          "script-src" => "https://script.example.org",
          "style-src" => "https://style.example.org",
          "img-src" => "https://img.example.org",
          "font-src" => "https://font.example.org",
          "connect-src" => "https://connect.example.org",
          "frame-src" => "https://frame.example.org",
          "media-src" => "https://example.org"
        }
      end
      let(:maps_host) { Decidim::Dev::Test::MapServer.host }

      controller do
        include Decidim::Headers::ContentSecurityPolicy

        def current_organization
          request.env["decidim.current_organization"]
        end

        def show
          render plain: "Hello World"
        end
      end

      before do
        request.env["decidim.current_organization"] = organization
        routes.draw { get "show" => "anonymous#show" }
      end

      it "sets the appropriate headers" do
        get :show
        expect(response.headers["Content-Security-Policy"]).to include("default-src 'self' 'unsafe-inline' localhost:* #{organization.host}:*; ")
        expect(response.headers["Content-Security-Policy"]).to include("script-src 'self' 'unsafe-inline' 'unsafe-eval' localhost:* #{organization.host}:*; ")
        expect(response.headers["Content-Security-Policy"]).to include("style-src 'self' 'unsafe-inline' localhost:* #{organization.host}:*;")
        expect(response.headers["Content-Security-Policy"]).to include("img-src 'self' *.hereapi.com data: https://via.placeholder.com #{maps_host} localhost:* #{organization.host}:*;")
        expect(response.headers["Content-Security-Policy"]).to include("connect-src 'self' *.hereapi.com data: #{maps_host} localhost:* #{organization.host}:*; ")
        expect(response.headers["Content-Security-Policy"]).to include("font-src 'self' localhost:* #{organization.host}:*; ")
        expect(response.headers["Content-Security-Policy"]).to include("frame-src 'self' www.youtube-nocookie.com player.vimeo.com localhost:* #{organization.host}:* www.example.org;")
        expect(response.headers["Content-Security-Policy"]).to include("media-src 'self' localhost:* #{organization.host}:*")
      end

      context "when content policy is added by organization" do
        let!(:organization) { create(:organization, content_security_policy: additional_content_security_policies) }

        it "sets the appropriate headers" do
          get :show
          expect(response.headers["Content-Security-Policy"]).to include("default-src 'self' 'unsafe-inline' https://example.org; ")
          expect(response.headers["Content-Security-Policy"]).to include("script-src 'self' 'unsafe-inline' 'unsafe-eval' https://script.example.org;")
          expect(response.headers["Content-Security-Policy"]).to include("style-src 'self' 'unsafe-inline' https://style.example.org;")
          expect(response.headers["Content-Security-Policy"]).to include("img-src 'self' *.hereapi.com data: https://via.placeholder.com #{maps_host} https://img.example.org;")
          expect(response.headers["Content-Security-Policy"]).to include("connect-src 'self' *.hereapi.com data: #{maps_host} https://connect.example.org;")
          expect(response.headers["Content-Security-Policy"]).to include("font-src 'self' https://font.example.org;")
          expect(response.headers["Content-Security-Policy"]).to include("frame-src 'self' www.youtube-nocookie.com player.vimeo.com https://frame.example.org;")
          expect(response.headers["Content-Security-Policy"]).to include("media-src 'self' https://example.org")
        end
      end

      context "when content policy is added via decidim config" do
        it "sets the appropriate headers" do
          allow(Decidim).to receive(:content_security_policies_extra).and_return(additional_content_security_policies)
          get :show
          expect(response.headers["Content-Security-Policy"]).to include("default-src 'self' 'unsafe-inline' https://example.org localhost:* #{organization.host}:*; ")
          expect(response.headers["Content-Security-Policy"]).to include("script-src 'self' 'unsafe-inline' 'unsafe-eval' https://script.example.org localhost:* #{organization.host}:*; ")
          expect(response.headers["Content-Security-Policy"]).to include("style-src 'self' 'unsafe-inline' https://style.example.org localhost:* #{organization.host}:*;")
          expect(response.headers["Content-Security-Policy"]).to include("img-src 'self' *.hereapi.com data: https://img.example.org localhost:* #{organization.host}:*;")
          expect(response.headers["Content-Security-Policy"]).to include("connect-src 'self' *.hereapi.com data: https://connect.example.org localhost:* #{organization.host}:*; ")
          expect(response.headers["Content-Security-Policy"]).to include("font-src 'self' https://font.example.org localhost:* #{organization.host}:*; ")
          expect(response.headers["Content-Security-Policy"]).to include("frame-src 'self' www.youtube-nocookie.com player.vimeo.com https://frame.example.org localhost:* #{organization.host}:* www.example.org;")
          expect(response.headers["Content-Security-Policy"]).to include("media-src 'self' https://example.org localhost:* #{organization.host}:*")
        end
      end

      context "when the organization has a content security policy" do
        controller do
          include Decidim::Headers::ContentSecurityPolicy

          def current_organization
            request.env["decidim.current_organization"]
          end

          before_action do |controller|
            {
              "default-src" => "https://example.org",
              "script-src" => "https://script.example.org",
              "style-src" => "https://style.example.org",
              "img-src" => "https://img.example.org",
              "font-src" => "https://font.example.org",
              "connect-src" => "https://connect.example.org",
              "frame-src" => "https://frame.example.org",
              "media-src" => "https://example.org"
            }.each do |key, value|
              controller.content_security_policy.append_csp_directive(key, value)
            end
          end

          def show
            render plain: "Hello World"
          end
        end

        before do
          request.env["decidim.current_organization"] = organization
          routes.draw { get "show" => "anonymous#show" }
        end

        it "sets the appropriate headers" do
          get :show
          expect(response.headers["Content-Security-Policy"]).to include("default-src 'self' 'unsafe-inline' https://example.org localhost:* #{organization.host}:*; ")
          expect(response.headers["Content-Security-Policy"]).to include("script-src 'self' 'unsafe-inline' 'unsafe-eval' https://script.example.org localhost:* #{organization.host}:*; ")
          expect(response.headers["Content-Security-Policy"]).to include("style-src 'self' 'unsafe-inline' https://style.example.org localhost:* #{organization.host}:*; ")
          expect(response.headers["Content-Security-Policy"]).to include("img-src 'self' *.hereapi.com data: https://img.example.org https://via.placeholder.com #{maps_host} localhost:* #{organization.host}:*;")
          expect(response.headers["Content-Security-Policy"]).to include("connect-src 'self' *.hereapi.com data: https://connect.example.org #{maps_host} localhost:* #{organization.host}:*;")
          expect(response.headers["Content-Security-Policy"]).to include("font-src 'self' https://font.example.org localhost:* #{organization.host}:*;")
          expect(response.headers["Content-Security-Policy"]).to include("frame-src 'self' www.youtube-nocookie.com player.vimeo.com https://frame.example.org localhost:* #{organization.host}:* www.example.org;")
          expect(response.headers["Content-Security-Policy"]).to include("media-src 'self' https://example.org localhost:* #{organization.host}:*")
        end
      end
    end
  end
end
