Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
83f7981
add a RestClient bean
dennisvang May 13, 2026
f6d47e1
add RestClient to SearchSparqlController (wip)
dennisvang May 13, 2026
7ee20e2
add a rudimentary proxySparqlEndpoint (wip)
dennisvang May 13, 2026
5059592
add response to proxySparqlEndpoint
dennisvang May 13, 2026
13ee532
move endpoint url logic into separate determineSparqlEndpointUrl method
dennisvang May 13, 2026
deb5086
remove hop-by-hop headers from sparql endpoint response (wip)
dennisvang May 13, 2026
0e20db8
move response header cleanup to separate cleanHeaders() method
dennisvang May 13, 2026
a10792b
rename cleanHeaders to cleanResponseHeaders
dennisvang May 15, 2026
d64505d
fix checkstyle errors
dennisvang May 15, 2026
049c6b2
forward request headers to backend triple store
dennisvang May 15, 2026
38b621f
convert adaptRequestHeaders into a factory method requestHeadersUpdater
dennisvang May 15, 2026
d04c3fb
simplify requestHeadersUpdater with the help of @RequestHeader
dennisvang May 15, 2026
badcc89
rename request/response variables for clarity
dennisvang May 15, 2026
dea739f
replace requestHeadersUpdater by lambda
dennisvang May 15, 2026
9b0b1ba
add sparql uri query parameters
dennisvang May 15, 2026
d7220c0
rudimentary test for sparql GET (wip)
dennisvang May 15, 2026
7dd51bb
make getSparqlUnauthenticated test conditional and evaluate response
dennisvang May 18, 2026
09cb6dd
require authentication for proxySparqlEndpoint
dennisvang May 18, 2026
260a155
replace old sparqlPost endpoint by new proxySparqlEndpoint
dennisvang May 18, 2026
7f638c0
replace custom NoneNestedConditions by a ConditionalOnExpression for …
dennisvang May 19, 2026
fbd6768
replace determineSparqlEndpointUrl by RepositoryProperties.getUrl
dennisvang May 19, 2026
7fb1a0f
remove condition from SearchSparqlController and return 404 instead
dennisvang May 19, 2026
5074de1
dry path in sparql controller test
dennisvang May 19, 2026
fc7f981
Merge branch 'support/1.19.x' into feature/896-sparql-protocol
dennisvang May 19, 2026
42ade5c
Merge branch 'support/1.19.x' into feature/896-sparql-protocol
dennisvang May 20, 2026
550759f
Merge branch 'support/1.19.x' into feature/896-sparql-protocol
dennisvang May 20, 2026
a33c65b
Merge branch 'support/1.19.x' into feature/896-sparql-protocol
dennisvang May 20, 2026
a746354
Merge branch 'support/1.19.x' into feature/896-sparql-protocol
dennisvang May 20, 2026
469cb32
start adapting sparql controller tests (wip)
dennisvang May 20, 2026
1fd0637
mark sparql proxy endpoint as experimental in api docs
dennisvang May 22, 2026
5c999ba
remove authorization header from sparql proxy request
dennisvang May 22, 2026
42fdddc
rename TestSearchSparqlController to SearchSparqlControllerTest
dennisvang May 22, 2026
fd21d96
rename TestSearchSparqlController to SearchSparqlControllerTest
dennisvang May 22, 2026
e65c2db
remove unused imports from sparql controller test
dennisvang May 22, 2026
2889b04
use mock user for sparql controller tests
dennisvang May 22, 2026
9109b4e
rename sparql controller test methods
dennisvang May 22, 2026
b06c807
switch from webmvctest to springboottest
dennisvang May 22, 2026
adf3a38
annotate HttpClientConfig with Profiles.NON_TESTING
dennisvang May 26, 2026
5b8b0fc
configure MockRestServiceServer for sparql tests (wip)
dennisvang May 26, 2026
5d5eba2
Merge branch 'support/1.19.x' into feature/896-sparql-protocol
dennisvang May 27, 2026
3f563ea
revert adding Profiiles.NON_TESTING to HttpClientConfig
dennisvang May 27, 2026
51a0699
mock RestTemplate in SearchSparqlControllerTest and remove test confi…
dennisvang May 27, 2026
7e92ffd
create RestClient bean using builder
dennisvang May 27, 2026
9236edd
fix mock sparql server expected url
dennisvang May 28, 2026
95245e3
rename mockRemoteSparqlServer to mockBackendSparqlServer
dennisvang May 28, 2026
9f5f718
put hop-by-hop headers into class constant with public getter
dennisvang May 28, 2026
e641a72
add request expectations for sparql proxy test
dennisvang May 28, 2026
d67690d
add mock response headers and response expectations for sparql proxy …
dennisvang May 28, 2026
c9c0990
rename sparql proxy test method
dennisvang May 28, 2026
9bc7a8c
test that proxy returns the json results body from a sparql query
dennisvang May 28, 2026
efe0e91
make sparql proxy controller optional (opt-in via intance.sparqlProxy…
dennisvang May 28, 2026
9134b18
disable checkstyle ExplicitInitialization
dennisvang May 28, 2026
7df4794
make sparqlEndpointUrl check reusable
dennisvang May 29, 2026
ffc2b7f
make request header cleanup reusable
dennisvang May 29, 2026
df84009
docstring for returnIfSparqlEndpointUnavailable
dennisvang May 29, 2026
ea592a2
rename sparql controller get method and corresponding test
dennisvang May 29, 2026
ac4dc44
make response cleanup exchange function reusable
dennisvang May 29, 2026
2106ffc
rename cleanRequestHeaders to cleanRequestHeadersFactory
dennisvang May 29, 2026
61676aa
explicitly expect GET request and verify that mock server received ex…
dennisvang May 29, 2026
11057a3
newlines in test
dennisvang May 29, 2026
a397d93
use constants for sparql protocol parameter names
dennisvang May 29, 2026
a8fa6bc
move mockJsonBody from tests method to class level
dennisvang May 29, 2026
44a3a40
add sparql proxy POST mapping for form data (wip)
dennisvang May 29, 2026
1c97685
get sparqlEndpointUrl from Repository instead of RepositoryProperties
dennisvang Jun 1, 2026
0d002a0
rename returnIfSparqlEndpointUnavailable to abortIf...
dennisvang Jun 1, 2026
ee1ea20
make MultiValueMap mutable
dennisvang Jun 1, 2026
0b4ff9c
add example sparql query to api-docs
dennisvang Jun 1, 2026
270b1a0
use ReflectionTestUtils to override sparqlEndpointUrl in tests
dennisvang Jun 1, 2026
cc9263d
fix MultiValueMap mutability
dennisvang Jun 1, 2026
9750956
fix mock server content type expectation for sparql form post
dennisvang Jun 1, 2026
67d9e38
add all possible sparql query response types to controller methods
dennisvang Jun 1, 2026
842677e
Revert "add all possible sparql query response types to controller me…
dennisvang Jun 1, 2026
6cae110
add sparl proxy post method for raw sparql (wip)
dennisvang Jun 1, 2026
69e6faf
add test for sparql proxy post raw query
dennisvang Jun 1, 2026
83f3aae
update proxy sparl method comments
dennisvang Jun 2, 2026
4f8e367
Merge branch 'support/1.19.x' into feature/896-sparql-protocol
dennisvang Jun 2, 2026
1da864f
fix hashmap type
dennisvang Jun 2, 2026
d7e5eb5
replace CollectionUtils by MultiValueMap in tests
dennisvang Jun 2, 2026
0ff25f8
abort sparql proxy post request if it contains an update field
dennisvang Jun 2, 2026
f0d0f90
introduce constants for sparql-query and sparql-update media types
dennisvang Jun 2, 2026
397de08
abort raw post request if content-type is sparql-update
dennisvang Jun 2, 2026
b11afe8
rename sparql post test methods and reuse sparql-query media type
dennisvang Jun 2, 2026
f9b8da1
remove debug log message
dennisvang Jun 2, 2026
19f8081
Revert "abort raw post request if content-type is sparql-update"
dennisvang Jun 2, 2026
77d9d43
test that sparql-update content-type is denied
dennisvang Jun 2, 2026
2ebd8b3
throw ResponseStatusException instead of returning ResponseEntity
dennisvang Jun 2, 2026
d50d48f
use PARAM_QUERY in test
dennisvang Jun 2, 2026
aeadc40
introduce HEADER_X_FORWARDED_FOR constant
dennisvang Jun 2, 2026
34cc5a0
add sparql query validation to sparql proxy endpoint
dennisvang Jun 2, 2026
2c3c423
bump package version from 1.19.1 to 1.20.0
dennisvang Jun 2, 2026
9100ff5
rename sparql test methods for consistency
dennisvang Jun 3, 2026
e66512b
move sparql query validation into separate SparqlQueryValidator class
dennisvang Jun 3, 2026
0290aeb
add tests for SparqlQueryValidator
dennisvang Jun 3, 2026
515aede
move sparql proxy cleaning methods into separate SparqlProxyHeaderCle…
dennisvang Jun 3, 2026
af33e7c
rename SearchSparqlController to SparqlProxyController
dennisvang Jun 3, 2026
25fdc19
rename SearchSparqlController to SparqlProxyController
dennisvang Jun 3, 2026
04aad86
rename SparqlProxyHeaderCleaner to SparqlProxyCleaningService
dennisvang Jun 3, 2026
efd61e6
clean up docstrings for SparqlProxyController
dennisvang Jun 4, 2026
af688e9
switch method signature from ConvertibleClientHttpResponse to ClientH…
dennisvang Jun 4, 2026
4c4541e
clean up SparqlProxyControllerTest and move header stuff into SparqlP…
dennisvang Jun 4, 2026
aeb3b3b
remove unnecessary UriComponentsBuilder use from SparqProxyController…
dennisvang Jun 4, 2026
f361662
add URL validation for SparqlProxyController graph-uri parameters
dennisvang Jun 4, 2026
c96a06e
note about @ValidIri
dennisvang Jun 4, 2026
c56e71a
Merge branch 'master' into feature/896-sparql-protocol
dennisvang Jun 10, 2026
ab93cd9
clean up debug leftovers
dennisvang Jun 12, 2026
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
4 changes: 2 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ For older versions, we highly recommend upgrading to the latest version.

| Version | Supported |
|---------| ------------------ |
| 1.19.x | :white_check_mark: |
| < 1.19 | :x: |
| 1.20.x | :white_check_mark: |
| < 1.20 | :x: |

## Current Recommendations

Expand Down
2 changes: 1 addition & 1 deletion checkstyle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@
<module name="EmptyStatement"/>
<module name="EqualsAvoidNull"/>
<module name="EqualsHashCode"/>
<module name="ExplicitInitialization"/>
<!-- <module name="ExplicitInitialization"/>-->
<module name="FallThrough"/>
<module name="FinalLocalVariable"/>
<module name="HiddenField">
Expand Down
16 changes: 1 addition & 15 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</parent>
<groupId>org.fairdatateam</groupId>
<artifactId>fairdatapoint</artifactId>
<version>1.19.1</version>
<version>1.20.0</version>
<packaging>jar</packaging>

<name>FairDataPoint</name>
Expand Down Expand Up @@ -214,20 +214,6 @@
<artifactId>rdf4j-sail-nativerdf</artifactId>
<version>${rdf4j-runtime.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.rdf4j</groupId>
<artifactId>rdf4j-spring-boot-sparql-web</artifactId>
<version>${rdf4j-runtime.version}</version>
<exclusions>
<!-- Exclude all transitive dependencies:
They cause unwanted side effects, such as spring data rest exposing all repository methods as
rest endpoints. Moreover, we only need the class definitions. -->
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* The MIT License
* Copyright © 2017 FAIR Data Team
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.fairdatateam.fairdatapoint.api.controller.search;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;
import java.util.function.Consumer;

/**
* Provides methods for cleaning request and response headers in a SPARQL proxy endpoint
*/
@Service
public class SparqlProxyCleaningService {

public static final String HEADER_X_FORWARDED_FOR = "X-Forwarded-For";

// standard hop-by-hop headers mentioned in rfc2616
private static final List<String> HOP_BY_HOP_HEADERS = List.of(
"Keep-Alive",
HttpHeaders.PROXY_AUTHENTICATE,
HttpHeaders.PROXY_AUTHORIZATION,
HttpHeaders.TE,
HttpHeaders.TRAILER,
HttpHeaders.TRANSFER_ENCODING,
HttpHeaders.UPGRADE
);

@Value("${openapi.title} ${openapi.version}")
private String headerServer;

/**
* Getter for hop-by-hop headers which need to be removed by a proxy.
*/
public static List<String> getHopByHopHeaders() {
return HOP_BY_HOP_HEADERS;
}

/**
* Returns a function that updates an HttpHeaders object based on the headers from a request.
* To be used as input for <code>RestClient.*.headers()</code> methods.
*/
Consumer<HttpHeaders> cleanRequestHeadersFactory(HttpServletRequest request, HttpHeaders requestHeaders) {
// Design note: It seems redundant to have both request and requestHeaders arguments because the headers are
// already available in the request. However, HttpServletRequest does not provide a simple way to get an
// HttpHeaders object, whereas the controller does provide one with @RequestHeader. Still, we do also need the
// HttpServletRequest to get the remote IP address.
return restRequestHeaders -> {
// copy all headers
restRequestHeaders.putAll(requestHeaders);
// remove any authorization to prevent privilege escalation attempts
restRequestHeaders.remove(HttpHeaders.AUTHORIZATION);
// forward the client ip (otherwise the upstream only sees the proxy ip)
restRequestHeaders.add(HEADER_X_FORWARDED_FOR, request.getRemoteAddr());
};
}

/**
* Extracts headers from response and removes the ones that should not be forwarded,
* such as hop-by-hop headers. These must be listed in the Connection header (see rfc9110 7.6.1).
*/
private HttpHeaders cleanResponseHeaders(ClientHttpResponse response) {
// copy all headers from the response
final HttpHeaders headers = new HttpHeaders();
headers.putAll(response.getHeaders());
// remove all headers listed in the "Connection" header
headers.getConnection().forEach(headers::remove);
headers.remove(HttpHeaders.CONNECTION);
// explicitly remove hop-by-hop headers (although Connection should list them too)
HOP_BY_HOP_HEADERS.forEach(headers::remove);
// rewrite the server header to hide the type of backend triple store
headers.set(HttpHeaders.SERVER, headerServer);
return headers;
}

/**
* Modifies the response from RestClient before returning it from the controller.
* To be used as input for <code>RestClient.*.exchange()</code> calls.
*/
ResponseEntity<byte[]> cleanResponse(
HttpRequest restRequest, ClientHttpResponse restResponse
) throws IOException {
return ResponseEntity
.status(restResponse.getStatusCode())
.headers(cleanResponseHeaders(restResponse))
.body(restResponse.getBody().readAllBytes());
}

}
Loading
Loading