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",