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 fdcb15e90 fix(acl): return 409 on concurrent duplicate accessKey 
creation (#2837)
fdcb15e90 is described below

commit fdcb15e90ae697620ee0e7536dd8e4fef3ee093c
Author: cyberslack_lee <[email protected]>
AuthorDate: Wed Sep 2 16:16:15 2026 +0800

    fix(acl): return 409 on concurrent duplicate accessKey creation (#2837)
    
    fix selectList insert bug
---
 .../instance/acl/MybatisPlusAclRepository.java       | 11 ++++++++++-
 .../instance/acl/MybatisPlusAclRepositoryTest.java   | 20 ++++++++++++++++++++
 2 files changed, 30 insertions(+), 1 deletion(-)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/instance/acl/MybatisPlusAclRepository.java
 
b/server/src/main/java/org/apache/rocketmq/studio/instance/acl/MybatisPlusAclRepository.java
index 442fa7b7c..8168211e6 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/instance/acl/MybatisPlusAclRepository.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/instance/acl/MybatisPlusAclRepository.java
@@ -27,6 +27,7 @@ import 
org.apache.rocketmq.studio.persistence.entity.RmqAclRule;
 import org.apache.rocketmq.studio.persistence.entity.RmqAclUser;
 import org.apache.rocketmq.studio.persistence.mapper.RmqAclRuleMapper;
 import org.apache.rocketmq.studio.persistence.mapper.RmqAclUserMapper;
+import org.springframework.dao.DuplicateKeyException;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.StringUtils;
@@ -237,7 +238,15 @@ public class MybatisPlusAclRepository implements 
AclRepository {
                         .set("white_remote_address", null));
             }
         } else {
-            userMapper.insert(entity);
+            try {
+                userMapper.insert(entity);
+            } catch (DuplicateKeyException exception) {
+                // A concurrent create of the same accessKey can win the 
check-then-insert
+                // race and hit the unique key instead; surface it as a 
conflict like the
+                // other duplicate-key paths (e.g. CloudCredentialService).
+                throw new BusinessException(409,
+                        "Plain access account already exists for accessKey: " 
+ config.getAccessKey());
+            }
         }
 
         upsertPlainAccessRules(config);
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/instance/acl/MybatisPlusAclRepositoryTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/instance/acl/MybatisPlusAclRepositoryTest.java
index a8604ba4e..ee5c17f90 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/instance/acl/MybatisPlusAclRepositoryTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/instance/acl/MybatisPlusAclRepositoryTest.java
@@ -284,6 +284,26 @@ class MybatisPlusAclRepositoryTest {
         verify(ruleMapper, never()).insert(any(RmqAclRule.class));
     }
 
+    @Test
+    void createShouldTranslateConcurrentDuplicateAccessKeyToConflict() {
+        
when(userMapper.selectList(any(QueryWrapper.class))).thenReturn(List.of());
+        when(userMapper.insert(any(RmqAclUser.class)))
+                .thenThrow(new 
org.springframework.dao.DuplicateKeyException("duplicate accessKey"));
+
+        PlainAccessConfigVO config = PlainAccessConfigVO.builder()
+                .accessKey("svc-x")
+                .secretKey("secret-x")
+                .build();
+
+        assertThatThrownBy(() -> 
repository.createAndUpdatePlainAccessConfig(config))
+                .isInstanceOf(BusinessException.class)
+                .hasMessage("Plain access account already exists for 
accessKey: svc-x")
+                .satisfies(ex -> assertThat(((BusinessException) 
ex).getCode()).isEqualTo(409));
+
+        // The conflict aborts before any permission rules are touched.
+        verifyNoInteractions(ruleMapper);
+    }
+
     @Test
     void updateWithBlankSecretShouldKeepStoredSecret() {
         RmqAclUser existing = userEntity(1L, "svc-x",

Reply via email to