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">
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]