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 f9f4ef15c fix(group): enforce ownership in offset reset previews 
(#5418)
f9f4ef15c is described below

commit f9f4ef15c68b65928feb8f04f04cd2ae0e51fc4c
Author: coder999o <[email protected]>
AuthorDate: Fri Oct 9 11:25:52 2026 +0800

    fix(group): enforce ownership in offset reset previews (#5418)
    
    `previewResetOffset` normalized the instance id but never checked 
ownership, so a cross-instance
    preview reported "can reset" for a group or topic belonging to another 
instance, and the AI tool's
    plan step contradicted its own execute step. Route it through 
`requireWriteInstance` for both the
    group and the topic resource, exactly as `resetOffset` and 
`skipAccumulated` already do.
---
 .../studio/instance/topic/MetadataService.java     |  5 ++--
 .../studio/instance/topic/MetadataServiceTest.java | 30 ++++++++++++++++++++++
 2 files changed, 33 insertions(+), 2 deletions(-)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/instance/topic/MetadataService.java
 
b/server/src/main/java/org/apache/rocketmq/studio/instance/topic/MetadataService.java
index dbd5dace1..1cc1562f9 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/instance/topic/MetadataService.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/instance/topic/MetadataService.java
@@ -552,10 +552,11 @@ public class MetadataService {
 
     public ResetConsumerOffsetPreviewVO previewResetOffset(String instanceId, 
String name,
                                                            long timestamp, 
String topic) {
-        instanceId = normalizeInstanceId(instanceId);
         String groupName = requireName(name, "consumer group name");
         String topicName = requireName(topic, "topic name");
-        return resolve(instanceId).previewResetOffset(instanceId, groupName, 
timestamp, topicName);
+        String target = requireWriteInstance(instanceId, new 
Resource(Kind.GROUP, groupName), true);
+        requireWriteInstance(target, ownershipGuard.topicResource(topicName), 
true);
+        return resolve(target).previewResetOffset(target, groupName, 
timestamp, topicName);
     }
 
     public void resetOffset(String instanceId, String name, long timestamp, 
String topic) {
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/instance/topic/MetadataServiceTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/instance/topic/MetadataServiceTest.java
index e2535c047..b94c5db76 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/instance/topic/MetadataServiceTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/instance/topic/MetadataServiceTest.java
@@ -1001,6 +1001,36 @@ class MetadataServiceTest {
         verifyNoInteractions(apacheProvider);
     }
 
+    @Test
+    void previewResetOffsetShouldRejectForeignGroupBeforeProviderAccess() {
+        doThrow(new BusinessException(409, "Resource belongs to another 
instance"))
+                .when(ownershipGuard).check(any(), eq(new 
ResourceOwnershipGuard.Resource(
+                        ResourceOwnershipGuard.Kind.GROUP, "cg-orders")), 
eq(true));
+
+        assertThatThrownBy(() -> metadataService.previewResetOffset(
+                "instance-a", "cg-orders", 1784246400000L, "orders"))
+                .isInstanceOf(BusinessException.class)
+                .hasMessage("Resource belongs to another instance")
+                .satisfies(error -> assertThat(((BusinessException) 
error).getCode()).isEqualTo(409));
+        verifyNoInteractions(apacheProvider);
+    }
+
+    @Test
+    void previewResetOffsetShouldRejectForeignTopicBeforeProviderAccess() {
+        when(ownershipGuard.check(any(), eq(new 
ResourceOwnershipGuard.Resource(
+                ResourceOwnershipGuard.Kind.GROUP, "cg-orders")), 
eq(true))).thenReturn(null);
+        doThrow(new BusinessException(409, "Resource belongs to another 
instance"))
+                .when(ownershipGuard).check(any(), eq(new 
ResourceOwnershipGuard.Resource(
+                        ResourceOwnershipGuard.Kind.TOPIC, "orders")), 
eq(true));
+
+        assertThatThrownBy(() -> metadataService.previewResetOffset(
+                "instance-a", "cg-orders", 1784246400000L, "orders"))
+                .isInstanceOf(BusinessException.class)
+                .hasMessage("Resource belongs to another instance")
+                .satisfies(error -> assertThat(((BusinessException) 
error).getCode()).isEqualTo(409));
+        verifyNoInteractions(apacheProvider);
+    }
+
     private ConsumerGroupVO consumerGroup(String name, String namespace, long 
lag, SubscriptionMode mode) {
         ConsumerGroupVO group = new ConsumerGroupVO();
         group.setName(name);

Reply via email to