diff --git a/CHANGELOG.md b/CHANGELOG.md index e430dd6d..4c8b3d4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## 2.6.0 (UNRELEASED) +- [ISSUE-144](https://github.com/SourceLabOrg/kafka-webview/issues/144) Make providing a TrustStore file when setting up a SSL enabled cluster optional. You might not want/need this option if your JVM is already configured to accept the SSL certificate served by the cluster, or if the cluster's certificate can be validated by a publically accessible CA. +- [PR-215](https://github.com/SourceLabOrg/kafka-webview/pull/215) Improve errors displayed when using the `test cluster` functionality. + ## 2.5.1 (05/19/2020) - [ISSUE-209](https://github.com/SourceLabOrg/kafka-webview/issues/209) Expose HealthCheck and App Info endpoints without requiring authentication. - Docker image now exposes port 9090 for Actuator end points. diff --git a/kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/controller/configuration/cluster/ClusterConfigController.java b/kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/controller/configuration/cluster/ClusterConfigController.java index d8094cac..d9ec9b06 100644 --- a/kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/controller/configuration/cluster/ClusterConfigController.java +++ b/kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/controller/configuration/cluster/ClusterConfigController.java @@ -24,6 +24,7 @@ package org.sourcelab.kafka.webview.ui.controller.configuration.cluster; +import org.apache.kafka.common.errors.TimeoutException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sourcelab.kafka.webview.ui.controller.BaseController; @@ -136,6 +137,7 @@ public String editClusterForm( clusterForm.setSsl(cluster.isSslEnabled()); clusterForm.setKeyStoreFilename(cluster.getKeyStoreFile()); clusterForm.setTrustStoreFilename(cluster.getTrustStoreFile()); + clusterForm.setUseTrustStore(clusterForm.hasTrustStoreFilename()); // Set SASL options final SaslProperties saslProperties = saslUtility.decodeProperties(cluster); @@ -181,11 +183,14 @@ public String clusterUpdate( if (clusterForm.getSsl()) { // If we're creating a new cluster if (!clusterForm.exists()) { - // Ensure that we have files uploaded - if (clusterForm.getTrustStoreFile() == null || clusterForm.getTrustStoreFile().isEmpty()) { - bindingResult.addError(new FieldError( - "clusterForm", "trustStoreFile", null, true, null, null, "Select a TrustStore JKS to upload") - ); + // Ensure that we have files uploaded for the truststore, + // but only if they elected to upload a truststore at all. + if (clusterForm.getUseTrustStore()) { + if (clusterForm.getTrustStoreFile() == null || clusterForm.getTrustStoreFile().isEmpty()) { + bindingResult.addError(new FieldError( + "clusterForm", "trustStoreFile", null, true, null, null, "Select a TrustStore JKS to upload") + ); + } } // Only require KeyStore if NOT using SASL @@ -233,8 +238,23 @@ public String clusterUpdate( // Flip flag to true cluster.setSslEnabled(true); - // Determine if we should update keystores - if (!clusterForm.exists() || (clusterForm.getTrustStoreFile() != null && !clusterForm.getTrustStoreFile().isEmpty())) { + // If they've selected to NOT use a trust store. + if (!clusterForm.getUseTrustStore()) { + // Delete previous trust store if exists + if (cluster.getTrustStoreFile() != null) { + uploadManager.deleteKeyStore(cluster.getTrustStoreFile()); + } + // Clear out properties + cluster.setTrustStoreFile(null); + cluster.setTrustStorePassword(null); + } + + /* + * Determine if we should update truststore. We Update it in the following scenarios: + * - If the cluster is being newly created. + * - If they uploaded a trust store, and there previously was a truststore. + */ + else if (!clusterForm.exists() || (clusterForm.getTrustStoreFile() != null && !clusterForm.getTrustStoreFile().isEmpty())) { // Delete previous trust store if updating if (cluster.getTrustStoreFile() != null) { uploadManager.deleteKeyStore(cluster.getTrustStoreFile()); @@ -262,6 +282,7 @@ public String clusterUpdate( } } + // Determine if we should update keystores if (!clusterForm.exists() || (clusterForm.getKeyStoreFile() != null && !clusterForm.getKeyStoreFile().isEmpty())) { // Delete previous key store if updating, or if SASL is enabled. if (clusterForm.getSasl() || cluster.getKeyStoreFile() != null) { @@ -421,7 +442,10 @@ public String testCluster(@PathVariable final Long id, final RedirectAttributes } } catch (final Exception e) { // Collect all reasons. - final String reason = e.getMessage(); + String reason = e.getMessage(); + if (e instanceof TimeoutException) { + reason = reason + " (This may indicate an authentication or connection problem)"; + } // Set error msg redirectAttributes.addFlashAttribute( @@ -450,5 +474,12 @@ private void setupBreadCrumbs(final Model model, final String name, final String } else { manager.addCrumb("Clusters", null); } + + // Add default trust store property. + String defaultTrustStore = System.getProperty("javax.net.ssl.trustStore", "/lib/security/cacerts"); + if (defaultTrustStore != null && defaultTrustStore.trim().isEmpty()) { + defaultTrustStore = "/lib/security/cacerts"; + } + model.addAttribute("defaultTrustStore", defaultTrustStore); } } \ No newline at end of file diff --git a/kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/controller/configuration/cluster/forms/ClusterForm.java b/kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/controller/configuration/cluster/forms/ClusterForm.java index c77094cf..b7e57276 100644 --- a/kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/controller/configuration/cluster/forms/ClusterForm.java +++ b/kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/controller/configuration/cluster/forms/ClusterForm.java @@ -45,6 +45,7 @@ public class ClusterForm { // SSL Options private Boolean ssl = false; + private Boolean useTrustStore = false; private MultipartFile trustStoreFile; private String trustStoreFilename; @@ -141,6 +142,22 @@ public String getTrustStoreFilename() { } } + public Boolean getUseTrustStore() { + return useTrustStore; + } + + public void setUseTrustStore(final Boolean useTrustStore) { + this.useTrustStore = useTrustStore; + } + + /** + * Is there a configured TrustStore file? + * @return true if so, false if not. + */ + public boolean hasTrustStoreFilename() { + return trustStoreFilename != null && !trustStoreFilename.isEmpty(); + } + public void setTrustStoreFilename(final String trustStoreFilename) { this.trustStoreFilename = trustStoreFilename; } diff --git a/kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/manager/kafka/KafkaClientConfigUtil.java b/kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/manager/kafka/KafkaClientConfigUtil.java index fd45dd7b..2f3f4c9e 100644 --- a/kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/manager/kafka/KafkaClientConfigUtil.java +++ b/kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/manager/kafka/KafkaClientConfigUtil.java @@ -131,8 +131,11 @@ private void applySslSettings(final ClusterConfig clusterConfig, final Map describeResource(final ConfigResource configResource) { } } - private RuntimeException handleExecutionException(final ExecutionException e) { - if (e.getCause() != null && e.getCause() instanceof RuntimeException) { - return (RuntimeException) e.getCause(); + /** + * Handle ExecutionException errors when raised. + * @param executionException The exception raised. + * @return Appropriate exception instance to throw. + */ + private RuntimeException handleExecutionException(final ExecutionException executionException) { + if (executionException.getCause() != null && executionException.getCause() instanceof RuntimeException) { + return (RuntimeException) executionException.getCause(); } - return new RuntimeException(e.getMessage(), e); + return new RuntimeException(executionException.getMessage(), executionException); } /** diff --git a/kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/manager/kafka/dto/ApiErrorResponse.java b/kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/manager/kafka/dto/ApiErrorResponse.java index f1ccb509..4602046e 100644 --- a/kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/manager/kafka/dto/ApiErrorResponse.java +++ b/kafka-webview-ui/src/main/java/org/sourcelab/kafka/webview/ui/manager/kafka/dto/ApiErrorResponse.java @@ -24,8 +24,6 @@ package org.sourcelab.kafka.webview.ui.manager.kafka.dto; -import net.bytebuddy.implementation.bytecode.Throw; - import java.util.ArrayList; import java.util.Arrays; import java.util.List; diff --git a/kafka-webview-ui/src/main/resources/templates/configuration/cluster/create.html b/kafka-webview-ui/src/main/resources/templates/configuration/cluster/create.html index ea8e5520..694134cb 100644 --- a/kafka-webview-ui/src/main/resources/templates/configuration/cluster/create.html +++ b/kafka-webview-ui/src/main/resources/templates/configuration/cluster/create.html @@ -22,6 +22,10 @@ var isSaslChecked = jQuery('#sasl').is(':checked'); jQuery('#ssl-keystore-options').toggle(!isSaslChecked); }); + jQuery('#useTrustStore').click(function() { + var isChecked = jQuery('#useTrustStore').is(':checked'); + jQuery('#ssl-truststore-options').toggle(isChecked); + }); jQuery('#sasl').click(function() { var isChecked = jQuery('#sasl').is(':checked'); @@ -114,37 +118,64 @@
SSL Settings
- +
-