mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 19:23:42 +08:00
- explicit SecurityConfig authorization for /swagger-ui*, /v3/api-docs*, /webjars/** - public for local/default profile; admin-only (ROLE_ADMIN) by default in production DB profiles - override via MATECLAW_OPENAPI_EXPOSE_UI; add RANDOM_PORT integration tests and docs
60 lines
2.4 KiB
Java
60 lines
2.4 KiB
Java
package vip.mate.config;
|
|
|
|
import org.junit.jupiter.api.DisplayName;
|
|
import org.junit.jupiter.api.Test;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.boot.test.context.SpringBootTest;
|
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
|
import org.springframework.http.HttpStatus;
|
|
import org.springframework.http.ResponseEntity;
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
|
|
/**
|
|
* Production posture: with {@code mateclaw.openapi.expose-ui=false} the Swagger
|
|
* UI / OpenAPI document paths must NOT be anonymously reachable. They fall under
|
|
* an explicit {@code hasRole('ADMIN')} rule in {@link SecurityConfig}, so an
|
|
* unauthenticated request is rejected by the authentication entry point (401)
|
|
* instead of leaking the full API surface.
|
|
*
|
|
* <p>Uses a real embedded servlet container ({@code RANDOM_PORT}) because the app
|
|
* registers a WebSocket endpoint that requires a servlet {@code ServerContainer},
|
|
* which the MockMvc-only environment does not provide.
|
|
*/
|
|
@SpringBootTest(
|
|
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
|
properties = {
|
|
"spring.flyway.enabled=true",
|
|
"spring.flyway.locations=classpath:db/migration/h2",
|
|
"mateclaw.openapi.expose-ui=false"
|
|
}
|
|
)
|
|
class OpenApiLockedDownAccessTest {
|
|
|
|
@Autowired
|
|
private TestRestTemplate rest;
|
|
|
|
@Test
|
|
@DisplayName("Anonymous OpenAPI JSON is blocked (401) when expose-ui=false")
|
|
void anonymousApiDocsBlocked() {
|
|
ResponseEntity<String> resp = rest.getForEntity("/v3/api-docs", String.class);
|
|
assertEquals(HttpStatus.UNAUTHORIZED, resp.getStatusCode());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Anonymous Swagger UI is blocked (401) when expose-ui=false")
|
|
void anonymousSwaggerUiBlocked() {
|
|
ResponseEntity<String> resp = rest.getForEntity("/swagger-ui/index.html", String.class);
|
|
assertEquals(HttpStatus.UNAUTHORIZED, resp.getStatusCode());
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("A genuinely public endpoint stays reachable when Swagger is locked")
|
|
void publicEndpointStillReachable() {
|
|
// GET /api/v1/settings/language is permitAll (first-paint i18n); proves
|
|
// the lockdown is scoped to the OpenAPI paths, not a blanket denial.
|
|
ResponseEntity<String> resp = rest.getForEntity("/api/v1/settings/language", String.class);
|
|
assertEquals(HttpStatus.OK, resp.getStatusCode());
|
|
}
|
|
}
|