This is an automated email from the ASF dual-hosted git repository.
reschke pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
The following commit(s) were added to refs/heads/trunk by this push:
new a711eb72d7 OAK-11657: JackrabbitSession.getExpandedName/Path need to
return stab… (#2306)
a711eb72d7 is described below
commit a711eb72d7678e1e0a21a7ebd934067ca157ddbd
Author: Julian Reschke <[email protected]>
AuthorDate: Mon May 26 20:03:14 2025 +0200
OAK-11657: JackrabbitSession.getExpandedName/Path need to return stab…
(#2306)
---
.../apache/jackrabbit/oak/namepath/NameMapper.java | 4 +--
.../oak/namepath/impl/GlobalNameMapper.java | 30 +++++++++++-------
.../apache/jackrabbit/api/JackrabbitSession.java | 9 +++++-
.../jackrabbit/oak/jcr/session/SessionImpl.java | 7 +++-
.../oak/jcr/session/JackrabbitSessionTest.java | 37 +++++++++++++++++++---
5 files changed, 68 insertions(+), 19 deletions(-)
diff --git
a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/NameMapper.java
b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/NameMapper.java
index 94c8d9bf84..dc94f4d706 100644
---
a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/NameMapper.java
+++
b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/NameMapper.java
@@ -87,11 +87,11 @@ public interface NameMapper {
* @param oakName Oak name
* @return JCR name in expanded form
* @since Oak 1.78.0
- * @throws IllegalStateException in case the namespace URI for the given
Oak name cannot be resolved
+ * @throws IllegalStateException in case the namespace URI for the given
+ * Oak name cannot be resolved or is invalid
*
* @see <a
href="https://s.apache.org/jcr-2.0-spec/3_Repository_Model.html#3.2.5.1%20Expanded%20Form">JCR
2.0, 3.2.5.1 Expanded Form</a>
*/
@NotNull
String getExpandedJcrName(@NotNull String oakName);
-
}
diff --git
a/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/impl/GlobalNameMapper.java
b/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/impl/GlobalNameMapper.java
index 546d9aab13..6783eb70b6 100644
---
a/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/impl/GlobalNameMapper.java
+++
b/oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/impl/GlobalNameMapper.java
@@ -35,6 +35,7 @@ import static
org.apache.jackrabbit.oak.spi.namespace.NamespaceConstants.REP_URI
import java.util.Map;
import java.util.Map.Entry;
+import javax.jcr.NamespaceException;
import javax.jcr.RepositoryException;
import org.apache.jackrabbit.oak.api.Root;
@@ -65,16 +66,17 @@ public class GlobalNameMapper implements NameMapper {
return name.startsWith(":");
}
+ private static boolean isValidNamespaceName(String namespace) {
+ // the empty namespace and "internal" are valid as well, otherwise it
always contains a colon (as it is a URI)
+ // compare with RFC 3986, Section 3
(https://datatracker.ietf.org/doc/html/rfc3986#section-3)
+ return namespace.isEmpty() ||
namespace.equals(NamespaceConstants.NAMESPACE_REP) || namespace.contains(":");
+ }
+
protected static boolean isExpandedName(String name) {
if (name.startsWith("{")) {
int brace = name.indexOf('}', 1);
if (brace != -1) {
- String namespace = name.substring(1, brace);
- // the empty namespace and "internal" are valid as well,
otherwise it always contains a colon (as it is a URI)
- // compare with RFC 3986, Section 3
(https://datatracker.ietf.org/doc/html/rfc3986#section-3)
- if (namespace.isEmpty() ||
namespace.equals(NamespaceConstants.NAMESPACE_REP)|| namespace.indexOf(':') !=
-1) {
- return true;
- }
+ return isValidNamespaceName(name.substring(1, brace));
}
}
return false;
@@ -143,17 +145,23 @@ public class GlobalNameMapper implements NameMapper {
int colon = oakName.indexOf(':');
if (colon > 0) {
String oakPrefix = oakName.substring(0, colon);
- // local mapping must take precedence...
- uri = getSessionLocalMappings().get(oakPrefix);
+ uri = getNamespacesProperty(oakPrefix);
+ // global mapping must take precedence...
if (uri == null) {
- // ...over global mappings
- uri = getNamespacesProperty(oakPrefix);
+ // ...over local mappings
+ uri = getSessionLocalMappings().get(oakPrefix);
}
if (uri == null) {
throw new IllegalStateException(
- "No namespace mapping found for " + oakName);
+ new NamespaceException("No namespace mapping found for " +
oakName));
}
localName = oakName.substring(colon + 1);
+ // check namespace name for validity in Oak
+ if (!isValidNamespaceName(uri)) {
+ throw new IllegalStateException(
+ new NamespaceException("Cannot determine expanded name for
'" + oakName +
+ "' as registered namespace name '" + uri + "' is
invalid"));
+ }
} else {
uri = "";
localName = oakName;
diff --git
a/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/JackrabbitSession.java
b/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/JackrabbitSession.java
index db1bf7cb0d..f7e1f71187 100644
---
a/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/JackrabbitSession.java
+++
b/oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/JackrabbitSession.java
@@ -25,6 +25,7 @@ import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.Session;
import javax.jcr.AccessDeniedException;
+import javax.jcr.NamespaceException;
import javax.jcr.RepositoryException;
import javax.jcr.UnsupportedRepositoryOperationException;
@@ -282,7 +283,10 @@ public interface JackrabbitSession extends Session {
* Returns the expanded name of the given {@code Item}.
* @param item the item for which to retrieve the name
* @return the name of the item in expanded form.
- * @throws RepositoryException if another error occurs.
+ * @throws NamespaceException when no expanded name can
+ * be determined (for instance, if the registered namespace
+ * (name) is invalid)
+ * @throws RepositoryException if another error occurs
* @since 1.78.0
* @see <a
href="https://s.apache.org/jcr-2.0-spec/3_Repository_Model.html#3.2.5.1%20Expanded%20Form">JCR
2.0, 3.2.5.1 Expanded Form</a>
*/
@@ -292,6 +296,9 @@ public interface JackrabbitSession extends Session {
* Returns the expanded path of the given {@code Item}.
* @param item the item for which to retrieve the name
* @return the path of the item in expanded form.
+ * @throws NamespaceException when no expanded name can
+ * be determined (for instance, if the registered namespace
+ * (name) is invalid)
* @throws RepositoryException if another error occurs.
* @since 1.78.0
* @see <a
href="https://s.apache.org/jcr-2.0-spec/3_Repository_Model.html#3.2.5.1%20Expanded%20Form">JCR
2.0, 3.2.5.1 Expanded Form</a>
diff --git
a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java
b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java
index 5eb76868cb..6f37d57b0b 100644
---
a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java
+++
b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java
@@ -860,7 +860,12 @@ public class SessionImpl implements JackrabbitSession {
ItemImpl<?> itemImpl = checkItemImpl(item);
return
itemImpl.sessionContext.getExpandedJcrName(itemImpl.getOakName());
} catch (IllegalStateException e) {
- throw new RepositoryException("Namespace exception " +
e.getMessage());
+ // unwrap RepositoryException when available
+ if (e.getCause() instanceof RepositoryException) {
+ throw (RepositoryException) e.getCause();
+ } else {
+ throw new RepositoryException("Namespace exception " +
e.getMessage());
+ }
}
}
diff --git
a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/JackrabbitSessionTest.java
b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/JackrabbitSessionTest.java
index f7c86562ad..926502a382 100644
---
a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/JackrabbitSessionTest.java
+++
b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/JackrabbitSessionTest.java
@@ -24,10 +24,13 @@ import org.jetbrains.annotations.Nullable;
import javax.jcr.GuestCredentials;
import javax.jcr.Item;
+import javax.jcr.NamespaceException;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
+import java.util.UUID;
+
import static org.mockito.Mockito.mock;
public class JackrabbitSessionTest extends AbstractJCRTest {
@@ -86,20 +89,46 @@ public class JackrabbitSessionTest extends AbstractJCRTest {
assertEquals("{}testroot", s.getExpandedName(testRootNode));
Node n = testRootNode.addNode("test:bar");
assertEquals("{http://www.apache.org/jackrabbit/test}bar",
s.getExpandedName(n));
- // now remap namespace uri
+ // now remap namespace uri - should not affect expanded name
+ assertEquals("prefix 'test' has unexpected mapping",
+ "http://www.apache.org/jackrabbit/test",
s.getNamespaceURI("test"));
s.setNamespacePrefix("test", "urn:foo");
- assertEquals("{urn:foo}bar", s.getExpandedName(n));
+ assertEquals("{http://www.apache.org/jackrabbit/test}bar",
s.getExpandedName(n));
// use special namespace uri
n = testRootNode.addNode("rep:bar");
assertEquals("{internal}bar", s.getExpandedName(n));
}
+ public void testGetExpandedNameBrokenNamespace() throws
RepositoryException {
+ // empty namespace uri
+ assertEquals("{}testroot", s.getExpandedName(testRootNode));
+
+ String randomNamespacePrefix = "prefix-" + UUID.randomUUID();
+ // below is not a valid namespace a.k.a. namespace URI
+ String randomNamespaceName = "name-" + UUID.randomUUID();
+
+ // register broken namespace prefix/name mapping
+
s.getWorkspace().getNamespaceRegistry().registerNamespace(randomNamespacePrefix,
randomNamespaceName);
+
+ try {
+ Node n = testRootNode.addNode(randomNamespacePrefix + ":qux");
+
+ // there is no expanded name, thus we expect an exception here
+ String result = s.getExpandedName(n);
+ fail("there is no expanded name in this case, so we expect the
call to fail, however we get: " + result);
+ } catch (NamespaceException ex) {
+ // expected
+ } finally {
+
s.getWorkspace().getNamespaceRegistry().unregisterNamespace(randomNamespacePrefix);
+ }
+ }
+
public void testGetExpandedPath() throws RepositoryException {
assertEquals("/{}testroot", s.getExpandedPath(testRootNode));
Node n = testRootNode.addNode("test:bar").addNode("rep:bar");
assertEquals("/{}testroot/{http://www.apache.org/jackrabbit/test}bar/{internal}bar",
s.getExpandedPath(n));
- // now remap namespace uri
+ // now remap namespace uri - should not affect expanded name
s.setNamespacePrefix("test", "urn:foo");
- assertEquals("/{}testroot/{urn:foo}bar/{internal}bar",
s.getExpandedPath(n));
+
assertEquals("/{}testroot/{http://www.apache.org/jackrabbit/test}bar/{internal}bar",
s.getExpandedPath(n));
}
}
\ No newline at end of file