diff options
author | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2020-01-10 17:56:12 +0100 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2020-01-13 16:26:40 +0100 |
commit | e4f98750a282bbd66482d0e7a1cd16e454728e85 (patch) | |
tree | 4438f29f4b3cac5ae3f56c4a5bc7a2233eae1ec9 /config-model | |
parent | ff6f9d5a0064b6084c66e0b8dde45ca0f35dfd58 (diff) |
Validate presence of http filter when 'access-control' is enabled
Add AccessControlFilterValidator that verifies precense of http filter.
Remove use of 'access-control' in ImplicitIndexingClusterTest.
Introduce HostedConfigModelRegistry for hosted specific tests using 'access-control'.
Diffstat (limited to 'config-model')
6 files changed, 209 insertions, 8 deletions
diff --git a/config-model/src/main/java/com/yahoo/config/model/test/HostedConfigModelRegistry.java b/config-model/src/main/java/com/yahoo/config/model/test/HostedConfigModelRegistry.java new file mode 100644 index 00000000000..9099a527dea --- /dev/null +++ b/config-model/src/main/java/com/yahoo/config/model/test/HostedConfigModelRegistry.java @@ -0,0 +1,19 @@ +// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.test; + +import com.yahoo.config.model.ConfigModelRegistry; +import com.yahoo.config.model.MapConfigModelRegistry; + +/** + * Creates a {@link ConfigModelRegistry} instance that simulates the hosted environment. + * + * @author bjorncs + */ +public class HostedConfigModelRegistry { + + private HostedConfigModelRegistry() {} + + public static ConfigModelRegistry create() { + return MapConfigModelRegistry.createFromList(new ModelBuilderAddingAccessControlFilter()); + } +} diff --git a/config-model/src/main/java/com/yahoo/config/model/test/ModelBuilderAddingAccessControlFilter.java b/config-model/src/main/java/com/yahoo/config/model/test/ModelBuilderAddingAccessControlFilter.java new file mode 100644 index 00000000000..c69efd5d447 --- /dev/null +++ b/config-model/src/main/java/com/yahoo/config/model/test/ModelBuilderAddingAccessControlFilter.java @@ -0,0 +1,86 @@ +// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.model.test; + +import com.yahoo.component.ComponentId; +import com.yahoo.component.ComponentSpecification; +import com.yahoo.component.chain.dependencies.Dependencies; +import com.yahoo.component.chain.model.ChainedComponentModel; +import com.yahoo.config.model.ConfigModel; +import com.yahoo.config.model.ConfigModelContext; +import com.yahoo.config.model.builder.xml.ConfigModelBuilder; +import com.yahoo.config.model.builder.xml.ConfigModelId; +import com.yahoo.container.bundle.BundleInstantiationSpecification; +import com.yahoo.vespa.model.container.ApplicationContainerCluster; +import com.yahoo.vespa.model.container.ContainerModel; +import com.yahoo.vespa.model.container.component.chain.Chain; +import com.yahoo.vespa.model.container.http.AccessControl; +import com.yahoo.vespa.model.container.http.Filter; +import com.yahoo.vespa.model.container.http.Http; +import com.yahoo.vespa.model.container.xml.ContainerModelBuilder; +import org.w3c.dom.Element; + +import java.util.Collection; +import java.util.List; + +/** + * A {@link ConfigModelBuilder} that configures a dummy filter component to the {@link AccessControl#ACCESS_CONTROL_CHAIN_ID} filter chain. + * + * @author bjorncs + */ +public class ModelBuilderAddingAccessControlFilter + extends ConfigModelBuilder<ModelBuilderAddingAccessControlFilter.ModelPlaceholder> { + + public ModelBuilderAddingAccessControlFilter() { + super(ModelPlaceholder.class); + } + + @Override + public List<ConfigModelId> handlesElements() { return ContainerModelBuilder.configModelIds; } + + @Override + public void doBuild(ModelPlaceholder model, Element spec, ConfigModelContext modelContext) { + for (ContainerModel containerModel : model.containers) { + addFilterToContainerCluster(containerModel); + } + } + + private static void addFilterToContainerCluster(ContainerModel containerModel) { + if (!(containerModel.getCluster() instanceof ApplicationContainerCluster)) return; + ApplicationContainerCluster cluster = (ApplicationContainerCluster) containerModel.getCluster(); + Http http = cluster.getHttp(); + if (http.getAccessControl().isPresent()) { + Chain<Filter> chain = http.getFilterChains() + .allChains() + .getComponent(AccessControl.ACCESS_CONTROL_CHAIN_ID); + if (chain == null) return; + if (!chain.getInnerComponents().isEmpty()) return; + chain.addInnerComponent(new DummyAccessControlFilterModel()); + } + } + + public static class ModelPlaceholder extends ConfigModel { + final Collection<ContainerModel> containers; + + public ModelPlaceholder(ConfigModelContext modelContext, Collection<ContainerModel> containers) { + super(modelContext); + this.containers = containers; + } + + @Override + public boolean isServing() { return false; } + } + + private static class DummyAccessControlFilterModel extends Filter { + + DummyAccessControlFilterModel() { super(createDummyComponentModel()); } + + static ChainedComponentModel createDummyComponentModel() { + return new ChainedComponentModel( + new BundleInstantiationSpecification( + new ComponentId("dummy-filter"), + new ComponentSpecification("com.test.DummyAccessControlFilter"), + new ComponentSpecification("dummy-bundle")), + Dependencies.emptyDependencies()); + } + } +} diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/AccessControlFilterValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/AccessControlFilterValidator.java new file mode 100644 index 00000000000..774f0ba52bc --- /dev/null +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/AccessControlFilterValidator.java @@ -0,0 +1,39 @@ +// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.model.application.validation; + +import com.yahoo.config.model.deploy.DeployState; +import com.yahoo.vespa.model.VespaModel; +import com.yahoo.vespa.model.container.component.chain.Chain; +import com.yahoo.vespa.model.container.http.AccessControl; +import com.yahoo.vespa.model.container.http.Filter; +import com.yahoo.vespa.model.container.http.FilterChains; +import com.yahoo.vespa.model.container.http.Http; + +/** + * Validates that 'access-control' is not enabled when no access control filter implementation is available. + * + * @author bjorncs + */ +public class AccessControlFilterValidator extends Validator { + + @Override + public void validate(VespaModel model, DeployState deployState) { + model.getContainerClusters().forEach((id, cluster) -> { + Http http = cluster.getHttp(); + if (http != null) { + if (http.getAccessControl().isPresent()) { + verifyAccessControlFilterPresent(http); + } + } + }); + } + + private static void verifyAccessControlFilterPresent(Http http) { + FilterChains filterChains = http.getFilterChains(); + Chain<Filter> chain = filterChains.allChains().getComponent(AccessControl.ACCESS_CONTROL_CHAIN_ID); + if (chain.getInnerComponents().isEmpty()) { + // No access control filter configured - it's up to a config model plugin to provide an implementation of an access control filter. + throw new IllegalArgumentException("The 'access-control' feature is not available in open-source Vespa."); + } + } +} diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java index dde2f62ee8f..4959970d98e 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java @@ -57,6 +57,7 @@ public class Validation { new RankingConstantsValidator().validate(model, deployState); new SecretStoreValidator().validate(model, deployState); new TlsSecretsValidator().validate(model, deployState); + new AccessControlFilterValidator().validate(model, deployState); List<ConfigChangeAction> result = Collections.emptyList(); if (deployState.getProperties().isFirstTimeDeployment()) { diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/AccessControlFilterValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/AccessControlFilterValidatorTest.java new file mode 100644 index 00000000000..8fad2aa9af4 --- /dev/null +++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/AccessControlFilterValidatorTest.java @@ -0,0 +1,61 @@ +package com.yahoo.vespa.model.application.validation;// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +import com.yahoo.config.model.MapConfigModelRegistry; +import com.yahoo.config.model.NullConfigModelRegistry; +import com.yahoo.config.model.deploy.DeployState; +import com.yahoo.config.model.test.MockApplicationPackage; +import com.yahoo.config.model.test.ModelBuilderAddingAccessControlFilter; +import com.yahoo.vespa.model.VespaModel; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.xml.sax.SAXException; + +import java.io.IOException; + +/** + * @author bjorncs + */ +public class AccessControlFilterValidatorTest { + + private static final String SERVICES_XML = String.join( + "\n", + "<services version='1.0'>", + " <container id='container-cluster-with-access-control' version='1.0'>", + " <http>", + " <filtering>", + " <access-control domain='foo' read='true' write='true'/>", + " </filtering>", + " </http>", + " </container>", + "</services>"); + + @Rule + public ExpectedException exceptionRule = ExpectedException.none(); + + @Test + public void validator_fails_with_empty_access_control_filter_chain() throws IOException, SAXException { + DeployState deployState = createDeployState(); + VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState); + + exceptionRule.expect(IllegalArgumentException.class); + exceptionRule.expectMessage("The 'access-control' feature is not available in open-source Vespa."); + new AccessControlFilterValidator().validate(model, deployState); + } + + @Test + public void validator_accepts_non_empty_access_control_filter_chain() throws IOException, SAXException { + DeployState deployState = createDeployState(); + VespaModel model = new VespaModel( + MapConfigModelRegistry.createFromList(new ModelBuilderAddingAccessControlFilter()), + deployState); + + new AccessControlFilterValidator().validate(model, deployState); + } + + private static DeployState createDeployState() { + return new DeployState.Builder() + .applicationPackage(new MockApplicationPackage.Builder().withServices(SERVICES_XML).build()) + .build(); + } +}
\ No newline at end of file diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/search/ImplicitIndexingClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/search/ImplicitIndexingClusterTest.java index 66ed86e3eaa..30a36715a17 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/search/ImplicitIndexingClusterTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/search/ImplicitIndexingClusterTest.java @@ -24,7 +24,9 @@ public class ImplicitIndexingClusterTest { " <container version=\"1.0\" id=\"jdisc\">\n" + // " <search />\n" + // " <nodes count=\"1\" />\n" + // - ACCESS_CONTROL_XML + // + " <http>\n" + // + " <server id=\"bar\" port=\"4080\" />\n" + // + " </http>\n" + // " </container>\n" + // " <content id=\"music\" version=\"1.0\">\n" + // " <redundancy>1</redundancy>\n" + // @@ -42,13 +44,6 @@ public class ImplicitIndexingClusterTest { assertNotNull("Indexing chain not added to jdisc", jdisc.getDocprocChains().allChains().getComponent("indexing")); } - private final String ACCESS_CONTROL_XML = "<http>\n" +// - " <filtering>\n" +// - " <access-control domain=\"foo\" />\n" +// - " </filtering>\n" +// - " <server id=\"bar\" port=\"4080\" />\n" +// - "</http>\n"; - private static VespaModel buildMultiTenantVespaModel(String servicesXml) { ModelContext.Properties properties = new TestProperties().setMultitenant(true).setHostedVespa(true); DeployState.Builder deployStateBuilder = new DeployState.Builder() |