diff --git a/gateway/src/main/java/org/georchestra/gateway/autoconfigure/app/CustomErrorAttributes.java b/gateway/src/main/java/org/georchestra/gateway/autoconfigure/app/CustomErrorAttributes.java index dcae1733..d5838e4f 100644 --- a/gateway/src/main/java/org/georchestra/gateway/autoconfigure/app/CustomErrorAttributes.java +++ b/gateway/src/main/java/org/georchestra/gateway/autoconfigure/app/CustomErrorAttributes.java @@ -47,7 +47,7 @@ * {@link java.net.UnknownHostException} and {@link java.net.ConnectException} * to {@link HttpStatus#SERVICE_UNAVAILABLE} */ -class CustomErrorAttributes extends DefaultErrorAttributes { +public class CustomErrorAttributes extends DefaultErrorAttributes { @Override public Map getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) { diff --git a/gateway/src/main/resources/application.yml b/gateway/src/main/resources/application.yml index fdb21a7d..9ff47941 100644 --- a/gateway/src/main/resources/application.yml +++ b/gateway/src/main/resources/application.yml @@ -15,6 +15,7 @@ server: ssl: enabled: false #TODO: configure SSL with a self-signed certificate + forward-headers-strategy: FRAMEWORK spring: config: @@ -52,6 +53,7 @@ spring: - RemoveSecurityHeaders # AddSecHeaders appends sec-* headers to proxied requests based on the currently authenticated user - AddSecHeaders + - PreserveHostHeader - ApplicationError global-filter: websocket-routing: diff --git a/gateway/src/test/java/org/georchestra/gateway/app/GeorchestraGatewayApplicationTests.java b/gateway/src/test/java/org/georchestra/gateway/app/GeorchestraGatewayApplicationTests.java index a7591c5a..6d5cde6e 100644 --- a/gateway/src/test/java/org/georchestra/gateway/app/GeorchestraGatewayApplicationTests.java +++ b/gateway/src/test/java/org/georchestra/gateway/app/GeorchestraGatewayApplicationTests.java @@ -29,6 +29,7 @@ import java.util.function.Function; import java.util.stream.Collectors; +import org.georchestra.gateway.autoconfigure.app.CustomErrorAttributes; import org.georchestra.gateway.autoconfigure.app.ErrorCustomizerAutoConfiguration; import org.georchestra.gateway.security.ldap.extended.GeorchestraUserNamePasswordAuthenticationToken; import org.junit.jupiter.api.Test; @@ -39,6 +40,7 @@ import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.gateway.route.Route; import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.context.ApplicationContext; import org.springframework.core.env.Environment; import org.springframework.http.HttpStatus; import org.springframework.security.core.Authentication; @@ -59,6 +61,8 @@ class GeorchestraGatewayApplicationTests { private @Autowired GeorchestraGatewayApplication application; private @Autowired WebTestClient testClient; + private @Autowired ApplicationContext context; + @Test void contextLoadsFromDatadir() { assertEquals("src/test/resources/test-datadir", env.getProperty("georchestra.datadir")); @@ -109,12 +113,13 @@ void makeSureWhoamiDoesNotProvideAnySensitiveInfo() { void errorCustomizerReturnsServiceUnavailableInsteadOfServerError() { Map routesById = routeLocator.getRoutes() .collect(Collectors.toMap(Route::getId, Function.identity())).block(); - + assertThat(context.getBean(CustomErrorAttributes.class)).isNotNull(); Route testRoute = routesById.get("unknownHostRoute"); assertNotNull(testRoute); assertThat(testRoute.getUri()).isEqualTo(URI.create("http://not.a.valid.host:80")); - testClient.get().uri("/path/to/unavailable/service").exchange().expectStatus() - .isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + testClient.get().uri("/path/to/unavailable/service")// + .header("Host", "localhost")// + .exchange().expectStatus().isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); } } diff --git a/gateway/src/test/java/org/georchestra/gateway/security/ResolveGeorchestraUserGlobalFilterIT.java b/gateway/src/test/java/org/georchestra/gateway/security/ResolveGeorchestraUserGlobalFilterIT.java index 238e2395..1e0ecf53 100644 --- a/gateway/src/test/java/org/georchestra/gateway/security/ResolveGeorchestraUserGlobalFilterIT.java +++ b/gateway/src/test/java/org/georchestra/gateway/security/ResolveGeorchestraUserGlobalFilterIT.java @@ -68,6 +68,7 @@ protected void doStart() { assertNotNull(context.getBean(JsonPayloadHeadersContributor.class)); testClient.get().uri("/echo/")// + .header("Host", "localhost")// .header("Authorization", "Basic dGVzdGFkbWluOnRlc3RhZG1pbg==") // testadmin:testadmin .exchange()// .expectStatus()// @@ -81,6 +82,7 @@ protected void doStart() { gatewayConfig.getDefaultHeaders().setJsonOrganization(Optional.of(false)); testClient.get().uri("/echo/")// + .header("Host", "localhost")// .header("Authorization", "Basic dGVzdGFkbWluOnRlc3RhZG1pbg==") // testadmin:testadmin .exchange()// .expectStatus()// @@ -96,6 +98,7 @@ protected void doStart() { gatewayConfig.getDefaultHeaders().setJsonOrganization(Optional.of(true)); testClient.get().uri("/echo/")// + .header("Host", "localhost")// .header("Authorization", "Basic dGVzdGFkbWluOnRlc3RhZG1pbg==") // testadmin:testadmin .exchange()// .expectStatus()// @@ -107,6 +110,7 @@ protected void doStart() { public @Test void testSecOrgnamePresent() { testClient.get().uri("/echo/")// + .header("Host", "localhost")// .header("Authorization", "Basic dGVzdGFkbWluOnRlc3RhZG1pbg==") // testadmin:testadmin .exchange()// .expectStatus()// diff --git a/gateway/src/test/java/org/georchestra/gateway/security/accessrules/AccessRulesCustomizerIT.java b/gateway/src/test/java/org/georchestra/gateway/security/accessrules/AccessRulesCustomizerIT.java index 7ee5ed25..5877a8f1 100644 --- a/gateway/src/test/java/org/georchestra/gateway/security/accessrules/AccessRulesCustomizerIT.java +++ b/gateway/src/test/java/org/georchestra/gateway/security/accessrules/AccessRulesCustomizerIT.java @@ -19,14 +19,10 @@ package org.georchestra.gateway.security.accessrules; -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.noContent; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; - -import java.net.URI; - +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import com.github.tomakehurst.wiremock.junit5.WireMockExtension; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import lombok.extern.slf4j.Slf4j; import org.georchestra.gateway.app.GeorchestraGatewayApplication; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -40,11 +36,9 @@ import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.web.reactive.server.WebTestClient; -import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import com.github.tomakehurst.wiremock.junit5.WireMockExtension; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; +import java.net.URI; -import lombok.extern.slf4j.Slf4j; +import static com.github.tomakehurst.wiremock.client.WireMock.*; /** * Integration tests for {@link AccessRulesCustomizer} for the access rules in @@ -133,8 +127,12 @@ private static void mockServiceTarget(DynamicPropertyRegistry registry, String s .withHeader("sec-proxy", equalTo("true"))// .willReturn(ok())); - testClient.get().uri("/header").exchange().expectStatus().isOk(); - testClient.get().uri("/header/img/logo.png").exchange().expectStatus().isOk(); + testClient.get().uri("/header")// + .header("Host", "localhost")// + .exchange().expectStatus().isOk(); + testClient.get().uri("/header/img/logo.png")// + .header("Host", "localhost")// + .exchange().expectStatus().isOk(); } /** @@ -172,10 +170,12 @@ private static void mockServiceTarget(DynamicPropertyRegistry registry, String s mockService.stubFor(get(urlMatching("/import(/.*)?")).willReturn(ok())); testClient.get().uri("/import")// + .header("Host", "localhost")// .exchange()// .expectStatus().isOk(); testClient.get().uri("/import/any/thing")// + .header("Host", "localhost")// .exchange()// .expectStatus().isOk(); } @@ -216,10 +216,12 @@ private static void mockServiceTarget(DynamicPropertyRegistry registry, String s mockService.stubFor(get(urlMatching("/analytics(/.*)?")).willReturn(ok())); testClient.get().uri("/analytics")// + .header("Host", "localhost")// .exchange()// .expectStatus().isOk(); testClient.get().uri("/analytics/any/thing")// + .header("Host", "localhost")// .exchange()// .expectStatus().isOk(); } @@ -264,10 +266,12 @@ void testGlobalAccessRule() { mockService.stubFor(get(urlMatching("/mapstore(/.*)?")).willReturn(ok())); testClient.get().uri("/mapstore")// + .header("Host", "localhost")// .exchange()// .expectStatus().isOk(); testClient.get().uri("/mapstore/any/thing")// + .header("Host", "localhost")// .exchange()// .expectStatus().isOk(); } @@ -281,6 +285,7 @@ void testQueryParamAuthentication_forbidden_when_anonymous() { .expectStatus().is3xxRedirection(); testClient.get().uri("/header")// + .header("Host", "localhost")// .exchange()// .expectStatus().isOk(); } @@ -291,10 +296,12 @@ void testQueryParamAuthentication_authorized_if_logged_in() { mockService.stubFor(get(urlMatching("/header(.*)?")).willReturn(ok())); testClient.get().uri("/header?login")// + .header("Host", "localhost")// .exchange()// .expectStatus().isOk(); testClient.get().uri("/header")// + .header("Host", "localhost")// .exchange()// .expectStatus().isOk(); }