From 894495bbe7ab4ea81b9e1831354917f1da137815 Mon Sep 17 00:00:00 2001 From: edelsbrunnerc Date: Thu, 25 Feb 2021 20:23:10 +0100 Subject: [PATCH] Add ACL Functionality and a small ACL UI Fix --- .../kafka/acl/viewer/AclViewerController.java | 15 ++- .../kafka/cluster/KafkaesqueAdminClient.java | 42 +++---- .../esque/kafka/dialogs/CreateACLDialog.java | 116 ++++++++++++++++++ src/main/resources/fxml/aclViewer.fxml | 23 +++- 4 files changed, 165 insertions(+), 31 deletions(-) create mode 100644 src/main/java/at/esque/kafka/dialogs/CreateACLDialog.java diff --git a/src/main/java/at/esque/kafka/acl/viewer/AclViewerController.java b/src/main/java/at/esque/kafka/acl/viewer/AclViewerController.java index 668a8f2..88abbbb 100644 --- a/src/main/java/at/esque/kafka/acl/viewer/AclViewerController.java +++ b/src/main/java/at/esque/kafka/acl/viewer/AclViewerController.java @@ -1,6 +1,7 @@ package at.esque.kafka.acl.viewer; import at.esque.kafka.cluster.KafkaesqueAdminClient; +import at.esque.kafka.dialogs.CreateACLDialog; import javafx.application.Platform; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; @@ -121,16 +122,22 @@ public void setup(KafkaesqueAdminClient adminClient) { private void startSearch(ActionEvent actionEvent) { runInDaemonThread(() -> { List aclList = new ArrayList<>(); + Platform.runLater(() -> refreshRunning.setValue(true)); + adminClient.getACLs(resourceTypeCombo.getValue(), resourcePatternCombo.getValue(), resourceName.getText()) + .forEach(acl -> Platform.runLater(() -> aclList.add(new Acl(acl)))); Platform.runLater(() -> { - refreshRunning.setValue(true); + // This needs to be after building the list otherwise somehow the table view stays empty resultView.setItems(FXCollections.observableArrayList(aclList)); + refreshRunning.setValue(false); }); - adminClient.getACLs(resourceTypeCombo.getValue(), resourcePatternCombo.getValue(), resourceName.getText()) - .forEach(acl -> Platform.runLater(() -> aclList.add(new Acl(acl)))); - Platform.runLater(() -> refreshRunning.setValue(false)); }); } + @FXML + private void addACL(ActionEvent actionEvent) + { + CreateACLDialog.show(adminClient); + } public void stop() { adminClient = null; diff --git a/src/main/java/at/esque/kafka/cluster/KafkaesqueAdminClient.java b/src/main/java/at/esque/kafka/cluster/KafkaesqueAdminClient.java index ad987d4..39133eb 100644 --- a/src/main/java/at/esque/kafka/cluster/KafkaesqueAdminClient.java +++ b/src/main/java/at/esque/kafka/cluster/KafkaesqueAdminClient.java @@ -1,25 +1,10 @@ package at.esque.kafka.cluster; import at.esque.kafka.alerts.ErrorAlert; -import at.esque.kafka.handlers.ConfigHandler; import at.esque.kafka.lag.viewer.Lag; import at.esque.kafka.topics.DescribeTopicWrapper; import javafx.application.Platform; -import org.apache.kafka.clients.admin.AdminClient; -import org.apache.kafka.clients.admin.AdminClientConfig; -import org.apache.kafka.clients.admin.Config; -import org.apache.kafka.clients.admin.ConsumerGroupListing; -import org.apache.kafka.clients.admin.CreateTopicsResult; -import org.apache.kafka.clients.admin.DeleteTopicsResult; -import org.apache.kafka.clients.admin.DescribeAclsResult; -import org.apache.kafka.clients.admin.DescribeConfigsResult; -import org.apache.kafka.clients.admin.DescribeTopicsResult; -import org.apache.kafka.clients.admin.ListConsumerGroupOffsetsResult; -import org.apache.kafka.clients.admin.ListConsumerGroupsResult; -import org.apache.kafka.clients.admin.ListTopicsOptions; -import org.apache.kafka.clients.admin.ListTopicsResult; -import org.apache.kafka.clients.admin.NewTopic; -import org.apache.kafka.clients.admin.TopicDescription; +import org.apache.kafka.clients.admin.*; import org.apache.kafka.common.TopicPartitionInfo; import org.apache.kafka.common.acl.AccessControlEntryFilter; import org.apache.kafka.common.acl.AclBinding; @@ -31,21 +16,14 @@ import org.apache.kafka.common.resource.ResourcePatternFilter; import org.apache.kafka.common.resource.ResourceType; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; public class KafkaesqueAdminClient { private AdminClient adminClient; - + public KafkaesqueAdminClient(String bootstrapServers, Map sslProps, Map saslProps) { Properties props = new Properties(); props.setProperty(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); @@ -155,6 +133,20 @@ public void deleteAcl(AclBinding aclBinding) { } } + public void addAcl(AclBinding aclBinding) + { + try { + + + CreateAclsResult result = adminClient.createAcls(Arrays.asList(aclBinding)); + + result.all().get(); + + } catch (Exception e) { + Platform.runLater(() -> ErrorAlert.show(e)); + } + } + public ListConsumerGroupOffsetsResult listConsumerGroupOffsets(String groupId) { return adminClient.listConsumerGroupOffsets(groupId); } diff --git a/src/main/java/at/esque/kafka/dialogs/CreateACLDialog.java b/src/main/java/at/esque/kafka/dialogs/CreateACLDialog.java new file mode 100644 index 0000000..0b11b4e --- /dev/null +++ b/src/main/java/at/esque/kafka/dialogs/CreateACLDialog.java @@ -0,0 +1,116 @@ +package at.esque.kafka.dialogs; + +import at.esque.kafka.Main; +import at.esque.kafka.acl.viewer.Acl; +import at.esque.kafka.alerts.ErrorAlert; +import at.esque.kafka.cluster.KafkaesqueAdminClient; +import com.dlsc.formsfx.model.structure.Field; +import com.dlsc.formsfx.model.structure.Form; +import com.dlsc.formsfx.model.structure.Group; +import com.dlsc.formsfx.model.util.BindingMode; +import com.dlsc.formsfx.view.renderer.FormRenderer; +import javafx.beans.property.*; +import javafx.collections.FXCollections; +import javafx.scene.Node; +import javafx.scene.control.ButtonBar; +import javafx.scene.control.ButtonType; +import javafx.scene.control.Dialog; +import org.apache.kafka.common.acl.AccessControlEntry; +import org.apache.kafka.common.acl.AclBinding; +import org.apache.kafka.common.acl.AclOperation; +import org.apache.kafka.common.acl.AclPermissionType; +import org.apache.kafka.common.resource.PatternType; +import org.apache.kafka.common.resource.ResourcePattern; +import org.apache.kafka.common.resource.ResourceType; + +import java.util.Arrays; +import java.util.Optional; + +public class CreateACLDialog { + + private CreateACLDialog() { + } + + public static void show(KafkaesqueAdminClient kafkaesqueAdminClient) { + + try { + + ListProperty resourceTypes = new SimpleListProperty<>(FXCollections.observableArrayList(ResourceType.values())); + ListProperty patternTypes = new SimpleListProperty<>(FXCollections.observableArrayList(PatternType.values())); + ListProperty aclOperations = new SimpleListProperty<>(FXCollections.observableArrayList(AclOperation.values())); + ListProperty permissionTypes = new SimpleListProperty<>(FXCollections.observableArrayList(AclPermissionType.values())); + + SimpleObjectProperty selectedResourceType = new SimpleObjectProperty<>(ResourceType.TOPIC); + SimpleStringProperty selectedResourceName = new SimpleStringProperty(""); + SimpleObjectProperty selectedPatternType = new SimpleObjectProperty<>(PatternType.LITERAL); + SimpleStringProperty selectedPrincipal = new SimpleStringProperty(""); + SimpleStringProperty selectedHost = new SimpleStringProperty(""); + SimpleObjectProperty seletedAclOperation = new SimpleObjectProperty<>(AclOperation.ALL); + SimpleObjectProperty selectedPermissionType = new SimpleObjectProperty<>(AclPermissionType.ALLOW); + + // Show dialog + Form form = Form.of( + Group.of( + Field.ofSingleSelectionType(resourceTypes) + .label("Res. Type:") + .bind(resourceTypes,selectedResourceType), + Field.ofStringType("") + .label("Res. Name:") + .bind(selectedResourceName), + Field.ofSingleSelectionType(patternTypes) + .label("Pattern Type:") + .bind(patternTypes,selectedPatternType) + ), + Group.of( + Field.ofStringType("User:") + .label("Principal:") + .bind(selectedPrincipal), + Field.ofStringType("*") + .label("Host:") + .bind(selectedHost), + Field.ofSingleSelectionType(aclOperations) + .label("ACL Operation:") + .bind(aclOperations, seletedAclOperation), + Field.ofSingleSelectionType(permissionTypes) + .label("Perm. Type:") + .bind(permissionTypes,selectedPermissionType) + ) + ).title("Create ACL") + .binding(BindingMode.CONTINUOUS); + + Dialog dialog = new Dialog<>(); + Main.applyIcon(dialog); + dialog.setTitle("Create ACL"); + + ButtonType createAclButton = new ButtonType("Create", ButtonBar.ButtonData.OK_DONE); + dialog.getDialogPane().getButtonTypes().addAll(createAclButton, ButtonType.CANCEL); + + Node addClusterButton = dialog.getDialogPane().lookupButton(createAclButton); + addClusterButton.getStyleClass().add("primary"); + + FormRenderer formRenderer = new FormRenderer(form); + formRenderer.setPrefWidth(500); + dialog.getDialogPane().setContent(formRenderer); + + dialog.setResultConverter(dialogButton -> { + if (dialogButton == createAclButton) { + AclBinding aclBinding = new AclBinding(new ResourcePattern(selectedResourceType.get(), selectedResourceName.get(), selectedPatternType.get()), + new AccessControlEntry(selectedPrincipal.get(),selectedHost.get(),seletedAclOperation.get(),selectedPermissionType.get())); + + return new SimpleObjectProperty(aclBinding); + } + return null; + }); + + Optional aclBinding = dialog.showAndWait(); + + + if (aclBinding.isPresent()) { + kafkaesqueAdminClient.addAcl((AclBinding) aclBinding.get().get()); + } + + } catch (Exception e) { + ErrorAlert.show(e); + } + } +} diff --git a/src/main/resources/fxml/aclViewer.fxml b/src/main/resources/fxml/aclViewer.fxml index 1d8e9fd..71f61c6 100644 --- a/src/main/resources/fxml/aclViewer.fxml +++ b/src/main/resources/fxml/aclViewer.fxml @@ -12,6 +12,7 @@ +
@@ -54,8 +55,8 @@ +