This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new cc97b1c83 fix(acl): read remote rules only from the configured cluster 
(#6502)
cc97b1c83 is described below

commit cc97b1c834f6c75526211beb4efdd82de91ad724
Author: Apulupie <[email protected]>
AuthorDate: Thu Oct 15 18:13:04 2026 +0800

    fix(acl): read remote rules only from the configured cluster (#6502)
    
    #6196 narrowed discoverClusters, refreshClusterDetail and discoverBrokers 
to the cluster
    configured for the instance, but ApacheAclReadService.listRules still 
walked every entry of
    the shared brokerAddrTable. On an instance bound to cluster A whose 
NameServer also serves
    cluster B, the remote-rule read returned cluster B's brokers in 
policiesByBroker /
    failuresByBroker, so the selected instance surfaced another cluster's ACL.
    
    The broker set is now resolved once through the same configuredClusterName 
lookup the
    cluster provider uses, and a configured cluster whose membership is missing 
from the
    topology fails closed with 503 instead of falling back to the whole table - 
the same
    condition and status discoverBrokers raises. An instance that names no 
cluster keeps
    reading every broker of the endpoint.
---
 .../studio/instance/acl/ApacheAclReadService.java  | 32 ++++++++++-
 .../instance/acl/ApacheAclReadServiceTest.java     | 67 +++++++++++++++++++++-
 2 files changed, 96 insertions(+), 3 deletions(-)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/instance/acl/ApacheAclReadService.java
 
b/server/src/main/java/org/apache/rocketmq/studio/instance/acl/ApacheAclReadService.java
index 78735a875..a4b729333 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/instance/acl/ApacheAclReadService.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/instance/acl/ApacheAclReadService.java
@@ -22,12 +22,16 @@ import org.apache.rocketmq.remoting.protocol.body.AclInfo;
 import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
 import org.apache.rocketmq.remoting.protocol.route.BrokerData;
 import org.apache.rocketmq.studio.cluster.broker.RuntimeAdminClientResolver;
+import org.apache.rocketmq.studio.common.exception.BusinessException;
 import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
 
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
+import java.util.TreeSet;
 
 @Service
 @RequiredArgsConstructor
@@ -35,6 +39,8 @@ public class ApacheAclReadService {
     private final RuntimeAdminClientResolver runtimeAdminClientResolver;
 
     public RemoteAclReadResult listRules(String instanceId, String subject, 
String resource) {
+        String configuredCluster = StringUtils.hasText(instanceId)
+                ? runtimeAdminClientResolver.configuredClusterName(instanceId) 
: null;
         return runtimeAdminClientResolver.execute(instanceId, admin -> {
             Map<String, List<RemoteAclPolicyVO>> policies = new 
LinkedHashMap<>();
             Map<String, String> failures = new LinkedHashMap<>();
@@ -42,8 +48,8 @@ public class ApacheAclReadService {
             if (clusterInfo == null || clusterInfo.getBrokerAddrTable() == 
null) {
                 return result(policies, failures);
             }
-            for (BrokerData broker : 
clusterInfo.getBrokerAddrTable().values()) {
-                String address = masterAddress(broker);
+            for (String brokerName : scopedBrokerNames(clusterInfo, 
configuredCluster)) {
+                String address = 
masterAddress(clusterInfo.getBrokerAddrTable().get(brokerName));
                 if (address == null || policies.containsKey(address) || 
failures.containsKey(address)) {
                     continue;
                 }
@@ -57,6 +63,28 @@ public class ApacheAclReadService {
         });
     }
 
+    /**
+     * The broker set this read is allowed to touch. An instance bound to one 
cluster on a NameServer
+     * shared by several must not surface another cluster's ACL, so the 
topology is narrowed the same
+     * way {@code RocketMQClusterProvider.discoverBrokers} narrows it; an 
instance that names no
+     * cluster keeps every broker of the endpoint. A configured cluster whose 
membership is missing
+     * from the topology cannot be resolved, so the read fails closed instead 
of reading the whole
+     * cluster table (mirrors the 503 that broker discovery raises for the 
same condition).
+     */
+    private Set<String> scopedBrokerNames(ClusterInfo clusterInfo, String 
configuredCluster) {
+        Set<String> names = new 
TreeSet<>(clusterInfo.getBrokerAddrTable().keySet());
+        if (configuredCluster == null) {
+            return names;
+        }
+        Map<String, Set<String>> clusters = clusterInfo.getClusterAddrTable();
+        if (clusters == null || clusters.containsKey(configuredCluster)
+                && clusters.get(configuredCluster) == null) {
+            throw new BusinessException(503, "Broker cluster membership is 
unavailable");
+        }
+        names.retainAll(clusters.getOrDefault(configuredCluster, Set.of()));
+        return names;
+    }
+
     private List<RemoteAclPolicyVO> mapPolicies(List<AclInfo> brokerPolicies) {
         if (brokerPolicies == null) {
             return List.of();
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/instance/acl/ApacheAclReadServiceTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/instance/acl/ApacheAclReadServiceTest.java
index 51c79466b..ff7464ad4 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/instance/acl/ApacheAclReadServiceTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/instance/acl/ApacheAclReadServiceTest.java
@@ -28,11 +28,15 @@ import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 class ApacheAclReadServiceTest {
@@ -109,6 +113,62 @@ class ApacheAclReadServiceTest {
                 });
     }
 
+    @Test
+    void readsRemoteRulesOnlyFromTheConfiguredClusterTest() throws Exception {
+        RuntimeAdminClientResolver resolver = 
mock(RuntimeAdminClientResolver.class);
+        MQAdminExt admin = mock(MQAdminExt.class);
+        when(admin.examineBrokerClusterInfo()).thenReturn(sharedTopology());
+        when(admin.listAcl("10.0.0.11:10911", null, 
null)).thenReturn(List.of());
+        
when(resolver.configuredClusterName("selected")).thenReturn("DefaultCluster");
+        executeWith(resolver, admin, "selected");
+
+        RemoteAclReadResult result = new 
ApacheAclReadService(resolver).listRules("selected", null, null);
+
+        
assertThat(result.getPoliciesByBroker()).containsOnlyKeys("10.0.0.11:10911");
+        verify(admin, never()).listAcl(eq("10.0.0.12:10911"), any(), any());
+    }
+
+    @Test
+    void readsRemoteRulesFromEveryBrokerWhenTheInstanceNamesNoClusterTest() 
throws Exception {
+        RuntimeAdminClientResolver resolver = 
mock(RuntimeAdminClientResolver.class);
+        MQAdminExt admin = mock(MQAdminExt.class);
+        when(admin.examineBrokerClusterInfo()).thenReturn(sharedTopology());
+        when(admin.listAcl(any(), any(), any())).thenReturn(List.of());
+        when(resolver.configuredClusterName("selected")).thenReturn(null);
+        executeWith(resolver, admin, "selected");
+
+        RemoteAclReadResult result = new 
ApacheAclReadService(resolver).listRules("selected", null, null);
+
+        
assertThat(result.getPoliciesByBroker()).containsOnlyKeys("10.0.0.11:10911", 
"10.0.0.12:10911");
+    }
+
+    @Test
+    void reportsUnavailableClusterMembershipInsteadOfEveryBrokerTest() throws 
Exception {
+        RuntimeAdminClientResolver resolver = 
mock(RuntimeAdminClientResolver.class);
+        MQAdminExt admin = mock(MQAdminExt.class);
+        ClusterInfo withoutClusterTable = sharedTopology();
+        withoutClusterTable.setClusterAddrTable(null);
+        when(admin.examineBrokerClusterInfo()).thenReturn(withoutClusterTable);
+        
when(resolver.configuredClusterName("selected")).thenReturn("DefaultCluster");
+        executeWith(resolver, admin, "selected");
+
+        assertThatThrownBy(() -> new 
ApacheAclReadService(resolver).listRules("selected", null, null))
+                
.isInstanceOf(org.apache.rocketmq.studio.common.exception.BusinessException.class)
+                .hasMessageContaining("Broker cluster membership is 
unavailable");
+    }
+
+    private ClusterInfo sharedTopology() {
+        BrokerData first = new BrokerData("DefaultCluster", "broker-a",
+                new HashMap<>(Map.of(0L, "10.0.0.11:10911")));
+        BrokerData second = new BrokerData("OtherCluster", "broker-b",
+                new HashMap<>(Map.of(0L, "10.0.0.12:10911")));
+        ClusterInfo clusterInfo = new ClusterInfo();
+        clusterInfo.setClusterAddrTable(new HashMap<>(Map.of(
+                "DefaultCluster", Set.of("broker-a"), "OtherCluster", 
Set.of("broker-b"))));
+        clusterInfo.setBrokerAddrTable(new HashMap<>(Map.of("broker-a", first, 
"broker-b", second)));
+        return clusterInfo;
+    }
+
     private ClusterInfo clusterInfo(String address) {
         BrokerData broker = new BrokerData();
         broker.setBrokerAddrs(new HashMap<>(Map.of(0L, address)));
@@ -118,7 +178,12 @@ class ApacheAclReadServiceTest {
     }
 
     private void executeWith(RuntimeAdminClientResolver resolver, MQAdminExt 
admin) throws Exception {
-        when(resolver.execute(eq("instance-1"), any())).thenAnswer(invocation 
-> {
+        executeWith(resolver, admin, "instance-1");
+    }
+
+    private void executeWith(RuntimeAdminClientResolver resolver, MQAdminExt 
admin, String instanceId)
+            throws Exception {
+        when(resolver.execute(eq(instanceId), any())).thenAnswer(invocation -> 
{
             MqAdminExtFactory.AdminAction<?> action = 
invocation.getArgument(1);
             return action.apply(admin);
         });

Reply via email to