// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model.container.xml;

import com.google.common.collect.ImmutableSet;
import com.yahoo.collections.CollectionUtil;
import com.yahoo.config.model.builder.xml.test.DomBuilderTest;
import com.yahoo.container.jdisc.state.StateHandler;
import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.container.http.AccessControl;
import com.yahoo.vespa.model.container.http.Http;
import com.yahoo.vespa.model.container.http.Http.Binding;
import org.junit.Test;
import org.w3c.dom.Element;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

/**
 * @author gjoranv
 */
public class AccessControlTest extends ContainerModelBuilderTestBase {

    private static final Set<String> REQUIRED_BINDINGS = ImmutableSet.of(
            "/custom-handler/",
            "/search/",
            "/feed/",
            "/remove/",
            "/removelocation/",
            "/get/",
            "/visit/",
            "/document/",
            "/feedstatus/",
            ContainerCluster.RESERVED_URI_PREFIX);

    private static final Set<String> FORBIDDEN_BINDINGS = ImmutableSet.of(
            "/ApplicationStatus",
            "/status.html",
            "/statistics/",
            StateHandler.STATE_API_ROOT,
            ContainerCluster.ROOT_HANDLER_BINDING);

    @Test
    public void access_control_filter_chain_is_set_up() throws Exception {
        Element clusterElem = DomBuilderTest.parse(
                "<jdisc id='default' version='1.0'>",
                "  <http>",
                "    <filtering>",
                "      <access-control domain='foo' />",
                "    </filtering>",
                "  </http>",
                "</jdisc>");

        createModel(root, clusterElem);
        ContainerCluster cluster = (ContainerCluster) root.getChildren().get("default");
        Http http = cluster.getHttp();
        assertNotNull(http);

        assertTrue(http.getFilterChains().hasChain(AccessControl.ACCESS_CONTROL_CHAIN_ID));
    }

    @Test
    public void access_control_filter_chain_has_correct_handler_bindings() throws Exception {
        Element clusterElem = DomBuilderTest.parse(
                "<jdisc id='default' version='1.0'>",
                "  <search/>",
                "  <document-api/>",
                "  <handler id='custom.Handler'>",
                "    <binding>http://*/custom-handler/*</binding>",
                "  </handler>",
                "  <http>",
                "    <filtering>",
                "      <access-control domain='foo' />",
                "    </filtering>",
                "  </http>",
                "</jdisc>");

        createModel(root, clusterElem);
        ContainerCluster cluster = (ContainerCluster) root.getChildren().get("default");
        Http http = cluster.getHttp();
        assertNotNull(http);

        Set<String> foundRequiredBindings = REQUIRED_BINDINGS.stream()
                .filter(requiredBinding -> containsBinding(http.getBindings(), requiredBinding))
                .collect(Collectors.toSet());
        Set<String> missingRequiredBindings = new HashSet<>(REQUIRED_BINDINGS);
        missingRequiredBindings.removeAll(foundRequiredBindings);
        assertTrue("Access control chain was not bound to: " + CollectionUtil.mkString(missingRequiredBindings, ", "),
                   missingRequiredBindings.isEmpty());

        FORBIDDEN_BINDINGS.forEach(forbiddenBinding -> http.getBindings().forEach(
                binding -> assertFalse("Access control chain was bound to: " + binding.binding,
                                       binding.binding.contains(forbiddenBinding))));
    }

    @Test
    public void handler_can_be_excluded_by_excluding_one_of_its_bindings() throws Exception {
        final String notExcludedBinding = "http://*/custom-handler/*";
        final String excludedBinding = "http://*/excluded/*";
        Element clusterElem = DomBuilderTest.parse(
                "<jdisc id='default' version='1.0'>",
                "  <handler id='custom.Handler'>",
                "    <binding>" + notExcludedBinding + "</binding>",
                "    <binding>" + excludedBinding + "</binding>",
                "  </handler>",
                "  <http>",
                "    <filtering>",
                "      <access-control domain='foo'>",
                "        <exclude>",
                "          <binding>" + excludedBinding + "</binding>",
                "        </exclude>",
                "      </access-control>",
                "    </filtering>",
                "  </http>",
                "</jdisc>");

        createModel(root, clusterElem);
        ContainerCluster cluster = (ContainerCluster) root.getChildren().get("default");
        Http http = cluster.getHttp();
        assertNotNull(http);

        assertFalse("Excluded binding was not removed.",
                    containsBinding(http.getBindings(), excludedBinding));
        assertFalse("Not all bindings of an excluded handler was removed.",
                    containsBinding(http.getBindings(), notExcludedBinding));

    }

    private boolean containsBinding(Collection<Binding> bindings, String binding) {
        for (Binding b : bindings) {
            if (b.binding.contains(binding))
                return true;
        }
        return false;
    }

}

@@ -636,16 +636,77 @@
 lement;%0A
+import org.xml.sax.SAXException;%0A%0Aimport java.io.IOException;
 %0Aimport 
@@ -1818,37 +1818,24 @@
      %22%3Cjdisc
- id='default'
  version='1.
@@ -2067,198 +2067,42 @@
     
-createModel(root, clusterElem);%0A        ContainerCluster cluster = (ContainerCluster) root.getChildren().get(%22default%22);%0A        Http http = cluster.getHttp();%0A        assertNotNull(http
+Http http = getHttp(clusterElem
 );%0A
-%0A
     
@@ -2371,37 +2371,24 @@
      %22%3Cjdisc
- id='default'
  version='1.
@@ -2839,194 +2839,39 @@
     
-createModel(root, clusterElem);%0A        ContainerCluster cluster = (ContainerCluster) root.getChildren().get(%22default%22);%0A        Http http = cluster.getHttp();%0A        assertNotNull(http
+Http http = getHttp(clusterElem
 );%0A%0A
@@ -3993,21 +3993,8 @@
 disc
- id='default'
  ver
@@ -4640,198 +4640,42 @@
     
-createModel(root, clusterElem);%0A        ContainerCluster cluster = (ContainerCluster) root.getChildren().get(%22default%22);%0A        Http http = cluster.getHttp();%0A        assertNotNull(http
+Http http = getHttp(clusterElem
 );%0A
-%0A
     
@@ -4960,24 +4960,328 @@
 ));%0A%0A    %7D%0A%0A
+    private Http getHttp(Element clusterElem) throws SAXException, IOException %7B%0A        createModel(root, clusterElem);%0A        ContainerCluster cluster = (ContainerCluster) root.getChildren().get(%22jdisc%22);%0A        Http http = cluster.getHttp();%0A        assertNotNull(http);%0A        return http;%0A    %7D%0A%0A
     private 

Simplify tests.