You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shiro.apache.org by bd...@apache.org on 2020/10/15 15:33:37 UTC

[shiro] 02/02: Update AbstractContainerIT to allow for HTTPS connections

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 <bd...@apache.org>
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");