Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Pre-validate paths for unregistered namespaces
- Add FedoraInvalidNamespaceException type
- Add FedoraInvalidNamespaceExceptionMapper
- Add new validatePath method to AbstractService
- Fix Unit Test that require a mock NamespaceRegistry

Resolves: https://www.pivotaltracker.com/story/show/61500426
  • Loading branch information
Jared Whiklo authored and Andrew Woods committed Sep 19, 2014
1 parent 4762faa commit f374560
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 0 deletions.
@@ -0,0 +1,47 @@
/**
* 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.commons.exceptionhandlers;

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

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

import org.fcrepo.kernel.exception.FedoraInvalidNamespaceException;

import org.slf4j.Logger;

/**
* For invalid namespace exceptions on CRUD actions for nodes/datastreams
*
* @author whikloj
* @since September 12, 2014
*/
@Provider
public class FedoraInvalidNamespaceExceptionMapper implements ExceptionMapper<FedoraInvalidNamespaceException> {

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

@Override
public Response toResponse(final FedoraInvalidNamespaceException e) {
LOGGER.trace("NamespaceExceptionMapper caught an exception: {}", e.getMessage());
return status(BAD_REQUEST).entity(e.getMessage()).build();
}

}
Expand Up @@ -15,14 +15,20 @@
*/
package org.fcrepo.kernel.impl.services;

import static com.google.common.base.Preconditions.checkNotNull;

import javax.inject.Inject;
import javax.jcr.NamespaceException;
import javax.jcr.NamespaceRegistry;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;

import org.fcrepo.jcr.FedoraJcrTypes;
import org.fcrepo.kernel.exception.FedoraInvalidNamespaceException;
import org.fcrepo.kernel.exception.RepositoryRuntimeException;
import org.fcrepo.kernel.services.Service;

import org.modeshape.jcr.api.JcrTools;


Expand Down Expand Up @@ -51,9 +57,50 @@ public void setRepository(final Repository repository) {
@Override
public boolean exists(final Session session, final String path) {
try {
validatePath(session, path);
return session.nodeExists(path);
} catch (final RepositoryException e) {
throw new RepositoryRuntimeException(e);
}
}


/**
* Validate resource path for unregistered namespace prefixes
*
* @param session the JCR session to use
* @param path the absolute path to the object
* @throws FedoraInvalidNamespaceException on unregistered namespaces
* @throws RepositoryRuntimeException
*/
private void validatePath(final Session session, final String path) {

final NamespaceRegistry namespaceRegistry;
try {
namespaceRegistry =
session.getWorkspace().getNamespaceRegistry();
checkNotNull(namespaceRegistry,
"Couldn't find namespace registry in repository!");
} catch (final RepositoryException e) {
throw new RepositoryRuntimeException(e);
}

final String relPath = path.replaceAll("^/+", "").replaceAll("/+$", "");
final String[] pathSegments = relPath.split("/");
for (final String segment : pathSegments) {
if (segment.length() > 0 && segment.contains(":") &&
segment.substring(0, segment.indexOf(":")) != "fedora") {
final String prefix = segment.substring(0, segment.indexOf(":"));
try {
namespaceRegistry.getURI(prefix);
} catch (final NamespaceException e) {
throw new FedoraInvalidNamespaceException(
String.format("The namespace prefix (%s) has not been registered", prefix), e);
} catch (final RepositoryException e) {
throw new RepositoryRuntimeException(e);
}
}
}
}

}
Expand Up @@ -17,8 +17,10 @@

import org.fcrepo.integration.kernel.impl.AbstractIT;
import org.fcrepo.kernel.FedoraResource;
import org.fcrepo.kernel.exception.FedoraInvalidNamespaceException;
import org.fcrepo.kernel.services.NodeService;
import org.fcrepo.kernel.services.ObjectService;

import org.junit.Test;
import org.springframework.test.context.ContextConfiguration;

Expand Down Expand Up @@ -82,4 +84,12 @@ public void testDeleteObjectWithInboundReferences() throws RepositoryException {
assertFalse(session.nodeExists("/" + pid + "/b"));

}

@Test(expected = FedoraInvalidNamespaceException.class)
public void testExistsWithBadNamespace() throws RepositoryException {
final Session session = repository.login();
final String path = "/bad_ns: " + getRandomPid();

objectService.exists(session, path);
}
}
Expand Up @@ -24,14 +24,17 @@

import java.io.InputStream;

import javax.jcr.NamespaceRegistry;
import javax.jcr.Node;
import javax.jcr.PropertyIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Workspace;
import javax.jcr.nodetype.NodeTypeIterator;

import org.fcrepo.kernel.exception.FedoraInvalidNamespaceException;
import org.fcrepo.kernel.services.NodeService;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand Down Expand Up @@ -74,6 +77,13 @@ public class NodeServiceImplTest {

private NodeService testObj;

@Mock
private NamespaceRegistry mockNameReg;

final private static String MOCK_PREFIX = "valid_ns";

final private static String MOCK_URI = "http://example.org";

@Before
public void setUp() throws RepositoryException {
initMocks(this);
Expand All @@ -84,6 +94,12 @@ public void setUp() throws RepositoryException {
when(mockWorkspace.getNodeTypeManager()).thenReturn(mockNodeTypeManager);
when(mockNodeTypeManager.getAllNodeTypes()).thenReturn(mockNTI);
when(mockEmptyIterator.hasNext()).thenReturn(false);
final String[] mockPrefixes = { MOCK_PREFIX };
mockNameReg = mock(NamespaceRegistry.class);
when(mockWorkspace.getNamespaceRegistry()).thenReturn(mockNameReg);
when(mockNameReg.getPrefixes()).thenReturn(mockPrefixes);
when(mockNameReg.getURI(MOCK_PREFIX)).thenReturn(MOCK_URI);

}

@Test
Expand Down Expand Up @@ -122,6 +138,13 @@ public void testExists() throws RepositoryException {
assertEquals(false, testObj.exists(mockSession, "/foo/bar"));
}

@Test(expected = FedoraInvalidNamespaceException.class)
public void testInvalidPath() throws RepositoryException {
final String badPath = "/foo/bad_ns:bar";
when(mockNameReg.getURI("bad_ns")).thenThrow(new FedoraInvalidNamespaceException("Invalid namespace (bad_ns)"));
testObj.exists(mockSession, badPath);
}

@Test
public void testGetAllNodeTypes() throws RepositoryException {
final NodeTypeIterator actual = testObj.getAllNodeTypes(mockSession);
Expand Down
@@ -0,0 +1,48 @@
/**
* 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.kernel.exception;


/**
* Indicates a namespace used in a CRUD request has not been registered in the repository
*
* @author whikloj
* @since September 12, 2014
*/
public class FedoraInvalidNamespaceException extends RepositoryRuntimeException {

private static final long serialVersionUID = 1L;

/**
* Ordinary constructor
*
* @param msg
*/
public FedoraInvalidNamespaceException(final String msg) {
super(msg);
}

/**
* Ordinary constructor
*
* @param msg
* @param rootCause
*/
public FedoraInvalidNamespaceException(final String msg, final Throwable rootCause) {
super(msg, rootCause);
}

}

0 comments on commit f374560

Please sign in to comment.