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 790d6e2c3b Various improvements to the DataSourceRealm
790d6e2c3b is described below
commit 790d6e2c3b4cd201a1fa556a23d5b7504dee18ad
Author: Mark Thomas <[email protected]>
AuthorDate: Fri Jul 31 08:49:10 2026 +0100
Various improvements to the DataSourceRealm
A failure to connect to the database or an exception during either user
or role lookup will now result in an authentication failure rather than
a partially populated Principal.
For CLIENT-CERT and SPNEGO authentication, the user must exist in the
database for authentication to succeed.
---
.../org/apache/catalina/realm/DataSourceRealm.java | 63 ++++++++++++++++++----
.../apache/catalina/realm/LocalStrings.properties | 1 +
.../apache/catalina/realm/TestDataSourceRealm.java | 9 +++-
webapps/docs/changelog.xml | 8 +++
4 files changed, 70 insertions(+), 11 deletions(-)
diff --git a/java/org/apache/catalina/realm/DataSourceRealm.java b/java/org/apache/catalina/realm/DataSourceRealm.java
index 845cecd545..249e1d70ff 100644
--- a/java/org/apache/catalina/realm/DataSourceRealm.java
+++ b/java/org/apache/catalina/realm/DataSourceRealm.java
@@ -23,6 +23,7 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
+import java.util.List;
import javax.naming.Context;
import javax.sql.DataSource;
@@ -317,9 +318,20 @@ public class DataSourceRealm extends RealmBase {
}
// Validate the user's credentials
- boolean validated = getCredentialHandler().matches(credentials, dbCredentials);
+ boolean authenticationSuccess = getCredentialHandler().matches(credentials, dbCredentials);
- if (validated) {
+ ArrayList<String> list = null;
+
+ if (authenticationSuccess) {
+ // Obtain the roles
+ list = getRoles(dbConnection, username);
+ if (list == null) {
+ // Role lookup failed so fail authentication
+ authenticationSuccess = false;
+ }
+ }
+
+ if (authenticationSuccess) {
if (containerLog.isTraceEnabled()) {
containerLog.trace(sm.getString("dataSourceRealm.authenticateSuccess", username));
}
@@ -330,8 +342,6 @@ public class DataSourceRealm extends RealmBase {
return null;
}
- ArrayList<String> list = getRoles(dbConnection, username);
-
// Create and return a suitable Principal for this user
return new GenericPrincipal(username, list);
}
@@ -447,26 +457,59 @@ public class DataSourceRealm extends RealmBase {
}
+ /**
+ * Confirms if the given user exists in the database.
+ *
+ * @param dbConnection The database connection to be used
+ * @param username Username to check
+ *
+ * @return {@code true} if the user exists, otherwise {@code false}
+ */
+ protected boolean validateUser(Connection dbConnection, String username) {
+ // Use the credentials lookup as a proxy for whether the user exists
+ try (PreparedStatement stmt = dbConnection.prepareStatement(preparedCredentials)) {
+ stmt.setString(1, username);
+
+ try (ResultSet rs = stmt.executeQuery()) {
+ if (rs.next()) {
+ return true;
+ }
+ }
+ } catch (SQLException e) {
+ containerLog.error(sm.getString("dataSourceRealm.validateUser.exception", username), e);
+ }
+
+ return false;
+ }
+
+
@Override
protected Principal getPrincipal(String username) {
Connection dbConnection = open();
if (dbConnection == null) {
- return new GenericPrincipal(username, null);
+ return null;
}
try {
- return new GenericPrincipal(username, getRoles(dbConnection, username));
+ if (!validateUser(dbConnection, username)) {
+ return null;
+ }
+ List<String> roles = getRoles(dbConnection, username);
+ if (roles == null) {
+ return null;
+ }
+ return new GenericPrincipal(username, roles);
} finally {
close(dbConnection);
}
-
}
+
/**
* Return the roles associated with the given username.
*
* @param username Username for which roles should be retrieved
*
- * @return an array list of the role names
+ * @return an array list of the role names or {@code null} if the lookup fails
*/
protected ArrayList<String> getRoles(String username) {
@@ -490,14 +533,14 @@ public class DataSourceRealm extends RealmBase {
* @param dbConnection The database connection to be used
* @param username Username for which roles should be retrieved
*
- * @return an array list of the role names
+ * @return an array list of the role names or {@code null} if the lookup fails
*/
protected ArrayList<String> getRoles(Connection dbConnection, String username) {
if (allRolesMode != AllRolesMode.STRICT_MODE && !isRoleStoreDefined()) {
// Using an authentication only configuration and no role store has
// been defined so don't spend cycles looking
- return null;
+ return new ArrayList<>(0);
}
try (PreparedStatement stmt = dbConnection.prepareStatement(preparedRoles)) {
diff --git a/java/org/apache/catalina/realm/LocalStrings.properties b/java/org/apache/catalina/realm/LocalStrings.properties
index 36abdca052..4e0cf9e312 100644
--- a/java/org/apache/catalina/realm/LocalStrings.properties
+++ b/java/org/apache/catalina/realm/LocalStrings.properties
@@ -40,6 +40,7 @@ dataSourceRealm.noUserCredCol=No column was specified for user credentials
dataSourceRealm.noUserNameCol=No column was specified for user names
dataSourceRealm.noUserTable=No user table was specified
dataSourceRealm.roleConfigMismatch=Role configuration is incomplete: both userRoleTable and roleNameCol must be specified together
+dataSourceRealm.validateUser.exception=Exception validating that user [{0}] exists
jaasCallback.username=Returned username [{0}]
diff --git a/test/org/apache/catalina/realm/TestDataSourceRealm.java b/test/org/apache/catalina/realm/TestDataSourceRealm.java
index 4b96c282c2..b897f1553b 100644
--- a/test/org/apache/catalina/realm/TestDataSourceRealm.java
+++ b/test/org/apache/catalina/realm/TestDataSourceRealm.java
@@ -148,7 +148,14 @@ public class TestDataSourceRealm extends LoggingBaseTest {
List<String> roles = db.getRoles("tomcat");
Assert.assertEquals(2, roles.size());
- db.stop();
+ p = db.getPrincipal("blabla");
+ Assert.assertNull(p);
+
+ p = db.getPrincipal("random");
+ Assert.assertTrue(p instanceof GenericPrincipal);
+ gp = (GenericPrincipal) p;
+ Assert.assertEquals(0, gp.getRoles().length);
+ db.stop();
}
}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 9d7bbb7d85..2a1d71bd19 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -202,6 +202,14 @@
<code>doAuthenticate()</code> and/or <code>restoreRequest()</code> will
require modification. (markt)
</fix>
+ <fix>
+ Various improvements to the <code>DataSourceRealm</code>. A failure to
+ connect to the database or an exception during either user or role
+ lookup will now result in an authentication failure rather than a
+ partially populated Principal. For <code>CLIENT-CERT</code> and
+ <code>SPNEGO</code> authentication, the user must exist in the database
+ for authentication to succeed. (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.