This is an automated email from the ASF dual-hosted git repository.
markt-asf pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push:
new 6dd75beb55 Add case sensitive attribute to LockOutRealm
6dd75beb55 is described below
commit 6dd75beb55bd42fc5f78e929596b25018cd17717
Author: Mark Thomas <[email protected]>
AuthorDate: Wed Apr 22 14:18:52 2026 +0100
Add case sensitive attribute to LockOutRealm
# Conflicts:
# webapps/docs/changelog.xml
# Conflicts:
# webapps/docs/changelog.xml
---
java/org/apache/catalina/realm/LockOutRealm.java | 25 +++++
.../apache/catalina/realm/TestLockoutRealm.java | 117 +++++++++++++++++++++
webapps/docs/changelog.xml | 7 ++
webapps/docs/config/realm.xml | 6 ++
4 files changed, 155 insertions(+)
diff --git a/java/org/apache/catalina/realm/LockOutRealm.java
b/java/org/apache/catalina/realm/LockOutRealm.java
index ef3964a791..cd066401e6 100644
--- a/java/org/apache/catalina/realm/LockOutRealm.java
+++ b/java/org/apache/catalina/realm/LockOutRealm.java
@@ -19,6 +19,7 @@ package org.apache.catalina.realm;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.LinkedHashMap;
+import java.util.Locale;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
@@ -71,6 +72,11 @@ public class LockOutRealm extends CombinedRealm {
*/
protected Map<String,LockRecord> failedUsers = null;
+ /*
+ * Should user names (the keys) in the failedUsers Map be treated in a
case sensitive manner. The default is false.
+ */
+ private boolean caseSensitive = false;
+
@Override
protected void startInternal() throws LifecycleException {
@@ -205,6 +211,9 @@ public class LockOutRealm extends CombinedRealm {
* time will be recorded and any attempt to authenticate a locked user
will log a warning.
*/
public boolean isLocked(String username) {
+ if (!getCaseSensitive()) {
+ username = username.toLowerCase(Locale.ROOT);
+ }
LockRecord lockRecord;
synchronized (this) {
lockRecord = failedUsers.get(username);
@@ -227,6 +236,9 @@ public class LockOutRealm extends CombinedRealm {
* After successful authentication, any record of previous authentication
failure is removed.
*/
private synchronized void registerAuthSuccess(String username) {
+ if (!getCaseSensitive()) {
+ username = username.toLowerCase(Locale.ROOT);
+ }
// Successful authentication means removal from the list of failed
users
failedUsers.remove(username);
}
@@ -236,6 +248,9 @@ public class LockOutRealm extends CombinedRealm {
* After a failed authentication, add the record of the failed
authentication.
*/
private void registerAuthFailure(String username) {
+ if (!getCaseSensitive()) {
+ username = username.toLowerCase(Locale.ROOT);
+ }
LockRecord lockRecord;
synchronized (this) {
if (!failedUsers.containsKey(username)) {
@@ -337,6 +352,16 @@ public class LockOutRealm extends CombinedRealm {
}
+ public boolean getCaseSensitive() {
+ return caseSensitive;
+ }
+
+
+ public void setCaseSensitive(boolean caseSensitive) {
+ this.caseSensitive = caseSensitive;
+ }
+
+
protected static class LockRecord {
private final AtomicInteger failures = new AtomicInteger(0);
private long lastFailureTime = 0;
diff --git a/test/org/apache/catalina/realm/TestLockoutRealm.java
b/test/org/apache/catalina/realm/TestLockoutRealm.java
new file mode 100644
index 0000000000..d859fc85b4
--- /dev/null
+++ b/test/org/apache/catalina/realm/TestLockoutRealm.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.catalina.realm;
+
+import java.security.Principal;
+import java.util.Locale;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.startup.TesterMapRealm;
+import org.apache.tomcat.unittest.TesterContext;
+
+public class TestLockoutRealm {
+
+ private static final String USER_NAME = "user";
+ private static final String PASSWORD = "password";
+
+ private LockOutRealm realm;
+
+
+ @Before
+ public void init() throws Exception {
+ Context context = new TesterContext();
+ TesterMapRealm tmr = new TesterMapRealm();
+ tmr.setContainer(context);
+ MessageDigestCredentialHandler ch = new
MessageDigestCredentialHandler();
+ tmr.setCredentialHandler(ch);
+ tmr.addUser(USER_NAME, PASSWORD);
+ tmr.start();
+
+ realm = new LockOutRealm();
+ realm.setContainer(context);
+ realm.addRealm(tmr);
+ realm.setFailureCount(2);
+ realm.start();
+ }
+
+
+ @Test
+ public void testLockoutAfterFailure() {
+ Principal p = realm.authenticate(USER_NAME, PASSWORD);
+ Assert.assertNotNull(p);
+
+ p = realm.authenticate(USER_NAME, "wrong");
+ p = realm.authenticate(USER_NAME, "wrong");
+ // Should be locked now
+ p = realm.authenticate(USER_NAME, PASSWORD);
+ Assert.assertNull(p);
+ }
+
+
+ @Test
+ public void testLockoutAfterFailureCaseSensitiveDefault() {
+ Principal p = realm.authenticate(USER_NAME, PASSWORD);
+ Assert.assertNotNull(p);
+
+ p = realm.authenticate(USER_NAME, "wrong");
+ p = realm.authenticate(USER_NAME.toUpperCase(Locale.ENGLISH), "wrong");
+ // Should be locked now
+ p = realm.authenticate(USER_NAME, PASSWORD);
+ Assert.assertNull(p);
+ }
+
+
+ @Test
+ public void testLockoutAfterFailureCaseSensitiveFalse() {
+ realm.setCaseSensitive(false);
+
+ Principal p = realm.authenticate(USER_NAME, PASSWORD);
+ Assert.assertNotNull(p);
+
+ p = realm.authenticate(USER_NAME, "wrong");
+ p = realm.authenticate(USER_NAME.toUpperCase(Locale.ENGLISH), "wrong");
+ // Should be locked now
+ p = realm.authenticate(USER_NAME, PASSWORD);
+ Assert.assertNull(p);
+ }
+
+
+ @Test
+ public void testLockoutAfterFailureCaseSensitiveTrue() {
+ realm.setCaseSensitive(true);
+
+ Principal p = realm.authenticate(USER_NAME, PASSWORD);
+ Assert.assertNotNull(p);
+
+ p = realm.authenticate(USER_NAME, "wrong");
+ p = realm.authenticate(USER_NAME.toUpperCase(Locale.ENGLISH), "wrong");
+ // Should not be locked yet
+ p = realm.authenticate(USER_NAME, PASSWORD);
+ Assert.assertNotNull(p);
+
+ p = realm.authenticate(USER_NAME, "wrong");
+ p = realm.authenticate(USER_NAME, "wrong");
+
+ // Both should be locked now
+ p = realm.authenticate(USER_NAME, PASSWORD);
+ Assert.assertNull(p);
+ }
+}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index a2f4e7f303..563bf4a0f5 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -162,6 +162,13 @@
request body for LOCK and PROPFIND. The default value is 4096 bytes.
(markt)
</fix>
+ <add>
+ Add a new <code>caseSensitive</code> attribute to the
+ <code>LockOutRealm</code> that controls the manner in which user names
+ are treated when making locking decisions. The default is
+ <code>false</code>, meaning user names are treated in a case
insensitive
+ manner. (markt)
+ </add>
</changelog>
</subsection>
<subsection name="Coyote">
diff --git a/webapps/docs/config/realm.xml b/webapps/docs/config/realm.xml
index 183c9c5373..d01c687a3d 100644
--- a/webapps/docs/config/realm.xml
+++ b/webapps/docs/config/realm.xml
@@ -1009,6 +1009,12 @@
1000.</p>
</attribute>
+ <attribute name="caseSensitive" required="false">
+ <p>The manner in which user names will be treated when making locking
+ decisions. Defaults to <code>false</code>, meaning user names will be
+ treated in a case insensitive manner.</p>
+ </attribute>
+
<attribute name="failureCount" required="false">
<p>The number of times in a row a user has to fail authentication to be
locked out. Defaults to 5.</p>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]