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 5ae73d4c0 fix(auth): report the login policy the interceptor enforces 
(#5525)
5ae73d4c0 is described below

commit 5ae73d4c08d76b67962b14f109ebc453f134d7b8
Author: Wang1rrr <[email protected]>
AuthorDate: Fri Oct 9 11:26:19 2026 +0800

    fix(auth): report the login policy the interceptor enforces (#5525)
    
    `AuthController.isLoginRequired` and `AuthInterceptor` kept two divergent 
copies of the same
    policy: a missing settings row meant "no login required" to the controller 
but "login required" to
    the interceptor, and an unreadable policy escaped as a 500 that stranded 
the frontend on its error
    screen with a retry that could never succeed. Fail closed in both branches 
so the endpoint reports
    exactly what the interceptor does; no third copy of the policy remains in 
the repository.
---
 .../rocketmq/studio/auth/AuthController.java       | 20 +++++++++++--
 .../rocketmq/studio/auth/AuthControllerTest.java   | 35 ++++++++++++++++++++++
 2 files changed, 53 insertions(+), 2 deletions(-)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/auth/AuthController.java 
b/server/src/main/java/org/apache/rocketmq/studio/auth/AuthController.java
index 9432eec09..616339752 100644
--- a/server/src/main/java/org/apache/rocketmq/studio/auth/AuthController.java
+++ b/server/src/main/java/org/apache/rocketmq/studio/auth/AuthController.java
@@ -60,12 +60,28 @@ public class AuthController {
                 .body(Result.ok(status));
     }
 
+    /**
+     * Reports the policy {@link AuthInterceptor} actually enforces, and 
answers exactly as it does.
+     * Both read the same static property and the same runtime "requireLogin" 
row, so the failure
+     * branches have to line up too: the frontend reads this flag to choose 
between the login page
+     * and the console, and a policy that fails differently in the two places 
strands the console.
+     * The interceptor fails closed (see its own {@code isLoginRequired}), so 
an unreadable policy
+     * still demands a login here; letting the read failure escape as a 500 
instead used to leave the
+     * frontend on its error screen, where the retry could never succeed and 
the login page that
+     * would in fact work was unreachable.
+     */
     private boolean isLoginRequired() {
         if (authProperties.isLoginRequired()) {
             return true;
         }
-        GeneralSettingsVO settings = settingsRepository.loadGeneralSettings();
-        return settings != null && settings.isRequireLogin();
+        try {
+            GeneralSettingsVO settings = 
settingsRepository.loadGeneralSettings();
+            // Fail closed, matching the interceptor: an absent policy row 
means it still demands a
+            // login, so this endpoint must report that rather than the 
opposite.
+            return settings == null || settings.isRequireLogin();
+        } catch (Exception exception) {
+            return true;
+        }
     }
 
     @PostMapping("/login")
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/auth/AuthControllerTest.java 
b/server/src/test/java/org/apache/rocketmq/studio/auth/AuthControllerTest.java
index 7d7cc7853..c1f503da1 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/auth/AuthControllerTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/auth/AuthControllerTest.java
@@ -19,6 +19,7 @@ package org.apache.rocketmq.studio.auth;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.rocketmq.studio.common.config.LegacyJackson2Config;
+import org.apache.rocketmq.studio.common.exception.BusinessException;
 import org.apache.rocketmq.studio.ops.ai.tool.catalog.ToolCatalog;
 import org.apache.rocketmq.studio.settings.GeneralSettingsVO;
 import org.apache.rocketmq.studio.settings.SettingsRepository;
@@ -72,6 +73,12 @@ class AuthControllerTest {
     @Test
     void statusShouldReportDisabledLoginProtection() throws Exception {
         when(authProperties.isLoginRequired()).thenReturn(false);
+        // The policy row exists and says login is not required. It has to be 
stubbed explicitly:
+        // an unstubbed mock returns null, and an absent policy row means 
"login required" here just
+        // as it does in the interceptor - see 
statusShouldReportLoginRequiredWhenRuntimePolicyIsMissing.
+        
when(settingsRepository.loadGeneralSettings()).thenReturn(GeneralSettingsVO.builder()
+                .requireLogin(false)
+                .build());
         
when(authService.getAuthenticatedUser(null)).thenReturn(Optional.empty());
 
         mockMvc.perform(get("/api/auth/status"))
@@ -96,6 +103,34 @@ class AuthControllerTest {
                 .andExpect(jsonPath("$.data.authenticated").value(false));
     }
 
+    @Test
+    void statusShouldReportLoginRequiredWhenRuntimePolicyIsMissingTest() 
throws Exception {
+        // AuthInterceptor fails closed when the policy row is absent, so the 
flag the frontend reads
+        // must not report "login not required" for the same state.
+        when(authProperties.isLoginRequired()).thenReturn(false);
+        when(settingsRepository.loadGeneralSettings()).thenReturn(null);
+        
when(authService.getAuthenticatedUser(null)).thenReturn(Optional.empty());
+
+        mockMvc.perform(get("/api/auth/status"))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$.data.loginRequired").value(true));
+    }
+
+    @Test
+    void statusShouldReportLoginRequiredWhenRuntimePolicyCannotBeReadTest() 
throws Exception {
+        // The interceptor swallows this failure and demands a login, so the 
endpoint that tells the
+        // frontend which screen to render must not answer with an error of 
its own.
+        when(authProperties.isLoginRequired()).thenReturn(false);
+        when(settingsRepository.loadGeneralSettings())
+                .thenThrow(new BusinessException(500, "Persisted general 
settings are invalid"));
+        
when(authService.getAuthenticatedUser(null)).thenReturn(Optional.empty());
+
+        mockMvc.perform(get("/api/auth/status"))
+                .andExpect(status().isOk())
+                .andExpect(jsonPath("$.data.loginRequired").value(true))
+                .andExpect(jsonPath("$.data.authenticated").value(false));
+    }
+
     @Test
     void statusShouldReportUnauthenticatedWhenTokenIsMissing() throws 
Exception {
         when(authProperties.isLoginRequired()).thenReturn(true);

Reply via email to