summaryrefslogtreecommitdiffstats
path: root/node-repository
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2018-03-12 15:58:29 +0100
committerMartin Polden <mpolden@mpolden.no>2018-03-13 08:53:37 +0100
commitb17afb040ad32887d6ec49c9b844a1610c36ed5e (patch)
tree19802dd1d209247e9e423d03fc86ef7433b75a78 /node-repository
parent7b9cf1820056d161ce761d0c040f7ceb21728f13 (diff)
Extract tester class
Diffstat (limited to 'node-repository')
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/Authorizer.java7
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilter.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilterTest.java141
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/FilterTester.java168
4 files changed, 188 insertions, 130 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/Authorizer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/Authorizer.java
index ec49560ebd9..29e98a4b0cb 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/Authorizer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/Authorizer.java
@@ -19,11 +19,8 @@ import java.util.function.BiPredicate;
import java.util.stream.Collectors;
/**
- * Authorizer for the node-repository and orchestrator REST APIs. This contains the authorization rules for all API
- * paths.
- *
- * Ideally, the authorization rules for orchestrator APIs should live in the orchestrator module. However, the node
- * repository is required to make decisions in some cases, which is not accessible in the orchestrator module.
+ * Authorizer for config server REST APIs. This contains the rules for all API paths where the authorization process
+ * requires information from the node-repository to make a decision
*
* @author mpolden
*/
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilter.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilter.java
index a54b6cd135c..bd0512ee306 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilter.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilter.java
@@ -31,6 +31,8 @@ import java.util.function.BiPredicate;
import java.util.logging.Logger;
/**
+ * Authorization filter for all paths in config server.
+ *
* @author mpolden
*/
public class AuthorizationFilter implements SecurityRequestFilter {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilterTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilterTest.java
index 4c80f4bc257..b14cc570a75 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilterTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilterTest.java
@@ -1,156 +1,47 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.restapi.v2.filter;
-import com.yahoo.application.container.handler.Request;
+import com.yahoo.application.container.handler.Request.Method;
import com.yahoo.config.provision.SystemName;
-import com.yahoo.container.jdisc.RequestHandlerTestDriver.MockResponseHandler;
-import com.yahoo.jdisc.http.filter.DiscFilterRequest;
-import com.yahoo.jdisc.http.servlet.ServletRequest;
import com.yahoo.vespa.curator.mock.MockCurator;
import com.yahoo.vespa.hosted.provision.restapi.v2.Authorizer;
+import com.yahoo.vespa.hosted.provision.restapi.v2.filter.FilterTester.Request;
import com.yahoo.vespa.hosted.provision.testutils.MockNodeFlavors;
import com.yahoo.vespa.hosted.provision.testutils.MockNodeRepository;
-import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.asn1.x509.BasicConstraints;
-import org.bouncycastle.asn1.x509.Extension;
-import org.bouncycastle.cert.X509v3CertificateBuilder;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
-import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.operator.ContentSigner;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.junit.Before;
import org.junit.Test;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.net.URI;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.Date;
-import java.util.Optional;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
/**
* @author mpolden
*/
public class AuthorizationFilterTest {
- private AuthorizationFilter filter;
+ private FilterTester tester;
@Before
public void before() {
- filter = new AuthorizationFilter(new Authorizer(SystemName.main, new MockNodeRepository(new MockCurator(),
+ tester = new FilterTester(new AuthorizationFilter(new Authorizer(SystemName.main,
+ new MockNodeRepository(new MockCurator(),
new MockNodeFlavors())),
- AuthorizationFilter::write);
+ AuthorizationFilter::write));
}
@Test
public void filter() {
// These are just rudimentary tests of the filter. See AuthorizerTest for more exhaustive tests
- Optional<Response> response = invokeFilter(request(Request.Method.GET, "/"));
- assertResponse(401, "{\"error-code\":\"UNAUTHORIZED\",\"message\":\"GET / denied for " +
- "unit-test: Missing credentials\"}", response);
-
- response = invokeFilter(request(Request.Method.GET, "/", "foo"));
- assertResponse(403, "{\"error-code\":\"FORBIDDEN\",\"message\":\"GET / " +
- "denied for unit-test: Invalid credentials\"}", response);
-
- response = invokeFilter(request(Request.Method.GET, "/nodes/v2/node/foo", "bar"));
- assertResponse(403, "{\"error-code\":\"FORBIDDEN\",\"message\":\"GET /nodes/v2/node/foo " +
- "denied for unit-test: Invalid credentials\"}", response);
-
- response = invokeFilter(request(Request.Method.GET, "/nodes/v2/node/foo", "foo"));
- assertSuccess(response);
- }
-
- private Optional<Response> invokeFilter(DiscFilterRequest request) {
- MockResponseHandler handler = new MockResponseHandler();
- filter.filter(request, handler);
- return Optional.ofNullable(handler.getResponse())
- .map(response -> new Response(response.getStatus(), handler.readAll()));
- }
-
- private static DiscFilterRequest request(Request.Method method, String path) {
- return request(method, path, null);
- }
-
- private static DiscFilterRequest request(Request.Method method, String path, String commonName) {
- DiscFilterRequest request = mock(DiscFilterRequest.class);
- when(request.getMethod()).thenReturn(method.name());
- when(request.getUri()).thenReturn(URI.create("http://localhost").resolve(path));
- when(request.getRemoteAddr()).thenReturn("unit-test");
- if (commonName != null) {
- X509Certificate cert = certificateFor(commonName, keyPair());
- when(request.getAttribute(ServletRequest.JDISC_REQUEST_X509CERT))
- .thenReturn(new X509Certificate[]{cert});
- }
- return request;
- }
-
- private static void assertSuccess(Optional<Response> response) {
- assertFalse("No error in response", response.isPresent());
- }
-
- private static void assertResponse(int status, String body, Optional<Response> response) {
- assertTrue("Expected response from filter", response.isPresent());
- assertEquals("Response body", body, response.get().body);
- assertEquals("Status code", status, response.get().status);
- }
-
- /** Create a RSA public/private key pair */
- private static KeyPair keyPair() {
- try {
- KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
- keyGen.initialize(2048);
- return keyGen.generateKeyPair();
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- }
- }
-
- /** Create a self signed certificate for commonName using given public/private key pair */
- private static X509Certificate certificateFor(String commonName, KeyPair keyPair) {
- try {
- ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSA")
- .build(keyPair.getPrivate());
- X500Name x500Name = new X500Name("CN=" + commonName);
- Instant now = Instant.now();
- Date notBefore = Date.from(now);
- Date notAfter = Date.from(now.plus(Duration.ofDays(30)));
- X509v3CertificateBuilder certificateBuilder =
- new JcaX509v3CertificateBuilder(
- x500Name,
- BigInteger.valueOf(now.toEpochMilli()), notBefore, notAfter, x500Name, keyPair.getPublic()
- ).addExtension(Extension.basicConstraints, true, new BasicConstraints(true));
- return new JcaX509CertificateConverter()
- .setProvider(new BouncyCastleProvider())
- .getCertificate(certificateBuilder.build(contentSigner));
- } catch (OperatorCreationException |IOException |CertificateException e) {
- throw new RuntimeException(e);
- }
- }
+ tester.assertRequest(new Request(Method.GET, "/"), 401,
+ "{\"error-code\":\"UNAUTHORIZED\",\"message\":\"GET / denied for " +
+ "unit-test: Missing credentials\"}");
- private static class Response {
+ tester.assertRequest(new Request(Method.GET, "/").commonName("foo"), 403,
+ "{\"error-code\":\"FORBIDDEN\",\"message\":\"GET / " +
+ "denied for unit-test: Invalid credentials\"}");
- private final int status;
- private final String body;
+ tester.assertRequest(new Request(Method.GET, "/nodes/v2/node/foo").commonName("bar"),
+ 403, "{\"error-code\":\"FORBIDDEN\",\"message\":\"GET /nodes/v2/node/foo " +
+ "denied for unit-test: Invalid credentials\"}");
- private Response(int status, String body) {
- this.status = status;
- this.body = body;
- }
+ tester.assertSuccess(new Request(Method.GET, "/nodes/v2/node/foo").commonName("foo"));
}
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/FilterTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/FilterTester.java
new file mode 100644
index 00000000000..ceb34cdfea8
--- /dev/null
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/FilterTester.java
@@ -0,0 +1,168 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.provision.restapi.v2.filter;
+
+import com.yahoo.application.container.handler.Request.Method;
+import com.yahoo.container.jdisc.RequestHandlerTestDriver;
+import com.yahoo.jdisc.http.filter.DiscFilterRequest;
+import com.yahoo.jdisc.http.filter.SecurityRequestFilter;
+import com.yahoo.jdisc.http.servlet.ServletRequest;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.net.URI;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Date;
+import java.util.Optional;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author mpolden
+ */
+public class FilterTester {
+
+ private final SecurityRequestFilter filter;
+
+ public FilterTester(SecurityRequestFilter filter) {
+ this.filter = filter;
+ }
+
+ public void assertSuccess(Request request) {
+ assertFalse("No response written by filter", getResponse(request).isPresent());
+ }
+
+ public void assertRequest(Request request, int status, String body) {
+ Optional<Response> response = getResponse(request);
+ assertTrue("Expected response from filter", response.isPresent());
+ assertEquals("Response body", body, response.get().body);
+ assertEquals("Status code", status, response.get().status);
+ }
+
+ private Optional<Response> getResponse(Request request) {
+ RequestHandlerTestDriver.MockResponseHandler handler = new RequestHandlerTestDriver.MockResponseHandler();
+ filter.filter(toDiscFilterRequest(request), handler);
+ return Optional.ofNullable(handler.getResponse())
+ .map(response -> new Response(response.getStatus(), handler.readAll()));
+ }
+
+ private static DiscFilterRequest toDiscFilterRequest(Request request) {
+ DiscFilterRequest r = mock(DiscFilterRequest.class);
+ when(r.getMethod()).thenReturn(request.method().name());
+ when(r.getUri()).thenReturn(URI.create("http://localhost").resolve(request.path()));
+ when(r.getRemoteAddr()).thenReturn(request.remoteAddr());
+ if (request.commonName().isPresent()) {
+ X509Certificate cert = certificateFor(request.commonName().get(), keyPair());
+ when(r.getAttribute(ServletRequest.JDISC_REQUEST_X509CERT))
+ .thenReturn(new X509Certificate[]{cert});
+ }
+ return r;
+ }
+
+ /** Create a RSA public/private key pair */
+ private static KeyPair keyPair() {
+ try {
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+ keyGen.initialize(2048);
+ return keyGen.generateKeyPair();
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** Create a self signed certificate for commonName using given public/private key pair */
+ private static X509Certificate certificateFor(String commonName, KeyPair keyPair) {
+ try {
+ ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSA")
+ .build(keyPair.getPrivate());
+ X500Name x500Name = new X500Name("CN=" + commonName);
+ Instant now = Instant.now();
+ Date notBefore = Date.from(now);
+ Date notAfter = Date.from(now.plus(Duration.ofDays(30)));
+ X509v3CertificateBuilder certificateBuilder =
+ new JcaX509v3CertificateBuilder(
+ x500Name,
+ BigInteger.valueOf(now.toEpochMilli()), notBefore, notAfter, x500Name, keyPair.getPublic()
+ ).addExtension(Extension.basicConstraints, true, new BasicConstraints(true));
+ return new JcaX509CertificateConverter()
+ .setProvider(new BouncyCastleProvider())
+ .getCertificate(certificateBuilder.build(contentSigner));
+ } catch (OperatorCreationException |IOException |CertificateException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static class Response {
+
+ private final int status;
+ private final String body;
+
+ private Response(int status, String body) {
+ this.status = status;
+ this.body = body;
+ }
+
+ }
+
+ public static class Request {
+
+ private final Method method;
+ private final String path;
+ private String remoteAddr;
+ private String commonName;
+
+ public Request(Method method, String path) {
+ this.method = method;
+ this.path = path;
+ this.commonName = null;
+ this.remoteAddr = "unit-test";
+ }
+
+ public Method method() {
+ return method;
+ }
+
+ public String path() {
+ return path;
+ }
+
+ public String remoteAddr() {
+ return remoteAddr;
+ }
+
+ public Optional<String> commonName() {
+ return Optional.ofNullable(commonName);
+ }
+
+ public Request commonName(String commonName) {
+ this.commonName = commonName;
+ return this;
+ }
+
+ public Request remoteAddr(String remoteAddr) {
+ this.remoteAddr = remoteAddr;
+ return this;
+ }
+
+ }
+
+}