Skip to content

Commit

Permalink
Respond with 400 status on request with empty path segments
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew Woods authored and ajs6f committed Jul 21, 2015
1 parent 5b4f3d3 commit ef08417
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 8 deletions.
Expand Up @@ -112,6 +112,10 @@ protected static HttpGet getObjMethod(final String id) {
return new HttpGet(serverAddress + id);
}

protected static HttpHead headObjMethod(final String id) {
return new HttpHead(serverAddress + id);
}

protected static HttpDelete deleteObjMethod(final String id) {
return new HttpDelete(serverAddress + id);
}
Expand Down
Expand Up @@ -194,15 +194,15 @@ public void testHeadRepositoryGraph() {
public void testHeadObject() throws IOException {
final String id = getRandomUniqueId();
createObject(id).close();
final HttpHead headObjMethod = new HttpHead(serverAddress + id);
final HttpHead headObjMethod = headObjMethod(id);
assertEquals(OK.getStatusCode(), getStatus(headObjMethod));
}

@Test
public void testHeadDefaultContainer() throws IOException {
final String id = getRandomUniqueId();
createObject(id).close();
final HttpHead headObjMethod = new HttpHead(serverAddress + id);
final HttpHead headObjMethod = headObjMethod(id);
try (final CloseableHttpResponse response = execute(headObjMethod)) {
assertTrue("Didn't find LDP container link header!", getLinkHeaders(response).contains(
BASIC_CONTAINER_LINK_HEADER));
Expand All @@ -216,7 +216,7 @@ public void testHeadBasicContainer() throws IOException {
createObjectAndClose(id);
addMixin(id, BASIC_CONTAINER.getURI());

final HttpHead headObjMethod = new HttpHead(serverAddress + id);
final HttpHead headObjMethod = headObjMethod(id);
try (final CloseableHttpResponse response = execute(headObjMethod)) {
final Collection<String> links = getLinkHeaders(response);
assertTrue("Didn't find LDP container link header!", links.contains(BASIC_CONTAINER_LINK_HEADER));
Expand All @@ -229,7 +229,7 @@ public void testHeadDirectContainer() throws IOException {
createObjectAndClose(id);
addMixin(id, DIRECT_CONTAINER.getURI());

final HttpHead headObjMethod = new HttpHead(serverAddress + id);
final HttpHead headObjMethod = headObjMethod(id);
try (final CloseableHttpResponse response = execute(headObjMethod)) {
final Collection<String> links = getLinkHeaders(response);
assertTrue("Didn't find LDP container link header!", links.contains(DIRECT_CONTAINER_LINKHEADER));
Expand All @@ -242,7 +242,7 @@ public void testHeadIndirectContainer() throws IOException {
createObjectAndClose(id);
addMixin(id, INDIRECT_CONTAINER.getURI());

final HttpHead headObjMethod = new HttpHead(serverAddress + id);
final HttpHead headObjMethod = headObjMethod(id);
try (final CloseableHttpResponse response = execute(headObjMethod)) {
final Collection<String> links = getLinkHeaders(response);
assertTrue("Didn't find LDP container link header!", links.contains(INDIRECT_CONTAINER_LINK_HEADER));
Expand All @@ -254,7 +254,7 @@ public void testHeadDatastream() throws IOException, ParseException {
final String id = getRandomUniqueId();
createDatastream(id, "x", "123");

final HttpHead headObjMethod = new HttpHead(serverAddress + id + "/x");
final HttpHead headObjMethod = headObjMethod(id + "/x");
try (final CloseableHttpResponse response = execute(headObjMethod)) {
assertEquals(OK.getStatusCode(), response.getStatusLine().getStatusCode());
assertEquals(TEXT_PLAIN, response.getFirstHeader("Content-Type").getValue());
Expand Down Expand Up @@ -1520,7 +1520,7 @@ public void testLastModifiedUpdatedAfterUpdates() throws IOException {

// check Last-Modified header is current
final long lastmod1;
try (final CloseableHttpResponse resp1 = execute(new HttpHead(serverAddress + "files/" + id))) {
try (final CloseableHttpResponse resp1 = execute(headObjMethod("files/" + id))) {
assertEquals(OK.getStatusCode(), getStatus(resp1));
lastmod1 = headerFormat.parse(resp1.getFirstHeader("Last-Modified").getValue()).getTime();
assertTrue((timestamp1 - lastmod1) < 1000); // because rounding
Expand All @@ -1534,7 +1534,7 @@ public void testLastModifiedUpdatedAfterUpdates() throws IOException {
}

// check Last-Modified header is updated
try (final CloseableHttpResponse resp2 = execute(new HttpHead(serverAddress + "files/" + id))) {
try (final CloseableHttpResponse resp2 = execute(headObjMethod("files/" + id))) {
assertEquals(OK.getStatusCode(), getStatus(resp2));
final long lastmod2 = headerFormat.parse(resp2.getFirstHeader("Last-Modified").getValue()).getTime();
assertTrue((timestamp2 - lastmod2) < 1000); // because rounding
Expand Down Expand Up @@ -1845,6 +1845,12 @@ public void testJsonLdProfile() throws IOException {
assertEquals("Should be two values!", 2, titles.findValues("@value").size());
}

@Test
public void testPathWithEmptySegment() {
final String badLocation = "test/me/mb/er/s//members/9528a300-22da-40f2-bf3c-5b345d71affb";
assertEquals(BAD_REQUEST.getStatusCode(), getStatus(headObjMethod(badLocation)));
}

private static Optional<Date> getDateFromModel(final Model model, final Resource subj, final Property pred)
throws NoSuchElementException, ParseException {
final StmtIterator stmts = model.listStatements(subj, pred, (String) null);
Expand Down
Expand Up @@ -48,6 +48,7 @@
import javax.ws.rs.core.UriBuilder;

import org.fcrepo.kernel.api.exception.IdentifierConversionException;
import org.fcrepo.kernel.api.exception.InvalidResourceIdentifierException;
import org.fcrepo.kernel.api.exception.RepositoryRuntimeException;
import org.fcrepo.kernel.api.exception.TombstoneException;
import org.fcrepo.kernel.api.identifiers.IdentifierConverter;
Expand Down Expand Up @@ -219,6 +220,11 @@ private String asString(final Resource resource, final Map<String, String> value
if (path.isEmpty()) {
return "/";
}

// Validate path
if (path.contains("//")) {
throw new InvalidResourceIdentifierException("Path contains empty element! " + path);
}
return path;
}
return null;
Expand Down
@@ -0,0 +1,46 @@
/**
* 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 org.fcrepo.kernel.api.exception.InvalidResourceIdentifierException;
import org.slf4j.Logger;

import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
import static javax.ws.rs.core.Response.status;
import static org.slf4j.LoggerFactory.getLogger;

/**
* The class translates {@link org.fcrepo.kernel.api.exception.InvalidResourceIdentifierException}s to its proper
* response code.
*
* @author awoods
* @since July 14, 2015.
*/
@Provider
public class InvalidResourceIdentifierExceptionMapper implements ExceptionMapper<InvalidResourceIdentifierException> {

private static final Logger LOGGER = getLogger(InvalidResourceIdentifierExceptionMapper.class);

@Override
public Response toResponse(final InvalidResourceIdentifierException e) {
LOGGER.debug("InvalidResourceIdentifierExceptionMapper caught exception: {}", e.getMessage());
return status(BAD_REQUEST).entity(e.getMessage()).build();
}
}
Expand Up @@ -17,6 +17,7 @@

import com.hp.hpl.jena.rdf.model.Resource;
import org.fcrepo.kernel.api.TxSession;
import org.fcrepo.kernel.api.exception.InvalidResourceIdentifierException;
import org.fcrepo.kernel.api.exception.RepositoryRuntimeException;
import org.fcrepo.kernel.api.models.FedoraBinary;
import org.fcrepo.kernel.api.models.FedoraResource;
Expand Down Expand Up @@ -248,4 +249,9 @@ public void testDoBackwardWithTransaction() throws Exception {
public void testToStringWithRoot() {
assertEquals("/", converter.asString(createResource("http://localhost:8080/some/")));
}

@Test (expected = InvalidResourceIdentifierException.class)
public void testToStringWithEmptPathSegment() {
converter.asString(createResource("http://localhost:8080/some/test/a//b/c/d"));
}
}
@@ -0,0 +1,39 @@
/**
* 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.kernel.api.exception;

/**
* This exception is used for invalid resource identifiers, such as when a resource path has empty segments.
* Note: This exception is *not* used for valid identifiers that point to non-existent resources.
* Additionally, the notion of "valid" will depend on the {@link org.fcrepo.kernel.api.identifiers.IdentifierConverter}
* chain in use.
*
* @author awoods
* @since July 14, 2015
*/
public class InvalidResourceIdentifierException extends RepositoryRuntimeException {

private static final long serialVersionUID = 1L;

/**
* Constructor
*
* @param msg root cause
*/
public InvalidResourceIdentifierException(final String msg) {
super(msg);
}
}

0 comments on commit ef08417

Please sign in to comment.