Merge pull request #259 from apache/tls-for-its-16x
Update AbstractContainerIT to allow for HTTPS connections Using a pre-generated keystore (1.6.x)
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..4814664 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.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 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 @@
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 @@
Server server = getDelegate();
+ // web app
ctx.setParentLoaderPriority(true);
ctx.setWar(webapp);
ctx.setServer(server);
@@ -109,6 +128,26 @@
}
};
+ 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 @@
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 @@
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();
+
+ // 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
--- /dev/null
+++ b/integration-tests/support/src/main/resources/test-keystore.jks
Binary files 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 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 @@
@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");