Skip to content
Permalink
Browse files

Prevent PUT/PATCH of resources that are not target of request

  • Loading branch information...
ajs6f authored and awoods committed Jul 13, 2015
1 parent 7141787 commit 6e755b2768b5a4e7496119efbcdddef1f009c163
@@ -16,13 +16,11 @@
package org.fcrepo.http.api;


import static com.google.common.base.Predicates.alwaysTrue;
import static com.google.common.base.Predicates.and;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.Iterators.concat;
import static com.google.common.collect.Iterators.filter;
import static com.google.common.collect.Iterators.transform;
import static com.hp.hpl.jena.rdf.model.ModelFactory.createDefaultModel;
import static com.hp.hpl.jena.vocabulary.RDF.type;
import static javax.ws.rs.core.HttpHeaders.CACHE_CONTROL;
import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM_TYPE;
import static javax.ws.rs.core.Response.ok;
@@ -41,13 +39,15 @@
import static org.fcrepo.kernel.RdfLexicon.INDIRECT_CONTAINER;
import static org.fcrepo.kernel.RdfLexicon.LDP_NAMESPACE;
import static org.fcrepo.kernel.RdfLexicon.isManagedNamespace;
import static org.fcrepo.kernel.impl.rdf.ManagedRdf.isManagedTriple;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Date;
import java.util.Iterator;
import java.util.function.Predicate;

import javax.inject.Inject;
import javax.jcr.AccessDeniedException;
@@ -74,7 +74,6 @@
import org.fcrepo.kernel.exception.InvalidChecksumException;
import org.fcrepo.kernel.exception.MalformedRdfException;
import org.fcrepo.kernel.exception.RepositoryRuntimeException;
import org.fcrepo.kernel.impl.rdf.ManagedRdf;
import org.fcrepo.kernel.impl.rdf.impl.AclRdfContext;
import org.fcrepo.kernel.impl.rdf.impl.BlankNodeRdfContext;
import org.fcrepo.kernel.impl.rdf.impl.ChildrenRdfContext;
@@ -101,21 +100,19 @@
import org.glassfish.jersey.media.multipart.ContentDisposition;
import org.jvnet.hk2.annotations.Optional;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
import com.hp.hpl.jena.graph.Triple;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.vocabulary.RDF;

/**
* An abstract class that sits between AbstractResource and any resource that
* wishes to share the routines for building responses containing binary
* content.
*
* @author Mike Durbin
* @author ajs6f
*/
public abstract class ContentExposingResource extends FedoraBaseResource {

@@ -139,6 +136,9 @@

private static final long MAX_BUFFER_SIZE = 10240000;

private static final Predicate<Triple> IS_MANAGED_TYPE = t -> t.getPredicate().equals(type.asNode()) &&
isManagedNamespace.apply(t.getObject().getNameSpace());

protected abstract String externalPath();

protected Response getContent(final String rangeValue,
@@ -158,14 +158,7 @@ protected Response getContent(final String rangeValue,
final Model inputModel = createDefaultModel()
.read(content, (resource()).toString(), format);

rdfStream.concat(Iterators.transform(inputModel.listStatements(),
new Function<Statement, Triple>() {

@Override
public Triple apply(final Statement input) {
return input.asTriple();
}
}));
rdfStream.concat(Iterators.transform(inputModel.listStatements(), Statement::asTriple));
} else {

final MediaType mediaType = MediaType.valueOf(contentTypeString);
@@ -213,24 +206,18 @@ protected RdfStream getResourceTriples() {

final Predicate<Triple> tripleFilter;
if (ldpPreferences.prefersServerManaged()) {
tripleFilter = alwaysTrue();
tripleFilter = x -> true;
} else {
tripleFilter = and(not(ManagedRdf.isManagedTriple), not(new Predicate<Triple>() {
@Override
public boolean apply(final Triple input) {
return input.getPredicate().equals(RDF.type.asNode())
&& isManagedNamespace.apply(input.getObject().getNameSpace());
}
}));
tripleFilter = IS_MANAGED_TYPE.or(isManagedTriple::apply).negate();
}

if (ldpPreferences.prefersServerManaged()) {
rdfStream.concat(getTriples(LdpRdfContext.class));
}

rdfStream.concat(filter(getTriples(TypeRdfContext.class), tripleFilter));
rdfStream.concat(filter(getTriples(TypeRdfContext.class), tripleFilter::test));

rdfStream.concat(filter(getTriples(PropertiesRdfContext.class), tripleFilter));
rdfStream.concat(filter(getTriples(PropertiesRdfContext.class), tripleFilter::test));

if (!returnPreference.getValue().equals("minimal")) {

@@ -258,15 +245,15 @@ public boolean apply(final Triple input) {
final FedoraResource described = ((NonRdfSourceDescription) resource()).getDescribedResource();
rdfStream.concat(filter(described.getTriples(translator(), ImmutableList.of(TypeRdfContext.class,
PropertiesRdfContext.class,
ContentRdfContext.class)), tripleFilter));
ContentRdfContext.class)), tripleFilter::test));
if (ldpPreferences.prefersServerManaged()) {
rdfStream.concat(getTriples(described,LdpRdfContext.class));
}
}

// Embed all hash and blank nodes
rdfStream.concat(filter(getTriples(HashRdfContext.class), tripleFilter));
rdfStream.concat(filter(getTriples(BlankNodeRdfContext.class), tripleFilter));
rdfStream.concat(filter(getTriples(HashRdfContext.class), tripleFilter::test));
rdfStream.concat(filter(getTriples(BlankNodeRdfContext.class), tripleFilter::test));

// Include inbound references to this object
if (ldpPreferences.prefersReferences()) {
@@ -278,17 +265,11 @@ public boolean apply(final Triple input) {

final Iterator<FedoraResource> children = resource().getChildren();

rdfStream.concat(filter(concat(transform(children,
new Function<FedoraResource, RdfStream>() {

@Override
public RdfStream apply(final FedoraResource child) {
return child.getTriples(translator(), ImmutableList.of(
TypeRdfContext.class,
PropertiesRdfContext.class,
BlankNodeRdfContext.class));
}
})), tripleFilter));
rdfStream.concat(filter(concat(transform(children, child ->
child.getTriples(translator(),
ImmutableList.of(
TypeRdfContext.class, PropertiesRdfContext.class, BlankNodeRdfContext.class)))),
tripleFilter::test));

}
}
@@ -602,10 +583,16 @@ protected void replaceResourceWithStream(final FedoraResource resource,
}

protected void patchResourcewithSparql(final FedoraResource resource,
final String requestBody,
final RdfStream resourceTriples)
throws MalformedRdfException, AccessDeniedException {
resource.updateProperties(translator(), requestBody, resourceTriples);
final String requestBody,
final RdfStream resourceTriples)
throws MalformedRdfException, AccessDeniedException {
if (resource instanceof NonRdfSourceDescription) {
// update the described resource instead
((NonRdfSourceDescription) resource).getDescribedResource()
.updateProperties(translator(), requestBody, resourceTriples);
} else {
resource.updateProperties(translator(), requestBody, resourceTriples);
}
}

/**
@@ -482,11 +482,17 @@ public void testPatchBinaryDescription() throws IOException {
final String location = serverAddress + id + "/x/fcr:metadata";
final HttpPatch patch = new HttpPatch(location);
patch.addHeader("Content-Type", "application/sparql-update");
patch.setEntity(new StringEntity("INSERT { <" + location + "> "
patch.setEntity(new StringEntity("INSERT { <" + serverAddress + id + "/x> "
+ "<http://purl.org/dc/elements/1.1/identifier> \"this is an identifier\" } WHERE {}"));
assertEquals(NO_CONTENT.getStatusCode(), getStatus(patch));
}

/**
* Descriptions of bitstreams contain only triples about the described thing, so only triples with the described
* thing as their subject are legal.
*
* @throws IOException on error
*/
@Test
public void testPatchBinaryDescriptionWithBinaryProperties() throws IOException {
final String id = getRandomUniqueId();
@@ -1670,7 +1676,7 @@ public void testEmbeddedChildResources() throws IOException {
"return=representation; include=\"http://fedora.info/definitions/v4/repository#EmbedResources\"");
try (final CloseableGraphStore graphStore = getGraphStore(httpGet)) {
assertTrue("Property on child binary should be found!" + graphStore, graphStore.contains(ANY,
createURI(serverAddress + id + "/" + binaryId + "/fcr:metadata"),
createURI(serverAddress + id + "/" + binaryId),
createURI("http://purl.org/dc/elements/1.1/title"), createLiteral("this is a title")));
}
}
@@ -1741,4 +1747,22 @@ public void testJsonLdProfile() throws IOException {
private static Collection<String> getLinkHeaders(final HttpResponse response) {
return stream(response.getHeaders("Link")).map(Header::getValue).collect(toList());
}

@Test
public void testUpdateObjectGraphWithNonLocalTriples() throws IOException {
final String pid = getRandomUniqueId();
createObject(pid);
final String otherPid = getRandomUniqueId();
createObject(otherPid);
final String location = serverAddress + pid;
final String otherLocation = serverAddress + otherPid;
final HttpPatch updateObjectGraphMethod = new HttpPatch(location);
updateObjectGraphMethod.addHeader("Content-Type", "application/sparql-update");
updateObjectGraphMethod.setEntity(new StringEntity("INSERT { <" + location +
"> <http://purl.org/dc/elements/1.1/identifier> \"this is an identifier\". " + "<" + otherLocation +
"> <http://purl.org/dc/elements/1.1/identifier> \"this is an identifier\"" + " } WHERE {}"));
assertEquals("It ought not be possible to use PATCH to create non-local triples!",
FORBIDDEN.getStatusCode(),getStatus(updateObjectGraphMethod));
}

}
@@ -0,0 +1,45 @@
/**
* Copyright 2015 DuraSpace, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fcrepo.http.commons.exceptionhandlers;

import static javax.ws.rs.core.Response.status;
import static javax.ws.rs.core.Response.Status.FORBIDDEN;

import org.fcrepo.kernel.exception.IncorrectTripleSubjectException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Link;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Provider;

/**
* @author ajs6f
* @since 2015-07-15
*/
@Provider
public class IncorrectTripleSubjectExceptionMapper extends ConstraintExceptionMapper<IncorrectTripleSubjectException> {

@Context
private UriInfo uriInfo;

@Override
public Response toResponse(final IncorrectTripleSubjectException e) {

final Link link = buildConstraintLink(e, uriInfo);
final String msg = e.getMessage();
return status(FORBIDDEN).entity(msg).links(link).build();
}
}
@@ -410,8 +410,8 @@ public void updateProperties(final IdentifierConverter<Resource, FedoraResource>

final Model model = originalTriples.asModel();

final JcrPropertyStatementListener listener =
new JcrPropertyStatementListener(idTranslator, getSession());
final JcrPropertyStatementListener listener = new JcrPropertyStatementListener(
idTranslator, getSession(), idTranslator.reverse().convert(this).asNode());

model.register(listener);

@@ -537,7 +537,8 @@ public Boolean isNew() {
public void replaceProperties(final IdentifierConverter<Resource, FedoraResource> idTranslator,
final Model inputModel, final RdfStream originalTriples) throws MalformedRdfException {

final RdfStream replacementStream = new RdfStream().namespaces(inputModel.getNsPrefixMap());
final RdfStream replacementStream = new RdfStream().namespaces(inputModel.getNsPrefixMap())
.topic(idTranslator.reverse().convert(this).asNode());

final GraphDifferencingIterator differencer =
new GraphDifferencingIterator(inputModel, originalTriples);
Oops, something went wrong.

0 comments on commit 6e755b2

Please sign in to comment.
You can’t perform that action at this time.