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(),