Frun1na opened a new pull request, #6809:
URL: https://github.com/apache/rocketmq-dashboard/pull/6809

   ### Which Issue(s) This PR Fixes
   
   - Fixes #6807
   
   ### Brief Description
   
   The ACL write paths accepted an identity longer than the column that stores 
it, so the write failed inside the insert and the caller received a 
persistence-layer 500 instead of a validation error.
   
   `rmq_acl_user.username` and `rmq_acl_rule.principal` are both `VARCHAR(128)` 
(`server/src/main/resources/db/schema.sql:284`, `:267`), and nothing bounded 
the values reaching them: `AclService.createUser` / `updateUser` / `createRule` 
/ `updateRule` checked only that the value had text, and 
`GlobalExceptionHandler` does not translate `DataIntegrityViolationException`.
   
   The four local-store paths now bound the identity to the column through one 
helper, counted in code points — the unit MySQL counts a `varchar` in — and 
report the field name: `username must not exceed 128 characters` / `principal 
must not exceed 128 characters`. That is the same shape `#6087` used for the 
topic and group registration text, and the same reasoning 
`InstanceService.requireTextWithin` documents for the instance fields.
   
   The bound is enforced in the service rather than only on the request DTOs 
because the tool handlers bypass bean validation: 
`UserCreateToolHandler.execute` builds an `AclUserVO` in code and calls 
`AclService.createUser` directly, and the tool catalog declares only 
`minLength: 1` for that field. A DTO `@Size` would also have been the wrong 
unit: it counts UTF-16 units, so it would reject a 128-code-point value made of 
surrogate pairs that the column holds.
   
   User-visible change: an over-long username or rule principal answers `400` 
naming the field instead of `500`, on the REST endpoints and on the AI tools 
alike.
   
   ### Other consumers
   
   The identity columns are written in four places, and each was checked:
   
   - `AclService.createUser` / `updateUser` — guarded here, on the local store 
path.
   - `AclService.createRule` / `updateRule` — guarded here; a rule principal 
carries the username (or the plain-access access key), so it is the same value 
in the second 128-character column.
   - The Tencent branch of all four methods — deliberately **not** bounded: it 
delegates to `TencentAclService` and writes a vendor role, not a row of these 
tables.
   - `web/src/pages/instance/acl.tsx` — the form has no length rule either, so 
the console path reaches the same service; no UI change is needed because the 
service answers 400 and the page already routes a rejected write to its failure 
notice.
   - The request DTOs (`CreateAclUserDTO`, `UpdateAclUserDTO`, 
`CreateAclRuleDTO`, `UpdateAclRuleDTO`) — deliberately left as presence-only: 
the guard is the code-point one in the service, and adding a UTF-16 `@Size` 
beside it would refuse values the column accepts.
   - `tool-catalog/tools/user.yaml` declares `minLength: 1` for `username` and 
no maximum; documenting the bound in the catalog is a separate change (it would 
regenerate `rmqctl/internal/catalog/catalog_gen.go`), not part of this one.
   - The other text fields of the same DTOs are a different class of value and 
are **not** part of this PR: `resource` (`VARCHAR(255)`), `resourceType` / 
`resourcePattern` (`VARCHAR(32)`), `actions` (`VARCHAR(128)`), `decision` 
(`VARCHAR(16)`), `scope` (`VARCHAR(64)`), `aclVersion` (`VARCHAR(16)`). None of 
them carries a username, and bounding them needs its own per-field reasoning.
   - Open PR #5878 adds the *blank* checks to `updateRule`'s principal and 
resource. It validates presence and this change validates length, so the two do 
not overlap in what they refuse; the guard is placed before `updateRule`'s id 
check so the two edits do not sit on the same lines.
   
   ### How Did You Test This Change?
   
   ```
   $ ./wb test --filter 
'AclServiceTest,AclControllerTest,MybatisPlusAclRepositoryTest,IpRangeMatcherTest'
   ✓   20.4s  mvn -B -ntp test -DforkCount=1 
-Dsurefire.failIfNoSpecifiedTests=false \
                
-Dtest=AclServiceTest,AclControllerTest,MybatisPlusAclRepositoryTest,IpRangeMatcherTest
   [INFO] Tests run: 87, Failures: 0, Errors: 0, Skipped: 0 -- in AclServiceTest
   [INFO] Tests run: 27, Failures: 0, Errors: 0, Skipped: 0 -- in 
AclControllerTest
   [INFO] Tests run: 26, Failures: 0, Errors: 0, Skipped: 0 -- in 
MybatisPlusAclRepositoryTest
   [INFO] Tests run: 15, Failures: 0, Errors: 0, Skipped: 0 -- in 
IpRangeMatcherTest
   [INFO] Tests run: 155, Failures: 0, Errors: 0, Skipped: 0
   ```
   
   The new tests fail without the fix:
   
   ```
   $ ./wb test --filter AclServiceTest
   ✗   mvn -B -ntp test -DforkCount=1 -Dsurefire.failIfNoSpecifiedTests=false 
-Dtest=AclServiceTest
   [ERROR] Tests run: 87, Failures: 4, Errors: 0, Skipped: 0
     AclServiceTest.createUserShouldRejectAUsernameThatCannotFitItsColumnTest
       java.lang.AssertionError: Expecting actual throwable to be an instance 
of:
         org.apache.rocketmq.studio.common.exception.BusinessException
       but was:
         java.lang.NullPointerException: Cannot invoke "…AclUserVO.getId()" 
because "user" is null
   ```
   
   The four assertions go red because they ask each write path to refuse a 
129-code-point identity with `400` and the field's name; with the guard absent 
the value passes every check and reaches the repository — the `createUser` case 
shows it as the unstubbed `saveUser` returning `null` and the audit call then 
dereferencing it, i.e. the value was never refused. Reverting only the guard 
turns exactly those four of the 87 tests red and leaves the other 83 green, and 
the positive case inside 
`createUserShouldRejectAUsernameThatCannotFitItsColumnTest` — 128 code points 
made of surrogate pairs — passes both with and without the guard, which is what 
shows the assertions pin the column bound rather than "the method throws".
   
   ### Checklist
   
   - [x] One coherent change; unrelated modifications are not bundled in
   - [x] Commit subject follows Conventional Commits (`feat:` / `fix:` / 
`refactor:` / `chore:` / `docs:` / `perf:`)
   - [x] Tests added or updated for non-trivial changes, test methods named 
`...Test`
   - [ ] New UI text has both Chinese and English entries under `web/src/i18n/`
   - [ ] Architecture constraints stay green (`mvn test` runs the ArchUnit 
checks)
   - [ ] New source files carry the ASF license header
   - [ ] Documentation touched where behaviour changed (README / `docs/` / 
in-app help)
   


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