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 cea345e69 fix(tencent): reject incomplete ACL role pages (#4533)
cea345e69 is described below

commit cea345e69d12b11a51eee4668e3690c75a68872b
Author: zmuxuny <[email protected]>
AuthorDate: Mon Sep 21 21:00:56 2026 +0800

    fix(tencent): reject incomplete ACL role pages (#4533)
    
    `TencentAclService` read the role catalog through three separate loops — 
`listUsers`, `listRules`, and the `findRole` lookup behind `updateUser` — and 
all three stopped at the first empty or short page, with `isLastRolePage` 
treating `returned < PAGE_SIZE` as authoritative even when `TotalCount` said 
more rows existed. A truncated role page thus produced a silently short ACL 
inventory, and `updateUser` reported `404 ACL user not found` for a role that 
existed but fell past the cut, tu [...]
    
    All three loops now call `requireCompleteRolePage`, which throws 
`BusinessException(502)` when a non-negative `TotalCount` contradicts a short 
page. `isLastRolePage` prefers `fetched >= totalCount` whenever the total is 
known and falls back to the short-page signal only when it is not, so responses 
that omit `TotalCount` still terminate.
    
    Maintainer edits on top of the contribution: trunk had already gained #4469 
(clear a role's permissions instead of deleting the role), and both changes add 
material at the same point in `TencentAclServiceTest`; the conflict was 
resolved by keeping #4469's two tests alongside this change's 
`incompleteRolePage()` helper. No production code was altered.
    
    Fixes #4532
---
 .../studio/provider/tencent/TencentAclService.java | 36 +++++++++++++------
 .../provider/tencent/TencentAclServiceTest.java    | 41 ++++++++++++++++++++++
 2 files changed, 67 insertions(+), 10 deletions(-)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentAclService.java
 
b/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentAclService.java
index a7d5434fb..ba1e048ff 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentAclService.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentAclService.java
@@ -70,16 +70,19 @@ public class TencentAclService {
         for (long offset = 0L; ; offset += PAGE_SIZE) {
             DescribeRoleListResponse response = describeRoles(context, offset);
             RoleItem[] data = response == null ? null : response.getData();
-            if (data == null || data.length == 0) {
+            Long totalCount = response == null ? null : 
response.getTotalCount();
+            int returned = data == null ? 0 : data.length;
+            requireCompleteRolePage(offset, returned, totalCount);
+            if (returned == 0) {
                 break;
             }
-            fetched += data.length;
+            fetched += returned;
             for (RoleItem role : data) {
                 if (role != null && StringUtils.hasText(role.getRoleName())) {
                     users.add(toUser(role, context.cloudInstanceId()));
                 }
             }
-            if (isLastRolePage(data.length, fetched, 
response.getTotalCount())) {
+            if (isLastRolePage(returned, fetched, totalCount)) {
                 break;
             }
         }
@@ -94,10 +97,13 @@ public class TencentAclService {
         for (long offset = 0L; ; offset += PAGE_SIZE) {
             DescribeRoleListResponse response = describeRoles(context, offset);
             RoleItem[] data = response == null ? null : response.getData();
-            if (data == null || data.length == 0) {
+            Long totalCount = response == null ? null : 
response.getTotalCount();
+            int returned = data == null ? 0 : data.length;
+            requireCompleteRolePage(offset, returned, totalCount);
+            if (returned == 0) {
                 break;
             }
-            fetched += data.length;
+            fetched += returned;
             for (RoleItem role : data) {
                 if (role == null || !StringUtils.hasText(role.getRoleName())) {
                     continue;
@@ -114,7 +120,7 @@ public class TencentAclService {
                 }
                 rules.add(toRule(role));
             }
-            if (isLastRolePage(data.length, fetched, 
response.getTotalCount())) {
+            if (isLastRolePage(returned, fetched, totalCount)) {
                 break;
             }
         }
@@ -181,16 +187,19 @@ public class TencentAclService {
         for (long offset = 0L; ; offset += PAGE_SIZE) {
             DescribeRoleListResponse response = describeRoles(context, offset);
             RoleItem[] data = response == null ? null : response.getData();
-            if (data == null || data.length == 0) {
+            Long totalCount = response == null ? null : 
response.getTotalCount();
+            int returned = data == null ? 0 : data.length;
+            requireCompleteRolePage(offset, returned, totalCount);
+            if (returned == 0) {
                 break;
             }
-            fetched += data.length;
+            fetched += returned;
             for (RoleItem role : data) {
                 if (role != null && roleName.equals(role.getRoleName())) {
                     return role;
                 }
             }
-            if (isLastRolePage(data.length, fetched, 
response.getTotalCount())) {
+            if (isLastRolePage(returned, fetched, totalCount)) {
                 break;
             }
         }
@@ -206,8 +215,15 @@ public class TencentAclService {
                 context.regionId(), client -> 
client.DescribeRoleList(request));
     }
 
+    private static void requireCompleteRolePage(long offset, int returned, 
Long totalCount) {
+        if (totalCount != null && totalCount >= 0L
+                && returned < PAGE_SIZE && offset + returned < totalCount) {
+            throw new BusinessException(502, "Tencent Cloud ACL role catalog 
returned an incomplete page");
+        }
+    }
+
     private static boolean isLastRolePage(int returned, long fetched, Long 
totalCount) {
-        return returned < PAGE_SIZE || totalCount != null && totalCount >= 0L 
&& fetched >= totalCount;
+        return totalCount != null && totalCount >= 0L ? fetched >= totalCount 
: returned < PAGE_SIZE;
     }
 
     public void deleteUser(String instanceId, String username) {
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentAclServiceTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentAclServiceTest.java
index d83940c33..dae359cd2 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentAclServiceTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentAclServiceTest.java
@@ -114,6 +114,38 @@ class TencentAclServiceTest {
                 .isEqualTo("reader-role");
     }
 
+    @Test
+    void 
listUsersShouldRejectIncompleteRolePageWhenTotalCountRequiresMoreTest() throws 
Exception {
+        DescribeRoleListResponse response = incompleteRolePage();
+        when(client.DescribeRoleList(any())).thenReturn(response);
+
+        assertThatThrownBy(() -> service.listUsers(INSTANCE_ID))
+                .isInstanceOf(BusinessException.class)
+                .satisfies(error -> assertThat(((BusinessException) 
error).getCode()).isEqualTo(502));
+    }
+
+    @Test
+    void 
listRulesShouldRejectIncompleteRolePageWhenTotalCountRequiresMoreTest() throws 
Exception {
+        DescribeRoleListResponse response = incompleteRolePage();
+        when(client.DescribeRoleList(any())).thenReturn(response);
+
+        assertThatThrownBy(() -> service.listRules(INSTANCE_ID, null))
+                .isInstanceOf(BusinessException.class)
+                .satisfies(error -> assertThat(((BusinessException) 
error).getCode()).isEqualTo(502));
+    }
+
+    @Test
+    void 
updateUserShouldRejectIncompleteRolePageInsteadOfReportingNotFoundTest() throws 
Exception {
+        DescribeRoleListResponse response = incompleteRolePage();
+        when(client.DescribeRoleList(any())).thenReturn(response);
+
+        assertThatThrownBy(() -> service.updateUser(INSTANCE_ID, 
AclUserVO.builder()
+                .username("role-b")
+                .build()))
+                .isInstanceOf(BusinessException.class)
+                .satisfies(error -> assertThat(((BusinessException) 
error).getCode()).isEqualTo(502));
+    }
+
     @Test
     void listUsersShouldFetchExactlyTenThousandTencentRolesTest() throws 
Exception {
         when(client.DescribeRoleList(any())).thenAnswer(invocation -> {
@@ -308,6 +340,15 @@ class TencentAclServiceTest {
                 .containsExactly("active-role", "revoked-role");
     }
 
+    private static DescribeRoleListResponse incompleteRolePage() {
+        RoleItem role = new RoleItem();
+        role.setRoleName("role-a");
+        DescribeRoleListResponse response = new DescribeRoleListResponse();
+        response.setTotalCount(2L);
+        response.setData(new RoleItem[]{role});
+        return response;
+    }
+
     private static RoleItem[] rolePage(Long offset, Long limit, int total) {
         int start = offset == null ? 0 : offset.intValue();
         int size = Math.min(limit == null ? TencentAclService.PAGE_SIZE : 
limit.intValue(),

Reply via email to