diff options
author | Øyvind Grønnesby <oyving@verizonmedia.com> | 2020-03-03 09:53:37 +0100 |
---|---|---|
committer | Øyvind Grønnesby <oyving@verizonmedia.com> | 2020-03-03 09:53:37 +0100 |
commit | c504788f5f61acdca835e97a56263d5f2af686b2 (patch) | |
tree | 82847d29f324184f386a2a7c9053a3f8f5fc0160 /controller-server | |
parent | 25707b0248f895e17058c09a782e1c88914a542f (diff) |
Fail early on missing security/clients.pem in application package
Diffstat (limited to 'controller-server')
5 files changed, 92 insertions, 9 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java index 1c8279f6d8d..98808ea5686 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java @@ -1956,6 +1956,12 @@ public class ApplicationApiHandler extends LoggingRequestHandler { ApplicationPackage applicationPackage = new ApplicationPackage(dataParts.get(EnvironmentResource.APPLICATION_ZIP), true); + // if we are in public and 'security/clients.pem' is not present (i.e. we don't have any trusted + // certificates), we should fail early instead of waiting for the configserver to fail. + if (controller.system().isPublic() && applicationPackage.trustedCertificates().isEmpty()) { + throw new IllegalArgumentException("Missing required file 'security/clients.pem'"); + } + controller.applications().verifyApplicationIdentityConfiguration(TenantName.from(tenant), Optional.empty(), Optional.empty(), diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java index df8787a2a4b..5c80e7e2524 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerTester.java @@ -5,24 +5,17 @@ import com.yahoo.application.container.JDisc; import com.yahoo.application.container.handler.Request; import com.yahoo.application.container.handler.Response; import com.yahoo.component.ComponentSpecification; -import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationName; -import com.yahoo.config.provision.zone.ZoneApi; import com.yahoo.container.http.filter.FilterChainRepository; import com.yahoo.jdisc.http.filter.SecurityRequestFilter; import com.yahoo.jdisc.http.filter.SecurityRequestFilterChain; import com.yahoo.vespa.athenz.api.AthenzDomain; import com.yahoo.vespa.athenz.api.AthenzIdentity; import com.yahoo.vespa.hosted.controller.Controller; -import com.yahoo.vespa.hosted.controller.api.identifiers.ScrewdriverId; import com.yahoo.vespa.hosted.controller.api.integration.athenz.ApplicationAction; import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactoryMock; -import com.yahoo.vespa.hosted.controller.application.SystemApplication; -import com.yahoo.vespa.hosted.controller.athenz.HostedAthenzIdentities; import com.yahoo.vespa.hosted.controller.integration.ConfigServerMock; import com.yahoo.vespa.hosted.controller.integration.ServiceRegistryMock; -import com.yahoo.vespa.hosted.controller.versions.ControllerVersion; -import com.yahoo.vespa.hosted.controller.versions.VersionStatus; import org.junit.ComparisonFailure; import java.io.File; diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerCloudTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerCloudTest.java index 16447f47ee8..72b2014894d 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerCloudTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerCloudTest.java @@ -1,12 +1,14 @@ // Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.restapi; +import ai.vespa.hosted.api.MultiPartStreamer; import com.yahoo.application.container.handler.Request; import com.yahoo.config.provision.SystemName; import com.yahoo.vespa.hosted.controller.api.integration.user.User; import com.yahoo.vespa.hosted.controller.api.role.Role; import com.yahoo.vespa.hosted.controller.api.role.SecurityContext; import com.yahoo.vespa.hosted.controller.api.role.SimplePrincipal; +import com.yahoo.yolean.Exceptions; import java.nio.charset.StandardCharsets; import java.security.Principal; @@ -68,12 +70,17 @@ public class ControllerContainerCloudTest extends ControllerContainerTest { private Principal principal = () -> "user@test"; private User user; private Set<Role> roles = Set.of(Role.everyone()); + private String contentType; private RequestBuilder(String path, Request.Method method) { this.path = path; this.method = method; } + public RequestBuilder contentType(String contentType) { this.contentType = contentType; return this; } + public RequestBuilder data(MultiPartStreamer streamer) { + return Exceptions.uncheck(() -> data(streamer.data().readAllBytes()).contentType(streamer.contentType())); + } public RequestBuilder data(byte[] data) { this.data = data; return this; } public RequestBuilder data(String data) { this.data = data.getBytes(StandardCharsets.UTF_8); return this; } public RequestBuilder principal(String principal) { this.principal = new SimplePrincipal(principal); return this; } @@ -85,7 +92,7 @@ public class ControllerContainerCloudTest extends ControllerContainerTest { Request request = new Request("http://localhost:8080" + path, data, method, principal); request.getAttributes().put(SecurityContext.ATTRIBUTE_NAME, new SecurityContext(principal, roles)); if (user != null) request.getAttributes().put(User.ATTRIBUTE_NAME, user); - request.getHeaders().put("Content-Type", "application/json"); + request.getHeaders().put("Content-Type", contentType); return request; } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java new file mode 100644 index 00000000000..ef7c66e22b3 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java @@ -0,0 +1,77 @@ +package com.yahoo.vespa.hosted.controller.restapi.application; + +import com.yahoo.config.provision.ApplicationName; +import com.yahoo.config.provision.Environment; +import com.yahoo.config.provision.TenantName; +import com.yahoo.vespa.hosted.controller.api.integration.user.User; +import com.yahoo.vespa.hosted.controller.ControllerTester; +import com.yahoo.vespa.hosted.controller.api.role.Role; +import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId; +import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; +import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; +import com.yahoo.vespa.hosted.controller.restapi.ContainerTester; +import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerCloudTest; +import com.yahoo.vespa.hosted.controller.security.CloudTenantSpec; +import com.yahoo.vespa.hosted.controller.security.Credentials; +import org.junit.Before; +import org.junit.Test; + +import java.util.Set; + +import static com.yahoo.application.container.handler.Request.Method.POST; +import static com.yahoo.vespa.hosted.controller.restapi.application.ApplicationApiTest.createApplicationSubmissionData; + +public class ApplicationApiCloudTest extends ControllerContainerCloudTest { + + private static final String responseFiles = "src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/"; + + private ContainerTester tester; + private DeploymentTester deploymentTester; + + private static final TenantName tenantName = TenantName.from("scoober"); + private static final ApplicationName applicationName = ApplicationName.from("albums"); + private static final User developerUser = new User("developer", "Joe Developer", "", ""); + + @Before + public void before() { + tester = new ContainerTester(container, responseFiles); + deploymentTester = new DeploymentTester(new ControllerTester(tester)); + deploymentTester.controllerTester().computeVersionStatus(); + } + + @Test + public void test_missing_security_clients_pem() { + setupTenantAndApplication(); + + var application = prodBuilder().build(); + + var deployRequest = request("/application/v4/tenant/scoober/application/albums/submit", POST) + .data(createApplicationSubmissionData(application, 0)) + .roles(Set.of(Role.developer(tenantName))); + + tester.assertResponse( + deployRequest, + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Missing required file 'security/clients.pem'\"}", + 400); + } + + + private ApplicationPackageBuilder prodBuilder() { + return new ApplicationPackageBuilder() + .instances("default") + .environment(Environment.prod) + .region("aws-us-east-1c"); + } + + private void setupTenantAndApplication() { + var tenantSpec = new CloudTenantSpec(tenantName, ""); + tester.controller().tenants().create(tenantSpec, credentials("developer@scoober")); + + var appId = TenantAndApplicationId.from(tenantName, applicationName); + tester.controller().applications().createApplication(appId, credentials("developer@scoober")); + } + + private static Credentials credentials(String name) { + return new Credentials(() -> name); + } +} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java index 45d87c6756a..407cae2bd3a 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java @@ -1502,7 +1502,7 @@ public class ApplicationApiTest extends ControllerContainerTest { return streamer; } - private MultiPartStreamer createApplicationSubmissionData(ApplicationPackage applicationPackage, long projectId) { + static MultiPartStreamer createApplicationSubmissionData(ApplicationPackage applicationPackage, long projectId) { return new MultiPartStreamer().addJson(EnvironmentResource.SUBMIT_OPTIONS, "{\"repository\":\"repository1\",\"branch\":\"master\",\"commit\":\"commit1\"," + "\"projectId\":" + projectId + ",\"authorEmail\":\"a@b\"}") .addBytes(EnvironmentResource.APPLICATION_ZIP, applicationPackage.zippedContent()) |