summaryrefslogtreecommitdiffstats
path: root/config-model
diff options
context:
space:
mode:
authorgjoranv <gv@yahoo-inc.com>2017-02-22 15:32:54 +0100
committergjoranv <gv@yahoo-inc.com>2017-02-22 15:32:54 +0100
commit30633fe895a45a8772ce53bee594868b96982ac8 (patch)
tree468482518f2dc55bda7e21471cccb040861fc86f /config-model
parent9112dca9392e634f49cb27e502d58002f32abdde (diff)
Support excluding access control bindings for servlets and rest-apis.
Diffstat (limited to 'config-model')
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/test/ConfigModelTestUtil.java5
-rwxr-xr-xconfig-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java5
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/Servlet.java2
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java37
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java8
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/jersey/Jersey2Servlet.java3
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java88
7 files changed, 131 insertions, 17 deletions
diff --git a/config-model/src/main/java/com/yahoo/config/model/test/ConfigModelTestUtil.java b/config-model/src/main/java/com/yahoo/config/model/test/ConfigModelTestUtil.java
index b75147774ab..f9ed7d329d3 100644
--- a/config-model/src/main/java/com/yahoo/config/model/test/ConfigModelTestUtil.java
+++ b/config-model/src/main/java/com/yahoo/config/model/test/ConfigModelTestUtil.java
@@ -33,8 +33,11 @@ public class ConfigModelTestUtil {
}
}
+ public static String joinLines(CharSequence... lines) {
+ return String.join("\n", lines);
+ }
+
private static InputSource inputSource(String str) {
return new InputSource(new StringReader(str));
}
-
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
index 993798789a4..6015aaaaa20 100755
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java
@@ -511,6 +511,11 @@ public final class ContainerCluster
return (Collection<Handler<?>>)(Collection)componentGroup.getComponents(Handler.class);
}
+ // Returns all servlets, including rest-api/jersey servlets.
+ public Collection<Servlet> getAllServlets() {
+ return allServlets().collect(Collectors.toCollection(ArrayList::new));
+ }
+
public Map<ComponentId, Component<?, ?>> getComponentsMap() {
return componentGroup.getComponentMap();
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/Servlet.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/Servlet.java
index 68ba3436209..c321ce84669 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/Servlet.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/Servlet.java
@@ -9,7 +9,7 @@ import com.yahoo.osgi.provider.model.ComponentModel;
* @author stiankri
*/
public class Servlet extends SimpleComponent {
- private final String bindingPath;
+ public final String bindingPath;
public Servlet(ComponentModel componentModel, String bindingPath) {
super(componentModel);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java
index 0b8b560382c..a2cc89092a8 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/AccessControl.java
@@ -7,8 +7,10 @@ import com.yahoo.component.ComponentSpecification;
import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.container.component.FileStatusHandlerComponent;
import com.yahoo.vespa.model.container.component.Handler;
+import com.yahoo.vespa.model.container.component.Servlet;
import com.yahoo.vespa.model.container.http.Http.Binding;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
@@ -16,6 +18,7 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Helper class for http access control.
@@ -42,6 +45,7 @@ public final class AccessControl {
private boolean writeEnabled = true;
private final Set<String> excludeBindings = new LinkedHashSet<>();
private Collection<Handler<?>> handlers = Collections.emptyList();
+ private Collection<Servlet> servlets = Collections.emptyList();
public Builder(String domain, String applicationId) {
this.domain = domain;
@@ -73,9 +77,14 @@ public final class AccessControl {
return this;
}
+ public Builder setServlets(Collection<Servlet> servlets) {
+ this.servlets = servlets;
+ return this;
+ }
+
public AccessControl build() {
return new AccessControl(domain, applicationId, writeEnabled, readEnabled,
- excludeBindings, vespaDomain, handlers);
+ excludeBindings, vespaDomain, servlets, handlers);
}
}
@@ -86,6 +95,7 @@ public final class AccessControl {
public final Optional<String> vespaDomain;
private final Set<String> excludedBindings;
private final Collection<Handler<?>> handlers;
+ private final Collection<Servlet> servlets;
private AccessControl(String domain,
String applicationId,
@@ -93,6 +103,7 @@ public final class AccessControl {
boolean readEnabled,
Set<String> excludedBindings,
Optional<String> vespaDomain,
+ Collection<Servlet> servlets,
Collection<Handler<?>> handlers) {
this.domain = domain;
this.applicationId = applicationId;
@@ -101,14 +112,26 @@ public final class AccessControl {
this.excludedBindings = Collections.unmodifiableSet(excludedBindings);
this.vespaDomain = vespaDomain;
this.handlers = handlers;
+ this.servlets = servlets;
}
public List<Binding> getBindings() {
+ return Stream.concat(getHandlerBindings(), getServletBindings())
+ .collect(Collectors.toCollection(ArrayList::new));
+ }
+
+ private Stream<Binding> getHandlerBindings() {
return handlers.stream()
.filter(this::shouldHandlerBeProtected)
.flatMap(handler -> handler.getServerBindings().stream())
- .map(AccessControl::accessControlBinding)
- .collect(Collectors.toList());
+ .map(AccessControl::accessControlBinding);
+ }
+
+ private Stream<Binding> getServletBindings() {
+ return servlets.stream()
+ .filter(this::shouldServletBeProtected)
+ .flatMap(AccessControl::servletBindings)
+ .map(AccessControl::accessControlBinding);
}
private boolean shouldHandlerBeProtected(Handler<?> handler) {
@@ -116,7 +139,15 @@ public final class AccessControl {
&& handler.getServerBindings().stream().noneMatch(excludedBindings::contains);
}
+ private boolean shouldServletBeProtected(Servlet servlet) {
+ return servletBindings(servlet).noneMatch(excludedBindings::contains);
+ }
+
private static Binding accessControlBinding(String binding) {
return new Binding(new ComponentSpecification(ACCESS_CONTROL_CHAIN_ID.stringValue()), binding);
}
+
+ private static Stream<String> servletBindings(Servlet servlet) {
+ return Stream.of("http://*/", "https://*/").map(protocol -> protocol + servlet.bindingPath);
+ }
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java
index 70246584cbf..e3847e4c7bc 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/HttpBuilder.java
@@ -20,7 +20,6 @@ import com.yahoo.vespa.model.container.http.Http.Binding;
import org.w3c.dom.Element;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Optional;
@@ -67,9 +66,10 @@ public class HttpBuilder extends VespaDomBuilder.DomConfigProducerBuilder<Http>
AccessControl.Builder builder = new AccessControl.Builder(accessControlElem.getAttribute("domain"), application);
- builder.setHandlers(getContainerCluster(ancestor)
- .map(ContainerCluster::getHandlers)
- .orElse(Collections.emptyList()));
+ getContainerCluster(ancestor).ifPresent(cluster -> {
+ builder.setHandlers(cluster.getHandlers());
+ builder.setServlets(cluster.getAllServlets());
+ });
XmlHelper.getOptionalAttribute(accessControlElem, "read").ifPresent(
readAttr -> builder.readEnabled(Boolean.valueOf(readAttr)));
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/jersey/Jersey2Servlet.java b/config-model/src/main/java/com/yahoo/vespa/model/container/jersey/Jersey2Servlet.java
index d7c9482cab4..8c01943484c 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/jersey/Jersey2Servlet.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/jersey/Jersey2Servlet.java
@@ -15,6 +15,7 @@ public class Jersey2Servlet extends Servlet {
public static final String BUNDLE = "container-jersey2";
public static final String CLASS = "com.yahoo.container.servlet.jersey.JerseyServletProvider";
+ public static final String BINDING_SUFFIX = "/*";
private static final ComponentId REST_API_NAMESPACE = ComponentId.fromString("rest-api");
@@ -23,7 +24,7 @@ public class Jersey2Servlet extends Servlet {
new BundleInstantiationSpecification(idSpecFromPath(bindingPath),
ComponentSpecification.fromString(CLASS),
ComponentSpecification.fromString(BUNDLE))),
- bindingPath + "/*");
+ bindingPath + BINDING_SUFFIX);
}
private static ComponentSpecification idSpecFromPath(String path) {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java
index c02e333c5fc..9c20ebe3a18 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/AccessControlTest.java
@@ -4,12 +4,14 @@ 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.config.model.test.ConfigModelTestUtil;
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 com.yahoo.vespa.model.container.http.xml.HttpBuilder;
+import com.yahoo.vespa.model.container.jersey.Jersey2Servlet;
import org.junit.Test;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
@@ -154,29 +156,102 @@ public class AccessControlTest extends ContainerModelBuilderTestBase {
final String excludedBinding = "http://*/excluded/*";
Element clusterElem = DomBuilderTest.parse(
"<jdisc version='1.0'>",
+ httpWithExcludedBinding(excludedBinding),
" <handler id='custom.Handler'>",
" <binding>" + notExcludedBinding + "</binding>",
" <binding>" + excludedBinding + "</binding>",
" </handler>",
+ "</jdisc>");
+
+ Http http = getHttp(clusterElem);
+ assertFalse("Excluded binding was not removed.",
+ containsBinding(http.getBindings(), excludedBinding));
+ assertFalse("Not all bindings of an excluded handler were removed.",
+ containsBinding(http.getBindings(), notExcludedBinding));
+
+ }
+
+ @Test
+ public void access_control_filter_chain_has_all_servlet_bindings() throws Exception {
+ final String servletPath = "servlet/path";
+ final String restApiPath = "api/v0";
+ final Set<String> requiredBindings = ImmutableSet.of(servletPath, restApiPath);
+ Element clusterElem = DomBuilderTest.parse(
+ "<jdisc version='1.0'>",
+ " <servlet id='foo' class='bar' bundle='baz'>",
+ " <path>" + servletPath + "</path>",
+ " </servlet>",
+ " <rest-api jersey2='true' path='" + restApiPath + "' />",
" <http>",
" <filtering>",
- " <access-control domain='foo'>",
- " <exclude>",
- " <binding>" + excludedBinding + "</binding>",
- " </exclude>",
- " </access-control>",
+ " <access-control domain='foo' />",
" </filtering>",
" </http>",
"</jdisc>");
Http http = getHttp(clusterElem);
+
+ Set<String> missingRequiredBindings = requiredBindings.stream()
+ .filter(requiredBinding -> ! containsBinding(http.getBindings(), requiredBinding))
+ .collect(Collectors.toSet());
+
+ assertTrue("Access control chain was not bound to: " + CollectionUtil.mkString(missingRequiredBindings, ", "),
+ missingRequiredBindings.isEmpty());
+ }
+
+ @Test
+ public void servlet_can_be_excluded_by_excluding_one_of_its_bindings() throws Exception {
+ final String servletPath = "servlet/path";
+ final String notExcludedBinding = "https://*/" + servletPath;
+ final String excludedBinding = "http://*/" + servletPath;
+ Element clusterElem = DomBuilderTest.parse(
+ "<jdisc version='1.0'>",
+ httpWithExcludedBinding(excludedBinding),
+ " <servlet id='foo' class='bar' bundle='baz'>",
+ " <path>" + servletPath + "</path>",
+ " </servlet>",
+ "</jdisc>");
+
+ Http http = getHttp(clusterElem);
assertFalse("Excluded binding was not removed.",
containsBinding(http.getBindings(), excludedBinding));
- assertFalse("Not all bindings of an excluded handler was removed.",
+ assertFalse("Not all bindings of an excluded servlet were removed.",
containsBinding(http.getBindings(), notExcludedBinding));
}
+ @Test
+ public void rest_api_can_be_excluded_by_excluding_one_of_its_bindings() throws Exception {
+ final String restApiPath = "api/v0";
+ final String notExcludedBinding = "http://*/" + restApiPath + Jersey2Servlet.BINDING_SUFFIX;;
+ final String excludedBinding = "https://*/" + restApiPath + Jersey2Servlet.BINDING_SUFFIX;;
+ Element clusterElem = DomBuilderTest.parse(
+ "<jdisc version='1.0'>",
+ httpWithExcludedBinding(excludedBinding),
+ " <rest-api jersey2='true' path='" + restApiPath + "' />",
+ "</jdisc>");
+
+ Http http = getHttp(clusterElem);
+ assertFalse("Excluded binding was not removed.",
+ containsBinding(http.getBindings(), excludedBinding));
+ assertFalse("Not all bindings of an excluded rest-api were removed.",
+ containsBinding(http.getBindings(), notExcludedBinding));
+
+ }
+
+ private String httpWithExcludedBinding(String excludedBinding) {
+ return ConfigModelTestUtil.joinLines(
+ " <http>",
+ " <filtering>",
+ " <access-control domain='foo'>",
+ " <exclude>",
+ " <binding>" + excludedBinding + "</binding>",
+ " </exclude>",
+ " </access-control>",
+ " </filtering>",
+ " </http>");
+ }
+
private Http getHttp(Element clusterElem) throws SAXException, IOException {
createModel(root, clusterElem);
ContainerCluster cluster = (ContainerCluster) root.getChildren().get("jdisc");
@@ -192,5 +267,4 @@ public class AccessControlTest extends ContainerModelBuilderTestBase {
}
return false;
}
-
}