From 223fc4d81fd58bbcca1ac9e8358785e05799bed4 Mon Sep 17 00:00:00 2001
From: Mason Woodford <63433735+masonwoodford@users.noreply.github.com>
Date: Tue, 7 May 2024 21:18:00 -0700
Subject: [PATCH 01/17] fix schema registry config
---
.../Sections/SchemaRegistry.tsx | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/frontend/src/widgets/ClusterConfigForm/Sections/SchemaRegistry.tsx b/frontend/src/widgets/ClusterConfigForm/Sections/SchemaRegistry.tsx
index bd36ee893..7b9eb2445 100644
--- a/frontend/src/widgets/ClusterConfigForm/Sections/SchemaRegistry.tsx
+++ b/frontend/src/widgets/ClusterConfigForm/Sections/SchemaRegistry.tsx
@@ -1,4 +1,5 @@
import React from 'react';
+import { useState } from 'react';
import Input from 'components/common/Input/Input';
import { useFormContext } from 'react-hook-form';
import SectionHeader from 'widgets/ClusterConfigForm/common/SectionHeader';
@@ -6,24 +7,21 @@ import SSLForm from 'widgets/ClusterConfigForm/common/SSLForm';
import Credentials from 'widgets/ClusterConfigForm/common/Credentials';
const SchemaRegistry = () => {
- const { setValue, watch } = useFormContext();
- const schemaRegistry = watch('schemaRegistry');
+ const { setValue } = useFormContext();
+ const [configOpen, setConfigOpen] = useState(false);
const toggleConfig = () => {
- setValue(
- 'schemaRegistry',
- schemaRegistry ? undefined : { url: '', isAuth: false },
- { shouldValidate: true, shouldDirty: true, shouldTouch: true }
- );
+ setConfigOpen((prevConfigOpen) => !prevConfigOpen);
+ setValue('schemaRegistry', { url: '', isAuth: false });
};
return (
<>
- {schemaRegistry && (
+ {configOpen && (
<>
Date: Tue, 7 May 2024 23:53:01 -0700
Subject: [PATCH 02/17] retain old behavior
---
frontend/src/widgets/ClusterConfigForm/Sections/KSQL.tsx | 8 +++++---
.../ClusterConfigForm/Sections/SchemaRegistry.tsx | 9 +++++++--
2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/frontend/src/widgets/ClusterConfigForm/Sections/KSQL.tsx b/frontend/src/widgets/ClusterConfigForm/Sections/KSQL.tsx
index 8d28ad65c..1b6fc2396 100644
--- a/frontend/src/widgets/ClusterConfigForm/Sections/KSQL.tsx
+++ b/frontend/src/widgets/ClusterConfigForm/Sections/KSQL.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, {useState} from 'react';
import Input from 'components/common/Input/Input';
import { useFormContext } from 'react-hook-form';
import SectionHeader from 'widgets/ClusterConfigForm/common/SectionHeader';
@@ -8,7 +8,9 @@ import Credentials from 'widgets/ClusterConfigForm/common/Credentials';
const KSQL = () => {
const { setValue, watch } = useFormContext();
const ksql = watch('ksql');
+ const [configOpen, setConfigOpen] = useState(false);
const toggleConfig = () => {
+ setConfigOpen((prevConfigOpen) => !prevConfigOpen);
setValue('ksql', ksql ? undefined : { url: '', isAuth: false }, {
shouldValidate: true,
shouldDirty: true,
@@ -19,11 +21,11 @@ const KSQL = () => {
<>
- {ksql && (
+ {configOpen && (
<>
{
- const { setValue } = useFormContext();
+ const { setValue, watch } = useFormContext();
+ const schemaRegistry = watch('schemaRegistry');
const [configOpen, setConfigOpen] = useState(false);
const toggleConfig = () => {
setConfigOpen((prevConfigOpen) => !prevConfigOpen);
- setValue('schemaRegistry', { url: '', isAuth: false });
+ setValue('schemaRegistry', schemaRegistry ? undefined : { url: '', isAuth: false }, {
+ shouldValidate: true,
+ shouldDirty: true,
+ shouldTouch: true,
+ });
};
return (
<>
From 396ba8f6549df87cd2033ea3d11abfa96ae06633 Mon Sep 17 00:00:00 2001
From: Mason Woodford <63433735+masonwoodford@users.noreply.github.com>
Date: Wed, 8 May 2024 18:58:12 -0700
Subject: [PATCH 03/17] fix remaining components
---
.../Sections/Authentication/Authentication.tsx | 13 ++++++++-----
.../widgets/ClusterConfigForm/Sections/KSQL.tsx | 11 ++++++-----
.../ClusterConfigForm/Sections/KafkaCluster.tsx | 11 +++++++----
.../widgets/ClusterConfigForm/Sections/Metrics.tsx | 14 +++++++++-----
.../ClusterConfigForm/Sections/SchemaRegistry.tsx | 5 +++--
frontend/src/widgets/ClusterConfigForm/types.ts | 3 +++
.../utils/transformFormDataToPayload.ts | 8 ++++----
7 files changed, 40 insertions(+), 25 deletions(-)
diff --git a/frontend/src/widgets/ClusterConfigForm/Sections/Authentication/Authentication.tsx b/frontend/src/widgets/ClusterConfigForm/Sections/Authentication/Authentication.tsx
index 0fad2cbf1..e2064b4de 100644
--- a/frontend/src/widgets/ClusterConfigForm/Sections/Authentication/Authentication.tsx
+++ b/frontend/src/widgets/ClusterConfigForm/Sections/Authentication/Authentication.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, {useState} from 'react';
import { useFormContext } from 'react-hook-form';
import { AUTH_OPTIONS, SECURITY_PROTOCOL_OPTIONS } from 'lib/constants';
import ControlledSelect from 'components/common/Select/ControlledSelect';
@@ -10,25 +10,28 @@ const Authentication: React.FC = () => {
const { watch, setValue } = useFormContext();
const hasAuth = !!watch('auth');
const authMethod = watch('auth.method');
+ const [configOpen, setConfigOpen] = useState(false);
const hasSecurityProtocolField =
authMethod && !['Delegation tokens', 'mTLS'].includes(authMethod);
- const toggle = () =>
- setValue('auth', hasAuth ? undefined : {}, {
+ const toggle = () => {
+ setConfigOpen((prevConfigOpen) => !prevConfigOpen);
+ setValue('auth', hasAuth ? { isActive: false } : { isActive: true }, {
shouldValidate: true,
shouldDirty: true,
shouldTouch: true,
});
+ };
return (
<>
- {hasAuth && (
+ {configOpen && (
<>
{
const [configOpen, setConfigOpen] = useState(false);
const toggleConfig = () => {
setConfigOpen((prevConfigOpen) => !prevConfigOpen);
- setValue('ksql', ksql ? undefined : { url: '', isAuth: false }, {
- shouldValidate: true,
- shouldDirty: true,
- shouldTouch: true,
- });
+ setValue('ksql', ksql ? { isActive: false } : { isActive: false, url: '', isAuth: false }, {
+ shouldValidate: true,
+ shouldDirty: true,
+ shouldTouch: true,
+ }
+ );
};
return (
<>
diff --git a/frontend/src/widgets/ClusterConfigForm/Sections/KafkaCluster.tsx b/frontend/src/widgets/ClusterConfigForm/Sections/KafkaCluster.tsx
index 8a1be6222..602a5a5b6 100644
--- a/frontend/src/widgets/ClusterConfigForm/Sections/KafkaCluster.tsx
+++ b/frontend/src/widgets/ClusterConfigForm/Sections/KafkaCluster.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, {useState} from 'react';
import Input from 'components/common/Input/Input';
import { useFieldArray, useFormContext } from 'react-hook-form';
import { FormError, InputHint } from 'components/common/Input/Input.styled';
@@ -21,19 +21,22 @@ const KafkaCluster: React.FC = () => {
name: 'bootstrapServers',
});
- const hasTrustStore = !!watch('truststore');
+ const [hasTrustStore, setHasTrustStore] = useState(false);
- const toggleSection = (section: string) => () =>
+ const toggleSection = (section: string) => () => {
+ setHasTrustStore((prevConfigOpen) => !prevConfigOpen);
setValue(
section,
watch(section)
- ? undefined
+ ? { isActive: false }
: {
+ isActive: true,
location: '',
password: '',
},
{ shouldValidate: true, shouldDirty: true, shouldTouch: true }
);
+ };
return (
<>
diff --git a/frontend/src/widgets/ClusterConfigForm/Sections/Metrics.tsx b/frontend/src/widgets/ClusterConfigForm/Sections/Metrics.tsx
index e9d67f201..2c803e380 100644
--- a/frontend/src/widgets/ClusterConfigForm/Sections/Metrics.tsx
+++ b/frontend/src/widgets/ClusterConfigForm/Sections/Metrics.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, {useState} from 'react';
import Input from 'components/common/Input/Input';
import { useFormContext } from 'react-hook-form';
import ControlledSelect from 'components/common/Select/ControlledSelect';
@@ -11,28 +11,32 @@ import Credentials from 'widgets/ClusterConfigForm/common/Credentials';
const Metrics = () => {
const { setValue, watch } = useFormContext();
const visibleMetrics = !!watch('metrics');
- const toggleMetrics = () =>
+ const [configOpen, setConfigOpen] = useState(false);
+ const toggleMetrics = () => {
+ setConfigOpen((prevConfigOpen) => !prevConfigOpen);
setValue(
'metrics',
visibleMetrics
- ? undefined
+ ? { isActive: false }
: {
+ isActive: true,
type: '',
port: 0,
isAuth: false,
},
{ shouldValidate: true, shouldDirty: true, shouldTouch: true }
);
+ };
return (
<>
- {visibleMetrics && (
+ {configOpen && (
<>
{
const [configOpen, setConfigOpen] = useState(false);
const toggleConfig = () => {
setConfigOpen((prevConfigOpen) => !prevConfigOpen);
- setValue('schemaRegistry', schemaRegistry ? undefined : { url: '', isAuth: false }, {
+ setValue('schemaRegistry', schemaRegistry ? { isActive: false }: { isActive: true, url: '', isAuth: false }, {
shouldValidate: true,
shouldDirty: true,
shouldTouch: true,
- });
+ }
+ );
};
return (
<>
diff --git a/frontend/src/widgets/ClusterConfigForm/types.ts b/frontend/src/widgets/ClusterConfigForm/types.ts
index 6af74b5f8..d10199a61 100644
--- a/frontend/src/widgets/ClusterConfigForm/types.ts
+++ b/frontend/src/widgets/ClusterConfigForm/types.ts
@@ -20,6 +20,7 @@ type WithAuth = {
type URLWithAuth = WithAuth &
WithKeystore & {
url?: string;
+ isActive?: string;
};
type KafkaConnect = WithAuth &
@@ -30,6 +31,7 @@ type KafkaConnect = WithAuth &
type Metrics = WithAuth &
WithKeystore & {
+ isActive?: string;
type: string;
port: string;
};
@@ -43,6 +45,7 @@ export type ClusterConfigFormValues = {
password: string;
};
auth?: WithKeystore & {
+ isActive?: string;
method: string;
securityProtocol: SecurityProtocol;
props: Record;
diff --git a/frontend/src/widgets/ClusterConfigForm/utils/transformFormDataToPayload.ts b/frontend/src/widgets/ClusterConfigForm/utils/transformFormDataToPayload.ts
index 91f9ad1e2..0dea696a1 100644
--- a/frontend/src/widgets/ClusterConfigForm/utils/transformFormDataToPayload.ts
+++ b/frontend/src/widgets/ClusterConfigForm/utils/transformFormDataToPayload.ts
@@ -52,7 +52,7 @@ export const transformFormDataToPayload = (data: ClusterConfigFormValues) => {
}
// Schema Registry
- if (data.schemaRegistry) {
+ if (data.schemaRegistry?.isActive) {
config.schemaRegistry = data.schemaRegistry.url;
config.schemaRegistryAuth = transformToCredentials(
data.schemaRegistry.isAuth,
@@ -65,7 +65,7 @@ export const transformFormDataToPayload = (data: ClusterConfigFormValues) => {
}
// KSQL
- if (data.ksql) {
+ if (data.ksql?.isActive) {
config.ksqldbServer = data.ksql.url;
config.ksqldbServerAuth = transformToCredentials(
data.ksql.isAuth,
@@ -88,7 +88,7 @@ export const transformFormDataToPayload = (data: ClusterConfigFormValues) => {
}
// Metrics
- if (data.metrics) {
+ if (data.metrics?.isActive) {
config.metrics = {
type: data.metrics.type,
port: Number(data.metrics.port),
@@ -106,7 +106,7 @@ export const transformFormDataToPayload = (data: ClusterConfigFormValues) => {
};
// Authentication
- if (data.auth) {
+ if (data.auth?.isActive) {
const { method, props, securityProtocol, keystore } = data.auth;
switch (method) {
case 'SASL/JAAS':
From 42c236dbae7874851698533bb865787950d2a9f7 Mon Sep 17 00:00:00 2001
From: Hugo Abreu <6619758+hugomiguelabreu@users.noreply.github.com>
Date: Sat, 11 May 2024 18:05:20 +0100
Subject: [PATCH 04/17] Infra: Ensure image arch automatic inference (#378)
---
README.md | 2 +-
api/Dockerfile | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 1ef454906..2b21a0b99 100644
--- a/README.md
+++ b/README.md
@@ -100,7 +100,7 @@ To run Kafbat UI, you can use either a pre-built Docker image or build it (or a
## Quick start (Demo run)
```
-docker run -it -p 8080:8080 -e DYNAMIC_CONFIG_ENABLED=true image: ghcr.io/kafbat/kafka-ui
+docker run -it -p 8080:8080 -e DYNAMIC_CONFIG_ENABLED=true ghcr.io/kafbat/kafka-ui
```
Then access the web UI at [http://localhost:8080](http://localhost:8080)
diff --git a/api/Dockerfile b/api/Dockerfile
index 422c020de..9e1173250 100644
--- a/api/Dockerfile
+++ b/api/Dockerfile
@@ -1,5 +1,4 @@
-#FROM azul/zulu-openjdk-alpine:17-jre-headless
-FROM azul/zulu-openjdk-alpine@sha256:d59f1266db40341318e563fd76c21b2880ffa5d371f0c097c29d33f89c3a0010
+FROM azul/zulu-openjdk-alpine:17.0.11-jre-headless
RUN apk add --no-cache \
# snappy codec
From ba92bcb8a4434d9751b853d74967329edc5c9303 Mon Sep 17 00:00:00 2001
From: Pavel Makarichev
Date: Thu, 16 May 2024 16:18:29 +0500
Subject: [PATCH 05/17] FE: Messages: Explode partitions list in API (#331)
Co-authored-by: Roman Zabaluev
---
frontend/src/lib/hooks/api/topicMessages.tsx | 8 ++++----
frontend/src/lib/hooks/useMessagesFilters.ts | 13 +++++++++----
2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/frontend/src/lib/hooks/api/topicMessages.tsx b/frontend/src/lib/hooks/api/topicMessages.tsx
index e552c0dbf..f9da9a436 100644
--- a/frontend/src/lib/hooks/api/topicMessages.tsx
+++ b/frontend/src/lib/hooks/api/topicMessages.tsx
@@ -94,10 +94,10 @@ export const useTopicMessages = ({
default:
}
- searchParams.getAll(MessagesFilterKeys.partitions).forEach((value) => {
- requestParams.append(MessagesFilterKeys.partitions, value);
- });
-
+ const partitions = searchParams.get(MessagesFilterKeys.partitions);
+ if (partitions !== null) {
+ requestParams.append(MessagesFilterKeys.partitions, partitions);
+ }
const { nextCursor, setNextCursor } = useMessageFiltersStore.getState();
const tempCompareUrl = new URLSearchParams(requestParams);
diff --git a/frontend/src/lib/hooks/useMessagesFilters.ts b/frontend/src/lib/hooks/useMessagesFilters.ts
index fee0b4fdf..613347433 100644
--- a/frontend/src/lib/hooks/useMessagesFilters.ts
+++ b/frontend/src/lib/hooks/useMessagesFilters.ts
@@ -92,7 +92,9 @@ export function useMessagesFilters() {
const search = searchParams.get(MessagesFilterKeys.stringFilter) || '';
- const partitions = searchParams.getAll(MessagesFilterKeys.partitions);
+ const partitions = (searchParams.get(MessagesFilterKeys.partitions) || '')
+ .split(',')
+ .filter((v) => v);
const smartFilterId =
searchParams.get(MessagesFilterKeys.activeFilterId) ||
@@ -166,9 +168,12 @@ export function useMessagesFilters() {
setSearchParams((params) => {
params.delete(MessagesFilterKeys.partitions);
- values.forEach((option) => {
- params.append(MessagesFilterKeys.partitions, option.value);
- });
+ if (values.length) {
+ params.append(
+ MessagesFilterKeys.partitions,
+ values.map((v) => v.value).join(',')
+ );
+ }
return params;
});
From ec1b35445fba6a02149bbe7508574c8a15e13013 Mon Sep 17 00:00:00 2001
From: svrmnk <153510907+svrmnk@users.noreply.github.com>
Date: Thu, 16 May 2024 14:19:25 +0300
Subject: [PATCH 06/17] FE: Rename "max size on disk" to "Max partition size"
(#388)
---
frontend/src/components/Topics/shared/Form/TopicForm.tsx | 2 +-
.../components/Topics/shared/Form/__tests__/TopicForm.spec.tsx | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/frontend/src/components/Topics/shared/Form/TopicForm.tsx b/frontend/src/components/Topics/shared/Form/TopicForm.tsx
index 891f03168..c4079e12c 100644
--- a/frontend/src/components/Topics/shared/Form/TopicForm.tsx
+++ b/frontend/src/components/Topics/shared/Form/TopicForm.tsx
@@ -206,7 +206,7 @@ const TopicForm: React.FC = ({
id="topicFormRetentionBytesLabel"
htmlFor="topicFormRetentionBytes"
>
- Max size on disk in GB
+ Max partition size in GB
{
expectByRoleAndNameToBeInDocument('button', '7 days');
expectByRoleAndNameToBeInDocument('button', '4 weeks');
- expectByRoleAndNameToBeInDocument('listbox', 'Max size on disk in GB');
+ expectByRoleAndNameToBeInDocument('listbox', 'Max partition size in GB');
expectByRoleAndNameToBeInDocument(
'spinbutton',
'Maximum message size in bytes'
From a7bdc1ffc4deb8c0f50d544d01785cb79d3e06ee Mon Sep 17 00:00:00 2001
From: Moon Jun Hyeong <114069644+NoRuTnT@users.noreply.github.com>
Date: Thu, 16 May 2024 20:20:04 +0900
Subject: [PATCH 07/17] Docs: Make broker id values unique (#386)
---
documentation/compose/kafbat-ui.yaml | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/documentation/compose/kafbat-ui.yaml b/documentation/compose/kafbat-ui.yaml
index c9a8a4be5..8848d6e5f 100644
--- a/documentation/compose/kafbat-ui.yaml
+++ b/documentation/compose/kafbat-ui.yaml
@@ -62,7 +62,7 @@ services:
- "9093:9092"
- "9998:9998"
environment:
- KAFKA_BROKER_ID: 1
+ KAFKA_BROKER_ID: 2
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: 'CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT'
KAFKA_ADVERTISED_LISTENERS: 'PLAINTEXT://kafka1:29092,PLAINTEXT_HOST://localhost:9092'
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
@@ -70,10 +70,10 @@ services:
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
KAFKA_JMX_PORT: 9998
- KAFKA_JMX_OPTS: -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=kafka0 -Dcom.sun.management.jmxremote.rmi.port=9998
+ KAFKA_JMX_OPTS: -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=kafka1 -Dcom.sun.management.jmxremote.rmi.port=9998
KAFKA_PROCESS_ROLES: 'broker,controller'
- KAFKA_NODE_ID: 1
- KAFKA_CONTROLLER_QUORUM_VOTERS: '1@kafka1:29093'
+ KAFKA_NODE_ID: 2
+ KAFKA_CONTROLLER_QUORUM_VOTERS: '2@kafka1:29093'
KAFKA_LISTENERS: 'PLAINTEXT://kafka1:29092,CONTROLLER://kafka1:29093,PLAINTEXT_HOST://0.0.0.0:9092'
KAFKA_INTER_BROKER_LISTENER_NAME: 'PLAINTEXT'
KAFKA_CONTROLLER_LISTENER_NAMES: 'CONTROLLER'
From 5d7cc233df3d202266beaa3273c67a27a08feb6b Mon Sep 17 00:00:00 2001
From: Renat Kalimulin <103274228+Nilumilak@users.noreply.github.com>
Date: Thu, 16 May 2024 14:28:33 +0300
Subject: [PATCH 08/17] FE: Fix clusters with spaces in names are not rendered
(#350)
---
frontend/package.json | 2 +-
frontend/pnpm-lock.yaml | 28 ++++++++++++++--------------
2 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/frontend/package.json b/frontend/package.json
index b37c2e579..8143e45cd 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -28,7 +28,7 @@
"react-hot-toast": "2.4.1",
"react-is": "18.2.0",
"react-multi-select-component": "4.3.4",
- "react-router-dom": "6.4.3",
+ "react-router-dom": "6.23.0",
"sass": "1.66.1",
"styled-components": "6.1.8",
"use-debounce": "10.0.0",
diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml
index f6ff5b6c9..be753238a 100644
--- a/frontend/pnpm-lock.yaml
+++ b/frontend/pnpm-lock.yaml
@@ -89,8 +89,8 @@ dependencies:
specifier: 4.3.4
version: 4.3.4(react-dom@18.2.0)(react@18.2.0)
react-router-dom:
- specifier: 6.4.3
- version: 6.4.3(react-dom@18.2.0)(react@18.2.0)
+ specifier: 6.23.0
+ version: 6.23.0(react-dom@18.2.0)(react@18.2.0)
sass:
specifier: 1.66.1
version: 1.66.1
@@ -1425,9 +1425,9 @@ packages:
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
dev: true
- /@remix-run/router@1.0.3:
- resolution: {integrity: sha512-ceuyTSs7PZ/tQqi19YZNBc5X7kj1f8p+4DIyrcIYFY9h+hd1OKm4RqtiWldR9eGEvIiJfsqwM4BsuCtRIuEw6Q==}
- engines: {node: '>=14'}
+ /@remix-run/router@1.16.0:
+ resolution: {integrity: sha512-Quz1KOffeEf/zwkCBM3kBtH4ZoZ+pT3xIXBG4PPW/XFtDP7EGhtTiC2+gpL9GnR7+Qdet5Oa6cYSvwKYg6kN9Q==}
+ engines: {node: '>=14.0.0'}
dev: false
/@rollup/rollup-android-arm-eabi@4.16.1:
@@ -6335,26 +6335,26 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
- /react-router-dom@6.4.3(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-MiaYQU8CwVCaOfJdYvt84KQNjT78VF0TJrA17SIQgNHRvLnXDJO6qsFqq8F/zzB1BWZjCFIrQpu4QxcshitziQ==}
- engines: {node: '>=14'}
+ /react-router-dom@6.23.0(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-Q9YaSYvubwgbal2c9DJKfx6hTNoBp3iJDsl+Duva/DwxoJH+OTXkxGpql4iUK2sla/8z4RpjAm6EWx1qUDuopQ==}
+ engines: {node: '>=14.0.0'}
peerDependencies:
react: '>=16.8'
react-dom: '>=16.8'
dependencies:
- '@remix-run/router': 1.0.3
+ '@remix-run/router': 1.16.0
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
- react-router: 6.4.3(react@18.2.0)
+ react-router: 6.23.0(react@18.2.0)
dev: false
- /react-router@6.4.3(react@18.2.0):
- resolution: {integrity: sha512-BT6DoGn6aV1FVP5yfODMOiieakp3z46P1Fk0RNzJMACzE7C339sFuHebfvWtnB4pzBvXXkHP2vscJzWRuUjTtA==}
- engines: {node: '>=14'}
+ /react-router@6.23.0(react@18.2.0):
+ resolution: {integrity: sha512-wPMZ8S2TuPadH0sF5irFGjkNLIcRvOSaEe7v+JER8508dyJumm6XZB1u5kztlX0RVq6AzRVndzqcUh6sFIauzA==}
+ engines: {node: '>=14.0.0'}
peerDependencies:
react: '>=16.8'
dependencies:
- '@remix-run/router': 1.0.3
+ '@remix-run/router': 1.16.0
react: 18.2.0
dev: false
From a4d678efb7e4be12bfec8568e54a35bf73a46a52 Mon Sep 17 00:00:00 2001
From: RomainDubois
Date: Sun, 19 May 2024 21:06:30 +0200
Subject: [PATCH 09/17] BE: Auth: Support LDAP nested groups (#391)
Co-authored-by: Romain Dubois
---
.../RbacLdapAuthoritiesExtractor.java | 44 +++----------------
1 file changed, 7 insertions(+), 37 deletions(-)
diff --git a/api/src/main/java/io/kafbat/ui/service/rbac/extractor/RbacLdapAuthoritiesExtractor.java b/api/src/main/java/io/kafbat/ui/service/rbac/extractor/RbacLdapAuthoritiesExtractor.java
index 8fd80ac88..3282ab1e2 100644
--- a/api/src/main/java/io/kafbat/ui/service/rbac/extractor/RbacLdapAuthoritiesExtractor.java
+++ b/api/src/main/java/io/kafbat/ui/service/rbac/extractor/RbacLdapAuthoritiesExtractor.java
@@ -1,11 +1,8 @@
package io.kafbat.ui.service.rbac.extractor;
-import io.kafbat.ui.config.auth.LdapProperties;
import io.kafbat.ui.model.rbac.Role;
import io.kafbat.ui.model.rbac.provider.Provider;
import io.kafbat.ui.service.rbac.AccessControlService;
-import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
@@ -14,25 +11,26 @@
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
-import org.springframework.util.Assert;
+import org.springframework.security.ldap.userdetails.NestedLdapAuthoritiesPopulator;
@Slf4j
-public class RbacLdapAuthoritiesExtractor extends DefaultLdapAuthoritiesPopulator {
+public class RbacLdapAuthoritiesExtractor extends NestedLdapAuthoritiesPopulator {
private final AccessControlService acs;
- private final LdapProperties props;
public RbacLdapAuthoritiesExtractor(ApplicationContext context,
BaseLdapPathContextSource contextSource, String groupFilterSearchBase) {
super(contextSource, groupFilterSearchBase);
this.acs = context.getBean(AccessControlService.class);
- this.props = context.getBean(LdapProperties.class);
}
@Override
protected Set getAdditionalRoles(DirContextOperations user, String username) {
- var ldapGroups = getRoles(user.getNameInNamespace(), username);
+ var ldapGroups = super.getGroupMembershipRoles(user.getNameInNamespace(), username)
+ .stream()
+ .map(GrantedAuthority::getAuthority)
+ .peek(group -> log.trace("Found LDAP group [{}] for user [{}]", group, username))
+ .collect(Collectors.toSet());
return acs.getRoles()
.stream()
@@ -47,32 +45,4 @@ protected Set getAdditionalRoles(DirContextOperations user, St
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toSet());
}
-
- private Set getRoles(String userDn, String username) {
- var groupSearchBase = props.getGroupFilterSearchBase();
- Assert.notNull(groupSearchBase, "groupSearchBase is empty");
-
- var groupRoleAttribute = props.getGroupRoleAttribute();
- if (groupRoleAttribute == null) {
-
- groupRoleAttribute = "cn";
- }
-
- log.trace(
- "Searching for roles for user [{}] with DN [{}], groupRoleAttribute [{}] and filter [{}] in search base [{}]",
- username, userDn, groupRoleAttribute, getGroupSearchFilter(), groupSearchBase);
-
- var ldapTemplate = getLdapTemplate();
- ldapTemplate.setIgnoreNameNotFoundException(true);
-
- Set