unbridled-41 opened a new pull request, #5072:
URL: https://github.com/apache/rocketmq-dashboard/pull/5072

   ## Problem
   
   Creating an ACL user whose username already exists — or renaming one onto an 
existing username — is answered with HTTP 500 "Internal Server Error" instead 
of a conflict.
   
   `rmq_acl_user` declares two unique keys 
(`server/src/main/resources/db/schema.sql:291-292`):
   
   ```sql
     UNIQUE KEY uk_username (username),
     UNIQUE KEY uk_access_key (access_key)
   ```
   
   Neither write path translates the resulting `DuplicateKeyException`:
   
   - create: `AclController.java:112-114` → `AclService.createUser` → 
`aclRepository.saveUser(user)` (`AclService.java:226`) → 
`userMapper.insert(entity)` (`MybatisPlusAclRepository.java:152`);
   - rename: `AclController.java:117-120` → `AclService.updateUser` (the 
console's edit form sends `username`) → `aclRepository.replaceUser(merged)` 
(`AclService.java:256`) → `userMapper.updateById(entity)` 
(`MybatisPlusAclRepository.java:167`).
   
   The exception reaches `GlobalExceptionHandler`, which only maps 
`BusinessException` and a few MVC exceptions; everything else falls through to 
the catch-all (`:109-115`) and becomes 500 "Internal Server Error". The 
operator gets no way to tell "that name is taken" from "the server is broken".
   
   The same repository already treats this class of failure as a conflict for 
the plain-access account, and says so in the comment: "surface it as a conflict 
like the other duplicate-key paths (e.g. CloudCredentialService)" 
(`MybatisPlusAclRepository.java:255-263`). The ACL user paths were simply left 
out.
   
   ## Evidence
   
   Pre-fix, on base `a562601d`, with the two new tests in place and only the 
production file at its base revision:
   
   ```
   $ cd server && mvn -o test -Dtest=MybatisPlusAclRepositoryTest
   [ERROR] Tests run: 25, Failures: 2, Errors: 0, Skipped: 0
   
   saveUserShouldReportADuplicateUsernameAsAConflict:
     Expecting actual throwable to be an instance of:
       org.apache.rocketmq.studio.common.exception.BusinessException
     but was:
       org.springframework.dao.DuplicateKeyException: uk_username
         at 
...MybatisPlusAclRepository.saveUser(MybatisPlusAclRepository.java:152)
   
   replaceUserShouldReportARenameOntoAnExistingUsernameAsAConflict:
     Expecting actual throwable to be an instance of:
       org.apache.rocketmq.studio.common.exception.BusinessException
     but was:
       org.springframework.dao.DuplicateKeyException: uk_username
         at 
...MybatisPlusAclRepository.replaceUser(MybatisPlusAclRepository.java:167)
   ```
   
   The `DuplicateKeyException` is exactly what the 500 path is fed: the 
handler's catch-all is reached with a `DataIntegrityViolationException` and no 
status mapping exists for it.
   
   ## Root cause
   
   `saveUser`'s insert branch and `replaceUser`'s update branch let the 
unique-key violation escape as a `DuplicateKeyException`, which the global 
advice can only answer with its 500 catch-all. The visible contract ("a name 
that is taken is a conflict") already exists in the sibling write path in the 
same file.
   
   ## Fix
   
   Both branches catch `DuplicateKeyException` and rethrow 
`BusinessException(409, "ACL user already exists: <username>")` through one 
small private helper, mirroring the wording and the reasoning of the 
plain-access block a hundred lines below. 
`GlobalExceptionHandler.handleBusinessException` maps the code straight to the 
status (`:42-47`), so the console now receives a 409 with a message that names 
the cause.
   
   ## Score
   
   `PRIORITY = 64` (impact 22: an operator (or an API client) is told the 
server failed while the request was simply a conflict, and has no way to act on 
it; scope 12: ACL user create and rename; reproducibility 20: two deterministic 
tests; maintenance value 10: the repository itself documents the intended 
answer for this class).
   `FIX_CONFIDENCE = 85`.
   
   ## Tests
   
   ```
   $ cd server && mvn -o test -Dtest=MybatisPlusAclRepositoryTest          # 
post-fix
   Tests run: 25, Failures: 0, Errors: 0, Skipped: 0
   BUILD SUCCESS
   
   $ cd server && mvn -o test 
-Dtest='MybatisPlusAclRepositoryTest,AclServiceTest,AclControllerTest'
   Tests run: 130, Failures: 0, Errors: 0, Skipped: 0
   BUILD SUCCESS
   ```
   
   The pre-fix run of the repository class (only 
`MybatisPlusAclRepository.java` reverted to `a562601d`, tests kept) is the 
failure quoted under Evidence. Both write paths are asserted, not one 
representative: the insert branch (create) and the update branch (rename), 
because the console's edit form submits `username` and can hit either. 
Checkstyle runs in the `validate` phase of both commands.
   
   ## Risk
   
   Low. The only behaviour that changes is the exception type and status for a 
duplicate write; the insert branch's `user.setId(...)` is skipped exactly as it 
was when the exception propagated. The successful paths are untouched (25 
repository tests, 78 service tests and 27 controller tests pass unchanged), and 
no caller catches `DuplicateKeyException` from these methods.
   
   ## Dedup
   
   - `gh search prs --repo apache/rocketmq-dashboard "duplicate acl user"` → 
only #1632 (merged, concurrent admin toggles, a different table); `gh search 
issues "duplicate acl user"` → nothing.
   - The files listed for the open ACL PRs (#4891 version filter, #4882 
principal directory, #4982 rule-update validation, #4720 plain-access view) do 
not include this path — `grep -F MybatisPlusAclRepository.java 
/tmp/open_pr_files.txt` shows the repository only for PRs that change other 
methods, and none adds conflict handling to `saveUser`/`replaceUser`.
   - `git log --oneline a562601d -- .../MybatisPlusAclRepository.java` → the 
plain-access conflict handling is the most recent relevant change; the user 
write paths date back to the ACL pagination work.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to