Skip to content

Commit

Permalink
Add ACL Functionality and a small ACL UI Fix
Browse files Browse the repository at this point in the history
  • Loading branch information
an0r0c committed Feb 25, 2021
1 parent 86fc5f6 commit 894495b
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 31 deletions.
15 changes: 11 additions & 4 deletions 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;
Expand Down Expand Up @@ -121,16 +122,22 @@ public void setup(KafkaesqueAdminClient adminClient) {
private void startSearch(ActionEvent actionEvent) {
runInDaemonThread(() -> {
List<Acl> 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;
Expand Down
42 changes: 17 additions & 25 deletions 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;
Expand All @@ -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<String, String> sslProps, Map<String, String> saslProps) {
Properties props = new Properties();
props.setProperty(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
Expand Down Expand Up @@ -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);
}
Expand Down
116 changes: 116 additions & 0 deletions 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<ResourceType> resourceTypes = new SimpleListProperty<>(FXCollections.observableArrayList(ResourceType.values()));
ListProperty<PatternType> patternTypes = new SimpleListProperty<>(FXCollections.observableArrayList(PatternType.values()));
ListProperty<AclOperation> aclOperations = new SimpleListProperty<>(FXCollections.observableArrayList(AclOperation.values()));
ListProperty<AclPermissionType> permissionTypes = new SimpleListProperty<>(FXCollections.observableArrayList(AclPermissionType.values()));

SimpleObjectProperty<ResourceType> selectedResourceType = new SimpleObjectProperty<>(ResourceType.TOPIC);
SimpleStringProperty selectedResourceName = new SimpleStringProperty("");
SimpleObjectProperty<PatternType> selectedPatternType = new SimpleObjectProperty<>(PatternType.LITERAL);
SimpleStringProperty selectedPrincipal = new SimpleStringProperty("");
SimpleStringProperty selectedHost = new SimpleStringProperty("");
SimpleObjectProperty<AclOperation> seletedAclOperation = new SimpleObjectProperty<>(AclOperation.ALL);
SimpleObjectProperty<AclPermissionType> 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<SimpleObjectProperty> 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>(aclBinding);
}
return null;
});

Optional<SimpleObjectProperty> aclBinding = dialog.showAndWait();


if (aclBinding.isPresent()) {
kafkaesqueAdminClient.addAcl((AclBinding) aclBinding.get().get());
}

} catch (Exception e) {
ErrorAlert.show(e);
}
}
}
23 changes: 21 additions & 2 deletions src/main/resources/fxml/aclViewer.fxml
Expand Up @@ -12,6 +12,7 @@
<?import javafx.scene.layout.RowConstraints?>
<?import org.kordamp.ikonli.javafx.FontIcon?>
<?import java.lang.String?>
<?import javafx.scene.control.Tooltip?>
<BorderPane prefHeight="295.0" prefWidth="497.0" xmlns="http://javafx.com/javafx/11.0.1"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="at.esque.kafka.acl.viewer.AclViewerController">
<center>
Expand Down Expand Up @@ -54,15 +55,33 @@
<ComboBox fx:id="resourcePatternCombo" maxWidth="1.7976931348623157E308" prefHeight="25.0"
prefWidth="176.0" GridPane.columnIndex="3" GridPane.hgrow="ALWAYS" GridPane.vgrow="ALWAYS"/>
<Button fx:id="searchButton" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity"
mnemonicParsing="false" onAction="#startSearch" prefWidth="30.0" GridPane.columnIndex="4"
GridPane.halignment="CENTER" GridPane.rowSpan="2" GridPane.valignment="CENTER">
mnemonicParsing="false" onAction="#startSearch" prefWidth="30.0"
GridPane.columnIndex="4" GridPane.rowIndex="1" GridPane.halignment="CENTER" GridPane.columnSpan="1">
<styleClass>
<String fx:value="success"/>
</styleClass>
<graphic>
<FontIcon iconColor="WHITE" iconLiteral="fa-search" iconSize="20"/>
</graphic>
</Button>
<Button fx:id="addButton" maxHeight="-Infinity" maxWidth="-Infinity"
minHeight="30.0" mnemonicParsing="false"
onAction="#addACL" prefWidth="30.0"
GridPane.columnIndex="4" GridPane.rowIndex="0" GridPane.halignment="CENTER" GridPane.columnSpan="1"
>
<tooltip>
<Tooltip text="Add ACL"/>
</tooltip>
<styleClass>
<!-- <String fx:value="btn"/>-->
<String fx:value="success"/>
<!-- <String fx:value="first"/>-->
</styleClass>
<graphic>
<FontIcon iconColor="WHITE" iconLiteral="fa-plus"
iconSize="20"/>
</graphic>
</Button>
</children>
</GridPane>
</top>
Expand Down

0 comments on commit 894495b

Please sign in to comment.