diff --git a/api/src/main/java/io/kafbat/ui/service/acl/AclsService.java b/api/src/main/java/io/kafbat/ui/service/acl/AclsService.java index 7521c5d79c..7cb886d16e 100644 --- a/api/src/main/java/io/kafbat/ui/service/acl/AclsService.java +++ b/api/src/main/java/io/kafbat/ui/service/acl/AclsService.java @@ -22,6 +22,7 @@ import io.kafbat.ui.model.KafkaCluster; import io.kafbat.ui.service.AdminClientService; import io.kafbat.ui.service.ReactiveAdminClient; +import io.kafbat.ui.service.ReactiveAdminClient.SupportedFeature; import io.kafbat.ui.service.index.AclBindingNgramFilter; import java.util.ArrayList; import java.util.Collection; @@ -89,6 +90,7 @@ public Mono deleteAcl(KafkaCluster cluster, AclBinding aclBinding) { public Flux listAcls(KafkaCluster cluster, ResourcePatternFilter filter, String principalSearch, Boolean fts) { return adminClientService.get(cluster) + .filter(c -> c.getClusterFeatures().contains(SupportedFeature.AUTHORIZED_SECURITY_ENABLED)) .flatMap(c -> c.listAcls(filter)) .map(lst -> filter(new ArrayList<>(lst), principalSearch, fts)) .flatMapMany(Flux::fromIterable) diff --git a/api/src/main/resources/static/openapi/kafbat-ui-api.yaml b/api/src/main/resources/static/openapi/kafbat-ui-api.yaml index 195489f015..a81010fea9 100644 --- a/api/src/main/resources/static/openapi/kafbat-ui-api.yaml +++ b/api/src/main/resources/static/openapi/kafbat-ui-api.yaml @@ -3130,6 +3130,8 @@ components: type: string clientSecret: type: string + clientAuthenticationMethod: + type: string clientName: type: string redirectUri: @@ -3306,6 +3308,29 @@ components: type: string password: type: string + oauth: + type: object + properties: + tokenUrl: + type: string + clientId: + type: string + clientSecret: + type: string + scopes: + type: array + items: + type: string + tokenCacheEnabled: + type: boolean + default: true + tokenRefreshBuffer: + type: string + format: duration + maxRetries: + type: integer + format: int32 + default: 1 schemaRegistrySsl: type: object properties: diff --git a/frontend/src/components/Nav/Nav.styled.ts b/frontend/src/components/Nav/Nav.styled.ts index 3f5e62ec44..f4e2749165 100644 --- a/frontend/src/components/Nav/Nav.styled.ts +++ b/frontend/src/components/Nav/Nav.styled.ts @@ -1,6 +1,10 @@ import styled from 'styled-components'; import { ClusterColorKey } from 'theme/theme'; +export const SearchWrapper = styled.div` + padding: 4px 4px 8px; +`; + export const List = styled.ul.attrs({ role: 'menu' })` & > & { padding: 0 0 0 8px; diff --git a/frontend/src/components/Nav/Nav.tsx b/frontend/src/components/Nav/Nav.tsx index 532f5e4dc2..337a9ca3c4 100644 --- a/frontend/src/components/Nav/Nav.tsx +++ b/frontend/src/components/Nav/Nav.tsx @@ -1,6 +1,7 @@ -import React, { type FC } from 'react'; +import React, { type FC, useState } from 'react'; import { useClusters } from 'lib/hooks/api/clusters'; import useCurrentClusterName from 'lib/hooks/useCurrentClusterName'; +import Search from 'components/common/Search/Search'; import * as S from './Nav.styled'; import MenuItem from './Menu/MenuItem'; @@ -9,14 +10,31 @@ import ClusterMenu from './ClusterMenu/ClusterMenu'; const Nav: FC = () => { const clusters = useClusters(); const clusterName = useCurrentClusterName(); + const [search, setSearch] = useState(''); + + const filteredClusters = + clusters.isSuccess && search.trim() + ? clusters.data.filter((c) => + c.name.toLowerCase().includes(search.toLowerCase()) + ) + : clusters.data; return (