Skip to content

Commit 3ab7679

Browse files
committed
clean up SparqlProxyControllerTest and move header stuff into SparqlProxyCleaningServiceTest
1 parent af688e9 commit 3ab7679

2 files changed

Lines changed: 122 additions & 41 deletions

File tree

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package org.fairdatateam.fairdatapoint.acceptance.search.sparql;
2+
3+
import jakarta.servlet.http.HttpServletRequest;
4+
import org.fairdatateam.fairdatapoint.api.controller.search.SparqlProxyCleaningService;
5+
import org.junit.jupiter.api.Test;
6+
import org.springframework.beans.factory.annotation.Autowired;
7+
import org.springframework.http.HttpHeaders;
8+
import org.springframework.http.HttpRequest;
9+
import org.springframework.http.MediaType;
10+
import org.springframework.http.ResponseEntity;
11+
import org.springframework.http.client.ClientHttpResponse;
12+
import org.springframework.mock.http.client.MockClientHttpRequest;
13+
import org.springframework.mock.http.client.MockClientHttpResponse;
14+
import org.springframework.mock.web.MockHttpServletRequest;
15+
import org.springframework.test.context.TestPropertySource;
16+
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
17+
import java.lang.reflect.InvocationTargetException;
18+
import java.lang.reflect.Method;
19+
import java.util.List;
20+
import java.util.function.Consumer;
21+
22+
import static org.assertj.core.api.Assertions.assertThat;
23+
24+
@SpringJUnitConfig(SparqlProxyCleaningService.class)
25+
@TestPropertySource(properties = {"openapi.title=Test API", "openapi.version=1.0"})
26+
public class SparqlProxyCleaningServiceTest {
27+
28+
private final SparqlProxyCleaningService cleaningService;
29+
30+
/**
31+
* Constructor
32+
*/
33+
@Autowired
34+
public SparqlProxyCleaningServiceTest(SparqlProxyCleaningService cleaningService) {
35+
this.cleaningService = cleaningService;
36+
}
37+
38+
@Test
39+
public void cleansRequestHeaders() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
40+
final String remoteIP = "1.2.3.4";
41+
// headers for the incoming request from a remote client
42+
HttpHeaders requestHeadersIncoming = new HttpHeaders();
43+
requestHeadersIncoming.setAccept(List.of(MediaType.APPLICATION_JSON));
44+
requestHeadersIncoming.setBearerAuth("dummy-token");
45+
MockHttpServletRequest requestIncoming = new MockHttpServletRequest();
46+
requestIncoming.setRemoteAddr(remoteIP);
47+
48+
// headers for the outgoing request to the upstream sparql endpoint
49+
HttpHeaders requestHeadersOutgoing = new HttpHeaders();
50+
51+
// get access to package-private method for testing
52+
Method methodUnderTest = SparqlProxyCleaningService.class.getDeclaredMethod(
53+
"cleanRequestHeadersFactory", HttpServletRequest.class, HttpHeaders.class
54+
);
55+
methodUnderTest.setAccessible(true);
56+
57+
// create callable using factory
58+
@SuppressWarnings("unchecked")
59+
Consumer<HttpHeaders> cleanRequestHeaders = (Consumer<HttpHeaders>) methodUnderTest.invoke(
60+
cleaningService, requestIncoming, requestHeadersIncoming);
61+
62+
// perform header clean-up
63+
cleanRequestHeaders.accept(requestHeadersOutgoing);
64+
65+
// check result
66+
assertThat(requestHeadersOutgoing.getAccept()).hasSize(1).contains(MediaType.APPLICATION_JSON);
67+
assertThat(requestHeadersOutgoing.containsKey(HttpHeaders.AUTHORIZATION)).isFalse();
68+
assertThat(requestHeadersOutgoing.get(SparqlProxyCleaningService.HEADER_X_FORWARDED_FOR))
69+
.hasSize(1).contains(remoteIP);
70+
}
71+
72+
@Test
73+
public void cleansResponseHeaders() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
74+
// mock headers "received" from the upstream sparql endpoint
75+
final HttpHeaders mockBackendResponseHeaders = new HttpHeaders();
76+
77+
// add custom header
78+
final String customHeaderName = "my-custom-header";
79+
mockBackendResponseHeaders.add(customHeaderName, "dummy");
80+
81+
// add standard hop-by-hop headers
82+
final List<String> hopByHopHeaders = SparqlProxyCleaningService.getHopByHopHeaders();
83+
hopByHopHeaders.forEach(header -> mockBackendResponseHeaders.add(header, "dummy"));
84+
85+
// add connection header (list is incomplete on purpose, e.g. it does not contain all hop-by-hop headers)
86+
mockBackendResponseHeaders.put(HttpHeaders.CONNECTION, List.of(customHeaderName, hopByHopHeaders.get(0)));
87+
88+
// add server header
89+
final String backendServerHeader = "mock backend sparql server 1.0";
90+
mockBackendResponseHeaders.add(HttpHeaders.SERVER, backendServerHeader);
91+
92+
// get access to package-private method for testing
93+
Method methodUnderTest = SparqlProxyCleaningService.class.getDeclaredMethod(
94+
"cleanResponse", HttpRequest.class, ClientHttpResponse.class
95+
);
96+
methodUnderTest.setAccessible(true);
97+
98+
try (final MockClientHttpResponse sparqlServerResponse = new MockClientHttpResponse()) {
99+
// clean the response
100+
@SuppressWarnings("unchecked")
101+
ResponseEntity<byte[]> cleanedResponse = (ResponseEntity<byte[]>) methodUnderTest.invoke(
102+
cleaningService, new MockClientHttpRequest(), sparqlServerResponse
103+
);
104+
105+
// check result
106+
final HttpHeaders cleanedResponseHeaders = cleanedResponse.getHeaders();
107+
System.out.println(cleanedResponseHeaders);
108+
assertThat(cleanedResponseHeaders).doesNotContainKey(customHeaderName);
109+
hopByHopHeaders.forEach(header -> assertThat(cleanedResponseHeaders).doesNotContainKey(header));
110+
assertThat(cleanedResponseHeaders).doesNotContainKey(HttpHeaders.CONNECTION);
111+
assertThat(cleanedResponseHeaders).doesNotContainEntry(HttpHeaders.SERVER, List.of(backendServerHeader));
112+
assertThat(cleanedResponseHeaders).containsEntry(HttpHeaders.SERVER, List.of("Test API 1.0"));
113+
}
114+
}
115+
}

src/test/java/org/fairdatateam/fairdatapoint/acceptance/search/sparql/SparqlProxyControllerTest.java

Lines changed: 7 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
import org.fairdatateam.fairdatapoint.Profiles;
2626
import org.fairdatateam.fairdatapoint.api.controller.search.SparqlProxyController;
27-
import org.fairdatateam.fairdatapoint.api.controller.search.SparqlProxyCleaningService;
2827
import org.junit.jupiter.api.BeforeEach;
2928
import org.junit.jupiter.api.Test;
3029
import org.springframework.beans.factory.annotation.Autowired;
@@ -44,14 +43,11 @@
4443
import org.springframework.web.client.RestTemplate;
4544
import org.springframework.web.util.UriComponentsBuilder;
4645
import java.net.URI;
47-
import java.util.List;
4846
import java.util.Map;
4947

5048
import static org.assertj.core.api.Assertions.assertThat;
5149
import static org.fairdatateam.fairdatapoint.api.controller.search.SparqlProxyController.*;
52-
import static org.fairdatateam.fairdatapoint.api.controller.search.SparqlProxyCleaningService.HEADER_X_FORWARDED_FOR;
5350
import static org.fairdatateam.fairdatapoint.api.controller.search.SparqlQueryValidator.MESSAGE_INVALID_QUERY;
54-
import static org.hamcrest.Matchers.matchesPattern;
5551
import static org.hamcrest.Matchers.startsWith;
5652
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
5753
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
@@ -109,15 +105,17 @@ public void setup() {
109105
@Test
110106
@WithAnonymousUser
111107
public void unauthenticatedRequestsAreDeniedWithoutContactingRemoteSparqlServer() {
112-
// specify request with url query, but without authentication
108+
// specify url query
113109
URI uriWithQuery = UriComponentsBuilder
114110
.fromPath(path)
115111
.queryParam(PARAM_QUERY, EXAMPLE_QUERY)
116112
.build()
117113
.toUri();
118114

115+
// perform request
119116
MvcTestResult testResult = mockMvc.get().uri(uriWithQuery).accept(MediaType.APPLICATION_JSON).exchange();
120117

118+
// should be denied
121119
assertThat(testResult).hasStatus(HttpStatus.FORBIDDEN);
122120
}
123121

@@ -172,7 +170,7 @@ public void formPostWithSparqlUpdateContentTypeIsDenied() {
172170

173171
@Test
174172
public void getRequestWithMaliciousUrlQueryIsDenied() {
175-
// specify request with malicious url query (trying to update)
173+
// specify malicious url query (trying to update)
176174
URI uriWithMaliciousQuery = UriComponentsBuilder
177175
.fromPath(path)
178176
.queryParam(PARAM_QUERY, MALICIOUS_SPARQL_UPDATE)
@@ -216,38 +214,14 @@ public void rawPostWithMaliciousQueryIsDenied() {
216214

217215
@Test
218216
public void proxyForwardingWorksForGetRequests() {
219-
// todo: move the header cleaning checks to a dedicated SparqlProxyCleaningServiceTest class
220-
221-
// mock headers for response from mock backend sparql server
222-
final HttpHeaders mockBackendResponseHeaders = new HttpHeaders();
223-
224-
// add a custom header
225-
final String customHeaderName = "my-custom-header";
226-
mockBackendResponseHeaders.add(customHeaderName, "dummy");
227-
228-
// add standard hop-by-hop headers
229-
final List<String> hopByHopHeaders = SparqlProxyCleaningService.getHopByHopHeaders();
230-
hopByHopHeaders.forEach(header -> mockBackendResponseHeaders.add(header, "dummy"));
231-
232-
// connection list is incomplete on purpose (does not contain all hop-by-hop headers)
233-
mockBackendResponseHeaders.put(HttpHeaders.CONNECTION, List.of(customHeaderName, hopByHopHeaders.get(0)));
234-
235-
// add server header
236-
final String backendServerHeader = "mock backend sparql server 1.0";
237-
mockBackendResponseHeaders.add(HttpHeaders.SERVER, backendServerHeader);
238-
239217
// configure mock server for remote SPARQL endpoint
240218
this.mockBackendSparqlServer
241219
// startsWith is required, otherwise it will expect a url without (query) parameters
242220
.expect(requestTo(startsWith(TEST_SPARQL_ENDPOINT_URL)))
243221
.andExpect(method(HttpMethod.GET))
244-
// forwarded header should have been added to request
245-
.andExpect(header(HEADER_X_FORWARDED_FOR, matchesPattern(".+")))
246-
// authorization headers should have been removed from request
247-
.andExpect(headerDoesNotExist(HttpHeaders.AUTHORIZATION))
248-
.andRespond(withSuccess().headers(mockBackendResponseHeaders).body(mockJsonBody));
222+
.andRespond(withSuccess().body(mockJsonBody));
249223

250-
// specify request with url query and normal user (non-admin)
224+
// specify url with query
251225
URI uriWithQuery = UriComponentsBuilder
252226
.fromPath(path)
253227
.queryParam(PARAM_QUERY, EXAMPLE_QUERY)
@@ -258,22 +232,14 @@ public void proxyForwardingWorksForGetRequests() {
258232
MvcTestResult testResult = mockMvc.get()
259233
.uri(uriWithQuery)
260234
.accept(MediaType.APPLICATION_JSON)
261-
.header(HttpHeaders.AUTHORIZATION, "dummy")
262235
.exchange();
263236

264237
// check that mock server has received expected requests
265238
// (for some reason this hides exceptions from the RestClient, so comment out to debug)
266239
mockBackendSparqlServer.verify();
267240

268-
// check response headers
269-
assertThat(testResult).hasStatusOk();
270-
assertThat(testResult).doesNotContainHeader(customHeaderName);
271-
assertThat(testResult).doesNotContainHeader(HttpHeaders.CONNECTION);
272-
hopByHopHeaders.forEach(header -> assertThat(testResult).doesNotContainHeader(header));
273-
assertThat(testResult).containsHeader(HttpHeaders.SERVER);
274-
assertThat(testResult).headers().doesNotContainEntry(HttpHeaders.SERVER, List.of(backendServerHeader));
275-
276241
// verify that proxy returns json response body
242+
assertThat(testResult).hasStatusOk();
277243
assertThat(testResult).bodyJson().hasPath("head.vars").hasPath("results.bindings");
278244

279245
}

0 commit comments

Comments
 (0)