From 4c35b0e02bfac83a60b3f85fa61ffb04cd7fb48a Mon Sep 17 00:00:00 2001 From: liuhy Date: Fri, 24 Jul 2026 07:34:47 -0700 Subject: [PATCH] fix: validate acl delete requests --- .../studio/instance/acl/AclController.java | 10 ++-- .../instance/acl/AclDeleteRequestDTO.java | 32 ++++++++++++ .../instance/acl/AclControllerTest.java | 50 +++++++++++++++++++ 3 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 server/src/main/java/com/rocketmq/studio/instance/acl/AclDeleteRequestDTO.java diff --git a/server/src/main/java/com/rocketmq/studio/instance/acl/AclController.java b/server/src/main/java/com/rocketmq/studio/instance/acl/AclController.java index 9c35cd6b..99dd2bb1 100644 --- a/server/src/main/java/com/rocketmq/studio/instance/acl/AclController.java +++ b/server/src/main/java/com/rocketmq/studio/instance/acl/AclController.java @@ -17,6 +17,7 @@ package com.rocketmq.studio.instance.acl; import com.rocketmq.studio.common.domain.Result; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -26,7 +27,6 @@ import org.springframework.web.bind.annotation.RestController; import java.util.List; -import java.util.Map; @RestController @RequestMapping("/api/acl") @@ -53,8 +53,8 @@ public Result updateRule(@RequestBody AclRuleVO rule) { } @PostMapping("/rules/delete") - public Result deleteRule(@RequestBody Map request) { - aclService.deleteRule(request.get("id")); + public Result deleteRule(@Valid @RequestBody AclDeleteRequestDTO request) { + aclService.deleteRule(request.getId()); return Result.ok(); } @@ -74,8 +74,8 @@ public Result updateUser(@RequestBody AclUserVO user) { } @PostMapping("/users/delete") - public Result deleteUser(@RequestBody Map request) { - aclService.deleteUser(request.get("id")); + public Result deleteUser(@Valid @RequestBody AclDeleteRequestDTO request) { + aclService.deleteUser(request.getId()); return Result.ok(); } } diff --git a/server/src/main/java/com/rocketmq/studio/instance/acl/AclDeleteRequestDTO.java b/server/src/main/java/com/rocketmq/studio/instance/acl/AclDeleteRequestDTO.java new file mode 100644 index 00000000..de657462 --- /dev/null +++ b/server/src/main/java/com/rocketmq/studio/instance/acl/AclDeleteRequestDTO.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.rocketmq.studio.instance.acl; + +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AclDeleteRequestDTO { + @NotBlank(message = "id is required") + private String id; +} diff --git a/server/src/test/java/com/rocketmq/studio/instance/acl/AclControllerTest.java b/server/src/test/java/com/rocketmq/studio/instance/acl/AclControllerTest.java index 3db19851..ce4b7c69 100644 --- a/server/src/test/java/com/rocketmq/studio/instance/acl/AclControllerTest.java +++ b/server/src/test/java/com/rocketmq/studio/instance/acl/AclControllerTest.java @@ -28,11 +28,13 @@ import java.time.LocalDateTime; import java.util.List; +import java.util.Map; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -137,6 +139,30 @@ void updateRuleShouldReturnUpdatedRule() throws Exception { .andExpect(jsonPath("$.data.decision").value("DENY")); } + @Test + void deleteRuleShouldPassValidatedRequest() throws Exception { + mockMvc.perform(post("/api/acl/rules/delete") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(Map.of("id", "rule-1")))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.message").value("success")); + + verify(aclService).deleteRule("rule-1"); + } + + @Test + void deleteRuleShouldRejectBlankId() throws Exception { + mockMvc.perform(post("/api/acl/rules/delete") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(Map.of("id", " ")))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value(400)) + .andExpect(jsonPath("$.message").value("id is required")); + + verifyNoInteractions(aclService); + } + @Test void listUsersShouldReturnAllUsers() throws Exception { AclUserVO user = AclUserVO.builder() @@ -179,4 +205,28 @@ void updateUserShouldReturnUpdatedUser() throws Exception { .andExpect(jsonPath("$.data.id").value("user-1")) .andExpect(jsonPath("$.data.admin").value(false)); } + + @Test + void deleteUserShouldPassValidatedRequest() throws Exception { + mockMvc.perform(post("/api/acl/users/delete") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(Map.of("id", "user-1")))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.message").value("success")); + + verify(aclService).deleteUser("user-1"); + } + + @Test + void deleteUserShouldRejectMissingId() throws Exception { + mockMvc.perform(post("/api/acl/users/delete") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(Map.of()))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value(400)) + .andExpect(jsonPath("$.message").value("id is required")); + + verifyNoInteractions(aclService); + } }