RANGER-2636 Modifying NiFi and NiFi Registry service def and clients to allow using Ranger's default SSL context

Signed-off-by: Velmurugan Periasamy <vel@apache.org>
diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-nifi-registry.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-nifi-registry.json
index 8235edc..ef5f7cf 100644
--- a/agents-common/src/main/resources/service-defs/ranger-servicedef-nifi-registry.json
+++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-nifi-registry.json
@@ -70,6 +70,18 @@
       "defaultValue": "NONE"
     },
     {
+      "itemId": 411,
+      "name": "nifi.registry.ssl.use.default.context",
+      "type": "bool",
+      "subType": "YesTrue:NoFalse",
+      "mandatory": true,
+      "defaultValue": "false",
+      "validationRegEx": "",
+      "validationMessage": "",
+      "uiHint": "{\"TextFieldWithIcon\":true, \"info\": \"If true, then Ranger's keystore and truststore will be used to communicate with NiFi Registry. If false, the keystore and truststore properties must be provided.\"}",
+      "label": "Use Ranger's Default SSL Context"
+    },
+    {
       "itemId":500,
       "name":"nifi.registry.ssl.keystore",
       "type":"string",
diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-nifi.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-nifi.json
index 5f1e46f..400e516 100644
--- a/agents-common/src/main/resources/service-defs/ranger-servicedef-nifi.json
+++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-nifi.json
@@ -65,6 +65,18 @@
       "defaultValue": "NONE"
     },
     {
+      "itemId": 411,
+      "name": "nifi.ssl.use.default.context",
+      "type": "bool",
+      "subType": "YesTrue:NoFalse",
+      "mandatory": true,
+      "defaultValue": "false",
+      "validationRegEx": "",
+      "validationMessage": "",
+      "uiHint": "{\"TextFieldWithIcon\":true, \"info\": \"If true, then Ranger's keystore and truststore will be used to communicate with NiFi. If false, the keystore and truststore properties must be provided.\"}",
+      "label": "Use Ranger's Default SSL Context"
+    },
+    {
       "itemId":500,
       "name":"nifi.ssl.keystore",
       "type":"string",
diff --git a/plugin-nifi-registry/src/main/java/org/apache/ranger/services/nifi/registry/client/NiFiRegistryConfigs.java b/plugin-nifi-registry/src/main/java/org/apache/ranger/services/nifi/registry/client/NiFiRegistryConfigs.java
index ea91ca4..248d061 100644
--- a/plugin-nifi-registry/src/main/java/org/apache/ranger/services/nifi/registry/client/NiFiRegistryConfigs.java
+++ b/plugin-nifi-registry/src/main/java/org/apache/ranger/services/nifi/registry/client/NiFiRegistryConfigs.java
@@ -34,4 +34,6 @@
     String NIFI_REG_SSL_TRUSTSTORE_TYPE = "nifi.registry.ssl.truststoreType";
     String NIFI_REG_SSL_TRUSTSTORE_PASSWORD = "nifi.registry.ssl.truststorePassword";
 
+    String NIFI_REG_SSL_USER_DEFAULT_CONTEXT = "nifi.registry.ssl.use.default.context";
+
 }
diff --git a/plugin-nifi-registry/src/main/java/org/apache/ranger/services/nifi/registry/client/NiFiRegistryConnectionMgr.java b/plugin-nifi-registry/src/main/java/org/apache/ranger/services/nifi/registry/client/NiFiRegistryConnectionMgr.java
index d606d25..412fcdd 100644
--- a/plugin-nifi-registry/src/main/java/org/apache/ranger/services/nifi/registry/client/NiFiRegistryConnectionMgr.java
+++ b/plugin-nifi-registry/src/main/java/org/apache/ranger/services/nifi/registry/client/NiFiRegistryConnectionMgr.java
@@ -19,8 +19,10 @@
 package org.apache.ranger.services.nifi.registry.client;
 
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.ranger.plugin.client.BaseClient;
 
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;
@@ -77,35 +79,56 @@
             final String keystoreType = configs.get(NiFiRegistryConfigs.NIFI_REG_SSL_KEYSTORE_TYPE);
             final String keystorePassword = configs.get(NiFiRegistryConfigs.NIFI_REG_SSL_KEYSTORE_PASSWORD);
 
-            validateNotBlank(keystore, "Keystore is required for " + serviceName + " with Authentication Type of SSL");
-            validateNotBlank(keystoreType, "Keystore Type is required for " + serviceName + " with Authentication Type of SSL");
-            validateNotBlank(keystorePassword, "Keystore Password is required for " + serviceName + " with Authentication Type of SSL");
-
             final String truststore = configs.get(NiFiRegistryConfigs.NIFI_REG_SSL_TRUSTSTORE);
             final String truststoreType = configs.get(NiFiRegistryConfigs.NIFI_REG_SSL_TRUSTSTORE_TYPE);
             final String truststorePassword = configs.get(NiFiRegistryConfigs.NIFI_REG_SSL_TRUSTSTORE_PASSWORD);
 
-            validateNotBlank(truststore, "Truststore is required for " + serviceName + " with Authentication Type of SSL");
-            validateNotBlank(truststoreType, "Truststore Type is required for " + serviceName + " with Authentication Type of SSL");
-            validateNotBlank(truststorePassword, "Truststore Password is required for " + serviceName + " with Authentication Type of SSL");
+            final String useDefaultSSLContext = configs.get(NiFiRegistryConfigs.NIFI_REG_SSL_USER_DEFAULT_CONTEXT);
 
-            LOG.debug("Creating SSLContext for NiFi Registry connection");
+            if (!StringUtils.isBlank(useDefaultSSLContext) && "true".equalsIgnoreCase(useDefaultSSLContext)) {
 
-            sslContext = createSslContext(
-                    keystore.trim(),
-                    keystorePassword.trim().toCharArray(),
-                    keystoreType.trim(),
-                    truststore.trim(),
-                    truststorePassword.trim().toCharArray(),
-                    truststoreType.trim(),
-                    "TLS");
+                if (!StringUtils.isBlank(keystore) || !StringUtils.isBlank(keystoreType) || !StringUtils.isBlank(keystorePassword)
+                        || !StringUtils.isBlank(truststore) || !StringUtils.isBlank(truststoreType) || !StringUtils.isBlank(truststorePassword)) {
+                    throw new IllegalArgumentException("Keystore and Truststore configuration cannot be provided when using default SSL context");
+                }
+
+                sslContext = SSLContext.getDefault();
+            } else {
+
+                validateNotBlank(keystore, "Keystore is required for " + serviceName + " with Authentication Type of SSL");
+                validateNotBlank(keystoreType, "Keystore Type is required for " + serviceName + " with Authentication Type of SSL");
+                validateNotBlank(keystorePassword, "Keystore Password is required for " + serviceName + " with Authentication Type of SSL");
+
+                validateNotBlank(truststore, "Truststore is required for " + serviceName + " with Authentication Type of SSL");
+                validateNotBlank(truststoreType, "Truststore Type is required for " + serviceName + " with Authentication Type of SSL");
+                validateNotBlank(truststorePassword, "Truststore Password is required for " + serviceName + " with Authentication Type of SSL");
+
+                LOG.debug("Creating SSLContext for NiFi Registry connection");
+
+                sslContext = createSslContext(
+                        keystore.trim(),
+                        keystorePassword.trim().toCharArray(),
+                        keystoreType.trim(),
+                        truststore.trim(),
+                        truststorePassword.trim().toCharArray(),
+                        truststoreType.trim(),
+                        "TLS");
+            }
         }
 
         return new NiFiRegistryClient(url.trim(), sslContext);
     }
 
     public static HashMap<String, Object> connectionTest(String serviceName, Map<String, String> configs) throws Exception {
-        NiFiRegistryClient client = getNiFiRegistryClient(serviceName, configs);
+        NiFiRegistryClient client;
+        try {
+            client = getNiFiRegistryClient(serviceName, configs);
+        } catch (Exception e) {
+            final HashMap<String,Object> ret = new HashMap<>();
+            BaseClient.generateResponseDataMap(false, "Error creating NiFi Registry client", e.getMessage(), null, null, ret);
+            return ret;
+        }
+
         return client.connectionTest();
     }
 
diff --git a/plugin-nifi/src/main/java/org/apache/ranger/services/nifi/client/NiFiConfigs.java b/plugin-nifi/src/main/java/org/apache/ranger/services/nifi/client/NiFiConfigs.java
index cc68710..744f1dd 100644
--- a/plugin-nifi/src/main/java/org/apache/ranger/services/nifi/client/NiFiConfigs.java
+++ b/plugin-nifi/src/main/java/org/apache/ranger/services/nifi/client/NiFiConfigs.java
@@ -34,4 +34,6 @@
     String NIFI_SSL_TRUSTSTORE_TYPE = "nifi.ssl.truststoreType";
     String NIFI_SSL_TRUSTSTORE_PASSWORD = "nifi.ssl.truststorePassword";
 
+    String NIFI_SSL_USER_DEFAULT_CONTEXT = "nifi.ssl.use.default.context";
+
 }
diff --git a/plugin-nifi/src/main/java/org/apache/ranger/services/nifi/client/NiFiConnectionMgr.java b/plugin-nifi/src/main/java/org/apache/ranger/services/nifi/client/NiFiConnectionMgr.java
index 19cb1a9..21fae0d 100644
--- a/plugin-nifi/src/main/java/org/apache/ranger/services/nifi/client/NiFiConnectionMgr.java
+++ b/plugin-nifi/src/main/java/org/apache/ranger/services/nifi/client/NiFiConnectionMgr.java
@@ -19,8 +19,10 @@
 package org.apache.ranger.services.nifi.client;
 
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.ranger.plugin.client.BaseClient;
 
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;
@@ -74,35 +76,56 @@
             final String keystoreType = configs.get(NiFiConfigs.NIFI_SSL_KEYSTORE_TYPE);
             final String keystorePassword = configs.get(NiFiConfigs.NIFI_SSL_KEYSTORE_PASSWORD);
 
-            validateNotBlank(keystore, "Keystore is required for " + serviceName + " with Authentication Type of SSL");
-            validateNotBlank(keystoreType, "Keystore Type is required for " + serviceName + " with Authentication Type of SSL");
-            validateNotBlank(keystorePassword, "Keystore Password is required for " + serviceName + " with Authentication Type of SSL");
-
             final String truststore = configs.get(NiFiConfigs.NIFI_SSL_TRUSTSTORE);
             final String truststoreType = configs.get(NiFiConfigs.NIFI_SSL_TRUSTSTORE_TYPE);
             final String truststorePassword = configs.get(NiFiConfigs.NIFI_SSL_TRUSTSTORE_PASSWORD);
 
-            validateNotBlank(truststore, "Truststore is required for " + serviceName + " with Authentication Type of SSL");
-            validateNotBlank(truststoreType, "Truststore Type is required for " + serviceName + " with Authentication Type of SSL");
-            validateNotBlank(truststorePassword, "Truststore Password is required for " + serviceName + " with Authentication Type of SSL");
+            final String useDefaultSSLContext = configs.get(NiFiConfigs.NIFI_SSL_USER_DEFAULT_CONTEXT);
 
-            LOG.debug("Creating SSLContext for NiFi connection");
+            if (!StringUtils.isBlank(useDefaultSSLContext) && "true".equalsIgnoreCase(useDefaultSSLContext)) {
 
-            sslContext = createSslContext(
-                    keystore.trim(),
-                    keystorePassword.trim().toCharArray(),
-                    keystoreType.trim(),
-                    truststore.trim(),
-                    truststorePassword.trim().toCharArray(),
-                    truststoreType.trim(),
-                    "TLS");
+                if (!StringUtils.isBlank(keystore) || !StringUtils.isBlank(keystoreType) || !StringUtils.isBlank(keystorePassword)
+                        || !StringUtils.isBlank(truststore) || !StringUtils.isBlank(truststoreType) || !StringUtils.isBlank(truststorePassword)) {
+                    throw new IllegalArgumentException("Keystore and Truststore configuration cannot be provided when using default SSL context");
+                }
+
+                sslContext = SSLContext.getDefault();
+            } else {
+
+                validateNotBlank(keystore, "Keystore is required for " + serviceName + " with Authentication Type of SSL");
+                validateNotBlank(keystoreType, "Keystore Type is required for " + serviceName + " with Authentication Type of SSL");
+                validateNotBlank(keystorePassword, "Keystore Password is required for " + serviceName + " with Authentication Type of SSL");
+
+                validateNotBlank(truststore, "Truststore is required for " + serviceName + " with Authentication Type of SSL");
+                validateNotBlank(truststoreType, "Truststore Type is required for " + serviceName + " with Authentication Type of SSL");
+                validateNotBlank(truststorePassword, "Truststore Password is required for " + serviceName + " with Authentication Type of SSL");
+
+                LOG.debug("Creating SSLContext for NiFi connection");
+
+                sslContext = createSslContext(
+                        keystore.trim(),
+                        keystorePassword.trim().toCharArray(),
+                        keystoreType.trim(),
+                        truststore.trim(),
+                        truststorePassword.trim().toCharArray(),
+                        truststoreType.trim(),
+                        "TLS");
+            }
         }
 
         return new NiFiClient(url.trim(), sslContext);
     }
 
     public static HashMap<String, Object> connectionTest(String serviceName, Map<String, String> configs) throws Exception {
-        NiFiClient client = getNiFiClient(serviceName, configs);
+        NiFiClient client;
+        try {
+            client = getNiFiClient(serviceName, configs);
+        } catch (Exception e) {
+            final HashMap<String,Object> ret = new HashMap<>();
+            BaseClient.generateResponseDataMap(false, "Error creating NiFi client", e.getMessage(), null, null, ret);
+            return ret;
+        }
+
         return client.connectionTest();
     }