GEODE-1570: add a test to verify rest security with SSL.

Project: http://git-wip-us.apache.org/repos/asf/incubator-geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-geode/commit/a515ee06
Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/a515ee06
Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/a515ee06

Branch: refs/heads/feature/GEODE-1930
Commit: a515ee06deeca3adf0ba053883a488787267ebcb
Parents: 08bbbe2
Author: Jinmei Liao <jil...@pivotal.io>
Authored: Mon Nov 7 18:10:52 2016 -0800
Committer: Jinmei Liao <jil...@pivotal.io>
Committed: Wed Nov 9 08:11:07 2016 -0800

----------------------------------------------------------------------
 .../rest/internal/web/GeodeRestClient.java      | 154 +++++++++++--------
 .../internal/web/RestSecurityWithSSLTest.java   |  85 ++++++++++
 .../src/test/resources/ssl/trusted.keystore     | Bin 0 -> 2241 bytes
 .../SecurityClusterConfigDUnitTest.java         |  32 ++--
 .../test/dunit/rules/LocatorStarterRule.java    |  14 ++
 5 files changed, 205 insertions(+), 80 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/a515ee06/geode-assembly/src/test/java/org/apache/geode/rest/internal/web/GeodeRestClient.java
----------------------------------------------------------------------
diff --git 
a/geode-assembly/src/test/java/org/apache/geode/rest/internal/web/GeodeRestClient.java
 
b/geode-assembly/src/test/java/org/apache/geode/rest/internal/web/GeodeRestClient.java
index 071b95c..4510e20 100644
--- 
a/geode-assembly/src/test/java/org/apache/geode/rest/internal/web/GeodeRestClient.java
+++ 
b/geode-assembly/src/test/java/org/apache/geode/rest/internal/web/GeodeRestClient.java
@@ -15,13 +15,10 @@
 
 package org.apache.geode.rest.internal.web;
 
-import org.apache.http.HttpEntity;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpResponse;
 import org.apache.http.auth.AuthScope;
 import org.apache.http.auth.UsernamePasswordCredentials;
-import org.apache.http.client.AuthCache;
-import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.CredentialsProvider;
 import org.apache.http.client.methods.HttpDelete;
 import org.apache.http.client.methods.HttpGet;
@@ -30,46 +27,85 @@ import org.apache.http.client.methods.HttpPost;
 import org.apache.http.client.methods.HttpPut;
 import org.apache.http.client.methods.HttpRequestBase;
 import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
 import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.auth.BasicScheme;
-import org.apache.http.impl.client.BasicAuthCache;
 import org.apache.http.impl.client.BasicCredentialsProvider;
-import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
 import org.apache.http.impl.client.HttpClients;
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.json.JSONTokener;
-import org.junit.Assert;
 
-import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.net.MalformedURLException;
 import java.nio.charset.StandardCharsets;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
 
 public class GeodeRestClient {
-
-  public final static String PROTOCOL = "http";
   public final static String CONTEXT = "/geode/v1";
 
   private int restPort = 0;
   private String bindAddress = null;
+  private String protocol = "http";
+  private boolean useHttps = false;
+  private KeyStore keyStore;
 
   public GeodeRestClient(String bindAddress, int restPort) {
     this.bindAddress = bindAddress;
     this.restPort = restPort;
   }
 
-  public HttpResponse doHEAD(String query, String username, String password)
-      throws MalformedURLException {
+  public GeodeRestClient(String bindAddress, int restPort, boolean useHttps) {
+    if (useHttps) {
+      this.protocol = "https";
+      this.useHttps = true;
+    } else {
+      this.protocol = "http";
+      this.useHttps = false;
+    }
+    this.bindAddress = bindAddress;
+    this.restPort = restPort;
+  }
+
+  public static String getContentType(HttpResponse response) {
+    return response.getEntity().getContentType().getValue();
+  }
+
+  /**
+   * Retrieve the status code of the HttpResponse
+   * 
+   * @param response The HttpResponse message received from the server
+   * @return a numeric value
+   */
+  public static int getCode(HttpResponse response) {
+    return response.getStatusLine().getStatusCode();
+  }
+
+  public static JSONObject getJsonObject(HttpResponse response) throws 
IOException, JSONException {
+    JSONTokener tokener = new JSONTokener(new 
InputStreamReader(response.getEntity().getContent()));
+    return new JSONObject(tokener);
+  }
+
+  public static JSONArray getJsonArray(HttpResponse response) throws 
IOException, JSONException {
+    JSONTokener tokener = new JSONTokener(new 
InputStreamReader(response.getEntity().getContent()));
+    return new JSONArray(tokener);
+  }
+
+  public HttpResponse doHEAD(String query, String username, String password) 
throws Exception {
     HttpHead httpHead = new HttpHead(CONTEXT + query);
     return doRequest(httpHead, username, password);
   }
 
   public HttpResponse doPost(String query, String username, String password, 
String body)
-      throws MalformedURLException {
+      throws Exception {
     HttpPost httpPost = new HttpPost(CONTEXT + query);
     httpPost.addHeader("content-type", "application/json");
     httpPost.setEntity(new StringEntity(body, StandardCharsets.UTF_8));
@@ -77,83 +113,69 @@ public class GeodeRestClient {
   }
 
   public HttpResponse doPut(String query, String username, String password, 
String body)
-      throws MalformedURLException {
+      throws Exception {
     HttpPut httpPut = new HttpPut(CONTEXT + query);
     httpPut.addHeader("content-type", "application/json");
     httpPut.setEntity(new StringEntity(body, StandardCharsets.UTF_8));
     return doRequest(httpPut, username, password);
   }
 
-  public HttpResponse doGet(String uri, String username, String password)
-      throws MalformedURLException {
+  public HttpResponse doGet(String uri, String username, String password) 
throws Exception {
     HttpGet getRequest = new HttpGet(CONTEXT + uri);
     return doRequest(getRequest, username, password);
   }
 
-  public HttpResponse doGetRequest(String url) throws MalformedURLException {
+  public HttpResponse doGetRequest(String url) throws Exception {
     HttpGet getRequest = new HttpGet(url);
     return doRequest(getRequest, null, null);
   }
 
-
-  public HttpResponse doDelete(String uri, String username, String password)
-      throws MalformedURLException {
+  public HttpResponse doDelete(String uri, String username, String password) 
throws Exception {
     HttpDelete httpDelete = new HttpDelete(CONTEXT + uri);
     return doRequest(httpDelete, username, password);
   }
 
-  public static String getContentType(HttpResponse response) {
-    return response.getEntity().getContentType().getValue();
-  }
-
-  /**
-   * Retrieve the status code of the HttpResponse
-   *
-   * @param response The HttpResponse message received from the server
-   *
-   * @return a numeric value
-   */
-  public static int getCode(HttpResponse response) {
-    return response.getStatusLine().getStatusCode();
-  }
-
-  public static JSONObject getJsonObject(HttpResponse response) throws 
IOException, JSONException {
-    JSONTokener tokener = new JSONTokener(new 
InputStreamReader(response.getEntity().getContent()));
-    return new JSONObject(tokener);
-  }
-
-  public static JSONArray getJsonArray(HttpResponse response) throws 
IOException, JSONException {
-    JSONTokener tokener = new JSONTokener(new 
InputStreamReader(response.getEntity().getContent()));
-    return new JSONArray(tokener);
-  }
-
   public HttpResponse doRequest(HttpRequestBase request, String username, 
String password)
-      throws MalformedURLException {
-    HttpHost targetHost = new HttpHost(bindAddress, restPort, PROTOCOL);
-    CloseableHttpClient httpclient = HttpClients.custom().build();
+      throws Exception {
+    HttpHost targetHost = new HttpHost(bindAddress, restPort, protocol);
+
+    HttpClientBuilder clientBuilder = HttpClients.custom();
     HttpClientContext clientContext = HttpClientContext.create();
-    // if username is null, do not put in authentication
+
+    // configures the clientBuilder and clientContext
     if (username != null) {
       CredentialsProvider credsProvider = new BasicCredentialsProvider();
       credsProvider.setCredentials(new AuthScope(targetHost.getHostName(), 
targetHost.getPort()),
           new UsernamePasswordCredentials(username, password));
-      httpclient = 
HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
-      AuthCache authCache = new BasicAuthCache();
-      BasicScheme basicAuth = new BasicScheme();
-      authCache.put(targetHost, basicAuth);
-      clientContext.setCredentialsProvider(credsProvider);
-      clientContext.setAuthCache(authCache);
+      clientBuilder.setDefaultCredentialsProvider(credsProvider);
+    }
+
+    if (useHttps) {
+      SSLContext ctx = SSLContext.getInstance("TLS");
+      ctx.init(new KeyManager[0], new TrustManager[]{new 
DefaultTrustManager()},
+          new SecureRandom());
+      clientBuilder.setSSLContext(ctx);
+      clientBuilder.setSSLHostnameVerifier(new NoopHostnameVerifier());
+    }
+
+    return clientBuilder.build().execute(targetHost, request, clientContext);
+  }
+
+  private static class DefaultTrustManager implements X509TrustManager {
+
+    @Override
+    public void checkClientTrusted(X509Certificate[] arg0, String arg1)
+        throws CertificateException {
+    }
+
+    @Override
+    public void checkServerTrusted(X509Certificate[] arg0, String arg1)
+        throws CertificateException {
     }
 
-    try {
-      return httpclient.execute(targetHost, request, clientContext);
-    } catch (ClientProtocolException e) {
-      e.printStackTrace();
-      Assert.fail("Rest GET should not have thrown ClientProtocolException!");
-    } catch (IOException e) {
-      e.printStackTrace();
-      Assert.fail("Rest GET Request should not have thrown IOException!");
+    @Override
+    public X509Certificate[] getAcceptedIssuers() {
+      return null;
     }
-    return null;
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/a515ee06/geode-assembly/src/test/java/org/apache/geode/rest/internal/web/RestSecurityWithSSLTest.java
----------------------------------------------------------------------
diff --git 
a/geode-assembly/src/test/java/org/apache/geode/rest/internal/web/RestSecurityWithSSLTest.java
 
b/geode-assembly/src/test/java/org/apache/geode/rest/internal/web/RestSecurityWithSSLTest.java
new file mode 100644
index 0000000..f1df632
--- /dev/null
+++ 
b/geode-assembly/src/test/java/org/apache/geode/rest/internal/web/RestSecurityWithSSLTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.rest.internal.web;
+
+import static 
org.apache.geode.distributed.ConfigurationProperties.HTTP_SERVICE_BIND_ADDRESS;
+import static 
org.apache.geode.distributed.ConfigurationProperties.HTTP_SERVICE_PORT;
+import static 
org.apache.geode.distributed.ConfigurationProperties.SECURITY_MANAGER;
+import static 
org.apache.geode.distributed.ConfigurationProperties.SSL_ENABLED_COMPONENTS;
+import static 
org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE;
+import static 
org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE_PASSWORD;
+import static 
org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE_TYPE;
+import static 
org.apache.geode.distributed.ConfigurationProperties.SSL_PROTOCOLS;
+import static 
org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE;
+import static 
org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE_PASSWORD;
+import static 
org.apache.geode.distributed.ConfigurationProperties.START_DEV_REST_API;
+import static org.junit.Assert.assertEquals;
+
+import org.apache.geode.internal.AvailablePortHelper;
+import org.apache.geode.internal.security.SecurableCommunicationChannel;
+import org.apache.geode.security.templates.SimpleSecurityManager;
+import org.apache.geode.test.dunit.rules.ServerStarterRule;
+import org.apache.geode.test.junit.categories.IntegrationTest;
+import org.apache.geode.test.junit.categories.SecurityTest;
+import org.apache.http.HttpResponse;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import java.net.URL;
+import java.util.Properties;
+
+@Category({IntegrationTest.class, SecurityTest.class})
+public class RestSecurityWithSSLTest {
+
+  private static int restPort = 
AvailablePortHelper.getRandomAvailableTCPPort();
+  ServerStarterRule serverStarter = null;
+
+  @Test
+  public void testRestSecurityWithSSL() throws Exception {
+    URL keystoreUrl =
+        
RestSecurityWithSSLTest.class.getClassLoader().getResource("ssl/trusted.keystore");
+
+    Properties properties = new Properties();
+    properties.setProperty(SECURITY_MANAGER, 
SimpleSecurityManager.class.getName());
+    properties.setProperty(START_DEV_REST_API, "true");
+    properties.setProperty(HTTP_SERVICE_BIND_ADDRESS, "localhost");
+    properties.setProperty(HTTP_SERVICE_PORT, restPort + "");
+    properties.setProperty(SSL_ENABLED_COMPONENTS, 
SecurableCommunicationChannel.WEB.getConstant());
+    properties.setProperty(SSL_KEYSTORE, keystoreUrl.getPath());
+    properties.setProperty(SSL_KEYSTORE_PASSWORD, "password");
+    properties.setProperty(SSL_KEYSTORE_TYPE, "JKS");
+    properties.setProperty(SSL_TRUSTSTORE, keystoreUrl.getPath());
+    properties.setProperty(SSL_TRUSTSTORE_PASSWORD, "password");
+    properties.setProperty(SSL_PROTOCOLS, "TLSv1.2,TLSv1.1");
+
+    serverStarter = new ServerStarterRule(properties);
+    serverStarter.startServer();
+
+    GeodeRestClient restClient = new GeodeRestClient("localhost", restPort, 
true);
+    HttpResponse response = restClient.doGet("/servers", "cluster", "cluster");
+
+    assertEquals(200, GeodeRestClient.getCode(response));
+  }
+
+  @After
+  public void after() {
+    if (serverStarter != null) {
+      serverStarter.after();
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/a515ee06/geode-assembly/src/test/resources/ssl/trusted.keystore
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/resources/ssl/trusted.keystore 
b/geode-assembly/src/test/resources/ssl/trusted.keystore
new file mode 100644
index 0000000..bd75039
Binary files /dev/null and 
b/geode-assembly/src/test/resources/ssl/trusted.keystore differ

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/a515ee06/geode-core/src/test/java/org/apache/geode/security/SecurityClusterConfigDUnitTest.java
----------------------------------------------------------------------
diff --git 
a/geode-core/src/test/java/org/apache/geode/security/SecurityClusterConfigDUnitTest.java
 
b/geode-core/src/test/java/org/apache/geode/security/SecurityClusterConfigDUnitTest.java
index cce4fdb..5d843d8 100644
--- 
a/geode-core/src/test/java/org/apache/geode/security/SecurityClusterConfigDUnitTest.java
+++ 
b/geode-core/src/test/java/org/apache/geode/security/SecurityClusterConfigDUnitTest.java
@@ -15,17 +15,14 @@
 
 package org.apache.geode.security;
 
-import static org.apache.geode.distributed.ConfigurationProperties.*;
-import static org.assertj.core.api.Java6Assertions.*;
-import static org.junit.Assert.*;
-
-import java.util.Properties;
-
-import org.apache.geode.test.dunit.rules.ServerStarterRule;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
+import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER;
+import static 
org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_PORT;
+import static 
org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_START;
+import static 
org.apache.geode.distributed.ConfigurationProperties.SECURITY_MANAGER;
+import static 
org.apache.geode.distributed.ConfigurationProperties.SECURITY_POST_PROCESSOR;
+import static org.assertj.core.api.Java6Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import org.apache.geode.GemFireConfigException;
 import org.apache.geode.distributed.DistributedSystem;
@@ -34,8 +31,15 @@ import 
org.apache.geode.security.templates.SimpleSecurityManager;
 import org.apache.geode.test.dunit.IgnoredException;
 import org.apache.geode.test.dunit.internal.JUnit4DistributedTestCase;
 import org.apache.geode.test.dunit.rules.LocatorServerStartupRule;
+import org.apache.geode.test.dunit.rules.ServerStarterRule;
 import org.apache.geode.test.junit.categories.DistributedTest;
 import org.apache.geode.test.junit.categories.SecurityTest;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import java.util.Properties;
 
 @Category({DistributedTest.class, SecurityTest.class})
 public class SecurityClusterConfigDUnitTest extends JUnit4DistributedTestCase {
@@ -51,9 +55,9 @@ public class SecurityClusterConfigDUnitTest extends 
JUnit4DistributedTestCase {
         
.addIgnoredException(LocalizedStrings.GEMFIRE_CACHE_SECURITY_MISCONFIGURATION_2.toString());
     Properties props = new Properties();
     props.setProperty(SECURITY_MANAGER, SimpleSecurityManager.class.getName());
-    props.put(JMX_MANAGER, "false");
-    props.put(JMX_MANAGER_START, "false");
-    props.put(JMX_MANAGER_PORT, 0);
+    props.setProperty(JMX_MANAGER, "false");
+    props.setProperty(JMX_MANAGER_START, "false");
+    props.setProperty(JMX_MANAGER_PORT, 0 + "");
     props.setProperty(SECURITY_POST_PROCESSOR, 
PDXPostProcessor.class.getName());
     lsRule.getLocatorVM(0, props);
   }

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/a515ee06/geode-core/src/test/java/org/apache/geode/test/dunit/rules/LocatorStarterRule.java
----------------------------------------------------------------------
diff --git 
a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/LocatorStarterRule.java
 
b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/LocatorStarterRule.java
index 72627a0..3329a40 100644
--- 
a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/LocatorStarterRule.java
+++ 
b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/LocatorStarterRule.java
@@ -15,6 +15,9 @@
 
 package org.apache.geode.test.dunit.rules;
 
+import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER;
+import static 
org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_PORT;
+import static 
org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_START;
 import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT;
 import static org.junit.Assert.assertTrue;
 
@@ -51,6 +54,17 @@ public class LocatorStarterRule extends ExternalResource 
implements Serializable
     if (!properties.containsKey(MCAST_PORT)) {
       properties.setProperty(MCAST_PORT, "0");
     }
+    if (properties.containsKey(JMX_MANAGER_PORT)) {
+      int jmxPort = Integer.parseInt(properties.getProperty(JMX_MANAGER_PORT));
+      if (jmxPort > 0) {
+        if (!properties.containsKey(JMX_MANAGER)) {
+          properties.put(JMX_MANAGER, "true");
+        }
+        if (!properties.containsKey(JMX_MANAGER_START)) {
+          properties.put(JMX_MANAGER_START, "true");
+        }
+      }
+    }
     locator = (InternalLocator) Locator.startLocatorAndDS(0, null, properties);
     int locatorPort = locator.getPort();
     locator.resetInternalLocatorFileNamesWithCorrectPortNumber(locatorPort);

Reply via email to