From 030aee91fd3aec55a8940770181825f9f04a43aa Mon Sep 17 00:00:00 2001 From: Jorge Hernandez Date: Wed, 1 Aug 2018 16:18:25 -0500 Subject: generic jetty https server support jetty https support in constructor, or by using ".https" when creating an http server service. Change-Id: I94e8e3e4b93eb6b194657028c740b6781316c7da Issue-ID: POLICY-940 Signed-off-by: Jorge Hernandez --- .gitignore | 3 + .../http/server/HttpServletServerFactory.java | 50 ++++++++++--- .../http/server/internal/JettyJerseyServer.java | 11 +-- .../http/server/internal/JettyServletServer.java | 59 ++++++++++++++-- .../endpoints/http/server/test/HttpClientTest.java | 77 ++++++++++++++++++--- .../endpoints/http/server/test/HttpServerTest.java | 6 +- policy-endpoints/src/test/resources/keystore-test | Bin 0 -> 3895 bytes 7 files changed, 176 insertions(+), 30 deletions(-) create mode 100644 policy-endpoints/src/test/resources/keystore-test diff --git a/.gitignore b/.gitignore index ae515bd8..13d4a1eb 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ target bin .metadata/ +.idea/ +**/*.iml +*/logs/ integrity-audit/sql/generatedCreateIA.ddl integrity-audit/sql/generatedDropIA.ddl integrity-audit/sql/iaTest.mv.db diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServerFactory.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServerFactory.java index f09893b2..c7d2b1bf 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServerFactory.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServerFactory.java @@ -37,9 +37,10 @@ import org.slf4j.LoggerFactory; public interface HttpServletServerFactory { /** - * builds an http server with support for servlets + * builds an http or https server with support for servlets * * @param name name + * @param https use secured http over tls connection * @param host binding host * @param port port * @param contextPath server base path @@ -48,9 +49,24 @@ public interface HttpServletServerFactory { * @return http server * @throws IllegalArgumentException when invalid parameters are provided */ - public HttpServletServer build(String name, String host, int port, String contextPath, boolean swagger, + HttpServletServer build(String name, boolean https, String host, int port, String contextPath, boolean swagger, boolean managed); + /** + * builds an http server with support for servlets + * + * @param name name + * @param host binding host + * @param port port + * @param contextPath server base path + * @param swagger enable swagger documentation + * @param managed is it managed by infrastructure + * @return http server + * @throws IllegalArgumentException when invalid parameters are provided + */ + HttpServletServer build(String name, String host, int port, String contextPath, boolean swagger, + boolean managed); + /** * list of http servers per properties * @@ -58,7 +74,7 @@ public interface HttpServletServerFactory { * @return list of http servers * @throws IllegalArgumentException when invalid parameters are provided */ - public List build(Properties properties); + List build(Properties properties); /** * gets a server based on the port @@ -66,26 +82,26 @@ public interface HttpServletServerFactory { * @param port port * @return http server */ - public HttpServletServer get(int port); + HttpServletServer get(int port); /** * provides an inventory of servers * * @return inventory of servers */ - public List inventory(); + List inventory(); /** * destroys server bound to a port * * @param port */ - public void destroy(int port); + void destroy(int port); /** * destroys the factory and therefore all servers */ - public void destroy(); + void destroy(); } @@ -107,14 +123,14 @@ class IndexedHttpServletServerFactory implements HttpServletServerFactory { protected HashMap servers = new HashMap<>(); @Override - public synchronized HttpServletServer build(String name, String host, int port, String contextPath, boolean swagger, + public synchronized HttpServletServer build(String name, boolean https, String host, int port, String contextPath, boolean swagger, boolean managed) { if (servers.containsKey(port)) { return servers.get(port); } - JettyJerseyServer server = new JettyJerseyServer(name, host, port, contextPath, swagger); + JettyJerseyServer server = new JettyJerseyServer(name, https, host, port, contextPath, swagger); if (managed) { servers.put(port, server); } @@ -122,6 +138,13 @@ class IndexedHttpServletServerFactory implements HttpServletServerFactory { return server; } + @Override + public synchronized HttpServletServer build(String name, String host, int port, String contextPath, + boolean swagger, boolean managed) { + return build(name, false, host, port, contextPath, swagger, managed); + } + + @Override public synchronized List build(Properties properties) { @@ -192,7 +215,14 @@ class IndexedHttpServletServerFactory implements HttpServletServerFactory { swagger = Boolean.parseBoolean(swaggerString); } - HttpServletServer service = build(serviceName, hostName, servicePort, contextUriPath, swagger, managed); + String httpsString = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + + serviceName + PolicyEndPointProperties.PROPERTY_HTTP_HTTPS_SUFFIX); + boolean https = false; + if (httpsString != null && !httpsString.isEmpty()) { + https = Boolean.parseBoolean(httpsString); + } + + HttpServletServer service = build(serviceName, https, hostName, servicePort, contextUriPath, swagger, managed); if (userName != null && !userName.isEmpty() && password != null && !password.isEmpty()) { service.setBasicAuthentication(userName, password, authUriPath); } diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java index cd286927..9932d094 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java @@ -104,6 +104,7 @@ public class JettyJerseyServer extends JettyServletServer { * Constructor * * @param name name + * @param https enable https? * @param host host server host * @param port port server port * @param swagger support swagger? @@ -111,19 +112,19 @@ public class JettyJerseyServer extends JettyServletServer { * * @throws IllegalArgumentException in invalid arguments are provided */ - public JettyJerseyServer(String name, String host, int port, String contextPath, boolean swagger) { + public JettyJerseyServer(String name, boolean https, String host, int port, String contextPath, boolean swagger) { - super(name, host, port, contextPath); + super(name, https, host, port, contextPath); if (swagger) { this.swaggerId = "swagger-" + this.port; - attachSwaggerServlet(); + attachSwaggerServlet(https); } } /** * attaches a swagger initialization servlet */ - protected void attachSwaggerServlet() { + protected void attachSwaggerServlet(boolean https) { ServletHolder swaggerServlet = context.addServlet(JerseyJaxrsConfig.class, "/"); @@ -133,7 +134,7 @@ public class JettyJerseyServer extends JettyServletServer { } swaggerServlet.setInitParameter(SWAGGER_API_BASEPATH, - "http://" + hostname + ":" + this.connector.getPort() + "/"); + ((https) ? "https://" : "http://") + hostname + ":" + this.connector.getPort() + "/"); swaggerServlet.setInitParameter(SWAGGER_CONTEXT_ID, swaggerId); swaggerServlet.setInitParameter(SWAGGER_SCANNER_ID, swaggerId); swaggerServlet.setInitParameter(SWAGGER_PRETTY_PRINT, "true"); diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyServletServer.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyServletServer.java index 97166ec7..a4cc9b5f 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyServletServer.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyServletServer.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * ONAP * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,12 +26,16 @@ import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.security.authentication.BasicAuthenticator; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.Slf4jRequestLog; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.util.security.Constraint; import org.eclipse.jetty.util.security.Credential; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.onap.policy.common.endpoints.http.server.HttpServletServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,6 +45,14 @@ import org.slf4j.LoggerFactory; */ public abstract class JettyServletServer implements HttpServletServer, Runnable { + /** + * Keystore/Truststore system property names + */ + public static final String SYSTEM_KEYSTORE_PROPERTY_NAME = "javax.net.ssl.keyStore"; + public static final String SYSTEM_KEYSTORE_PASSWORD_PROPERTY_NAME = "javax.net.ssl.keyStorePassword"; + public static final String SYSTEM_TRUSTSTORE_PROPERTY_NAME = "javax.net.ssl.trustStore"; + public static final String SYSTEM_TRUSTSTORE_PASSWORD_PROPERTY_NAME = "javax.net.ssl.trustStorePassword"; + /** * Logger */ @@ -111,7 +123,7 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable * * @throws IllegalArgumentException if invalid parameters are passed in */ - public JettyServletServer(String name, String host, int port, String contextPath) { + public JettyServletServer(String name, boolean https, String host, int port, String contextPath) { String srvName = name; String srvHost = host; String ctxtPath = contextPath; @@ -120,7 +132,7 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable srvName = "http-" + port; } - if (port <= 0 && port >= 65535) { + if (port <= 0 || port >= 65535) { throw new IllegalArgumentException("Invalid Port provided: " + port); } @@ -145,7 +157,11 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable this.jettyServer = new Server(); this.jettyServer.setRequestLog(new Slf4jRequestLog()); - this.connector = new ServerConnector(this.jettyServer); + if (https) + this.connector = httpsConnector(); + else + this.connector = httpConnector(); + this.connector.setName(srvName); this.connector.setReuseAddress(true); this.connector.setPort(port); @@ -155,6 +171,41 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable this.jettyServer.setHandler(context); } + public JettyServletServer(String name, String host, int port, String contextPath) { + this(name, false, host, port, contextPath); + } + + public ServerConnector httpsConnector() { + SslContextFactory sslContextFactory = new SslContextFactory(); + + String keyStore = System.getProperty(SYSTEM_KEYSTORE_PROPERTY_NAME); + if (keyStore != null) { + sslContextFactory.setKeyStorePath(keyStore); + + String ksPassword = System.getProperty(SYSTEM_KEYSTORE_PASSWORD_PROPERTY_NAME); + if (ksPassword != null) + sslContextFactory.setKeyStorePassword(ksPassword); + } + + String trustStore = System.getProperty(SYSTEM_TRUSTSTORE_PROPERTY_NAME); + if (trustStore != null) { + sslContextFactory.setTrustStorePath(trustStore); + + String tsPassword = System.getProperty(SYSTEM_TRUSTSTORE_PASSWORD_PROPERTY_NAME); + if (tsPassword != null) + sslContextFactory.setTrustStorePassword(tsPassword); + } + + HttpConfiguration https = new HttpConfiguration(); + https.addCustomizer(new SecureRequestCustomizer()); + + return new ServerConnector(jettyServer, sslContextFactory, new HttpConnectionFactory(https)); + } + + public ServerConnector httpConnector() { + return new ServerConnector(this.jettyServer); + } + @Override public void setBasicAuthentication(String user, String password, String servletPath) { String srvltPath = servletPath; diff --git a/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/HttpClientTest.java b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/HttpClientTest.java index 08399e91..6ec9bc21 100644 --- a/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/HttpClientTest.java +++ b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/HttpClientTest.java @@ -1,8 +1,8 @@ /*- * ============LICENSE_START======================================================= - * policy-endpoints + * ONAP * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ package org.onap.policy.common.endpoints.http.server.test; import static org.junit.Assert.assertTrue; import java.io.IOException; +import java.util.HashMap; import java.util.List; import java.util.Properties; @@ -33,20 +34,22 @@ import org.junit.BeforeClass; import org.junit.Test; import org.onap.policy.common.endpoints.http.client.HttpClient; import org.onap.policy.common.endpoints.http.server.HttpServletServer; +import org.onap.policy.common.endpoints.http.server.internal.JettyJerseyServer; import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties; import org.onap.policy.common.utils.network.NetworkUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HttpClientTest { + private static final Logger logger = LoggerFactory.getLogger(HttpClientTest.class); - private static Logger logger = LoggerFactory.getLogger(HttpClientTest.class); + private static final HashMap savedValuesMap = new HashMap<>(); @BeforeClass public static void setUp() throws InterruptedException, IOException { logger.info("-- setup() --"); - /* echo server */ + /* echo server - http + no auth */ final HttpServletServer echoServerNoAuth = HttpServletServer.factory.build("echo", "localhost", 6666, "/", false, true); @@ -57,10 +60,38 @@ public class HttpClientTest { throw new IllegalStateException("cannot connect to port " + echoServerNoAuth.getPort()); } - /* no auth echo server */ + String keyStoreSystemProperty = System.getProperty(JettyJerseyServer.SYSTEM_KEYSTORE_PROPERTY_NAME); + if (keyStoreSystemProperty != null) { + savedValuesMap.put(JettyJerseyServer.SYSTEM_KEYSTORE_PROPERTY_NAME, keyStoreSystemProperty); + } + + String keyStorePasswordSystemProperty = System.getProperty(JettyJerseyServer.SYSTEM_KEYSTORE_PASSWORD_PROPERTY_NAME); + if (keyStorePasswordSystemProperty != null) { + savedValuesMap.put(JettyJerseyServer.SYSTEM_KEYSTORE_PASSWORD_PROPERTY_NAME, keyStorePasswordSystemProperty); + } + + String trustStoreSystemProperty = System.getProperty(JettyJerseyServer.SYSTEM_TRUSTSTORE_PROPERTY_NAME); + if (trustStoreSystemProperty != null) { + savedValuesMap + .put(JettyJerseyServer.SYSTEM_TRUSTSTORE_PROPERTY_NAME, trustStoreSystemProperty); + } + + String trustStorePasswordSystemProperty = System.getProperty(JettyJerseyServer.SYSTEM_TRUSTSTORE_PASSWORD_PROPERTY_NAME); + if (trustStorePasswordSystemProperty != null) { + savedValuesMap + .put(JettyJerseyServer.SYSTEM_TRUSTSTORE_PASSWORD_PROPERTY_NAME, trustStorePasswordSystemProperty); + } + + System.setProperty(JettyJerseyServer.SYSTEM_KEYSTORE_PROPERTY_NAME, "src/test/resources/keystore-test"); + System.setProperty(JettyJerseyServer.SYSTEM_KEYSTORE_PASSWORD_PROPERTY_NAME, "kstest"); + + System.setProperty(JettyJerseyServer.SYSTEM_TRUSTSTORE_PROPERTY_NAME, "src/test/resources/keystore-test"); + System.setProperty(JettyJerseyServer.SYSTEM_TRUSTSTORE_PASSWORD_PROPERTY_NAME, "kstest"); + + /* echo server - https + basic auth */ final HttpServletServer echoServerAuth = - HttpServletServer.factory.build("echo", "localhost", 6667, "/", false, true); + HttpServletServer.factory.build("echo", true, "localhost", 6667, "/", false, true); echoServerAuth.setBasicAuthentication("x", "y", null); echoServerAuth.addServletPackage("/*", HttpClientTest.class.getPackage().getName()); echoServerAuth.waitedStart(5000); @@ -76,6 +107,36 @@ public class HttpClientTest { HttpServletServer.factory.destroy(); HttpClient.factory.destroy(); + + if (savedValuesMap.containsKey(JettyJerseyServer.SYSTEM_KEYSTORE_PROPERTY_NAME)) { + System.setProperty(JettyJerseyServer.SYSTEM_KEYSTORE_PROPERTY_NAME, savedValuesMap.get(JettyJerseyServer.SYSTEM_KEYSTORE_PROPERTY_NAME)); + savedValuesMap.remove(JettyJerseyServer.SYSTEM_KEYSTORE_PROPERTY_NAME); + } else { + System.clearProperty(JettyJerseyServer.SYSTEM_KEYSTORE_PROPERTY_NAME); + } + + if (savedValuesMap.containsKey(JettyJerseyServer.SYSTEM_KEYSTORE_PASSWORD_PROPERTY_NAME)) { + System.setProperty(JettyJerseyServer.SYSTEM_KEYSTORE_PASSWORD_PROPERTY_NAME, savedValuesMap.get(JettyJerseyServer.SYSTEM_KEYSTORE_PASSWORD_PROPERTY_NAME)); + savedValuesMap.remove(JettyJerseyServer.SYSTEM_KEYSTORE_PASSWORD_PROPERTY_NAME); + } else { + System.clearProperty(JettyJerseyServer.SYSTEM_KEYSTORE_PASSWORD_PROPERTY_NAME); + } + + if (savedValuesMap.containsKey(JettyJerseyServer.SYSTEM_TRUSTSTORE_PROPERTY_NAME)) { + System.setProperty(JettyJerseyServer.SYSTEM_TRUSTSTORE_PROPERTY_NAME, savedValuesMap.get(JettyJerseyServer.SYSTEM_TRUSTSTORE_PROPERTY_NAME)); + savedValuesMap.remove(JettyJerseyServer.SYSTEM_TRUSTSTORE_PROPERTY_NAME); + } else { + System.clearProperty(JettyJerseyServer.SYSTEM_TRUSTSTORE_PROPERTY_NAME); + } + + if (savedValuesMap.containsKey(JettyJerseyServer.SYSTEM_TRUSTSTORE_PASSWORD_PROPERTY_NAME)) { + System.setProperty(JettyJerseyServer.SYSTEM_TRUSTSTORE_PASSWORD_PROPERTY_NAME, savedValuesMap.get(JettyJerseyServer.SYSTEM_TRUSTSTORE_PASSWORD_PROPERTY_NAME)); + savedValuesMap.remove(JettyJerseyServer.SYSTEM_TRUSTSTORE_PASSWORD_PROPERTY_NAME); + } else { + System.clearProperty(JettyJerseyServer.SYSTEM_TRUSTSTORE_PASSWORD_PROPERTY_NAME); + } + + } @Test @@ -95,7 +156,7 @@ public class HttpClientTest { public void testHttpAuthClient() throws Exception { logger.info("-- testHttpAuthClient() --"); - final HttpClient client = HttpClient.factory.build("testHttpAuthClient", false, false, "localhost", 6667, + final HttpClient client = HttpClient.factory.build("testHttpAuthClient", true, true,"localhost", 6667, "junit/echo", "x", "y", true); final Response response = client.get("hello"); final String body = HttpClient.getBody(response, String.class); @@ -108,7 +169,7 @@ public class HttpClientTest { public void testHttpAuthClient401() throws Exception { logger.info("-- testHttpAuthClient401() --"); - final HttpClient client = HttpClient.factory.build("testHttpAuthClient401", false, false, "localhost", 6667, + final HttpClient client = HttpClient.factory.build("testHttpAuthClient401", true, true, "localhost", 6667, "junit/echo", null, null, true); final Response response = client.get("hello"); assertTrue(response.getStatus() == 401); diff --git a/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/HttpServerTest.java b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/HttpServerTest.java index b6f0c0e8..0db6cfe1 100644 --- a/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/HttpServerTest.java +++ b/policy-endpoints/src/test/java/org/onap/policy/common/endpoints/http/server/test/HttpServerTest.java @@ -1,8 +1,8 @@ /*- * ============LICENSE_START======================================================= - * policy-endpoints + * ONAP * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -77,7 +77,7 @@ public class HttpServerTest { public void testMultipleServers() throws Exception { logger.info("-- testMultipleServers() --"); - HttpServletServer server1 = HttpServletServer.factory.build("echo-1", "localhost", 5688, "/", true, true); + HttpServletServer server1 = HttpServletServer.factory.build("echo-1", false,"localhost", 5688, "/", true, true); server1.addServletPackage("/*", this.getClass().getPackage().getName()); server1.waitedStart(5000); diff --git a/policy-endpoints/src/test/resources/keystore-test b/policy-endpoints/src/test/resources/keystore-test new file mode 100644 index 00000000..5820e0f0 Binary files /dev/null and b/policy-endpoints/src/test/resources/keystore-test differ -- cgit 1.2.3-korg