GEODE-1945: split and refactor JSSESocketJUnitTest The testing for ClientSocketFactory is extracted into the new test ClientSocketFactoryIntegrationTest. Test has been updated to use AssertJ and Rules.
The testing for SocketCreatorFactory and SSL is extract into the new test SSLSocketIntegrationTest. Test has been updated to be more readable and to use AssertJ, Awaitility and Rules. Project: http://git-wip-us.apache.org/repos/asf/incubator-geode/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-geode/commit/8c7efba8 Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/8c7efba8 Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/8c7efba8 Branch: refs/heads/feature/e2e-testing Commit: 8c7efba86bcc8b6bc7009e75737e591b9f5afc4f Parents: b196015 Author: Kirk Lund <kl...@apache.org> Authored: Tue Sep 27 17:09:48 2016 -0700 Committer: Kirk Lund <kl...@apache.org> Committed: Wed Sep 28 10:13:54 2016 -0700 ---------------------------------------------------------------------- .../net/ClientSocketFactoryIntegrationTest.java | 100 +++++++++ .../geode/internal/net/JSSESocketJUnitTest.java | 223 ------------------- .../internal/net/SSLSocketIntegrationTest.java | 184 +++++++++++++++ 3 files changed, 284 insertions(+), 223 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8c7efba8/geode-core/src/test/java/org/apache/geode/internal/net/ClientSocketFactoryIntegrationTest.java ---------------------------------------------------------------------- diff --git a/geode-core/src/test/java/org/apache/geode/internal/net/ClientSocketFactoryIntegrationTest.java b/geode-core/src/test/java/org/apache/geode/internal/net/ClientSocketFactoryIntegrationTest.java new file mode 100644 index 0000000..72e5c4f --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/internal/net/ClientSocketFactoryIntegrationTest.java @@ -0,0 +1,100 @@ +/* + * 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. + */ +package org.apache.geode.internal.net; + +import static org.apache.geode.distributed.ConfigurationProperties.*; +import static org.apache.geode.distributed.internal.DistributionConfig.*; +import static org.apache.geode.internal.security.SecurableCommunicationChannel.*; +import static org.assertj.core.api.Assertions.*; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.util.Properties; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.RestoreSystemProperties; +import org.junit.experimental.categories.Category; + +import org.apache.geode.distributed.ClientSocketFactory; +import org.apache.geode.distributed.internal.DistributionConfig; +import org.apache.geode.distributed.internal.DistributionConfigImpl; +import org.apache.geode.test.junit.categories.IntegrationTest; + +/** + * Integration tests for ClientSocketFactory. + * + * <p>Extracted from {@code JSSESocketJUnitTest}. + */ +@Category(IntegrationTest.class) +public class ClientSocketFactoryIntegrationTest { + + private static final String EXCEPTION_MESSAGE = "TSocketFactory createSocket threw an IOException"; + + private static volatile boolean invokedCreateSocket; + + private Socket socket; + + @Rule + public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); + + @Before + public void setUp() throws Exception { + System.setProperty(GEMFIRE_PREFIX + "clientSocketFactory", TSocketFactory.class.getName()); + + Properties properties = new Properties(); + properties.setProperty(CLUSTER_SSL_ENABLED, "false"); + DistributionConfig distributionConfig = new DistributionConfigImpl(properties); + + SocketCreatorFactory.setDistributionConfig(distributionConfig); + } + + @After + public void tearDown() throws Exception { + if (this.socket != null) { + this.socket.close(); + } + + System.clearProperty(GEMFIRE_PREFIX + "clientSocketFactory"); + SocketCreatorFactory.getSocketCreatorForComponent(CLUSTER).initializeClientSocketFactory(); + + invokedCreateSocket = false; + } + + @Test + public void testClientSocketFactory() throws Exception { + assertThatThrownBy(() -> this.socket = SocketCreatorFactory.getSocketCreatorForComponent(CLUSTER).connectForClient("localhost", 12345, 0)) + .isExactlyInstanceOf(IOException.class) + .hasMessage(EXCEPTION_MESSAGE); + + assertThat(invokedCreateSocket).isTrue(); + } + + private static class TSocketFactory implements ClientSocketFactory { + + public TSocketFactory() { + } + + public Socket createSocket(final InetAddress address, final int port) throws IOException { + invokedCreateSocket = true; + throw new IOException(EXCEPTION_MESSAGE); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8c7efba8/geode-core/src/test/java/org/apache/geode/internal/net/JSSESocketJUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-core/src/test/java/org/apache/geode/internal/net/JSSESocketJUnitTest.java b/geode-core/src/test/java/org/apache/geode/internal/net/JSSESocketJUnitTest.java deleted file mode 100755 index e63a46f..0000000 --- a/geode-core/src/test/java/org/apache/geode/internal/net/JSSESocketJUnitTest.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * 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. - */ -package org.apache.geode.internal.net; - -import static org.apache.geode.distributed.ConfigurationProperties.*; -import static org.junit.Assert.*; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.LineNumberReader; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.StringReader; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.Properties; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.core.Appender; -import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.Logger; -import org.apache.logging.log4j.core.appender.AbstractAppender; -import org.apache.logging.log4j.core.config.LoggerConfig; -import org.apache.logging.log4j.core.layout.PatternLayout; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.contrib.java.lang.system.SystemErrRule; -import org.junit.contrib.java.lang.system.SystemOutRule; -import org.junit.experimental.categories.Category; -import org.junit.rules.TestName; - -import org.apache.geode.distributed.ClientSocketFactory; -import org.apache.geode.distributed.internal.DistributionConfig; -import org.apache.geode.distributed.internal.DistributionConfigImpl; -import org.apache.geode.internal.AvailablePort; -import org.apache.geode.internal.logging.LogService; -import org.apache.geode.internal.security.SecurableCommunicationChannel; -import org.apache.geode.test.dunit.ThreadUtils; -import org.apache.geode.test.junit.categories.IntegrationTest; -import org.apache.geode.util.test.TestUtil; - -/** - * Test creation of server sockets and client sockets with various JSSE - * configurations. - */ -@Category(IntegrationTest.class) -public class JSSESocketJUnitTest { - - private static volatile boolean factoryInvoked; - - private ServerSocket acceptor; - private Socket server; - private int randport; - - @Rule - public TestName name = new TestName(); - - @Rule - public SystemOutRule systemOutRule = new SystemOutRule(); - - @Before - public void setUp() throws Exception { - randport = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET); - } - - @After - public void tearDown() throws Exception { - if (server != null) { - server.close(); - } - if (acceptor != null) { - acceptor.close(); - } - SocketCreatorFactory.close(); - } - - @Test - public void testSSLSocket() throws Exception { - systemOutRule.mute().enableLog(); - - final Object[] receiver = new Object[1]; - - // Get original base log level - Level originalBaseLevel = LogService.getBaseLogLevel(); - try { - // Set base log level to debug to log the SSL messages - LogService.setBaseLogLevel(Level.DEBUG); - { - System.setProperty(DistributionConfig.GEMFIRE_PREFIX + MCAST_PORT, "0"); - System.setProperty(DistributionConfig.GEMFIRE_PREFIX + CLUSTER_SSL_ENABLED, "true"); - System.setProperty(DistributionConfig.GEMFIRE_PREFIX + CLUSTER_SSL_REQUIRE_AUTHENTICATION, "true"); - System.setProperty(DistributionConfig.GEMFIRE_PREFIX + CLUSTER_SSL_CIPHERS, "any"); - System.setProperty(DistributionConfig.GEMFIRE_PREFIX + CLUSTER_SSL_PROTOCOLS, "TLSv1.2"); - - File jks = findTestJKS(); - System.setProperty("javax.net.ssl.trustStore", jks.getCanonicalPath()); - System.setProperty("javax.net.ssl.trustStorePassword", "password"); - System.setProperty("javax.net.ssl.keyStore", jks.getCanonicalPath()); - System.setProperty("javax.net.ssl.keyStorePassword", "password"); - } - - DistributionConfigImpl distributionConfig = new DistributionConfigImpl(new Properties()); - - SocketCreatorFactory.setDistributionConfig(distributionConfig); - assertTrue(SocketCreatorFactory.getSocketCreatorForComponent(SecurableCommunicationChannel.CLUSTER).useSSL()); - - final ServerSocket serverSocket = SocketCreatorFactory.getSocketCreatorForComponent(SecurableCommunicationChannel.CLUSTER).createServerSocket(randport, 0, InetAddress.getByName("localhost")); - - Thread serverThread = startServer(serverSocket, receiver); - - Socket client = SocketCreatorFactory.getSocketCreatorForComponent(SecurableCommunicationChannel.CLUSTER).connectForServer(InetAddress.getByName("localhost"), randport); - - ObjectOutputStream oos = new ObjectOutputStream(client.getOutputStream()); - String expected = "testing " + name.getMethodName(); - oos.writeObject(expected); - oos.flush(); - - ThreadUtils.join(serverThread, 30 * 1000); - - client.close(); - serverSocket.close(); - assertEquals("Expected \"" + expected + "\" but received \"" + receiver[0] + "\"", expected, receiver[0]); - - String stdOut = systemOutRule.getLog(); - int foundExpectedString = 0; - - Pattern pattern = Pattern.compile(".*peer CN=.*"); - Matcher matcher = pattern.matcher(stdOut); - while (matcher.find()) { - foundExpectedString++; - } - - assertEquals(2, foundExpectedString); - - } finally { - // Reset original base log level - LogService.setBaseLogLevel(originalBaseLevel); - } - } - - /** - * not actually related to this test class, but this is as good a place - * as any for this little test of the client-side ability to tell gemfire - * to use a given socket factory. We just test the connectForClient method - * to see if it's used - */ - @Test - public void testClientSocketFactory() { - System.getProperties().put(DistributionConfig.GEMFIRE_PREFIX + "clientSocketFactory", TSocketFactory.class.getName()); - System.getProperties().put(DistributionConfig.GEMFIRE_PREFIX + CLUSTER_SSL_ENABLED, "false"); - DistributionConfigImpl distributionConfig = new DistributionConfigImpl(new Properties()); - SocketCreatorFactory.setDistributionConfig(distributionConfig); - factoryInvoked = false; - try { - try { - Socket sock = SocketCreatorFactory.getSocketCreatorForComponent(SecurableCommunicationChannel.CLUSTER).connectForClient("localhost", 12345, 0); - sock.close(); - fail("socket factory was invoked"); - } catch (IOException e) { - assertTrue("socket factory was not invoked: " + factoryInvoked, factoryInvoked); - } - } finally { - System.getProperties().remove(DistributionConfig.GEMFIRE_PREFIX + "clientSocketFactory"); - SocketCreatorFactory.getSocketCreatorForComponent(SecurableCommunicationChannel.CLUSTER).initializeClientSocketFactory(); - } - } - - private File findTestJKS() { - return new File(TestUtil.getResourcePath(getClass(), "/ssl/trusted.keystore")); - } - - private Thread startServer(final ServerSocket serverSocket, final Object[] receiver) throws Exception { - Thread t = new Thread(new Runnable() { - public void run() { - try { - Socket s = serverSocket.accept(); - SocketCreatorFactory.getSocketCreatorForComponent(SecurableCommunicationChannel.CLUSTER).configureServerSSLSocket(s); - ObjectInputStream ois = new ObjectInputStream(s.getInputStream()); - receiver[0] = ois.readObject(); - server = s; - acceptor = serverSocket; - } catch (Exception e) { - e.printStackTrace(); - receiver[0] = e; - } - } - }, name.getMethodName() + "-server"); - t.start(); - return t; - } - - private static class TSocketFactory implements ClientSocketFactory { - - public TSocketFactory() { - } - - public Socket createSocket(InetAddress address, int port) throws IOException { - JSSESocketJUnitTest.factoryInvoked = true; - throw new IOException("splort!"); - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/8c7efba8/geode-core/src/test/java/org/apache/geode/internal/net/SSLSocketIntegrationTest.java ---------------------------------------------------------------------- diff --git a/geode-core/src/test/java/org/apache/geode/internal/net/SSLSocketIntegrationTest.java b/geode-core/src/test/java/org/apache/geode/internal/net/SSLSocketIntegrationTest.java new file mode 100755 index 0000000..70f12d7 --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/internal/net/SSLSocketIntegrationTest.java @@ -0,0 +1,184 @@ +/* + * 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. + */ +package org.apache.geode.internal.net; + +import static com.jayway.awaitility.Awaitility.*; +import static org.apache.geode.distributed.ConfigurationProperties.*; +import static org.apache.geode.internal.security.SecurableCommunicationChannel.*; +import static org.assertj.core.api.Assertions.*; + +import java.io.File; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URL; +import java.util.Properties; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.RestoreSystemProperties; +import org.junit.experimental.categories.Category; +import org.junit.rules.ErrorCollector; +import org.junit.rules.TemporaryFolder; +import org.junit.rules.TestName; + +import org.apache.geode.distributed.internal.DistributionConfig; +import org.apache.geode.distributed.internal.DistributionConfigImpl; +import org.apache.geode.internal.FileUtil; +import org.apache.geode.test.junit.categories.IntegrationTest; + +/** + * Integration tests for SocketCreatorFactory with SSL. + * <p> + * <p>Renamed from {@code JSSESocketJUnitTest}. + * + * @see ClientSocketFactoryIntegrationTest + */ +@Category(IntegrationTest.class) +public class SSLSocketIntegrationTest { + + private static final String MESSAGE = SSLSocketIntegrationTest.class.getName() + " Message"; + + private AtomicReference<String> messageFromClient = new AtomicReference<>(); + + private DistributionConfig distributionConfig; + private SocketCreator socketCreator; + private InetAddress localHost; + private Thread serverThread; + private ServerSocket serverSocket; + private Socket clientSocket; + + @Rule + public ErrorCollector errorCollector = new ErrorCollector(); + + @Rule + public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Rule + public TestName testName = new TestName(); + + @Before + public void setUp() throws Exception { + File keystore = findTestKeystore(); + System.setProperty("javax.net.ssl.trustStore", keystore.getCanonicalPath()); + System.setProperty("javax.net.ssl.trustStorePassword", "password"); + System.setProperty("javax.net.ssl.keyStore", keystore.getCanonicalPath()); + System.setProperty("javax.net.ssl.keyStorePassword", "password"); + + Properties properties = new Properties(); + properties.setProperty(MCAST_PORT, "0"); + properties.setProperty(CLUSTER_SSL_ENABLED, "true"); + properties.setProperty(CLUSTER_SSL_REQUIRE_AUTHENTICATION, "true"); + properties.setProperty(CLUSTER_SSL_CIPHERS, "any"); + properties.setProperty(CLUSTER_SSL_PROTOCOLS, "TLSv1.2"); + + this.distributionConfig = new DistributionConfigImpl(properties); + + SocketCreatorFactory.setDistributionConfig(this.distributionConfig); + this.socketCreator = SocketCreatorFactory.getSocketCreatorForComponent(CLUSTER); + + this.localHost = InetAddress.getLocalHost(); + } + + @After + public void tearDown() throws Exception { + if (this.clientSocket != null) { + this.clientSocket.close(); + } + if (this.serverSocket != null) { + this.serverSocket.close(); + } + if (this.serverThread != null && this.serverThread.isAlive()) { + this.serverThread.interrupt(); + } + SocketCreatorFactory.close(); + } + + @Test + public void socketCreatorShouldUseSsl() throws Exception { + assertThat(this.socketCreator.useSSL()).isTrue(); + } + + @Test + public void securedSocketTransmissionShouldWork() throws Exception { + this.serverSocket = this.socketCreator.createServerSocket(0, 0, this.localHost); + this.serverThread = startServer(this.serverSocket); + + int serverPort = this.serverSocket.getLocalPort(); + this.clientSocket = this.socketCreator.connectForServer(this.localHost, serverPort); + + // transmit expected string from Client to Server + ObjectOutputStream output = new ObjectOutputStream(this.clientSocket.getOutputStream()); + output.writeObject(MESSAGE); + output.flush(); + + // this is the real assertion of this test + await().atMost(1, TimeUnit.MINUTES).until(() -> assertThat(this.messageFromClient.get()).isEqualTo(MESSAGE)); + } + + private File findTestKeystore() throws IOException { + return copyKeystoreResourceToFile("/ssl/trusted.keystore"); + } + + public File copyKeystoreResourceToFile(final String name) throws IOException { + URL resource = getClass().getResource(name); + assertThat(resource).isNotNull(); + + File file = this.temporaryFolder.newFile(name.replaceFirst(".*/", "")); + FileUtil.copy(resource, file); + return file; + } + + private Thread startServer(final ServerSocket serverSocket) throws Exception { + Thread serverThread = new Thread(new MyThreadGroup(this.testName.getMethodName()), () -> { + try { + Socket socket = serverSocket.accept(); + SocketCreatorFactory.getSocketCreatorForComponent(CLUSTER).configureServerSSLSocket(socket); + ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); + messageFromClient.set((String) ois.readObject()); + } catch (IOException | ClassNotFoundException e) { + throw new Error(e); + } + }, this.testName.getMethodName() + "-server"); + + serverThread.start(); + return serverThread; + } + + private class MyThreadGroup extends ThreadGroup { + + public MyThreadGroup(final String name) { + super(name); + } + + @Override + public void uncaughtException(final Thread thread, final Throwable throwable) { + errorCollector.addError(throwable); + } + } + +}