diff --git a/fcrepo-auth-common/src/test/resources/spring-test/rest.xml b/fcrepo-auth-common/src/test/resources/spring-test/rest.xml index 54fe5b4332..69b46f1c8d 100644 --- a/fcrepo-auth-common/src/test/resources/spring-test/rest.xml +++ b/fcrepo-auth-common/src/test/resources/spring-test/rest.xml @@ -6,6 +6,8 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> + + diff --git a/fcrepo-http-api/src/main/java/org/fcrepo/http/api/FedoraHttpConfiguration.java b/fcrepo-http-api/src/main/java/org/fcrepo/http/api/FedoraHttpConfiguration.java new file mode 100644 index 0000000000..ea6880c0fe --- /dev/null +++ b/fcrepo-http-api/src/main/java/org/fcrepo/http/api/FedoraHttpConfiguration.java @@ -0,0 +1,38 @@ +/** + * Copyright 2014 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.api; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * @author cabeer + * @since 10/17/14 + */ +@Component +public class FedoraHttpConfiguration { + + @Value("${fcrepo.http.ldp.putRequiresIfMatch:false}") + private boolean putRequiresIfMatch; + + /** + * Should PUT requests require an If-Match header? + * @return + */ + public boolean putRequiresIfMatch() { + return putRequiresIfMatch; + } +} diff --git a/fcrepo-http-api/src/main/java/org/fcrepo/http/api/FedoraLdp.java b/fcrepo-http-api/src/main/java/org/fcrepo/http/api/FedoraLdp.java index 88a99f00e8..d559d0f8a7 100644 --- a/fcrepo-http-api/src/main/java/org/fcrepo/http/api/FedoraLdp.java +++ b/fcrepo-http-api/src/main/java/org/fcrepo/http/api/FedoraLdp.java @@ -18,6 +18,7 @@ import com.codahale.metrics.annotation.Timed; import com.google.common.annotations.VisibleForTesting; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; import org.apache.jena.riot.RiotException; import org.fcrepo.http.commons.domain.ContentLocation; import org.fcrepo.http.commons.domain.PATCH; @@ -105,6 +106,8 @@ public class FedoraLdp extends ContentExposingResource { @PathParam("path") protected String externalPath; + @Inject private FedoraHttpConfiguration httpConfiguration; + /** * Default JAX-RS entry point */ @@ -216,7 +219,8 @@ public Response createOrReplaceObjectRdf( @HeaderParam("Content-Type") final MediaType requestContentType, @ContentLocation final InputStream requestBodyStream, @QueryParam("checksum") final String checksum, - @HeaderParam("Content-Disposition") final ContentDisposition contentDisposition) + @HeaderParam("Content-Disposition") final ContentDisposition contentDisposition, + @HeaderParam("If-Match") final String ifMatch) throws InvalidChecksumException, IOException { final FedoraResource resource; @@ -239,6 +243,10 @@ public Response createOrReplaceObjectRdf( response = created(location).entity(location.toString()); } + if (httpConfiguration.putRequiresIfMatch() && StringUtils.isBlank(ifMatch) && !resource.isNew()) { + throw new ClientErrorException("An If-Match header is required", 428); + } + evaluateRequestPreconditions(request, servletResponse, resource, session); if (requestBodyStream == null && !resource.isNew()) { diff --git a/fcrepo-http-api/src/main/resources/META-INF/spring/rest.xml b/fcrepo-http-api/src/main/resources/META-INF/spring/rest.xml index 58c04ee109..15d989499b 100644 --- a/fcrepo-http-api/src/main/resources/META-INF/spring/rest.xml +++ b/fcrepo-http-api/src/main/resources/META-INF/spring/rest.xml @@ -6,6 +6,8 @@ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> + + diff --git a/fcrepo-http-api/src/test/java/org/fcrepo/http/api/FedoraLdpTest.java b/fcrepo-http-api/src/test/java/org/fcrepo/http/api/FedoraLdpTest.java index bb48d9d94e..9664656d6c 100644 --- a/fcrepo-http-api/src/test/java/org/fcrepo/http/api/FedoraLdpTest.java +++ b/fcrepo-http-api/src/test/java/org/fcrepo/http/api/FedoraLdpTest.java @@ -124,6 +124,9 @@ public class FedoraLdpTest { @Mock private BinaryService mockBinaryService; + @Mock + private FedoraHttpConfiguration mockHttpConfiguration; + @Before public void setUp() throws Exception { initMocks(this); @@ -144,6 +147,9 @@ public void setUp() throws Exception { setField(testObj, "nodeService", mockNodeService); setField(testObj, "objectService", mockObjectService); setField(testObj, "binaryService", mockBinaryService); + setField(testObj, "httpConfiguration", mockHttpConfiguration); + + when(mockHttpConfiguration.putRequiresIfMatch()).thenReturn(false); when(mockObject.getEtagValue()).thenReturn(""); when(mockObject.getPath()).thenReturn(path); @@ -519,7 +525,7 @@ public void testPutNewObject() throws Exception { when(mockNodeService.exists(mockSession, "/some/path")).thenReturn(false); when(mockObjectService.findOrCreateObject(mockSession, "/some/path")).thenReturn(mockObject); - final Response actual = testObj.createOrReplaceObjectRdf(null, null, null, null); + final Response actual = testObj.createOrReplaceObjectRdf(null, null, null, null, null); assertEquals(CREATED.getStatusCode(), actual.getStatus()); } @@ -534,7 +540,7 @@ public void testPutNewObjectWithRdf() throws Exception { when(mockObjectService.findOrCreateObject(mockSession, "/some/path")).thenReturn(mockObject); final Response actual = testObj.createOrReplaceObjectRdf(NTRIPLES_TYPE, - toInputStream("_:a _:c ."), null, null); + toInputStream("_:a _:c ."), null, null, null); assertEquals(CREATED.getStatusCode(), actual.getStatus()); verify(mockObject).replaceProperties(eq(identifierConverter), any(Model.class), any(RdfStream.class)); @@ -549,7 +555,7 @@ public void testPutNewBinary() throws Exception { when(mockBinaryService.findOrCreateBinary(mockSession, "/some/path")).thenReturn(mockBinary); final Response actual = testObj.createOrReplaceObjectRdf(TEXT_PLAIN_TYPE, - toInputStream("xyz"), null, null); + toInputStream("xyz"), null, null, null); assertEquals(CREATED.getStatusCode(), actual.getStatus()); } @@ -566,12 +572,28 @@ public void testPutReplaceRdfObject() throws Exception { when(mockObjectService.findOrCreateObject(mockSession, "/some/path")).thenReturn(mockObject); final Response actual = testObj.createOrReplaceObjectRdf(NTRIPLES_TYPE, - toInputStream("_:a _:c ."), null, null); + toInputStream("_:a _:c ."), null, null, null); assertEquals(NO_CONTENT.getStatusCode(), actual.getStatus()); verify(mockObject).replaceProperties(eq(identifierConverter), any(Model.class), any(RdfStream.class)); } + @Test(expected = ClientErrorException.class) + public void testPutWithStrictIfMatchHandling() throws Exception { + + when(mockHttpConfiguration.putRequiresIfMatch()).thenReturn(true); + final FedoraObject mockObject = (FedoraObject)setResource(FedoraObject.class); + doReturn(mockObject).when(testObj).resource(); + when(mockObject.isNew()).thenReturn(false); + + when(mockNodeService.exists(mockSession, "/some/path")).thenReturn(true); + when(mockObjectService.findOrCreateObject(mockSession, "/some/path")).thenReturn(mockObject); + + testObj.createOrReplaceObjectRdf(NTRIPLES_TYPE, + toInputStream("_:a _:c ."), null, null, null); + + } + @Test public void testPatchObject() throws Exception { diff --git a/fcrepo-http-api/src/test/resources/spring-test/master.xml b/fcrepo-http-api/src/test/resources/spring-test/master.xml index cce76bb746..86c98c2e63 100644 --- a/fcrepo-http-api/src/test/resources/spring-test/master.xml +++ b/fcrepo-http-api/src/test/resources/spring-test/master.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> - + diff --git a/fcrepo-http-api/src/test/resources/spring-test/rest.xml b/fcrepo-http-api/src/test/resources/spring-test/rest.xml index 03b063f340..c03147737a 100644 --- a/fcrepo-http-api/src/test/resources/spring-test/rest.xml +++ b/fcrepo-http-api/src/test/resources/spring-test/rest.xml @@ -6,6 +6,8 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> + + diff --git a/fcrepo-integration-ldp/src/test/resources/application.properties b/fcrepo-integration-ldp/src/test/resources/application.properties new file mode 100644 index 0000000000..41ccf9431e --- /dev/null +++ b/fcrepo-integration-ldp/src/test/resources/application.properties @@ -0,0 +1 @@ +fcrepo.http.ldp.putRequiresIfMatch = true \ No newline at end of file diff --git a/fcrepo-integration-ldp/src/test/resources/spring-test/rest.xml b/fcrepo-integration-ldp/src/test/resources/spring-test/rest.xml index e886ff0ba0..c75ebb0684 100644 --- a/fcrepo-integration-ldp/src/test/resources/spring-test/rest.xml +++ b/fcrepo-integration-ldp/src/test/resources/spring-test/rest.xml @@ -6,6 +6,8 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> + +