This is an automated email from the ASF dual-hosted git repository. bdemers pushed a commit to branch tls-for-its-16x in repository https://gitbox.apache.org/repos/asf/shiro.git
commit a7bc294ba4f6a971f7dfd082aade1029fdd7f462 Author: Brian Demers <[email protected]> AuthorDate: Thu Oct 15 11:33:21 2020 -0400 Update AbstractContainerIT to allow for HTTPS connections Using a pre-generated keystore --- .../shiro/testing/web/AbstractContainerIT.java | 69 +++++++++++++++++++++ .../support/src/main/resources/createKeyStore.sh | 65 +++++++++++++++++++ .../support/src/main/resources/test-keystore.jks | Bin 0 -> 2272 bytes .../support/src/main/resources/test-keystore.pem | 22 +++++++ pom.xml | 2 + ...onIT.java => WebAppContainerIntegrationIT.java} | 6 +- 6 files changed, 161 insertions(+), 3 deletions(-) diff --git a/integration-tests/support/src/main/java/org/apache/shiro/testing/web/AbstractContainerIT.java b/integration-tests/support/src/main/java/org/apache/shiro/testing/web/AbstractContainerIT.java index 5d3fcc0..cb79d56 100644 --- a/integration-tests/support/src/main/java/org/apache/shiro/testing/web/AbstractContainerIT.java +++ b/integration-tests/support/src/main/java/org/apache/shiro/testing/web/AbstractContainerIT.java @@ -24,8 +24,15 @@ import com.gargoylesoftware.htmlunit.WebClient; import com.github.mjeanroy.junit.servers.jetty.EmbeddedJetty; import com.github.mjeanroy.junit.servers.jetty.EmbeddedJettyConfiguration; import org.eclipse.jetty.annotations.AnnotationConfiguration; +import org.eclipse.jetty.http.HttpVersion; +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.util.resource.FileResource; +import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.FragmentConfiguration; import org.eclipse.jetty.webapp.JettyWebXmlConfiguration; @@ -39,7 +46,13 @@ import org.junit.BeforeClass; import java.io.File; import java.io.FilenameFilter; +import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.net.ServerSocket; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import static com.github.mjeanroy.junit.servers.commons.Strings.isNotBlank; import static org.eclipse.jetty.util.resource.Resource.newResource; @@ -50,8 +63,13 @@ public abstract class AbstractContainerIT { protected static EmbeddedJetty jetty; + protected static int tlsPort; + protected final WebClient webClient = new WebClient(); + protected static final File TEST_KEYSTORE_PATH = setupKeyStore(); + protected static final String TEST_KEYSTORE_PASSWORD = "password"; + @BeforeClass public static void startContainer() throws Exception { @@ -98,6 +116,7 @@ public abstract class AbstractContainerIT { Server server = getDelegate(); + // web app ctx.setParentLoaderPriority(true); ctx.setWar(webapp); ctx.setServer(server); @@ -109,6 +128,26 @@ public abstract class AbstractContainerIT { } }; + Server server = jetty.getDelegate(); + + // TLS + tlsPort = getFreePort(); + + final SslContextFactory sslContextFactory = new SslContextFactory.Server(); + sslContextFactory.setKeyStorePath(TEST_KEYSTORE_PATH.getAbsolutePath()); + sslContextFactory.setKeyStorePassword(TEST_KEYSTORE_PASSWORD); + sslContextFactory.setKeyManagerPassword(TEST_KEYSTORE_PASSWORD); + + HttpConfiguration https = new HttpConfiguration(); + https.addCustomizer(new SecureRequestCustomizer()); + + final ServerConnector httpsConnector = new ServerConnector( + server, + new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), + new HttpConnectionFactory(https)); + httpsConnector.setPort(tlsPort); + server.addConnector(httpsConnector); + jetty.start(); assertTrue(jetty.isStarted()); @@ -118,6 +157,10 @@ public abstract class AbstractContainerIT { return "http://localhost:" + jetty.getPort() + "/"; } + protected static String getTlsBaseUri() { + return "https://localhost:" + tlsPort + "/"; + } + protected static String getWarDir() { File[] warFiles = new File("target").listFiles(new FilenameFilter() { @Override @@ -150,4 +193,30 @@ public abstract class AbstractContainerIT { jetty.stop(); } } + + private static int getFreePort() { + try (ServerSocket socket = new ServerSocket(0)) { + return socket.getLocalPort(); + } catch (IOException e) { + throw new IllegalStateException("Failed to allocate free port", e); + } + } + + // Dealing with a keystore is NOT fun, it's easier to script one with the keytool + // see src/main/resources/createKeyStore.sh for more info + private static File setupKeyStore() { + try { + Path outKeyStoreFile = File.createTempFile("test-keystore", "jks").toPath(); + URL keyStoreResource = Thread.currentThread().getContextClassLoader().getResource("test-keystore.jks"); + Files.copy(keyStoreResource.openStream(), outKeyStoreFile, StandardCopyOption.REPLACE_EXISTING); + File keyStoreFile = outKeyStoreFile.toFile(); + + // _most_ clients will pick up the ssl keystore this way, so just set SSL properties + System.setProperty("javax.net.ssl.trustStore", keyStoreFile.getAbsolutePath()); + System.setProperty("javax.net.ssl.trustStorePassword", TEST_KEYSTORE_PASSWORD); + return keyStoreFile; + } catch (IOException e) { + throw new IllegalStateException("Failed to create test keystore", e); + } + } } diff --git a/integration-tests/support/src/main/resources/createKeyStore.sh b/integration-tests/support/src/main/resources/createKeyStore.sh new file mode 100755 index 0000000..bf71085 --- /dev/null +++ b/integration-tests/support/src/main/resources/createKeyStore.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +set -e + +# This script will generate a self signed certificate, store it in a Java keystore and PEM format. +# The generated certificate is good for 10 years, and should NOT need to be recreated until then (unless +# changes to the certificate are needed). +# Last run with Java 1.8.0_252 +# +# Usage: For JVM based applications, the resulting keystore MUST be configured to in order for clients to accept TLS +# connections. Typical usage requires setting of the Java system property 'javax.net.ssl.trustStore' to the file path +# of the keystore. Either by adding a command line parameter: `-Djavax.net.ssl.trustStore=/path/to/keystore` or +# programmatically: `System.setProperty("javax.net.ssl.trustStore", "/path/to/keystore")` + +dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" +keystore_file="${dir}/test-keystore.jks" +file_prefix="${dir}/test-keystore" +rm -f ${file_prefix}* + +echo "generate new keystore" +keytool -genkey \ + -keystore "${file_prefix}.jks" \ + -alias "localhost" \ + -keyalg RSA \ + -keysize 2048 \ + -validity 3650 \ + -dname "C=US; ST=Unknown; L=Springfield; O=Unknown; OU=Unknown; CN=localhost" \ + -ext SAN=dns:localhost \ + -keypass password \ + -storepass password \ + -noprompt + +echo "self sign" +keytool -selfcert \ + -alias "localhost" \ + -keystore "${file_prefix}.jks" \ + -validity 3650 \ + -storepass password \ + -noprompt + +echo "export to pem" +keytool -export \ + -alias "localhost" \ + -keystore "${file_prefix}.jks" \ + -rfc \ + -file "${file_prefix}.pem" \ + -storepass password \ + -noprompt diff --git a/integration-tests/support/src/main/resources/test-keystore.jks b/integration-tests/support/src/main/resources/test-keystore.jks new file mode 100644 index 0000000..0cea846 Binary files /dev/null and b/integration-tests/support/src/main/resources/test-keystore.jks differ diff --git a/integration-tests/support/src/main/resources/test-keystore.pem b/integration-tests/support/src/main/resources/test-keystore.pem new file mode 100644 index 0000000..bbbad01 --- /dev/null +++ b/integration-tests/support/src/main/resources/test-keystore.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDjzCCAnegAwIBAgIEDGro0DANBgkqhkiG9w0BAQsFADBtMRIwEAYDVQQDEwls +b2NhbGhvc3QxEDAOBgNVBAsTB1Vua25vd24xEDAOBgNVBAoTB1Vua25vd24xFDAS +BgNVBAcTC1NwcmluZ2ZpZWxkMRAwDgYDVQQIEwdVbmtub3duMQswCQYDVQQGEwJV +UzAeFw0yMDEwMTUxNTE4MThaFw0zMDEwMTMxNTE4MThaMG0xEjAQBgNVBAMTCWxv +Y2FsaG9zdDEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEChMHVW5rbm93bjEUMBIG +A1UEBxMLU3ByaW5nZmllbGQxEDAOBgNVBAgTB1Vua25vd24xCzAJBgNVBAYTAlVT +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmlRyaRblUwPFtvMR+NZa +hW27UEidgyOIXeXS+iwOXbc49B9ADA6utnQybiaKWG5IXMCD1dsPd3Pqx/5Bqmhd +1QQJCQxjx1McieEhXuhON7wqpYlmt+sIVUvPNHFldyuv/CqqZLBLWYj1uaba15E7 +oLPVT+XBRY/uvWR4q6HtweNcBU0m183eaFSzYwpUuhHCgI5V3+qC40eDoCPd6jrA +S6BGldvR+Ysbe5fGQUmL7IUks6YO/AGMRZjmR5pKGE5dmoCPsuusnmHvm8iKfkh0 +KzMDcxpY0ZlwNXIsNfIuIBBBAkGZG2RVmJeOJG6Bv6DCSt3ypOLbb0vHiEm7wLrL +YwIDAQABozcwNTAUBgNVHREEDTALgglsb2NhbGhvc3QwHQYDVR0OBBYEFNDDMtIb +fscg7DRkbQV6ZuKNG7YlMA0GCSqGSIb3DQEBCwUAA4IBAQCLJ6743hPQNx1Sop0v +0Fm+2dm66Xj77aGfB9Xq64/BsQP+imWYuAv0oQq2tgCtXSMBhyfBKQKfEbQcd+m5 +WsiSfkxpvcCWR7Ttc7GuElG6Bb+CqLxLDZuEogIOsVM7MgEfqC5zp94vLhSrLmrl +HzZzisVL/PH0wMpAuD0IRWgdYyPWavBipVHCJYWJRehQon9D+qi1EuwSOZYvq9af +YSfZtkndXrVfpmnO9+xrszO5Yu9qgZfvRLhpal/cmsBpRVyZIWdUj4B1wfPtKT3/ +x+85LN5wNyiRkROf1M0S6mb2Ac7zMJrNI5hKfKM2OOLC/g/ec3NOWH8/7k9FlcRv +alPL +-----END CERTIFICATE----- diff --git a/pom.xml b/pom.xml index 7fe5070..4ad4993 100644 --- a/pom.xml +++ b/pom.xml @@ -293,6 +293,7 @@ <exclude>**/*.json</exclude> <exclude>**/spring.factories</exclude> <exclude>**/spring.provides</exclude> + <exclude>**/*.iml</exclude> </excludes> </configuration> </plugin> @@ -1282,6 +1283,7 @@ <exclude>**/*.json</exclude> <exclude>**/spring.factories</exclude> <exclude>**/spring.provides</exclude> + <exclude>**/*.iml</exclude> </excludes> </configuration> </plugin> diff --git a/samples/web/src/test/java/org/apache/shiro/test/ContainerIntegrationIT.java b/samples/web/src/test/java/org/apache/shiro/test/WebAppContainerIntegrationIT.java similarity index 91% rename from samples/web/src/test/java/org/apache/shiro/test/ContainerIntegrationIT.java rename to samples/web/src/test/java/org/apache/shiro/test/WebAppContainerIntegrationIT.java index 3b8e5b8..3f724f3 100644 --- a/samples/web/src/test/java/org/apache/shiro/test/ContainerIntegrationIT.java +++ b/samples/web/src/test/java/org/apache/shiro/test/WebAppContainerIntegrationIT.java @@ -31,14 +31,14 @@ import org.junit.Test; import java.io.IOException; import java.net.MalformedURLException; -public class ContainerIntegrationIT extends AbstractContainerIT { +public class WebAppContainerIntegrationIT extends AbstractContainerIT { protected final WebClient webClient = new WebClient(); @Before public void logOut() throws IOException { // Make sure we are logged out - final HtmlPage homePage = webClient.getPage(getBaseUri()); + final HtmlPage homePage = webClient.getPage(getTlsBaseUri()); try { homePage.getAnchorByHref("/logout").click(); } @@ -50,7 +50,7 @@ public class ContainerIntegrationIT extends AbstractContainerIT { @Test public void logIn() throws FailingHttpStatusCodeException, MalformedURLException, IOException, InterruptedException { - HtmlPage page = webClient.getPage(getBaseUri() + "login.jsp"); + HtmlPage page = webClient.getPage(getTlsBaseUri() + "login.jsp"); HtmlForm form = page.getFormByName("loginform"); form.<HtmlInput>getInputByName("username").setValueAttribute("root"); form.<HtmlInput>getInputByName("password").setValueAttribute("secret");
