|
|
@@ -0,0 +1,54 @@
|
|
|
+package com.zksy.web.controller.cockpit;
|
|
|
+
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
+
|
|
|
+import java.nio.file.Files;
|
|
|
+import java.nio.file.Path;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
|
+import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Locks the confirmed cockpit anonymous-read policy (2026-09-28): GET /api/cockpit/v1/**
|
|
|
+ * is served without authentication, everything else still requires authentication, and
|
|
|
+ * deployment relies on intranet network isolation.
|
|
|
+ *
|
|
|
+ * <p>The full Spring Security chain needs the application context, so this test freezes
|
|
|
+ * the matcher contract directly on {@code zksy-framework/.../SecurityConfig.java}.
|
|
|
+ */
|
|
|
+class CockpitAnonymousAccessSecurityConfigTest {
|
|
|
+
|
|
|
+ private static final Path SECURITY_CONFIG = Path.of(
|
|
|
+ "..", "zksy-framework", "src", "main", "java", "com", "zksy", "framework",
|
|
|
+ "config", "SecurityConfig.java");
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void cockpitPermitAllIsGetScopedAndSingleRule() throws Exception {
|
|
|
+ assertTrue(Files.exists(SECURITY_CONFIG), "SecurityConfig.java must exist at " + SECURITY_CONFIG);
|
|
|
+ List<String> lines = Files.readAllLines(SECURITY_CONFIG);
|
|
|
+
|
|
|
+ long cockpitPermitLines = lines.stream()
|
|
|
+ .filter(line -> line.contains("permitAll") && line.contains("/api/cockpit"))
|
|
|
+ .count();
|
|
|
+ assertEquals(1, cockpitPermitLines, "exactly one cockpit permitAll rule may exist");
|
|
|
+
|
|
|
+ String permitLine = lines.stream()
|
|
|
+ .filter(line -> line.contains("permitAll") && line.contains("/api/cockpit"))
|
|
|
+ .findFirst()
|
|
|
+ .orElseThrow();
|
|
|
+ assertTrue(permitLine.contains("HttpMethod.GET"),
|
|
|
+ "cockpit permitAll must be scoped to GET only");
|
|
|
+ assertTrue(permitLine.contains("\"/api/cockpit/v1/**\""),
|
|
|
+ "cockpit permitAll must cover /api/cockpit/v1/**");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void everythingOutsideCockpitGetStillRequiresAuthentication() throws Exception {
|
|
|
+ String source = Files.readString(SECURITY_CONFIG);
|
|
|
+ assertTrue(source.contains(".anyRequest().authenticated()"),
|
|
|
+ "the catch-all rule must still require authentication");
|
|
|
+ assertTrue(source.contains("\"/ws/deviceStatus\""),
|
|
|
+ "the WebSocket endpoint must keep its existing access rule");
|
|
|
+ }
|
|
|
+}
|