You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rocketmq.apache.org by li...@apache.org on 2026/07/28 12:27:33 UTC

(rocketmq-dashboard) branch rocketmq-studio updated: fix: validate audit log date filters (#567)

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 51a3424a fix: validate audit log date filters (#567)
51a3424a is described below

commit 51a3424aefab961488199b4eff6942a875c2b080
Author: yx9o <ya...@163.com>
AuthorDate: Tue Jul 28 20:27:28 2026 +0800

    fix: validate audit log date filters (#567)
    
    * fix: align frontend API success response handling (#429)
    
    * ci: add build workflow and PR review skill (#430)
    
    * fix: connect K8s certificate page to APIs (#440)
    
    * feat: extend translation keys & add useLanguage compatible alias in LangContext (#433)
    
    * fix: validate audit query and cleanup parameters (#442)
    
    * chore: fix Dockerfile missing style/ copy and standardize pr-review skill (#444)
    
    - Add COPY style ./style to server/Dockerfile so Maven checkstyle can
      find style/rmq_checkstyle.xml during docker compose build
    - Rewrite pr-review SKILL.md with an 8-stage pipeline checklist,
      Dockerfile pre-flight fix step, and [Studio] title format spec
    
    * feat: add centralized ThemeContext and useTheme hook for dark mode management (#435)
    
    * fix: preserve SSE framing for AI chat (#438)
    
    * feat: add Prometheus range query adapter (#432)
    
    Add a real Prometheus /api/v1/query_range adapter as the foundation for observability (#431): configurable base URL, timeouts, Basic/Bearer auth, error mapping, and tests.
    
    * feat: implement login page, auth & AI modules, simplify theme management (#473)
    
    Add login page with full auth workflow, Auth/AI API modules, Zustand state, and simplified theme management.
    
    * feat: add Ops page (NameServer management, VIPChannel, TLS) (#474)
    
    Add Ops management page for NameServer address management, VIP channel toggle, and TLS switch.
    
    * feat: add Producer page (#475)
    
    Add a page to query producer client connections by Topic and Producer Group.
    
    * feat: add AlertManagement page for alert rule operations (#476)
    
    Add alert rule management page: parse/render Prometheus AlertManager YAML rules with search, toggle, and CRUD.
    
    * feat: add SslSettings page for SSL/TLS configuration management (#477)
    
    Add SSL/TLS configuration page: SSL toggle, TLS version, KeyStore/TrustStore, client auth mode.
    
    * feat: add broker cluster overview page (#479)
    
    Add Broker Cluster overview page with tab-based NameServer/Broker/Proxy management and cluster stats.
    
    * feat: add consumer group management page (#480)
    
    Add consumer group management page.
    
    * feat: add lite topic management page (#481)
    
    Add lite topic management page.
    
    * feat: add Proxy management page (#482)
    
    Add Proxy management page (address list, status, TPS, memory, CPU, uptime).
    
    * feat: add LLM Settings configuration page (#483)
    
    Add LLM settings configuration page (provider, apiKey, model, temperature, etc.).
    
    * feat: connect frontend pages to backend APIs and align contracts (#445)
    
    Consolidated API-integration work (#445,#446,#447,#448,#449,#450,#451,#452,#453,#454,#458,#459,#460,#461,#463).
    
    * feat: add service capabilities and interaction enhancements (#462)
    
    Consolidated service/interaction work (#462,#468,#469,#464,#470,#471,#472,#488).
    
    * feat: add preferences persistence, accessibility and env config (#465)
    
    Consolidated preferences/a11y/config work (#465,#466,#485,#486,#487,#484).
    
    * fix: include remaining consolidated frontend changes (#490)
    
    topicService topic API contract, authStore persisted session, vite env config.
    
    * fix: align login response contract (#492)
    
    Align frontend LoginResponse type with backend LoginVO contract: change flat username/role to nested user.username/admin structure.
    
    * fix: use runtime DNS resolver for Nginx API proxy (#494)
    
    Fix Nginx API proxy failure caused by hardcoded DNS resolver by switching to container runtime dynamic resolution.
    
    * feat: enhance i18n support with labelKey-based translations (#495)
    
    Migrate hardcoded Chinese UI text to i18n labelKey pattern, add ~280 translation entries (zh+en), fix duplicate key compilation errors.
    
    * test: fix frontend baseline assertions (#498)
    
    Fix test assertions to align with backend PageResult contract and current i18n labels.
    
    * fix: initialize deploy helpers before validation (#499)
    
    Move logging/error helper initialization before config validation in deploy.sh to fix err() undefined when REMOTE_HOST is missing.
    
    * ci: build frontend Docker image (#500)
    
    Add frontend Docker image build to CI workflow to validate Dockerfile/nginx deployment path.
    
    * fix: load producer topics from Studio API response (#501)
    
    Fix Producer page topic list to read from Studio API new format (data[].name) with backward compatibility for legacy topicList format.
    
    * fix: generate data source keys on creation (#509)
    
    Fix ConcurrentHashMap NPE when creating data sources with null key by auto-generating UUID in service layer.
    
    * feat: add producer connection endpoint (#510)
    
    Add producer connection query endpoint with topic/producerGroup filtering, returning backward-compatible connectionSet format.
    
    * fix: avoid returning General Settings API key (#512)
    
    Fix security issue where General Settings endpoint returned API key in plaintext. Replace with apiKeyConfigured boolean flag and support explicit key clearing.
    
    * feat: add catalog-driven read-only tools (#514)
    
    Introduce versioned YAML tool catalog with JSON Schema validation, SHA-256 digest, deep immutability, and two read-only L1 tool handlers (rmq.cluster.list / rmq.capabilities).
    
    * feat: add LiteTopic backend endpoints (#502)
    
    Add backend stub endpoints for LiteTopic page: list, session details, TTL extension, quota, and capability check.
    
    * feat: add ops backend endpoints (#504)
    
    Add ops page backend endpoints: NameServer address management, VIP Channel, and TLS switches.
    
    * feat: add proxy address endpoints (#505)
    
    Add proxy address list and add-address endpoints supporting Studio frontend form submission.
    
    * feat: add LLM settings endpoints (#506)
    
    Add LLM settings backend endpoints: config read/write, connectivity test, and model listing.
    
    * feat: add alert rules YAML endpoint (#507)
    
    Add alert rules Prometheus YAML export endpoint with default RocketMQ alert template fallback.
    
    * fix: ACL page API integration (#516)
    
    Wire ACL management page to backend API, add update endpoints for ACL rules and users with proper validation.
    
    * fix: sort queried messages by store time (#519)
    
    Sort message query results by storeTime in descending order so latest messages appear first.
    
    * feat: add consumer stack diagnostics API (#521)
    
    Add consumer thread stack diagnostics endpoint (GET /api/groups/{name}/instances/{clientId}/stack) with Provider/Stub/Service layering.
    
    * fix: validate audit log date filters
    
    ---------
    
    Co-authored-by: btlqql <29...@qq.com>
    Co-authored-by: lizhimins <70...@qq.com>
    Co-authored-by: zhaohai <33...@users.noreply.github.com>
    Co-authored-by: terrancesli <35...@qq.com>
    Co-authored-by: wizcraft_kris <99...@users.noreply.github.com>
    Co-authored-by: Loyal-Young <hj...@gmail.com>
    Co-authored-by: majialong <ma...@gmail.com>
    Co-authored-by: aias00 <li...@apache.org>
    Co-authored-by: PiliLily <ma...@163.com>
---
 docs/api-spec.md                                   |  2 ++
 .../rocketmq/studio/ops/audit/AuditService.java    | 15 +++++++-----
 .../studio/ops/audit/AuditServiceTest.java         | 28 +++++++++++++++++-----
 3 files changed, 33 insertions(+), 12 deletions(-)

diff --git a/docs/api-spec.md b/docs/api-spec.md
index e75158f9..2fa9602f 100644
--- a/docs/api-spec.md
+++ b/docs/api-spec.md
@@ -1424,6 +1424,8 @@ GET /api/audit-logs?page={page}&pageSize={pageSize}&search={search}&operationTyp
 | `endDate` | `string` | 否 | 结束日期 (YYYY-MM-DD) |
 | `result` | `string` | 否 | 结果过滤: `success` / `failure` |
 
+`startDate` 或 `endDate` 格式错误,以及 `startDate` 晚于 `endDate` 时,接口返回 HTTP 400。
+
 **Response `data`:**
 
 | 字段 | 类型 | 说明 |
diff --git a/server/src/main/java/org/apache/rocketmq/studio/ops/audit/AuditService.java b/server/src/main/java/org/apache/rocketmq/studio/ops/audit/AuditService.java
index 8972bbe7..27588437 100644
--- a/server/src/main/java/org/apache/rocketmq/studio/ops/audit/AuditService.java
+++ b/server/src/main/java/org/apache/rocketmq/studio/ops/audit/AuditService.java
@@ -26,6 +26,7 @@ import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
 import java.util.List;
 
 @Slf4j
@@ -43,8 +44,11 @@ public class AuditService {
         log.info("Querying audit logs, page={}, pageSize={}, search={}, operationType={}, result={}",
                 page, pageSize, search, operationType, result);
 
-        LocalDateTime start = parseDate(startDate, true);
-        LocalDateTime end = parseDate(endDate, false);
+        LocalDateTime start = parseDate(startDate, true, "startDate");
+        LocalDateTime end = parseDate(endDate, false, "endDate");
+        if (start != null && end != null && start.isAfter(end)) {
+            throw new BusinessException(400, "startDate must not be after endDate");
+        }
 
         List<AuditRecordVO> allRecords = auditRepository.findAll(search, operationType, start, end, result);
         long total = allRecords.size();
@@ -76,16 +80,15 @@ public class AuditService {
         }
     }
 
-    private LocalDateTime parseDate(String dateStr, boolean startOfDay) {
+    private LocalDateTime parseDate(String dateStr, boolean startOfDay, String parameterName) {
         if (dateStr == null || dateStr.isEmpty()) {
             return null;
         }
         try {
             LocalDate date = LocalDate.parse(dateStr, DateTimeFormatter.ISO_LOCAL_DATE);
             return startOfDay ? date.atStartOfDay() : date.atTime(LocalTime.MAX);
-        } catch (Exception e) {
-            log.warn("Failed to parse date: {}", dateStr, e);
-            return null;
+        } catch (DateTimeParseException e) {
+            throw new BusinessException(400, parameterName + " must use YYYY-MM-DD");
         }
     }
 }
diff --git a/server/src/test/java/org/apache/rocketmq/studio/ops/audit/AuditServiceTest.java b/server/src/test/java/org/apache/rocketmq/studio/ops/audit/AuditServiceTest.java
index e50eca77..fefb3651 100644
--- a/server/src/test/java/org/apache/rocketmq/studio/ops/audit/AuditServiceTest.java
+++ b/server/src/test/java/org/apache/rocketmq/studio/ops/audit/AuditServiceTest.java
@@ -188,14 +188,30 @@ class AuditServiceTest {
     }
 
     @Test
-    void queryLogsShouldHandleInvalidDateFormat() {
-        when(auditRepository.findAll(isNull(), isNull(), isNull(), isNull(), isNull()))
-                .thenReturn(Collections.emptyList());
+    void queryLogsShouldRejectInvalidStartDate() {
+        assertThatThrownBy(() -> auditService.queryLogs(
+                1, 10, null, null, "invalid-date", null, null))
+                .isInstanceOf(BusinessException.class)
+                .hasMessage("startDate must use YYYY-MM-DD")
+                .satisfies(ex -> assertThat(((BusinessException) ex).getCode()).isEqualTo(400));
+    }
 
-        PageResult<AuditRecordVO> result = auditService.queryLogs(1, 10, null, null, "invalid-date", null, null);
+    @Test
+    void queryLogsShouldRejectInvalidEndDate() {
+        assertThatThrownBy(() -> auditService.queryLogs(
+                1, 10, null, null, null, "2025-02-30", null))
+                .isInstanceOf(BusinessException.class)
+                .hasMessage("endDate must use YYYY-MM-DD")
+                .satisfies(ex -> assertThat(((BusinessException) ex).getCode()).isEqualTo(400));
+    }
 
-        assertThat(result).isNotNull();
-        assertThat(result.getItems()).isEmpty();
+    @Test
+    void queryLogsShouldRejectReversedDateRange() {
+        assertThatThrownBy(() -> auditService.queryLogs(
+                1, 10, null, null, "2025-02-01", "2025-01-31", null))
+                .isInstanceOf(BusinessException.class)
+                .hasMessage("startDate must not be after endDate")
+                .satisfies(ex -> assertThat(((BusinessException) ex).getCode()).isEqualTo(400));
     }
 
     @Test