Skip to content

Commit afb6810

Browse files
namedgraphclaude
andcommitted
Replace proxy-detection heuristics with ac:uri() / $ac:uri throughout
- ApplicationFilter: store external URI as AC.uri context property; strip ?uri= from UriInfo - ProxyRequestFilter: read proxy target from AC.uri context property; bypass HTML requests - XsltExecutableFilter: remove SYSTEM_ID_PROPERTY; XSLTWriterBase reads AC.uri directly - XSLTWriterBase: pass $ac:uri to server-side XSLT when proxying - layout.xsl: declare $ac:uri param; use it for export links and search input pre-fill - document.xsl: remove proxy spinner branch from bs2:ContentBody - client/functions.xsl: add ac:uri() function (dynamic read of ixsl:query-params()?uri); ldh:base-uri() now calls ac:uri() instead of stale global $ac:uri - client.xsl: drop global $ac:uri param; ldh:HTMLDocumentLoaded passes ldh:base-uri(.) to ldh:RDFDocumentLoad after pushState so URL is already updated - ProxyRequestFilterTest: update mocks to use AC.uri context property Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c11a0d1 commit afb6810

File tree

9 files changed

+101
-136
lines changed

9 files changed

+101
-136
lines changed

src/main/java/com/atomgraph/linkeddatahub/server/filter/request/ApplicationFilter.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,19 @@ public void filter(ContainerRequestContext request) throws IOException
110110
else
111111
{
112112
request.setProperty(AC.uri.getURI(), graphURI); // authoritative external proxy marker
113-
requestURI = request.getUriInfo().getRequestUri();
113+
114+
// strip ?uri= from the effective request URI — server-side sees only the path;
115+
// the ContainerRequestContext property is the sole indicator of proxy mode
116+
MultivaluedMap<String, String> externalQueryParams = new MultivaluedHashMap();
117+
externalQueryParams.putAll(request.getUriInfo().getQueryParameters());
118+
externalQueryParams.remove(AC.uri.getLocalName());
119+
120+
UriBuilder externalBuilder = UriBuilder.fromUri(request.getUriInfo().getAbsolutePath());
121+
for (Entry<String, List<String>> params : externalQueryParams.entrySet())
122+
for (String value : params.getValue())
123+
externalBuilder.queryParam(params.getKey(), value);
124+
125+
requestURI = externalBuilder.build();
114126
}
115127
}
116128
catch (URISyntaxException ex)

src/main/java/com/atomgraph/linkeddatahub/server/filter/request/ProxyRequestFilter.java

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -245,19 +245,9 @@ else if (agentContext instanceof IDTokenSecurityContext idTokenSecurityContext)
245245
*/
246246
protected Optional<URI> resolveTargetURI(ContainerRequestContext requestContext)
247247
{
248-
// Case 1: explicit ?uri= query parameter
249-
String uriParam = requestContext.getUriInfo().getQueryParameters().getFirst(AC.uri.getLocalName());
250-
if (uriParam != null)
251-
{
252-
URI targetURI = URI.create(uriParam);
253-
@SuppressWarnings("unchecked")
254-
Optional<com.atomgraph.linkeddatahub.apps.model.Application> appOpt =
255-
(Optional<com.atomgraph.linkeddatahub.apps.model.Application>) requestContext.getProperty(LAPP.Application.getURI());
256-
// ApplicationFilter rewrites ?uri= values that are relative to the app base URI; skip those
257-
if (appOpt != null && appOpt.isPresent() && !appOpt.get().getBaseURI().relativize(targetURI).isAbsolute())
258-
return Optional.empty();
259-
return Optional.of(targetURI);
260-
}
248+
// Case 1: external ?uri= — ApplicationFilter strips it from UriInfo and stores it here
249+
URI proxyTarget = (URI) requestContext.getProperty(AC.uri.getURI());
250+
if (proxyTarget != null) return Optional.of(proxyTarget);
261251

262252
// Case 2: lapp:Dataset proxy
263253
@SuppressWarnings("unchecked")

src/main/java/com/atomgraph/linkeddatahub/server/filter/response/XsltExecutableFilter.java

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818

1919
import com.atomgraph.client.vocabulary.AC;
2020
import com.atomgraph.linkeddatahub.MediaType;
21-
import static com.atomgraph.linkeddatahub.writer.XSLTWriterBase.SYSTEM_ID_PROPERTY;
22-
import static com.atomgraph.server.status.UnprocessableEntityStatus.UNPROCESSABLE_ENTITY;
2321
import java.io.ByteArrayInputStream;
2422
import java.io.IOException;
2523
import java.io.InputStream;
@@ -38,7 +36,6 @@
3836
import jakarta.ws.rs.core.Context;
3937
import jakarta.ws.rs.core.Response;
4038
import jakarta.ws.rs.core.UriInfo;
41-
import java.net.URISyntaxException;
4239
import java.util.Optional;
4340
import javax.xml.transform.Source;
4441
import javax.xml.transform.stream.StreamSource;
@@ -79,24 +76,6 @@ public void filter(ContainerRequestContext req, ContainerResponseContext resp) t
7976
if (stylesheet != null) req.setProperty(AC.stylesheet.getURI(), getXsltExecutable(stylesheet));
8077
else req.setProperty(AC.stylesheet.getURI(), getSystem().getXsltExecutable());
8178

82-
// systemId (base URI) is only set on successful documents or '422 Unprocessable Entity' (ConstraintViolation) error responses
83-
if (resp.getStatusInfo().getFamily().equals(Response.Status.Family.SUCCESSFUL) ||
84-
resp.getStatusInfo().getStatusCode() == UNPROCESSABLE_ENTITY.getStatusCode())
85-
{
86-
final URI systemId;
87-
88-
try
89-
{
90-
if (getURI() != null) systemId = getURI();
91-
else systemId = req.getUriInfo().getRequestUri();
92-
93-
req.setProperty(SYSTEM_ID_PROPERTY, systemId);
94-
}
95-
catch (URISyntaxException ex)
96-
{
97-
throw new InternalServerErrorException(ex);
98-
}
99-
}
10079
}
10180
}
10281

@@ -203,36 +182,6 @@ public Source getSource(String url) throws IOException
203182
return null;
204183
}
205184

206-
/**
207-
* Gets the URI parameter from the request.
208-
*
209-
* @return the URI parameter
210-
* @throws URISyntaxException if the URI is malformed
211-
*/
212-
public URI getURI() throws URISyntaxException
213-
{
214-
return getURIParam(getUriInfo(), AC.uri.getLocalName());
215-
}
216-
217-
/**
218-
* Gets a URI parameter from the provided UriInfo.
219-
*
220-
* @param uriInfo the URI information
221-
* @param name the parameter name
222-
* @return the URI parameter value
223-
* @throws URISyntaxException if the URI is malformed
224-
*/
225-
public URI getURIParam(UriInfo uriInfo, String name) throws URISyntaxException
226-
{
227-
if (uriInfo == null) throw new IllegalArgumentException("UriInfo cannot be null");
228-
if (name == null) throw new IllegalArgumentException("String cannot be null");
229-
230-
if (uriInfo.getQueryParameters().containsKey(name))
231-
return new URI(uriInfo.getQueryParameters().getFirst(name));
232-
233-
return null;
234-
}
235-
236185
/**
237186
* Returns HTTP client.
238187
*

src/main/java/com/atomgraph/linkeddatahub/writer/XSLTWriterBase.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,6 @@ public abstract class XSLTWriterBase extends com.atomgraph.client.writer.XSLTWri
7676
private static final Set<String> NAMESPACES;
7777
/** The relative URL of the RDF file with localized labels */
7878
public static final String TRANSLATIONS_PATH = "static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/translations.rdf";
79-
/** System property name for the XSLT system ID. */
80-
public static final String SYSTEM_ID_PROPERTY = "com.atomgraph.linkeddatahub.writer.XSLTWriterBase.systemId";
8179

8280
static
8381
{
@@ -135,7 +133,10 @@ public <T extends XdmValue> Map<QName, XdmValue> getParameters(MultivaluedMap<St
135133

136134
URI endpointURI = getLinkURI(headerMap, SD.endpoint);
137135
if (endpointURI != null) params.put(new QName("sd", SD.endpoint.getNameSpace(), SD.endpoint.getLocalName()), new XdmAtomicValue(endpointURI));
138-
136+
137+
URI proxyTargetURI = (URI) getContainerRequestContext().getProperty(AC.uri.getURI());
138+
if (proxyTargetURI != null) params.put(new QName("ac", AC.uri.getNameSpace(), AC.uri.getLocalName()), new XdmAtomicValue(proxyTargetURI));
139+
139140
String forShapeURI = getUriInfo().getQueryParameters().getFirst(LDH.forShape.getLocalName());
140141
if (forShapeURI != null) params.put(new QName("ldh", LDH.forShape.getNameSpace(), LDH.forShape.getLocalName()), new XdmAtomicValue(URI.create(forShapeURI)));
141142

@@ -249,10 +250,11 @@ public StreamSource getSource(Model model) throws IOException
249250
@Override
250251
public String getSystemId()
251252
{
252-
if (getContainerRequestContext().hasProperty(SYSTEM_ID_PROPERTY))
253-
return getContainerRequestContext().getProperty(SYSTEM_ID_PROPERTY).toString();
254-
255-
return null;
253+
// for proxy requests, use the external URI as the XSLT document base URI
254+
URI proxyTarget = (URI) getContainerRequestContext().getProperty(AC.uri.getURI());
255+
if (proxyTarget != null) return proxyTarget.toString();
256+
257+
return getContainerRequestContext().getUriInfo().getRequestUri().toString();
256258
}
257259

258260
/**

src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/functions.xsl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,16 @@ exclude-result-prefixes="#all"
4141
<xsl:sequence select="xs:anyURI(ixsl:location())"/>
4242
</xsl:function>
4343

44+
<xsl:function name="ac:uri" as="xs:anyURI?">
45+
<xsl:sequence select="if (ixsl:query-params()?uri) then xs:anyURI(ixsl:query-params()?uri) else ()"/>
46+
</xsl:function>
47+
4448
<xsl:function name="ldh:base-uri" as="xs:anyURI">
4549
<xsl:param name="arg" as="node()"/> <!-- ignored -->
4650

4751
<xsl:choose>
48-
<xsl:when test="ixsl:query-params()?uri">
49-
<xsl:sequence select="ac:document-uri(ixsl:query-params()?uri)"/>
52+
<xsl:when test="ac:uri()">
53+
<xsl:sequence select="ac:document-uri(ac:uri())"/>
5054
</xsl:when>
5155
<xsl:otherwise>
5256
<!-- ignore query params such as ?mode -->

src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/document.xsl

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -182,13 +182,31 @@ extension-element-prefixes="ixsl"
182182

183183
<!-- CONTENT BODY -->
184184

185+
<xsl:template match="rdf:RDF[exists($ldh:requestUri) and key('resources-by-type', '&http;Response') and not(key('resources-by-type', '&spin;ConstraintViolation')) and not(key('resources-by-type', '&sh;ValidationResult'))]" mode="bs2:ContentBody" priority="1">
186+
<xsl:param name="id" select="'content-body'" as="xs:string?"/>
187+
<xsl:param name="class" select="'container-fluid'" as="xs:string?"/>
188+
189+
<div>
190+
<xsl:if test="$id">
191+
<xsl:attribute name="id" select="$id"/>
192+
</xsl:if>
193+
<xsl:if test="$class">
194+
<xsl:attribute name="class" select="$class"/>
195+
</xsl:if>
196+
197+
<!-- error responses always rendered in bs2:Row mode, no matter what $mode specifies -->
198+
<xsl:apply-templates select="." mode="bs2:Row">
199+
<xsl:sort select="ac:label(.)"/>
200+
</xsl:apply-templates>
201+
</div>
202+
</xsl:template>
203+
185204
<xsl:template match="rdf:RDF" mode="bs2:ContentBody">
186205
<xsl:param name="id" select="'content-body'" as="xs:string?"/>
187206
<xsl:param name="class" select="'container-fluid'" as="xs:string?"/>
188207
<xsl:param name="about" select="ac:absolute-path(ldh:base-uri(.))" as="xs:anyURI?"/>
189208
<xsl:param name="typeof" select="key('resources', ac:absolute-path(ldh:base-uri(.)))/rdf:type/@rdf:resource/xs:anyURI(.)" as="xs:anyURI*"/>
190209
<xsl:param name="mode" as="xs:anyURI"/>
191-
<xsl:param name="ldh:requestUri" select="$ldh:requestUri" as="xs:anyURI?"/>
192210

193211
<div>
194212
<xsl:if test="$id">
@@ -205,48 +223,30 @@ extension-element-prefixes="ixsl"
205223
</xsl:if>
206224

207225
<xsl:choose>
208-
<!-- error responses always rendered in bs2:Row mode, no matter what $mode specifies -->
209-
<xsl:when test="exists($ldh:requestUri) and key('resources-by-type', '&http;Response') and not(key('resources-by-type', '&spin;ConstraintViolation')) and not(key('resources-by-type', '&sh;ValidationResult'))">
210-
<xsl:apply-templates select="." mode="bs2:Row">
226+
<xsl:when test="$mode = '&ldh;ContentMode'">
227+
<xsl:apply-templates select="." mode="ldh:ContentList"/>
228+
</xsl:when>
229+
<xsl:when test="$mode = '&ac;MapMode'">
230+
<xsl:apply-templates select="." mode="bs2:Map">
231+
<xsl:with-param name="id" select="generate-id() || '-map-canvas'"/>
211232
<xsl:sort select="ac:label(.)"/>
212233
</xsl:apply-templates>
213234
</xsl:when>
214-
<!-- the request is proxied using ?uri, render it client-side in client.xsl -->
215-
<xsl:when test="exists($ldh:requestUri) and not(ldh:base-uri(.) = $ldh:requestUri)">
216-
<div class="row-fluid">
217-
<div class="span12 progress progress-striped active">
218-
<div style="width: 33%;" class="bar"></div>
219-
</div>
220-
</div>
235+
<xsl:when test="$mode = '&ac;ChartMode'">
236+
<xsl:apply-templates select="." mode="bs2:Chart">
237+
<xsl:with-param name="canvas-id" select="generate-id() || '-chart-canvas'"/>
238+
<xsl:with-param name="show-save" select="false()"/>
239+
<xsl:sort select="ac:label(.)"/>
240+
</xsl:apply-templates>
241+
</xsl:when>
242+
<xsl:when test="$mode = '&ac;GraphMode'">
243+
<xsl:variable name="canvas-id" select="generate-id() || '-graph-canvas'" as="xs:string"/>
244+
<div id="{$canvas-id}" class="graph-3d-canvas"/>
221245
</xsl:when>
222246
<xsl:otherwise>
223-
<xsl:choose>
224-
<xsl:when test="$mode = '&ldh;ContentMode'">
225-
<xsl:apply-templates select="." mode="ldh:ContentList"/>
226-
</xsl:when>
227-
<xsl:when test="$mode = '&ac;MapMode'">
228-
<xsl:apply-templates select="." mode="bs2:Map">
229-
<xsl:with-param name="id" select="generate-id() || '-map-canvas'"/>
230-
<xsl:sort select="ac:label(.)"/>
231-
</xsl:apply-templates>
232-
</xsl:when>
233-
<xsl:when test="$mode = '&ac;ChartMode'">
234-
<xsl:apply-templates select="." mode="bs2:Chart">
235-
<xsl:with-param name="canvas-id" select="generate-id() || '-chart-canvas'"/>
236-
<xsl:with-param name="show-save" select="false()"/>
237-
<xsl:sort select="ac:label(.)"/>
238-
</xsl:apply-templates>
239-
</xsl:when>
240-
<xsl:when test="$mode = '&ac;GraphMode'">
241-
<xsl:variable name="canvas-id" select="generate-id() || '-graph-canvas'" as="xs:string"/>
242-
<div id="{$canvas-id}" class="graph-3d-canvas"/>
243-
</xsl:when>
244-
<xsl:otherwise>
245-
<xsl:apply-templates select="." mode="bs2:Row">
246-
<xsl:sort select="ac:label(.)"/>
247-
</xsl:apply-templates>
248-
</xsl:otherwise>
249-
</xsl:choose>
247+
<xsl:apply-templates select="." mode="bs2:Row">
248+
<xsl:sort select="ac:label(.)"/>
249+
</xsl:apply-templates>
250250
</xsl:otherwise>
251251
</xsl:choose>
252252
</div>

src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/layout.xsl

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ exclude-result-prefixes="#all">
102102

103103
<xsl:param name="lapp:origin" as="xs:anyURI?"/>
104104
<xsl:param name="ldh:requestUri" as="xs:anyURI"/>
105+
<xsl:param name="ac:uri" as="xs:anyURI?"/>
105106
<xsl:param name="ac:endpoint" select="if ($ldt:base) then resolve-uri('sparql', $ldt:base) else ()" as="xs:anyURI?"/>
106107
<xsl:param name="sd:endpoint" as="xs:anyURI?"/>
107108
<xsl:param name="acl:agent" as="xs:anyURI?"/>
@@ -597,7 +598,7 @@ exclude-result-prefixes="#all">
597598
</select>
598599

599600
<input type="text" id="uri" name="uri" class="input-xxlarge typeahead">
600-
<xsl:if test="not(ac:absolute-path(ldh:base-uri(.)) = ac:absolute-path(ldh:request-uri()))">
601+
<xsl:if test="$ac:uri">
601602
<xsl:attribute name="value" select="ldh:base-uri(.)"/>
602603
</xsl:if>
603604
</input>
@@ -883,9 +884,24 @@ exclude-result-prefixes="#all">
883884
<div id="visible-body">
884885
<xsl:apply-templates select="." mode="bs2:NavBar"/>
885886

886-
<xsl:apply-templates select="." mode="bs2:ContentBody">
887-
<xsl:with-param name="mode" select="$ac:mode"/>
888-
</xsl:apply-templates>
887+
<xsl:choose>
888+
<!-- the request is proxied using ?uri, render it client-side in client.xsl -->
889+
<!-- ldh:rdf-document-response bypasses this branch by passing ac:uri=() as a local override -->
890+
<xsl:when test="$ac:uri">
891+
<div id="content-body" class="container-fluid">
892+
<div class="row-fluid">
893+
<div class="span12 progress progress-striped active">
894+
<div style="width: 33%;" class="bar"></div>
895+
</div>
896+
</div>
897+
</div>
898+
</xsl:when>
899+
<xsl:otherwise>
900+
<xsl:apply-templates select="." mode="bs2:ContentBody">
901+
<xsl:with-param name="mode" select="$ac:mode"/>
902+
</xsl:apply-templates>
903+
</xsl:otherwise>
904+
</xsl:choose>
889905

890906
<xsl:apply-templates select="." mode="bs2:Footer"/>
891907
</div>
@@ -1075,15 +1091,15 @@ exclude-result-prefixes="#all">
10751091
</button>
10761092
<ul class="dropdown-menu">
10771093
<li>
1078-
<xsl:variable name="href" select="ac:build-uri(ac:absolute-path(ldh:request-uri()), let $params := map{ 'accept': 'application/rdf+xml' } return if (not(starts-with(ac:absolute-path(ldh:base-uri(.)), $ldt:base))) then map:merge(($params, map{ 'uri': string($uri) })) else $params)" as="xs:anyURI"/>
1094+
<xsl:variable name="href" select="ac:build-uri(ac:absolute-path(ldh:request-uri()), let $params := map{ 'accept': 'application/rdf+xml' } return if ($ac:uri) then map:merge(($params, map{ 'uri': string($uri) })) else $params)" as="xs:anyURI"/>
10791095
<a href="{$href}" title="application/rdf+xml" target="_blank">RDF/XML</a>
10801096
</li>
10811097
<li>
1082-
<xsl:variable name="href" select="ac:build-uri(ac:absolute-path(ldh:request-uri()), let $params := map{ 'accept': 'text/turtle' } return if (not(starts-with(ac:absolute-path(ldh:base-uri(.)), $ldt:base))) then map:merge(($params, map{ 'uri': string($uri) })) else $params)" as="xs:anyURI"/>
1098+
<xsl:variable name="href" select="ac:build-uri(ac:absolute-path(ldh:request-uri()), let $params := map{ 'accept': 'text/turtle' } return if ($ac:uri) then map:merge(($params, map{ 'uri': string($uri) })) else $params)" as="xs:anyURI"/>
10831099
<a href="{$href}" title="text/turtle" target="_blank">Turtle</a>
10841100
</li>
10851101
<li>
1086-
<xsl:variable name="href" select="ac:build-uri(ac:absolute-path(ldh:request-uri()), let $params := map{ 'accept': 'application/ld+json' } return if (not(starts-with(ac:absolute-path(ldh:base-uri(.)), $ldt:base))) then map:merge(($params, map{ 'uri': string($uri) })) else $params)" as="xs:anyURI"/>
1102+
<xsl:variable name="href" select="ac:build-uri(ac:absolute-path(ldh:request-uri()), let $params := map{ 'accept': 'application/ld+json' } return if ($ac:uri) then map:merge(($params, map{ 'uri': string($uri) })) else $params)" as="xs:anyURI"/>
10871103
<a href="{$href}" title="application/ld+json" target="_blank">JSON-LD</a>
10881104
</li>
10891105
</ul>

0 commit comments

Comments
 (0)