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 a24826048 fix(acl): clear a Tencent role's permissions instead of 
deleting the role (#4469)
a24826048 is described below

commit a248260484d714f4ed4d75c42f6f7bd4524ead10
Author: 0 <[email protected]>
AuthorDate: Mon Sep 21 11:57:10 2026 +0800

    fix(acl): clear a Tencent role's permissions instead of deleting the role 
(#4469)
    
    On Tencent a role also owns the credentials, so DeleteRole removed the ACL 
user
    along with the rule. Revoke by clearing PermRead/PermWrite through 
ModifyRole
    instead, and hide permission-less roles from the rule list while keeping 
them
    visible as users so operators can re-enable them.
---
 .../studio/provider/tencent/TencentAclService.java | 17 +++++++++--
 .../provider/tencent/TencentAclServiceTest.java    | 33 ++++++++++++++++++++++
 2 files changed, 48 insertions(+), 2 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 b656056e6..a7d5434fb 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
@@ -102,6 +102,12 @@ public class TencentAclService {
                 if (role == null || !StringUtils.hasText(role.getRoleName())) {
                     continue;
                 }
+                // A role with both permissions disabled has no effective ACL 
rule; it is still
+                // listed as a user so operators can re-enable permissions.
+                if (!Boolean.TRUE.equals(role.getPermRead())
+                        && !Boolean.TRUE.equals(role.getPermWrite())) {
+                    continue;
+                }
                 if (requestedPrincipal != null
                         && !requestedPrincipal.equals(role.getRoleName())) {
                     continue;
@@ -239,14 +245,21 @@ public class TencentAclService {
         return createRule(instanceId, rule);
     }
 
+    /**
+     * Revokes the cluster-wide rule for a Tencent role by clearing 
PermRead/PermWrite.
+     * The role account itself must stay: on Tencent a role also owns the 
credentials, so
+     * calling DeleteRole here would silently remove the ACL user.
+     */
     public void deleteRule(String instanceId, String principal) {
         Context context = resolve(instanceId);
         String roleName = requireRoleName(principal, "ACL principal");
-        DeleteRoleRequest request = new DeleteRoleRequest();
+        ModifyRoleRequest request = new ModifyRoleRequest();
         request.setInstanceId(context.cloudInstanceId());
         request.setRole(roleName);
+        request.setPermRead(false);
+        request.setPermWrite(false);
         clientFactory.call(context.credentialId(), context.regionId(),
-                client -> client.DeleteRole(request));
+                client -> client.ModifyRole(request));
     }
 
     private static String requireRulePrincipal(AclRuleVO rule) {
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 482277c9b..d83940c33 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
@@ -43,6 +43,7 @@ import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -275,6 +276,38 @@ class TencentAclServiceTest {
                 .satisfies(error -> assertThat(((BusinessException) 
error).getCode()).isEqualTo(400));
     }
 
+    @Test
+    void deleteRuleRevokesPermissionsWithoutDeletingTheRoleTest() throws 
Exception {
+        service.deleteRule(INSTANCE_ID, "  reader-role  ");
+
+        ArgumentCaptor<ModifyRoleRequest> requestCaptor = 
ArgumentCaptor.forClass(ModifyRoleRequest.class);
+        verify(client).ModifyRole(requestCaptor.capture());
+        ModifyRoleRequest request = requestCaptor.getValue();
+        assertThat(request.getRole()).isEqualTo("reader-role");
+        assertThat(request.getPermRead()).isFalse();
+        assertThat(request.getPermWrite()).isFalse();
+        verify(client, never()).DeleteRole(any());
+    }
+
+    @Test
+    void listRulesHidesPermissionlessRolesThatStillExistAsUsersTest() throws 
Exception {
+        RoleItem active = role("active-role");
+        RoleItem revoked = new RoleItem();
+        revoked.setRoleName("revoked-role");
+        revoked.setPermRead(false);
+        revoked.setPermWrite(false);
+        DescribeRoleListResponse response = new DescribeRoleListResponse();
+        response.setData(new RoleItem[]{active, revoked});
+        when(client.DescribeRoleList(any())).thenReturn(response);
+
+        assertThat(service.listRules(INSTANCE_ID, null))
+                .extracting(AclRuleVO::getPrincipal)
+                .containsExactly("active-role");
+        assertThat(service.listUsers(INSTANCE_ID))
+                .extracting(AclUserVO::getUsername)
+                .containsExactly("active-role", "revoked-role");
+    }
+
     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