Skip to content

Commit a711eb7

Browse files
authored
OAK-11657: JackrabbitSession.getExpandedName/Path need to return stab… (#2306)
1 parent e47aa9b commit a711eb7

File tree

5 files changed

+68
-19
lines changed

5 files changed

+68
-19
lines changed

oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/NameMapper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,11 @@ public interface NameMapper {
8787
* @param oakName Oak name
8888
* @return JCR name in expanded form
8989
* @since Oak 1.78.0
90-
* @throws IllegalStateException in case the namespace URI for the given Oak name cannot be resolved
90+
* @throws IllegalStateException in case the namespace URI for the given
91+
* Oak name cannot be resolved or is invalid
9192
*
9293
* @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>
9394
*/
9495
@NotNull
9596
String getExpandedJcrName(@NotNull String oakName);
96-
9797
}

oak-core/src/main/java/org/apache/jackrabbit/oak/namepath/impl/GlobalNameMapper.java

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.util.Map;
3636
import java.util.Map.Entry;
3737

38+
import javax.jcr.NamespaceException;
3839
import javax.jcr.RepositoryException;
3940

4041
import org.apache.jackrabbit.oak.api.Root;
@@ -65,16 +66,17 @@ protected static boolean isHiddenName(String name) {
6566
return name.startsWith(":");
6667
}
6768

69+
private static boolean isValidNamespaceName(String namespace) {
70+
// the empty namespace and "internal" are valid as well, otherwise it always contains a colon (as it is a URI)
71+
// compare with RFC 3986, Section 3 (https://datatracker.ietf.org/doc/html/rfc3986#section-3)
72+
return namespace.isEmpty() || namespace.equals(NamespaceConstants.NAMESPACE_REP) || namespace.contains(":");
73+
}
74+
6875
protected static boolean isExpandedName(String name) {
6976
if (name.startsWith("{")) {
7077
int brace = name.indexOf('}', 1);
7178
if (brace != -1) {
72-
String namespace = name.substring(1, brace);
73-
// the empty namespace and "internal" are valid as well, otherwise it always contains a colon (as it is a URI)
74-
// compare with RFC 3986, Section 3 (https://datatracker.ietf.org/doc/html/rfc3986#section-3)
75-
if (namespace.isEmpty() || namespace.equals(NamespaceConstants.NAMESPACE_REP)|| namespace.indexOf(':') != -1) {
76-
return true;
77-
}
79+
return isValidNamespaceName(name.substring(1, brace));
7880
}
7981
}
8082
return false;
@@ -143,17 +145,23 @@ public String getExpandedJcrName(@NotNull String oakName) {
143145
int colon = oakName.indexOf(':');
144146
if (colon > 0) {
145147
String oakPrefix = oakName.substring(0, colon);
146-
// local mapping must take precedence...
147-
uri = getSessionLocalMappings().get(oakPrefix);
148+
uri = getNamespacesProperty(oakPrefix);
149+
// global mapping must take precedence...
148150
if (uri == null) {
149-
// ...over global mappings
150-
uri = getNamespacesProperty(oakPrefix);
151+
// ...over local mappings
152+
uri = getSessionLocalMappings().get(oakPrefix);
151153
}
152154
if (uri == null) {
153155
throw new IllegalStateException(
154-
"No namespace mapping found for " + oakName);
156+
new NamespaceException("No namespace mapping found for " + oakName));
155157
}
156158
localName = oakName.substring(colon + 1);
159+
// check namespace name for validity in Oak
160+
if (!isValidNamespaceName(uri)) {
161+
throw new IllegalStateException(
162+
new NamespaceException("Cannot determine expanded name for '" + oakName +
163+
"' as registered namespace name '" + uri + "' is invalid"));
164+
}
157165
} else {
158166
uri = "";
159167
localName = oakName;

oak-jackrabbit-api/src/main/java/org/apache/jackrabbit/api/JackrabbitSession.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import javax.jcr.Property;
2626
import javax.jcr.Session;
2727
import javax.jcr.AccessDeniedException;
28+
import javax.jcr.NamespaceException;
2829
import javax.jcr.RepositoryException;
2930
import javax.jcr.UnsupportedRepositoryOperationException;
3031

@@ -282,7 +283,10 @@ default Node getParentOrNull(@NotNull Item item) throws RepositoryException {
282283
* Returns the expanded name of the given {@code Item}.
283284
* @param item the item for which to retrieve the name
284285
* @return the name of the item in expanded form.
285-
* @throws RepositoryException if another error occurs.
286+
* @throws NamespaceException when no expanded name can
287+
* be determined (for instance, if the registered namespace
288+
* (name) is invalid)
289+
* @throws RepositoryException if another error occurs
286290
* @since 1.78.0
287291
* @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>
288292
*/
@@ -292,6 +296,9 @@ default Node getParentOrNull(@NotNull Item item) throws RepositoryException {
292296
* Returns the expanded path of the given {@code Item}.
293297
* @param item the item for which to retrieve the name
294298
* @return the path of the item in expanded form.
299+
* @throws NamespaceException when no expanded name can
300+
* be determined (for instance, if the registered namespace
301+
* (name) is invalid)
295302
* @throws RepositoryException if another error occurs.
296303
* @since 1.78.0
297304
* @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>

oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/session/SessionImpl.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,12 @@ public String getExpandedName(@NotNull Item item) throws RepositoryException {
860860
ItemImpl<?> itemImpl = checkItemImpl(item);
861861
return itemImpl.sessionContext.getExpandedJcrName(itemImpl.getOakName());
862862
} catch (IllegalStateException e) {
863-
throw new RepositoryException("Namespace exception " + e.getMessage());
863+
// unwrap RepositoryException when available
864+
if (e.getCause() instanceof RepositoryException) {
865+
throw (RepositoryException) e.getCause();
866+
} else {
867+
throw new RepositoryException("Namespace exception " + e.getMessage());
868+
}
864869
}
865870
}
866871

oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/session/JackrabbitSessionTest.java

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@
2424

2525
import javax.jcr.GuestCredentials;
2626
import javax.jcr.Item;
27+
import javax.jcr.NamespaceException;
2728
import javax.jcr.Node;
2829
import javax.jcr.Property;
2930
import javax.jcr.RepositoryException;
3031

32+
import java.util.UUID;
33+
3134
import static org.mockito.Mockito.mock;
3235

3336
public class JackrabbitSessionTest extends AbstractJCRTest {
@@ -86,20 +89,46 @@ public void testGetExpandedName() throws RepositoryException {
8689
assertEquals("{}testroot", s.getExpandedName(testRootNode));
8790
Node n = testRootNode.addNode("test:bar");
8891
assertEquals("{http://www.apache.org/jackrabbit/test}bar", s.getExpandedName(n));
89-
// now remap namespace uri
92+
// now remap namespace uri - should not affect expanded name
93+
assertEquals("prefix 'test' has unexpected mapping",
94+
"http://www.apache.org/jackrabbit/test", s.getNamespaceURI("test"));
9095
s.setNamespacePrefix("test", "urn:foo");
91-
assertEquals("{urn:foo}bar", s.getExpandedName(n));
96+
assertEquals("{http://www.apache.org/jackrabbit/test}bar", s.getExpandedName(n));
9297
// use special namespace uri
9398
n = testRootNode.addNode("rep:bar");
9499
assertEquals("{internal}bar", s.getExpandedName(n));
95100
}
96101

102+
public void testGetExpandedNameBrokenNamespace() throws RepositoryException {
103+
// empty namespace uri
104+
assertEquals("{}testroot", s.getExpandedName(testRootNode));
105+
106+
String randomNamespacePrefix = "prefix-" + UUID.randomUUID();
107+
// below is not a valid namespace a.k.a. namespace URI
108+
String randomNamespaceName = "name-" + UUID.randomUUID();
109+
110+
// register broken namespace prefix/name mapping
111+
s.getWorkspace().getNamespaceRegistry().registerNamespace(randomNamespacePrefix, randomNamespaceName);
112+
113+
try {
114+
Node n = testRootNode.addNode(randomNamespacePrefix + ":qux");
115+
116+
// there is no expanded name, thus we expect an exception here
117+
String result = s.getExpandedName(n);
118+
fail("there is no expanded name in this case, so we expect the call to fail, however we get: " + result);
119+
} catch (NamespaceException ex) {
120+
// expected
121+
} finally {
122+
s.getWorkspace().getNamespaceRegistry().unregisterNamespace(randomNamespacePrefix);
123+
}
124+
}
125+
97126
public void testGetExpandedPath() throws RepositoryException {
98127
assertEquals("/{}testroot", s.getExpandedPath(testRootNode));
99128
Node n = testRootNode.addNode("test:bar").addNode("rep:bar");
100129
assertEquals("/{}testroot/{http://www.apache.org/jackrabbit/test}bar/{internal}bar", s.getExpandedPath(n));
101-
// now remap namespace uri
130+
// now remap namespace uri - should not affect expanded name
102131
s.setNamespacePrefix("test", "urn:foo");
103-
assertEquals("/{}testroot/{urn:foo}bar/{internal}bar", s.getExpandedPath(n));
132+
assertEquals("/{}testroot/{http://www.apache.org/jackrabbit/test}bar/{internal}bar", s.getExpandedPath(n));
104133
}
105134
}

0 commit comments

Comments
 (0)