This is an automated email from the ASF dual-hosted git repository.
markt-asf pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/11.0.x by this push:
new 8bafd79a3b Ensure security constraint with longest matching path is selected
8bafd79a3b is described below
commit 8bafd79a3b54684e80e9cb1bafd4746aede7d3f5
Author: Mark Thomas <[email protected]>
AuthorDate: Tue Jul 21 18:52:39 2026 +0100
Ensure security constraint with longest matching path is selected
---
java/org/apache/catalina/realm/RealmBase.java | 17 ++++----
test/org/apache/catalina/realm/TestRealmBase.java | 46 ++++++++++++++++++++++
test/org/apache/tomcat/unittest/TesterRequest.java | 16 +++++++-
webapps/docs/changelog.xml | 4 ++
4 files changed, 73 insertions(+), 10 deletions(-)
diff --git a/java/org/apache/catalina/realm/RealmBase.java b/java/org/apache/catalina/realm/RealmBase.java
index 55014f1987..a526001c51 100644
--- a/java/org/apache/catalina/realm/RealmBase.java
+++ b/java/org/apache/catalina/realm/RealmBase.java
@@ -68,9 +68,8 @@ import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSName;
/**
- * Abstract base class for Realm implementations.
- * Provides common functionality including credential handling, security constraint evaluation,
- * and GSS-API authentication support.
+ * Abstract base class for Realm implementations. Provides common functionality including credential handling, security
+ * constraint evaluation, and GSS-API authentication support.
*/
public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
@@ -324,8 +323,8 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
/**
- * Returns the comma separated names of user attributes to additionally query from the realm. These will be
- * provided to the user through the created Principal's <i>attributes</i> map.
+ * Returns the comma separated names of user attributes to additionally query from the realm. These will be provided
+ * to the user through the created Principal's <i>attributes</i> map.
*
* @return The comma separated names of user attributes to additionally query from the realm
*/
@@ -636,7 +635,8 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
boolean matched = false;
int length = -1;
for (String pattern : patterns) {
- if (pattern.startsWith("/") && pattern.endsWith("/*") && pattern.length() >= longest) {
+ if (pattern.startsWith("/") && pattern.endsWith("/*") && pattern.length() >= longest &&
+ pattern.length() >= length) {
if (pattern.length() == 2) {
matched = true;
@@ -1127,8 +1127,9 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
* Check whether the current credential handler uses the specified message digest algorithm.
*
* @param algorithm The name of the message digest algorithm to check
- * @return {@code true} if the credential handler is a {@link MessageDigestCredentialHandler}
- * using the specified algorithm
+ *
+ * @return {@code true} if the credential handler is a {@link MessageDigestCredentialHandler} using the specified
+ * algorithm
*/
protected boolean hasMessageDigest(String algorithm) {
CredentialHandler ch = credentialHandler;
diff --git a/test/org/apache/catalina/realm/TestRealmBase.java b/test/org/apache/catalina/realm/TestRealmBase.java
index 3f920976dd..726225224d 100644
--- a/test/org/apache/catalina/realm/TestRealmBase.java
+++ b/test/org/apache/catalina/realm/TestRealmBase.java
@@ -939,4 +939,50 @@ public class TestRealmBase {
Assert.assertFalse(mapRealm.hasResourcePermission(
request, response, constraints, null));
}
+
+
+ @Test
+ public void testOverlappingConstraints() throws Exception {
+ // Deny access to levels 1 & 3
+ SecurityConstraint outerConstraint = new SecurityConstraint();
+ SecurityCollection outerCollection = new SecurityCollection();
+ outerCollection.addPattern("/level1/level2/level3/*");
+ outerCollection.addPattern("/level1/*");
+ outerConstraint.addCollection(outerCollection);
+ // Empty auth -> deny
+ outerConstraint.setAuthConstraint(true);
+
+ // Allow access to level 2
+ SecurityConstraint innerConstraint = new SecurityConstraint();
+ SecurityCollection innerCollection = new SecurityCollection();
+ innerCollection.addPattern("/level1/level2/*");
+ innerConstraint.addCollection(innerCollection);
+ // No auth -> allow
+
+ TesterMapRealm mapRealm = new TesterMapRealm();
+
+ // Set up the mock request and response
+ TesterRequest request = new TesterRequest("/level1/index.jsp");
+ Response response = new TesterResponse();
+ Context context = request.getContext();
+ request.getMappingData().context = context;
+
+ // Add the constraints to the context
+ context.addConstraint(outerConstraint);
+ context.addConstraint(innerConstraint);
+
+ // Level 1 should be blocked
+ SecurityConstraint[] constraints = mapRealm.findSecurityConstraints(request, context);
+ Assert.assertFalse(mapRealm.hasResourcePermission(request, response, constraints, null));
+
+ // Level 2 should be blocked
+ request = new TesterRequest("/level1/level2/index.jsp");
+ constraints = mapRealm.findSecurityConstraints(request, context);
+ Assert.assertTrue(mapRealm.hasResourcePermission(request, response, constraints, null));
+
+ // Level 3 should be blocked
+ request = new TesterRequest("/level1/level2/level3/index.jsp");
+ constraints = mapRealm.findSecurityConstraints(request, context);
+ Assert.assertFalse(mapRealm.hasResourcePermission(request, response, constraints, null));
+ }
}
diff --git a/test/org/apache/tomcat/unittest/TesterRequest.java b/test/org/apache/tomcat/unittest/TesterRequest.java
index 793be4495e..fb5a87d0d3 100644
--- a/test/org/apache/tomcat/unittest/TesterRequest.java
+++ b/test/org/apache/tomcat/unittest/TesterRequest.java
@@ -37,14 +37,25 @@ public class TesterRequest extends Request {
private final TesterContext context;
private final TesterServletContext servletContext;
+ private final String requestUri;
public TesterRequest() {
- this(false);
+ this(false, "/level1/level2/foo.html");
}
public TesterRequest(boolean withSession) {
+ this(withSession, "/level1/level2/foo.html");
+ }
+
+
+ public TesterRequest(String requestUri) {
+ this(false, requestUri);
+ }
+
+
+ public TesterRequest(boolean withSession, String requestUri) {
super(null, null);
context = new TesterContext();
servletContext = new TesterServletContext();
@@ -58,6 +69,7 @@ public class TesterRequest extends Request {
session.setId("1234", false);
session.setValid(true);
}
+ this.requestUri = requestUri;
}
@@ -79,7 +91,7 @@ public class TesterRequest extends Request {
@Override
public String getRequestURI() {
- return "/level1/level2/foo.html";
+ return requestUri;
}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 1e570849dc..9368dce631 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -189,6 +189,10 @@
application basis. This attribute will be removed in Tomcat 12 where it
will effectively be hard-coded to <code>true</code>. (markt)
</fix>
+ <fix>
+ Ensure the security constraint with the longest matching path is
+ selected when more than one constraint matches the request path. (markt)
+ </fix>
</changelog>
</subsection>
<subsection name="Coyote">
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.