Skip to content

Commit 4d821b5

Browse files
committed
Separated documentTypeQuery from documentOwnerQuery
Some `try/finally` refactoring New HTTP tests for the `Access` endpoint
1 parent a7fbd12 commit 4d821b5

7 files changed

Lines changed: 191 additions & 60 deletions

File tree

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
initialize_dataset "$END_USER_BASE_URL" "$TMP_END_USER_DATASET" "$END_USER_ENDPOINT_URL"
5+
initialize_dataset "$ADMIN_BASE_URL" "$TMP_ADMIN_DATASET" "$ADMIN_ENDPOINT_URL"
6+
purge_cache "$END_USER_VARNISH_SERVICE"
7+
purge_cache "$ADMIN_VARNISH_SERVICE"
8+
purge_cache "$FRONTEND_VARNISH_SERVICE"
9+
10+
# add agent to the writers group
11+
12+
add-agent-to-group.sh \
13+
-f "$OWNER_CERT_FILE" \
14+
-p "$OWNER_CERT_PWD" \
15+
--agent "$AGENT_URI" \
16+
"${ADMIN_BASE_URL}acl/groups/writers/"
17+
18+
# create container
19+
20+
slug="test"
21+
22+
container=$(create-container.sh \
23+
-f "$AGENT_CERT_FILE" \
24+
-p "$AGENT_CERT_PWD" \
25+
-b "$END_USER_BASE_URL" \
26+
--title "Test" \
27+
--slug "$slug" \
28+
--parent "$END_USER_BASE_URL")
29+
30+
# check that the access metadata for the container URI inclused owner authorization for the agent
31+
32+
ntriples=$(curl -k -s -G \
33+
-E "$AGENT_CERT_FILE":"$AGENT_CERT_PWD" \
34+
-H "Accept: application/n-triples" \
35+
--data "this=${container}" \
36+
"${ADMIN_BASE_URL}access"
37+
)
38+
39+
auth1=$(cat "$ntriples" | grep "<http://www.w3.org/1999/02/22-rdf-syntax-ns#> <https://w3id.org/atomgraph/linkeddatahub/admin/acl#OwnerAuthorization>" | cut -d' ' -f1 | head -n1)
40+
auth2=$(cat "$ntriples" | grep "<http://www.w3.org/ns/auth/acl#agent> <${AGENT_URI}>" | cut -d' ' -f1 | head -n1)
41+
42+
# if the subjects of the two triples are different, the agent is not the owner of the container
43+
if [ "$auth1" != "$auth2" ]; then
44+
exit 1
45+
fi

http-tests/document-hierarchy/acl-owner-container.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ container=$(create-container.sh \
2727
--slug "$slug" \
2828
--parent "$END_USER_BASE_URL")
2929

30-
# check that the item was created at the expected URL and attached to the document hierarchy
30+
# check that the created container has the agent as owner
3131

3232
get.sh \
3333
-f "$AGENT_CERT_FILE" \

http-tests/document-hierarchy/acl-owner-item.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ item=$(create-item.sh \
2727
--slug "$slug" \
2828
--container "$END_USER_BASE_URL")
2929

30-
# check that the item was created at the expected URL and attached to the document hierarchy
30+
# check that the created item has the agent as owner
3131

3232
get.sh \
3333
-f "$AGENT_CERT_FILE" \

http-tests/document-hierarchy/create-container.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ container=$(create-container.sh \
2727
--slug "$slug" \
2828
--parent "$END_USER_BASE_URL")
2929

30-
# check that the item was created at the expected URL and attached to the document hierarchy
30+
# check that the container was created at the expected URL and attached to the document hierarchy
3131

3232
get.sh \
3333
-f "$AGENT_CERT_FILE" \

src/main/java/com/atomgraph/linkeddatahub/resource/acl/Access.java

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -119,22 +119,23 @@ public Response get(@QueryParam(QUERY) Query query,
119119
try
120120
{
121121
if (!getUriInfo().getQueryParameters().containsKey(SPIN.THIS_VAR_NAME)) throw new BadRequestException("?this query param is not provided");
122+
122123
Resource accessTo = ResourceFactory.createResource(new URI(getUriInfo().getQueryParameters().getFirst(SPIN.THIS_VAR_NAME)).toString()); // ?this query param needs to be passed
123124
if (log.isDebugEnabled()) log.debug("Loading current agent's authorizations for the <{}> document", accessTo);
124125

125-
QuerySolutionMap docTypeQsm = new QuerySolutionMap();
126-
docTypeQsm.add(SPIN.THIS_VAR_NAME, accessTo);
127-
ResultSetRewindable docTypes = loadResultSet(getEndUserService(), getDocumentTypeQuery(), docTypeQsm);
126+
QuerySolutionMap thisQsm = new QuerySolutionMap();
127+
thisQsm.add(SPIN.THIS_VAR_NAME, accessTo);
128+
ResultSetRewindable docTypesResult = loadResultSet(getEndUserService(), getDocumentTypeQuery(), thisQsm);
129+
128130
try
129131
{
130132
authPss.setParams(new AuthorizationParams(getApplication().getBase(), accessTo, agent).get());
131-
query = new SetResultSetValues().apply(authPss.asQuery(), docTypes);
133+
query = new SetResultSetValues().apply(authPss.asQuery(), docTypesResult);
132134
assert query.toString().contains("VALUES");
133-
docTypes.reset();
134135

135136
Model authModel = getEndpointAccessor().loadModel(query, defaultGraphUris, namedGraphUris);
136137
// special case where the agent is the owner of the requested document - automatically grant acl:Read/acl:Append/acl:Write access
137-
if (isOwner(docTypes, agent))
138+
if (isOwner(accessTo, agent))
138139
{
139140
log.debug("Agent <{}> is the owner of <{}>, granting acl:Read/acl:Append/acl:Write access", agent, accessTo);
140141
authModel.add(createOwnerAuthorization(accessTo, agent).getModel());
@@ -144,7 +145,7 @@ public Response get(@QueryParam(QUERY) Query query,
144145
}
145146
finally
146147
{
147-
docTypes.close();
148+
docTypesResult.close();
148149
}
149150
}
150151
catch (URISyntaxException ex)
@@ -188,21 +189,47 @@ protected ResultSetRewindable loadResultSet(com.atomgraph.linkeddatahub.model.Se
188189
/**
189190
* Checks if the given agent is the <code>acl:owner</code> of the document.
190191
*
191-
* @param docTypes The result set containing document metadata.
192-
* @param agent The agent whose ownership is checked.
192+
* @param accessTo the document URI
193+
* @param agent the agent whose ownership is checked.
193194
* @return true if the agent is the owner, false otherwise.
194195
*/
195-
protected boolean isOwner(ResultSetRewindable docTypes, Resource agent)
196+
protected boolean isOwner(Resource accessTo, Resource agent)
196197
{
198+
QuerySolutionMap qsm = new QuerySolutionMap();
199+
qsm.add(SPIN.THIS_VAR_NAME, accessTo);
200+
201+
ResultSetRewindable docOwnerResult = loadResultSet(getApplication().getService(), getDocumentOwnerQuery(), qsm); // could use ASK query in principle
202+
try
203+
{
204+
return isOwner(docOwnerResult, agent);
205+
}
206+
finally
207+
{
208+
docOwnerResult.close();
209+
}
210+
}
211+
212+
/**
213+
* Checks if the given agent is the <code>acl:owner</code> of the document.
214+
*
215+
* @param docOwnerResult the result set containing document metadata
216+
* @param agent the agent whose ownership is checked
217+
* @return true if the agent is the owner, false otherwise.
218+
*/
219+
protected boolean isOwner(ResultSetRewindable docOwnerResult, Resource agent)
220+
{
221+
if (docOwnerResult == null) throw new IllegalArgumentException("ResultSet cannot be null");
222+
if (agent == null) throw new IllegalArgumentException("Agent resource cannot be null");
223+
197224
Resource owner = null;
198225

199-
while (docTypes.hasNext())
226+
while (docOwnerResult.hasNext())
200227
{
201-
QuerySolution qs = docTypes.next();
228+
QuerySolution qs = docOwnerResult.next();
202229
if (owner == null && qs.contains("owner")) owner = qs.getResource("owner");
203230
}
204231

205-
docTypes.reset();
232+
docOwnerResult.reset();
206233

207234
return owner != null && owner.equals(agent);
208235
}
@@ -303,4 +330,15 @@ public ParameterizedSparqlString getDocumentTypeQuery()
303330
return documentTypeQuery.copy();
304331
}
305332

333+
public ParameterizedSparqlString getDocumentOwnerQuery()
334+
{
335+
return new ParameterizedSparqlString("PREFIX acl: <http://www.w3.org/ns/auth/acl#>\n" +
336+
"\n" +
337+
"SELECT ?owner\n" +
338+
"WHERE\n" +
339+
" { GRAPH $this\n" +
340+
" { $this acl:owner ?owner }\n" +
341+
" }");
342+
}
343+
306344
}

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

Lines changed: 78 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -166,53 +166,60 @@ public Resource authorize(ContainerRequestContext request, Resource agent, Resou
166166
{
167167
Resource accessTo = ResourceFactory.createResource(request.getUriInfo().getAbsolutePath().toString());
168168

169-
QuerySolutionMap docTypeQsm = new QuerySolutionMap();
170-
docTypeQsm.add(SPIN.THIS_VAR_NAME, accessTo);
171-
ResultSetRewindable docTypes = loadResultSet(getApplication().getService(), getDocumentTypeQuery(), docTypeQsm);
169+
// special case where the agent is the owner of the requested document - automatically grant acl:Read/acl:Append/acl:Write access
170+
if (agent != null && isOwner(accessTo, agent))
171+
{
172+
log.debug("Agent <{}> is the owner of <{}>, granting acl:Read/acl:Append/acl:Write access", agent, accessTo);
173+
return createOwnerAuthorization(accessTo, agent);
174+
}
175+
176+
QuerySolutionMap thisQsm = new QuerySolutionMap();
177+
thisQsm.add(SPIN.THIS_VAR_NAME, accessTo);
172178

179+
ResultSetRewindable docTypesResult = loadResultSet(getApplication().getService(), getDocumentTypeQuery(), thisQsm);
173180
try
174181
{
175-
// special case where the agent is the owner of the requested document - automatically grant acl:Read/acl:Append/acl:Write access
176-
if (isOwner(docTypes, agent))
177-
{
178-
log.debug("Agent <{}> is the owner of <{}>, granting acl:Read/acl:Append/acl:Write access", agent, accessTo);
179-
return createOwnerAuthorization(accessTo, agent);
180-
}
181-
182-
if (!docTypes.hasNext()) // if the document resource has no types, we assume the document does not exist
182+
if (!docTypesResult.hasNext()) // if the document resource has no types, we assume the document does not exist
183183
{
184184
// special case for PUT requests to non-existing document: allow if the agent has acl:Write acess to the *parent* URI
185185
if (request.getMethod().equals(HttpMethod.PUT) && accessMode.equals(ACL.Write))
186186
{
187187
URI parentURI = URI.create(accessTo.getURI()).resolve("..");
188188
log.debug("Requested document <{}> not found, falling back to parent URI <{}>", accessTo, parentURI);
189189
accessTo = ResourceFactory.createResource(parentURI.toString());
190-
191-
docTypeQsm = new QuerySolutionMap();
192-
docTypeQsm.add(SPIN.THIS_VAR_NAME, accessTo);
193-
docTypes.close();
194-
docTypes = loadResultSet(getApplication().getService(), getDocumentTypeQuery(), docTypeQsm);
195-
196-
Set<Resource> parentTypes = new HashSet<>();
197-
docTypes.forEachRemaining(qs -> parentTypes.add(qs.getResource("Type")));
198-
199-
// only root and containers allow child documents
200-
if (Collections.disjoint(parentTypes, Set.of(Default.Root, DH.Container))) return null;
201-
202-
docTypes.reset(); // rewind result set to the beginning
203-
190+
191+
thisQsm = new QuerySolutionMap();
192+
thisQsm.add(SPIN.THIS_VAR_NAME, accessTo);
193+
204194
// special case where the agent is the owner of the requested document - automatically grant acl:Read/acl:Append/acl:Write access
205-
if (isOwner(docTypes, agent))
195+
if (agent != null && isOwner(accessTo, agent))
206196
{
207197
log.debug("Agent <{}> is the owner of <{}>, granting acl:Read/acl:Append/acl:Write access", agent, accessTo);
208198
return createOwnerAuthorization(accessTo, agent);
209199
}
200+
201+
docTypesResult.close();
202+
docTypesResult = loadResultSet(getApplication().getService(), getDocumentTypeQuery(), thisQsm);
203+
try
204+
{
205+
Set<Resource> parentTypes = new HashSet<>();
206+
docTypesResult.forEachRemaining(qs -> parentTypes.add(qs.getResource("Type")));
207+
208+
// only root and containers allow child documents
209+
if (Collections.disjoint(parentTypes, Set.of(Default.Root, DH.Container))) return null;
210+
211+
docTypesResult.reset(); // rewind result set to the beginning
212+
}
213+
finally
214+
{
215+
docTypesResult.close();
216+
}
210217
}
211218
else return null;
212219
}
213220

214221
ParameterizedSparqlString pss = getApplication().canAs(EndUserApplication.class) ? getACLQuery() : getOwnerACLQuery();
215-
Query query = new SetResultSetValues().apply(pss.asQuery(), docTypes);
222+
Query query = new SetResultSetValues().apply(pss.asQuery(), docTypesResult);
216223
pss = new ParameterizedSparqlString(query.toString()); // make sure VALUES are now part of the query string
217224
assert pss.toString().contains("VALUES");
218225

@@ -221,7 +228,7 @@ public Resource authorize(ContainerRequestContext request, Resource agent, Resou
221228
}
222229
finally
223230
{
224-
docTypes.close();
231+
docTypesResult.close();
225232
}
226233
}
227234

@@ -248,21 +255,47 @@ public Resource getAuthorizationByMode(Model authModel, Resource accessMode)
248255
/**
249256
* Checks if the given agent is the <code>acl:owner</code> of the document.
250257
*
251-
* @param docTypes The result set containing document metadata.
252-
* @param agent The agent whose ownership is checked.
258+
* @param accessTo the document URI
259+
* @param agent the agent whose ownership is checked.
253260
* @return true if the agent is the owner, false otherwise.
254261
*/
255-
protected boolean isOwner(ResultSetRewindable docTypes, Resource agent)
262+
protected boolean isOwner(Resource accessTo, Resource agent)
256263
{
264+
QuerySolutionMap qsm = new QuerySolutionMap();
265+
qsm.add(SPIN.THIS_VAR_NAME, accessTo);
266+
267+
ResultSetRewindable docOwnerResult = loadResultSet(getApplication().getService(), getDocumentOwnerQuery(), qsm); // could use ASK query in principle
268+
try
269+
{
270+
return isOwner(docOwnerResult, agent);
271+
}
272+
finally
273+
{
274+
docOwnerResult.close();
275+
}
276+
}
277+
278+
/**
279+
* Checks if the given agent is the <code>acl:owner</code> of the document.
280+
*
281+
* @param docOwnerResult the result set containing document metadata
282+
* @param agent the agent whose ownership is checked
283+
* @return true if the agent is the owner, false otherwise.
284+
*/
285+
protected boolean isOwner(ResultSetRewindable docOwnerResult, Resource agent)
286+
{
287+
if (docOwnerResult == null) throw new IllegalArgumentException("ResultSet cannot be null");
288+
if (agent == null) throw new IllegalArgumentException("Agent resource cannot be null");
289+
257290
Resource owner = null;
258291

259-
while (docTypes.hasNext())
292+
while (docOwnerResult.hasNext())
260293
{
261-
QuerySolution qs = docTypes.next();
294+
QuerySolution qs = docOwnerResult.next();
262295
if (owner == null && qs.contains("owner")) owner = qs.getResource("owner");
263296
}
264297

265-
docTypes.reset();
298+
docOwnerResult.reset();
266299

267300
return owner != null && owner.equals(agent);
268301
}
@@ -363,6 +396,17 @@ public ParameterizedSparqlString getDocumentTypeQuery()
363396
return documentTypeQuery.copy();
364397
}
365398

399+
public ParameterizedSparqlString getDocumentOwnerQuery()
400+
{
401+
return new ParameterizedSparqlString("PREFIX acl: <http://www.w3.org/ns/auth/acl#>\n" +
402+
"\n" +
403+
"SELECT ?owner\n" +
404+
"WHERE\n" +
405+
" { GRAPH $this\n" +
406+
" { $this acl:owner $owner }\n" +
407+
" }");
408+
}
409+
366410
/**
367411
* Returns the SPARQL service for agent data.
368412
*

0 commit comments

Comments
 (0)