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