Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.reactive.result.method.RequestMappingInfoHandlerMapping;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;

/**
* A custom {@link RequestMappingInfoHandlerMapping} that makes web endpoints available on
Expand Down Expand Up @@ -110,8 +111,9 @@ public Publisher<ResponseEntity<Object>> links(ServerWebExchange exchange) {
return new ResponseEntity<>(securityResponse.getStatus());
}
AccessLevel accessLevel = exchange.getAttribute(AccessLevel.REQUEST_ATTRIBUTE);
String requestUri = UriComponentsBuilder.fromUri(request.getURI()).replaceQuery(null).toUriString();
Map<String, Link> links = CloudFoundryWebFluxEndpointHandlerMapping.this.linksResolver
.resolveLinks(request.getURI().toString());
.resolveLinks(requestUri);
return new ResponseEntity<>(
Collections.singletonMap("_links", getAccessibleLinks(accessLevel, links)), HttpStatus.OK);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.cors.CorsConfiguration;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
Expand Down Expand Up @@ -145,23 +146,23 @@ void linksToOtherEndpointsWithFullAccess() {
.jsonPath("_links.length()")
.isEqualTo(5)
.jsonPath("_links.self.href")
.isNotEmpty()
.value(isLinkTo("/cfApplication"))
.jsonPath("_links.self.templated")
.isEqualTo(false)
.jsonPath("_links.info.href")
.isNotEmpty()
.value(isLinkTo("/cfApplication/info"))
.jsonPath("_links.info.templated")
.isEqualTo(false)
.jsonPath("_links.env.href")
.isNotEmpty()
.value(isLinkTo("/cfApplication/env"))
.jsonPath("_links.env.templated")
.isEqualTo(false)
.jsonPath("_links.test.href")
.isNotEmpty()
.value(isLinkTo("/cfApplication/test"))
.jsonPath("_links.test.templated")
.isEqualTo(false)
.jsonPath("_links.test-part.href")
.isNotEmpty()
.value(isLinkTo("/cfApplication/test/{part}"))
.jsonPath("_links.test-part.templated")
.isEqualTo(true)));
}
Expand Down Expand Up @@ -195,11 +196,11 @@ void linksToOtherEndpointsWithRestrictedAccess() {
.jsonPath("_links.length()")
.isEqualTo(2)
.jsonPath("_links.self.href")
.isNotEmpty()
.value(isLinkTo("/cfApplication"))
.jsonPath("_links.self.templated")
.isEqualTo(false)
.jsonPath("_links.info.href")
.isNotEmpty()
.value(isLinkTo("/cfApplication/info"))
.jsonPath("_links.info.templated")
.isEqualTo(false)
.jsonPath("_links.env")
Expand All @@ -210,12 +211,34 @@ void linksToOtherEndpointsWithRestrictedAccess() {
.doesNotExist()));
}

@Test
void whenRequestHasAQueryStringLinksToOtherEndpointsDoNotHaveAQueryString() {
given(this.tokenValidator.validate(any())).willReturn(Mono.empty());
given(this.securityService.getAccessLevel(any(), eq("app-id"))).willReturn(Mono.just(AccessLevel.RESTRICTED));
this.contextRunner.run(withWebTestClient((client) -> client.get()
.uri("/cfApplication?x=1")
.accept(MediaType.APPLICATION_JSON)
.header("Authorization", "bearer " + mockAccessToken())
.exchange()
.expectStatus()
.isOk()
.expectBody()
.jsonPath("_links.self.href")
.value(isLinkTo("/cfApplication"))
.jsonPath("_links.info.href")
.value(isLinkTo("/cfApplication/info"))));
}

@Test
void unknownEndpointsAreForbidden() {
this.contextRunner.run(withWebTestClient(
(client) -> client.get().uri("/cfApplication/unknown").exchange().expectStatus().isForbidden()));
}

private Consumer<Object> isLinkTo(String target) {
return (href) -> assertThat(href).asString().doesNotContain("?").endsWith(target);
}

private ContextConsumer<AssertableReactiveWebApplicationContext> withWebTestClient(
Consumer<WebTestClient> clientConsumer) {
return (context) -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
Expand Down Expand Up @@ -133,23 +134,23 @@ void linksToOtherEndpointsWithFullAccess() {
.jsonPath("_links.length()")
.isEqualTo(5)
.jsonPath("_links.self.href")
.isNotEmpty()
.value(isLinkTo("/cfApplication"))
.jsonPath("_links.self.templated")
.isEqualTo(false)
.jsonPath("_links.info.href")
.isNotEmpty()
.value(isLinkTo("/cfApplication/info"))
.jsonPath("_links.info.templated")
.isEqualTo(false)
.jsonPath("_links.env.href")
.isNotEmpty()
.value(isLinkTo("/cfApplication/env"))
.jsonPath("_links.env.templated")
.isEqualTo(false)
.jsonPath("_links.test.href")
.isNotEmpty()
.value(isLinkTo("/cfApplication/test"))
.jsonPath("_links.test.templated")
.isEqualTo(false)
.jsonPath("_links.test-part.href")
.isNotEmpty()
.value(isLinkTo("/cfApplication/test/{part}"))
.jsonPath("_links.test-part.templated")
.isEqualTo(true));
}
Expand Down Expand Up @@ -184,11 +185,11 @@ void linksToOtherEndpointsWithRestrictedAccess() {
.jsonPath("_links.length()")
.isEqualTo(2)
.jsonPath("_links.self.href")
.isNotEmpty()
.value(isLinkTo("/cfApplication"))
.jsonPath("_links.self.templated")
.isEqualTo(false)
.jsonPath("_links.info.href")
.isNotEmpty()
.value(isLinkTo("/cfApplication/info"))
.jsonPath("_links.info.templated")
.isEqualTo(false)
.jsonPath("_links.env")
Expand All @@ -199,6 +200,24 @@ void linksToOtherEndpointsWithRestrictedAccess() {
.doesNotExist());
}

@Test
void whenRequestHasAQueryStringLinksToOtherEndpointsDoNotHaveAQueryString() {
given(this.securityService.getAccessLevel(any(), eq("app-id"))).willReturn(AccessLevel.RESTRICTED);
load(TestEndpointConfiguration.class,
(client) -> client.get()
.uri("/cfApplication?x=1")
.accept(MediaType.APPLICATION_JSON)
.header("Authorization", "bearer " + mockAccessToken())
.exchange()
.expectStatus()
.isOk()
.expectBody()
.jsonPath("_links.self.href")
.value(isLinkTo("/cfApplication"))
.jsonPath("_links.info.href")
.value(isLinkTo("/cfApplication/info")));
}

@Test
void unknownEndpointsAreForbidden() {
load(TestEndpointConfiguration.class,
Expand All @@ -210,6 +229,10 @@ void unknownEndpointsAreForbidden() {
.isForbidden());
}

private Consumer<Object> isLinkTo(String target) {
return (href) -> assertThat(href).asString().doesNotContain("?").endsWith(target);
}

private void load(Class<?> configuration, Consumer<WebTestClient> clientConsumer) {
BiConsumer<ApplicationContext, WebTestClient> consumer = (context, client) -> clientConsumer.accept(client);
new WebApplicationContextRunner(AnnotationConfigServletWebServerApplicationContext::new)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,15 +162,40 @@ void linksToOtherEndpointsAreProvided() {
.jsonPath("_links.length()")
.isEqualTo(3)
.jsonPath("_links.self.href")
.isNotEmpty()
.value(isLinkTo("/endpoints"))
.jsonPath("_links.self.templated")
.isEqualTo(false)
.jsonPath("_links.test.href")
.isNotEmpty()
.value(isLinkTo("/endpoints/test"))
.jsonPath("_links.test.templated")
.isEqualTo(false)
.jsonPath("_links.test-part.href")
.isNotEmpty()
.value(isLinkTo("/endpoints/test/{part}"))
.jsonPath("_links.test-part.templated")
.isEqualTo(true));
}

@Test
void whenRequestHasAQueryStringLinksToOtherEndpointsDoNotHaveAQueryString() {
load(TestEndpointConfiguration.class,
(client) -> client.get()
.uri("?a=alpha")
.exchange()
.expectStatus()
.isOk()
.expectBody()
.jsonPath("_links.length()")
.isEqualTo(3)
.jsonPath("_links.self.href")
.value(isLinkTo("/endpoints"))
.jsonPath("_links.self.templated")
.isEqualTo(false)
.jsonPath("_links.test.href")
.value(isLinkTo("/endpoints/test"))
.jsonPath("_links.test.templated")
.isEqualTo(false)
.jsonPath("_links.test-part.href")
.value(isLinkTo("/endpoints/test/{part}"))
.jsonPath("_links.test-part.templated")
.isEqualTo(true));
}
Expand Down Expand Up @@ -668,6 +693,10 @@ void endpointCanProduceAResponseWithACustomStatus() {
(client) -> client.get().uri("/customstatus").exchange().expectStatus().isEqualTo(234));
}

private Consumer<Object> isLinkTo(String target) {
return (href) -> assertThat(href).asString().doesNotContain("?").endsWith(target);
}

protected abstract int getPort(T context);

protected void validateErrorBody(WebTestClient.BodyContentSpec body, HttpStatus status, String path,
Expand Down
Loading