summaryrefslogtreecommitdiffstats
path: root/athenz-identity-provider-service/src/test/java
diff options
context:
space:
mode:
Diffstat (limited to 'athenz-identity-provider-service/src/test/java')
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java18
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java109
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/ContainerTester.java9
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java30
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/InstanceValidatorMock.java27
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/PrincipalFromHeaderFilter.java34
6 files changed, 206 insertions, 21 deletions
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java
index 5ce0f3cdd7e..a35dfd878c5 100644
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java
@@ -58,10 +58,12 @@ public class InstanceValidatorTest {
private final String domain = "domain";
private final String service = "service";
+ private final AthenzService vespaTenantDomain = new AthenzService("vespa.vespa.tenant");
+
@Test
public void application_does_not_exist() {
SuperModelProvider superModelProvider = mockSuperModelProvider();
- InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, null);
+ InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, null, vespaTenantDomain);
assertFalse(instanceValidator.isValidInstance(createRegisterInstanceConfirmation(applicationId, domain, service)));
}
@@ -69,7 +71,7 @@ public class InstanceValidatorTest {
public void application_does_not_have_domain_set() {
SuperModelProvider superModelProvider = mockSuperModelProvider(
mockApplicationInfo(applicationId, 5, Collections.emptyList()));
- InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null);
+ InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, new IdentityDocumentSigner(), vespaTenantDomain);
assertFalse(instanceValidator.isValidInstance(createRegisterInstanceConfirmation(applicationId, domain, service)));
}
@@ -81,7 +83,7 @@ public class InstanceValidatorTest {
SuperModelProvider superModelProvider = mockSuperModelProvider(
mockApplicationInfo(applicationId, 5, Collections.singletonList(serviceInfo)));
- InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, null);
+ InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, null, vespaTenantDomain);
assertFalse(instanceValidator.isValidInstance(createRegisterInstanceConfirmation(applicationId, domain, service)));
}
@@ -99,7 +101,7 @@ public class InstanceValidatorTest {
mockApplicationInfo(applicationId, 5, Collections.singletonList(serviceInfo)));
IdentityDocumentSigner signer = mock(IdentityDocumentSigner.class);
when(signer.hasValidSignature(any(), any())).thenReturn(true);
- InstanceValidator instanceValidator = new InstanceValidator(mock(KeyProvider.class), superModelProvider, null, signer);
+ InstanceValidator instanceValidator = new InstanceValidator(mock(KeyProvider.class), superModelProvider, null, signer, vespaTenantDomain);
assertTrue(instanceValidator.isValidInstance(createRegisterInstanceConfirmation(applicationId, domain, service)));
}
@@ -107,7 +109,7 @@ public class InstanceValidatorTest {
@Test
public void rejects_invalid_provider_unique_id_in_csr() {
SuperModelProvider superModelProvider = mockSuperModelProvider();
- InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, null);
+ InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, null, vespaTenantDomain);
InstanceConfirmation instanceConfirmation = createRegisterInstanceConfirmation(applicationId, domain, service);
VespaUniqueInstanceId tamperedId = new VespaUniqueInstanceId(0, "default", "instance", "app", "tenant", "us-north-1", "dev", IdentityType.NODE);
instanceConfirmation.set("sanDNS", tamperedId.asDottedString() + ".instanceid.athenz.dev-us-north-1.vespa.yahoo.cloud");
@@ -117,7 +119,7 @@ public class InstanceValidatorTest {
@Test
public void accepts_valid_refresh_requests() {
NodeRepository nodeRepository = mock(NodeRepository.class);
- InstanceValidator instanceValidator = new InstanceValidator(null, null, nodeRepository);
+ InstanceValidator instanceValidator = new InstanceValidator(null, null, nodeRepository, new IdentityDocumentSigner(), vespaTenantDomain);
List<Node> nodeList = createNodes(10);
Node node = nodeList.get(0);
@@ -132,7 +134,7 @@ public class InstanceValidatorTest {
@Test
public void rejects_refresh_on_ip_mismatch() {
NodeRepository nodeRepository = mock(NodeRepository.class);
- InstanceValidator instanceValidator = new InstanceValidator(null, null, nodeRepository);
+ InstanceValidator instanceValidator = new InstanceValidator(null, null, nodeRepository, new IdentityDocumentSigner(), vespaTenantDomain);
List<Node> nodeList = createNodes(10);
Node node = nodeList.get(0);
@@ -149,7 +151,7 @@ public class InstanceValidatorTest {
@Test
public void rejects_refresh_when_node_is_not_allocated() {
NodeRepository nodeRepository = mock(NodeRepository.class);
- InstanceValidator instanceValidator = new InstanceValidator(null, null, nodeRepository);
+ InstanceValidator instanceValidator = new InstanceValidator(null, null, nodeRepository, new IdentityDocumentSigner(), vespaTenantDomain);
List<Node> nodeList = createNodes(10);
when(nodeRepository.getNodes()).thenReturn(nodeList);
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java
index e377009b18c..88ee154dee8 100644
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java
@@ -2,25 +2,34 @@
package com.yahoo.vespa.hosted.ca.restapi;
import com.yahoo.application.container.handler.Request;
+import com.yahoo.jdisc.http.servlet.ServletRequest;
import com.yahoo.security.KeyAlgorithm;
import com.yahoo.security.KeyUtils;
import com.yahoo.security.Pkcs10Csr;
import com.yahoo.security.Pkcs10CsrUtils;
import com.yahoo.security.X509CertificateUtils;
+import com.yahoo.text.StringUtilities;
+import com.yahoo.vespa.athenz.api.AthenzPrincipal;
import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.athenz.client.zts.DefaultZtsClient;
import com.yahoo.vespa.config.SlimeUtils;
import com.yahoo.vespa.hosted.ca.CertificateTester;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.methods.HttpUriRequest;
import org.junit.Before;
import org.junit.Test;
import javax.net.ssl.SSLContext;
import java.net.URI;
import java.nio.charset.StandardCharsets;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
import java.util.List;
+import java.util.Optional;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
/**
* @author mpolden
@@ -29,6 +38,8 @@ public class CertificateAuthorityApiTest extends ContainerTester {
private static final String INSTANCE_ID = "1.cluster1.default.app1.tenant1.us-north-1.prod.node";
private static final String INSTANCE_ID_WITH_SUFFIX = INSTANCE_ID + ".instanceid.athenz.dev-us-north-1.vespa.aws.oath.cloud";
+ private static final String INVALID_INSTANCE_ID = "1.cluster1.default.otherapp.othertenant.us-north-1.prod.node";
+ private static final String INVALID_INSTANCE_ID_WITH_SUFFIX = INVALID_INSTANCE_ID + ".instanceid.athenz.dev-us-north-1.vespa.aws.oath.cloud";
@Before
public void before() {
@@ -44,24 +55,47 @@ public class CertificateAuthorityApiTest extends ContainerTester {
Request.Method.POST));
// POST instance registration with ZTS client
- var ztsClient = new DefaultZtsClient(URI.create("http://localhost:12345/ca/v1/"), SSLContext.getDefault());
+ var ztsClient = new TestZtsClient(new AthenzPrincipal(new AthenzService("vespa.external.tenant-host")), null, URI.create("http://localhost:12345/ca/v1/"), SSLContext.getDefault());
var instanceIdentity = ztsClient.registerInstance(new AthenzService("vespa.external", "provider_prod_us-north-1"),
new AthenzService("vespa.external", "tenant"),
- "identity document generated by config server",
+ getAttestationData(),
csr);
assertEquals("CN=Vespa CA", instanceIdentity.certificate().getIssuerX500Principal().getName());
}
+ private X509Certificate registerInstance() throws Exception {
+ // POST instance registration
+ var csr = CertificateTester.createCsr(List.of("node1.example.com", INSTANCE_ID_WITH_SUFFIX));
+ assertIdentityResponse(new Request("http://localhost:12345/ca/v1/instance/",
+ instanceRegistrationJson(csr),
+ Request.Method.POST));
+
+ // POST instance registration with ZTS client
+ var ztsClient = new TestZtsClient(new AthenzPrincipal(new AthenzService("vespa.external.tenant-host")), null, URI.create("http://localhost:12345/ca/v1/"), SSLContext.getDefault());
+ var instanceIdentity = ztsClient.registerInstance(new AthenzService("vespa.external", "provider_prod_us-north-1"),
+ new AthenzService("vespa.external", "tenant"),
+ getAttestationData(),
+ csr);
+ return instanceIdentity.certificate();
+ }
+
@Test
public void refresh_instance() throws Exception {
+ // Register instance to get cert
+ var certificate = registerInstance();
+
// POST instance refresh
var csr = CertificateTester.createCsr(List.of("node1.example.com", INSTANCE_ID_WITH_SUFFIX));
- assertIdentityResponse(new Request("http://localhost:12345/ca/v1/instance/vespa.external.provider_prod_us-north-1/vespa.external/tenant/" + INSTANCE_ID,
+ var principal = new AthenzPrincipal(new AthenzService("vespa.external.tenant"));
+ var request = new Request("http://localhost:12345/ca/v1/instance/vespa.external.provider_prod_us-north-1/vespa.external/tenant/" + INSTANCE_ID,
instanceRefreshJson(csr),
- Request.Method.POST));
+ Request.Method.POST,
+ principal);
+ request.getAttributes().put(ServletRequest.JDISC_REQUEST_X509CERT, new X509Certificate[]{certificate});
+ assertIdentityResponse(request);
// POST instance refresh with ZTS client
- var ztsClient = new DefaultZtsClient(URI.create("http://localhost:12345/ca/v1/"), SSLContext.getDefault());
+ var ztsClient = new TestZtsClient(principal, certificate, URI.create("http://localhost:12345/ca/v1/"), SSLContext.getDefault());
var instanceIdentity = ztsClient.refreshInstance(new AthenzService("vespa.external", "provider_prod_us-north-1"),
new AthenzService("vespa.external", "tenant"),
INSTANCE_ID,
@@ -70,7 +104,7 @@ public class CertificateAuthorityApiTest extends ContainerTester {
}
@Test
- public void invalid_requests() {
+ public void invalid_requests() throws Exception {
// POST instance registration with missing fields
assertResponse(400, "{\"error-code\":\"BAD_REQUEST\",\"message\":\"POST http://localhost:12345/ca/v1/instance/ failed: Missing required field 'provider'\"}",
new Request("http://localhost:12345/ca/v1/instance/",
@@ -91,11 +125,28 @@ public class CertificateAuthorityApiTest extends ContainerTester {
Request.Method.POST));
// POST instance refresh where instanceId does not match CSR dnsName
+ var principal = new AthenzPrincipal(new AthenzService("vespa.external.tenant"));
csr = CertificateTester.createCsr(List.of("node1.example.com", INSTANCE_ID_WITH_SUFFIX));
assertResponse(400, "{\"error-code\":\"BAD_REQUEST\",\"message\":\"POST http://localhost:12345/ca/v1/instance/vespa.external.provider_prod_us-north-1/vespa.external/tenant/foobar failed: Mismatch between instance ID in URL path and instance ID in CSR [instanceId=foobar,instanceIdFromCsr=1.cluster1.default.app1.tenant1.us-north-1.prod.node]\"}",
new Request("http://localhost:12345/ca/v1/instance/vespa.external.provider_prod_us-north-1/vespa.external/tenant/foobar",
instanceRefreshJson(csr),
- Request.Method.POST));
+ Request.Method.POST,
+ principal));
+
+ // POST instance refresh using zts client where client cert does not contain instanceid
+ var certificate = registerInstance();
+ var ztsClient = new TestZtsClient(principal, certificate, URI.create("http://localhost:12345/ca/v1/"), SSLContext.getDefault());
+ try {
+ var invalidCsr = CertificateTester.createCsr(List.of("node1.example.com", INVALID_INSTANCE_ID_WITH_SUFFIX));
+ var instanceIdentity = ztsClient.refreshInstance(new AthenzService("vespa.external", "provider_prod_us-north-1"),
+ new AthenzService("vespa.external", "tenant"),
+ INSTANCE_ID,
+ invalidCsr);
+ fail("Refresh instance should have failed");
+ } catch (Exception e) {
+ String expectedMessage = "Received error from ZTS: code=0, message=\"POST http://localhost:12345/ca/v1/instance/vespa.external.provider_prod_us-north-1/vespa.external/tenant/1.cluster1.default.app1.tenant1.us-north-1.prod.node failed: Mismatch between instance ID in URL path and instance ID in CSR [instanceId=1.cluster1.default.app1.tenant1.us-north-1.prod.node,instanceIdFromCsr=1.cluster1.default.otherapp.othertenant.us-north-1.prod.node]\"";
+ assertEquals(expectedMessage, e.getMessage());
+ }
}
private void setCaCertificateAndKey() {
@@ -132,10 +183,52 @@ public class CertificateAuthorityApiTest extends ContainerTester {
" \"provider\": \"vespa.external.provider_prod_us-north-1\",\n" +
" \"domain\": \"vespa.external\",\n" +
" \"service\": \"tenant\",\n" +
- " \"attestationData\": \"identity document generated by config server\",\n" +
+ " \"attestationData\": \""+getAttestationData()+"\",\n" +
" \"csr\": \"" + csrPem + "\"\n" +
"}";
return json.getBytes(StandardCharsets.UTF_8);
}
+ private static String getAttestationData () {
+ var json = "{\n" +
+ " \"signature\": \"SIGNATURE\",\n" +
+ " \"signing-key-version\": 0,\n" +
+ " \"provider-unique-id\": \"0.default.default.application.tenant.us-north-1.dev.tenant\",\n" +
+ " \"provider-service\": \"domain.service\",\n" +
+ " \"document-version\": 1,\n" +
+ " \"configserver-hostname\": \"localhost\",\n" +
+ " \"instance-hostname\": \"docker-container\",\n" +
+ " \"created-at\": 1572000079.00000,\n" +
+ " \"ip-addresses\": [\n" +
+ " \"::1\"\n" +
+ " ],\n" +
+ " \"identity-type\": \"tenant\"\n" +
+ "}";
+ return StringUtilities.escape(json);
+ }
+
+ /*
+ Zts client that adds principal as header (since setting up ssl in test is cumbersome)
+ */
+ private static class TestZtsClient extends DefaultZtsClient {
+
+ private final Principal principal;
+ private final X509Certificate certificate;
+
+ public TestZtsClient(Principal principal, X509Certificate certificate, URI ztsUrl, SSLContext sslContext) {
+ super(ztsUrl, sslContext);
+ this.principal = principal;
+ this.certificate = certificate;
+ }
+
+ @Override
+ protected <T> T execute(HttpUriRequest request, ResponseHandler<T> responseHandler) {
+ request.addHeader("PRINCIPAL", principal.getName());
+ Optional.ofNullable(certificate).ifPresent(cert -> {
+ var pem = X509CertificateUtils.toPem(certificate);
+ request.addHeader("CERTIFICATE", StringUtilities.escape(pem));
+ });
+ return super.execute(request, responseHandler);
+ }
+ }
}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/ContainerTester.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/ContainerTester.java
index 139314b0f86..0eda6bd946b 100644
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/ContainerTester.java
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/ContainerTester.java
@@ -67,13 +67,20 @@ public class ContainerTester {
" <ztsUrl>https://localhost:123/</ztsUrl>\n" +
" </config>\n" +
" <component id='com.yahoo.vespa.hosted.ca.restapi.mock.SecretStoreMock'/>\n" +
+ " <component id='com.yahoo.vespa.hosted.ca.restapi.mock.InstanceValidatorMock'/>\n" +
" <handler id='com.yahoo.vespa.hosted.ca.restapi.CertificateAuthorityApiHandler'>\n" +
" <binding>http://*/ca/v1/*</binding>\n" +
" </handler>\n" +
" <http>\n" +
" <server id='default' port='12345'/>\n" +
+ " <filtering>\n" +
+ " <request-chain id=\"my-default-chain\">\n" +
+ " <filter id='com.yahoo.vespa.hosted.ca.restapi.mock.PrincipalFromHeaderFilter' />\n" +
+ " <binding>http://*/*</binding>\n" +
+ " </request-chain>\n" +
+ " </filtering>\n" +
" </http>\n" +
"</container>";
}
-}
+} \ No newline at end of file
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java
index 83ea9249ad0..edc8bec2370 100644
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java
@@ -4,6 +4,12 @@ package com.yahoo.vespa.hosted.ca.restapi;
import com.yahoo.security.Pkcs10CsrUtils;
import com.yahoo.security.X509CertificateUtils;
import com.yahoo.slime.Slime;
+import com.yahoo.text.StringUtilities;
+import com.yahoo.vespa.athenz.api.AthenzService;
+import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper;
+import com.yahoo.vespa.athenz.identityprovider.api.IdentityType;
+import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument;
+import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId;
import com.yahoo.vespa.config.SlimeUtils;
import com.yahoo.vespa.hosted.ca.CertificateTester;
import com.yahoo.vespa.hosted.ca.instance.InstanceIdentity;
@@ -14,6 +20,9 @@ import org.junit.Test;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Collections;
import java.util.Optional;
import static org.junit.Assert.assertEquals;
@@ -27,15 +36,28 @@ public class InstanceSerializerTest {
public void deserialize_instance_registration() {
var csr = CertificateTester.createCsr();
var csrPem = Pkcs10CsrUtils.toPem(csr);
- var json = "{\n" +
+ SignedIdentityDocument signedIdentityDocument = new SignedIdentityDocument(
+ "signature",
+ 0,
+ new VespaUniqueInstanceId(0, "cluster", "instance", "application", "tenant", "region", "prod", IdentityType.NODE),
+ new AthenzService("domain", "service"),
+ 0,
+ "configserverhostname",
+ "instancehostname",
+ Instant.now().truncatedTo(ChronoUnit.MILLIS),
+ // Instant.ofEpochSecond(1572000079),
+ Collections.emptySet(),
+ IdentityType.NODE);
+
+ var json = String.format("{\n" +
" \"provider\": \"provider_prod_us-north-1\",\n" +
" \"domain\": \"vespa.external\",\n" +
" \"service\": \"tenant\",\n" +
- " \"attestationData\": \"identity document from configserevr\",\n" +
+ " \"attestationData\":\"%s\",\n" +
" \"csr\": \"" + csrPem + "\"\n" +
- "}";
+ "}", StringUtilities.escape(EntityBindingsMapper.toAttestationData(signedIdentityDocument)));
var instanceRegistration = new InstanceRegistration("provider_prod_us-north-1", "vespa.external",
- "tenant", "identity document from configserevr",
+ "tenant", signedIdentityDocument,
csr);
var deserialized = InstanceSerializer.registrationFromSlime(SlimeUtils.jsonToSlime(json));
assertEquals(instanceRegistration, deserialized);
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/InstanceValidatorMock.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/InstanceValidatorMock.java
new file mode 100644
index 00000000000..9c1d4c49b07
--- /dev/null
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/InstanceValidatorMock.java
@@ -0,0 +1,27 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.ca.restapi.mock;
+
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation.InstanceConfirmation;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation.InstanceValidator;
+
+/**
+ * @author mortent
+ */
+public class InstanceValidatorMock extends InstanceValidator {
+
+ public InstanceValidatorMock() {
+ super(null, null, null, null, null);
+ }
+
+ @Override
+ public boolean isValidInstance(InstanceConfirmation instanceConfirmation) {
+ return instanceConfirmation.attributes.get(SAN_DNS_ATTRNAME) != null &&
+ instanceConfirmation.attributes.get(SAN_IPS_ATTRNAME) != null;
+ }
+
+ @Override
+ public boolean isValidRefresh(InstanceConfirmation confirmation) {
+ return confirmation.attributes.get(SAN_DNS_ATTRNAME) != null &&
+ confirmation.attributes.get(SAN_IPS_ATTRNAME) != null;
+ }
+}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/PrincipalFromHeaderFilter.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/PrincipalFromHeaderFilter.java
new file mode 100644
index 00000000000..d9ee4c8bb9b
--- /dev/null
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/PrincipalFromHeaderFilter.java
@@ -0,0 +1,34 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.ca.restapi.mock;
+
+import com.yahoo.jdisc.handler.ResponseHandler;
+import com.yahoo.jdisc.http.filter.DiscFilterRequest;
+import com.yahoo.jdisc.http.filter.SecurityRequestFilter;
+import com.yahoo.jdisc.http.servlet.ServletRequest;
+import com.yahoo.security.X509CertificateUtils;
+import com.yahoo.text.StringUtilities;
+import com.yahoo.vespa.athenz.api.AthenzPrincipal;
+import com.yahoo.vespa.athenz.api.AthenzService;
+
+import java.security.cert.X509Certificate;
+import java.util.Optional;
+
+/**
+ * Read principal from http header
+ *
+ * @author mortent
+ */
+public class PrincipalFromHeaderFilter implements SecurityRequestFilter {
+
+ @Override
+ public void filter(DiscFilterRequest request, ResponseHandler handler) {
+ String principal = request.getHeader("PRINCIPAL");
+ request.setUserPrincipal(new AthenzPrincipal(new AthenzService(principal)));
+
+ Optional<String> certificate = Optional.ofNullable(request.getHeader("CERTIFICATE"));
+ certificate.ifPresent(cert -> {
+ var x509cert = X509CertificateUtils.fromPem(StringUtilities.unescape(cert));
+ request.setAttribute(ServletRequest.JDISC_REQUEST_X509CERT, new X509Certificate[]{x509cert});
+ });
+ }
+}