diff --git a/src/test/java/org/jenkinsci/plugins/kubernetes/credentials/AbstractOpenShiftBearerTokenCredentialFIPSTest.java b/src/test/java/org/jenkinsci/plugins/kubernetes/credentials/AbstractOpenShiftBearerTokenCredentialFIPSTest.java index cc02346..c10f877 100644 --- a/src/test/java/org/jenkinsci/plugins/kubernetes/credentials/AbstractOpenShiftBearerTokenCredentialFIPSTest.java +++ b/src/test/java/org/jenkinsci/plugins/kubernetes/credentials/AbstractOpenShiftBearerTokenCredentialFIPSTest.java @@ -1,17 +1,10 @@ package org.jenkinsci.plugins.kubernetes.credentials; import com.cloudbees.plugins.credentials.CredentialsScope; +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpsConfigurator; +import com.sun.net.httpserver.HttpsServer; import edu.umd.cs.findbugs.annotations.NonNull; -import org.eclipse.jetty.server.Connector; -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.SslConnectionFactory; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.After; import org.junit.Before; import org.junit.ClassRule; @@ -19,7 +12,13 @@ import org.jvnet.hudson.test.FlagRule; import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; import java.net.URL; +import java.security.KeyStore; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; import static org.junit.Assert.fail; @@ -49,11 +48,7 @@ public abstract class AbstractOpenShiftBearerTokenCredentialFIPSTest { protected String motivation; - private Server server; - - private ServerConnector sslConnector; - - private ServerConnector serverConnector; + private HttpServer server; public AbstractOpenShiftBearerTokenCredentialFIPSTest( @@ -69,30 +64,35 @@ public void prepareFakeOAuthServer() throws Exception { if (keystore == null) { fail("Unable to find keystore.jks"); } - server = new Server(); - HttpConfiguration httpsConfig = new HttpConfiguration(); - httpsConfig.addCustomizer(new SecureRequestCustomizer()); + InetSocketAddress address = new InetSocketAddress(0); + if ("https".equals(scheme)) { + server = HttpsServer.create(address, 0); + setupHttps((HttpsServer) server); + } else { + server = HttpServer.create(address, 0); + } + OpenShiftBearerTokenCredentialMockServer.registerHttpHandlers(server); - SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystore.toExternalForm()); - sslContextFactory.setKeyManagerPassword("unittest"); - sslContextFactory.setKeyStorePassword("unittest"); + server.start(); + } - sslConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, "http/1.1"), new HttpConnectionFactory(httpsConfig)); - serverConnector = new ServerConnector(server); - server.setConnectors(new Connector[]{serverConnector, sslConnector}); + private void setupHttps(HttpsServer httpsServer) throws Exception { + SSLContext sslContext = SSLContext.getInstance("TLS"); + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + KeyStore ks = KeyStore.getInstance("JKS"); + try (InputStream is = keystore.openStream()) { + ks.load(is, "unittest".toCharArray()); + } + kmf.init(ks, "unittest".toCharArray()); - ServletContextHandler context = new ServletContextHandler(); - context.setContextPath("/"); - context.addServlet(new ServletHolder(new MockHttpServlet()), "/*"); - server.setHandler(context); - server.start(); + sslContext.init(kmf.getKeyManagers(), null, null); + httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); } @After - public void unprepareFakeOAuthServer() throws Exception { - server.stop(); + public void unprepareFakeOAuthServer() { + server.stop(0); } @Test @@ -100,13 +100,7 @@ public void ensureFIPSCompliantURIRequest() throws IOException { OpenShiftBearerTokenCredentialImpl cred; cred = new OpenShiftBearerTokenCredentialImpl(CredentialsScope.GLOBAL, "id", "description", "username", "password"); try { - int port; - if ("https".equals(scheme)) { - port = sslConnector.getLocalPort(); - } else { - port = serverConnector.getLocalPort(); - } - cred.getToken(scheme + "://localhost:" + port + "/valid-response", null, skipTLSVerify); + cred.getToken(scheme + "://localhost:" + server.getAddress().getPort() + "/valid-response", null, skipTLSVerify); if (!shouldPass) { fail("This test was expected to fail, reason: " + motivation); } diff --git a/src/test/java/org/jenkinsci/plugins/kubernetes/credentials/MockHttpServlet.java b/src/test/java/org/jenkinsci/plugins/kubernetes/credentials/MockHttpServlet.java deleted file mode 100644 index 681864d..0000000 --- a/src/test/java/org/jenkinsci/plugins/kubernetes/credentials/MockHttpServlet.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.jenkinsci.plugins.kubernetes.credentials; - -import org.eclipse.jetty.server.Request; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * @author Max Laverse - */ -public class MockHttpServlet extends HttpServlet { - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - switch (request.getPathInfo()) { - case "/bad-location/oauth/authorize": - response.sendRedirect("bad"); - break; - case "/missing-location/oauth/authorize": - response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); - break; - case "/bad-response/oauth/authorize": - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - break; - case "/valid-response/oauth/authorize": - response.sendRedirect("http://my-service/#access_token=1234&expires_in=86400"); - break; - case "/valid-response2/oauth/authorize": - response.sendRedirect("http://my-service/#access_token=1235&expires_in=86400"); - break; - default: - Pattern r = Pattern.compile("(.*)/.well-known/oauth-authorization-server"); - // Now create matcher object. - Matcher m = r.matcher(request.getPathInfo()); - if (m.find()) { - String responseToClient = "{\n" + - " \"issuer\": \"" + ((Request) request).getRootURL() + "\",\n" + - " \"authorization_endpoint\": \"" + ((Request) request).getRootURL() + "/" + m.group(1) + "/oauth/authorize\",\n" + - " \"token_endpoint\": \"" + ((Request) request).getRootURL() + "/oauth/token\",\n" + - " \"scopes_supported\": [\n" + - " \"user:check-access\",\n" + - " \"user:full\",\n" + - " \"user:info\",\n" + - " \"user:list-projects\",\n" + - " \"user:list-scoped-projects\"\n" + - " ],\n" + - " \"response_types_supported\": [\n" + - " \"code\",\n" + - " \"token\"\n" + - " ],\n" + - " \"grant_types_supported\": [\n" + - " \"authorization_code\",\n" + - " \"implicit\"\n" + - " ],\n" + - " \"code_challenge_methods_supported\": [\n" + - " \"plain\",\n" + - " \"S256\"\n" + - " ]\n" + - "}"; - response.setStatus(HttpServletResponse.SC_OK); - response.getWriter().write(responseToClient); - response.getWriter().flush(); - response.getWriter().close(); - return; - } - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Bad test: unknown path " + request.getPathInfo()); - break; - } - } -} diff --git a/src/test/java/org/jenkinsci/plugins/kubernetes/credentials/OpenShiftBearerTokenCredentialMockServer.java b/src/test/java/org/jenkinsci/plugins/kubernetes/credentials/OpenShiftBearerTokenCredentialMockServer.java new file mode 100644 index 0000000..c65d097 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/kubernetes/credentials/OpenShiftBearerTokenCredentialMockServer.java @@ -0,0 +1,106 @@ +package org.jenkinsci.plugins.kubernetes.credentials; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpsServer; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Nevin Sunny + */ +public class OpenShiftBearerTokenCredentialMockServer { + + public static void registerHttpHandlers(HttpServer server) { + server.createContext( + "/bad-location/oauth/authorize", + OpenShiftBearerTokenCredentialMockServer::badLocationHandler); + server.createContext( + "/missing-location/oauth/authorize", + OpenShiftBearerTokenCredentialMockServer::missingLocationHandler); + server.createContext( + "/bad-response/oauth/authorize", + OpenShiftBearerTokenCredentialMockServer::badResponseHandler); + server.createContext( + "/valid-response/oauth/authorize", + OpenShiftBearerTokenCredentialMockServer::validResponseHandler1); + server.createContext( + "/valid-response2/oauth/authorize", + OpenShiftBearerTokenCredentialMockServer::validResponseHandler2); + server.createContext("/", OpenShiftBearerTokenCredentialMockServer::defaultHandler); + } + + private static void badLocationHandler(HttpExchange he) throws IOException { + he.getResponseHeaders().set("Location", "bad"); + he.sendResponseHeaders(302, -1); + } + + private static void missingLocationHandler(HttpExchange he) throws IOException { + he.sendResponseHeaders(302, -1); // No Location header + } + + private static void badResponseHandler(HttpExchange he) throws IOException { + he.sendResponseHeaders(400, -1); + } + + private static void validResponseHandler1(HttpExchange he) throws IOException { + he.getResponseHeaders().set("Location", "http://my-service/#access_token=1234&expires_in=86400"); + he.sendResponseHeaders(302, -1); + } + + private static void validResponseHandler2(HttpExchange he) throws IOException { + he.getResponseHeaders().set("Location", "http://my-service/#access_token=1235&expires_in=86400"); + he.sendResponseHeaders(302, -1); + } + + private static void defaultHandler(HttpExchange he) throws IOException { + String path = he.getRequestURI().getPath(); + Pattern r = Pattern.compile("(.*)/.well-known/oauth-authorization-server"); + // Now create matcher object. + Matcher m = r.matcher(path); + if (m.find()) { + String scheme = he.getHttpContext().getServer() instanceof HttpsServer ? "https" : "http"; + String rootURL = scheme + "://localhost:" + he.getLocalAddress().getPort() + "/"; + String responseToClient = "{\n" + + " \"issuer\": \"" + rootURL + "\",\n" + + " \"authorization_endpoint\": \"" + rootURL + "/" + m.group(1) + "/oauth/authorize\",\n" + + " \"token_endpoint\": \"" + rootURL + "/oauth/token\",\n" + + " \"scopes_supported\": [\n" + + " \"user:check-access\",\n" + + " \"user:full\",\n" + + " \"user:info\",\n" + + " \"user:list-projects\",\n" + + " \"user:list-scoped-projects\"\n" + + " ],\n" + + " \"response_types_supported\": [\n" + + " \"code\",\n" + + " \"token\"\n" + + " ],\n" + + " \"grant_types_supported\": [\n" + + " \"authorization_code\",\n" + + " \"implicit\"\n" + + " ],\n" + + " \"code_challenge_methods_supported\": [\n" + + " \"plain\",\n" + + " \"S256\"\n" + + " ]\n" + + "}"; + byte[] responseBytes = responseToClient.getBytes(StandardCharsets.UTF_8); + he.sendResponseHeaders(200, responseBytes.length); + try (OutputStream os = he.getResponseBody()) { + os.write(responseBytes); + } + } else { + String responseToClient = "Bad test: unknown path " + path; + byte[] responseBytes = responseToClient.getBytes(StandardCharsets.UTF_8); + he.sendResponseHeaders(500, responseBytes.length); + try (OutputStream os = he.getResponseBody()) { + os.write(responseBytes); + } + } + } +} diff --git a/src/test/java/org/jenkinsci/plugins/kubernetes/credentials/OpenShiftBearerTokenCredentialTest.java b/src/test/java/org/jenkinsci/plugins/kubernetes/credentials/OpenShiftBearerTokenCredentialTest.java index cdee860..c62adde 100644 --- a/src/test/java/org/jenkinsci/plugins/kubernetes/credentials/OpenShiftBearerTokenCredentialTest.java +++ b/src/test/java/org/jenkinsci/plugins/kubernetes/credentials/OpenShiftBearerTokenCredentialTest.java @@ -1,10 +1,8 @@ package org.jenkinsci.plugins.kubernetes.credentials; import com.cloudbees.plugins.credentials.CredentialsScope; +import com.sun.net.httpserver.HttpServer; import hudson.util.Secret; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -33,37 +31,39 @@ public class OpenShiftBearerTokenCredentialTest { @Rule public ExpectedException expectedEx = ExpectedException.none(); - private Server server; + private HttpServer server; @Before public void prepareFakeOAuthServer() throws Exception { - InetSocketAddress addr = new InetSocketAddress("localhost", 0); - server = new Server(addr); - ServletContextHandler context = new ServletContextHandler(); - context.setContextPath("/"); - context.addServlet(new ServletHolder(new MockHttpServlet()), "/*"); - server.setHandler(context); + InetSocketAddress address = new InetSocketAddress("localhost", 0); + server = HttpServer.create(address, 0); + OpenShiftBearerTokenCredentialMockServer.registerHttpHandlers(server); server.start(); } @After - public void unprepareFakeOAuthServer() throws Exception { - server.stop(); + public void unprepareFakeOAuthServer() { + server.stop(0); + } + + private String getURI() { + InetSocketAddress address = server.getAddress(); + return "http://" + address.getHostString() + ":" + address.getPort() + "/"; } @Test public void testValidResponse() throws IOException { OpenShiftBearerTokenCredentialImpl t = new OpenShiftBearerTokenCredentialImpl(CredentialsScope.GLOBAL, CREDENTIAL_ID, "sample", USERNAME, PASSWORD); - String token = t.getToken(server.getURI() + "valid-response", null, true); + String token = t.getToken(getURI() + "valid-response", null, true); assertEquals("1234", token); } @Test public void testMultipleCachedTokens() throws IOException { OpenShiftBearerTokenCredentialImpl t = new OpenShiftBearerTokenCredentialImpl(CredentialsScope.GLOBAL, CREDENTIAL_ID, "sample", USERNAME, PASSWORD); - String token1 = t.getToken(server.getURI() + "valid-response", null, true); - String token2 = t.getToken(server.getURI() + "valid-response2", null, true); - String token3 = t.getToken(server.getURI() + "valid-response", null, true); + String token1 = t.getToken(getURI() + "valid-response", null, true); + String token2 = t.getToken(getURI() + "valid-response2", null, true); + String token3 = t.getToken(getURI() + "valid-response", null, true); assertEquals("1234", token1); assertEquals("1235", token2); assertEquals("1234", token3); @@ -75,7 +75,7 @@ public void testBadStatusCode() throws IOException { expectedEx.expectMessage("The response from the OAuth server was invalid: The OAuth service didn't respond with a redirection but with '400: Bad Request'"); OpenShiftBearerTokenCredentialImpl t = new OpenShiftBearerTokenCredentialImpl(CredentialsScope.GLOBAL, CREDENTIAL_ID, "sample", USERNAME, PASSWORD); - t.getToken(server.getURI() + "bad-response", null, true); + t.getToken(getURI() + "bad-response", null, true); } @Test @@ -84,7 +84,7 @@ public void testMissingLocation() throws IOException { expectedEx.expectMessage("The response from the OAuth server was invalid: The OAuth service didn't respond with location header"); OpenShiftBearerTokenCredentialImpl t = new OpenShiftBearerTokenCredentialImpl(CredentialsScope.GLOBAL, CREDENTIAL_ID, "sample", USERNAME, PASSWORD); - t.getToken(server.getURI() + "missing-location", null, true); + t.getToken(getURI() + "missing-location", null, true); } @Test @@ -93,7 +93,7 @@ public void testBadLocation() throws IOException { expectedEx.expectMessage("The response from the OAuth server was invalid: The response contained no token"); OpenShiftBearerTokenCredentialImpl t = new OpenShiftBearerTokenCredentialImpl(CredentialsScope.GLOBAL, CREDENTIAL_ID, "sample", USERNAME, PASSWORD); - t.getToken(server.getURI() + "bad-location", null, true); + t.getToken(getURI() + "bad-location", null, true); } @Test