This is an automated email from the ASF dual-hosted git repository.

rmaucher pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 14c79424420a24b1787307e58d3c7511a37acc4d
Author: opencode <[email protected]>
AuthorDate: Wed Oct 7 15:23:36 2026 +0200

    Fix DataSourceRealm role lookup when no role store is configured
    
    startInternal() endorses a realm with no role store (both userRoleTable
    and roleNameCol unset) but then unconditionally built the roles prepared
    statement by concatenating the unset role column and role table,
    producing the literal invalid statement SELECT null FROM null ... .
    With the default allRolesMode of strict the early guard in getRoles()
    did not short-circuit, so every role lookup ran the invalid statement,
    logged a swallowed SQLException and returned null, which failed
    authentication of valid credentials and made getPrincipal return null.
    The realm started successfully but failed 100% of authentications.
    
    Skip the role lookup whenever no role store is defined, regardless of
    allRolesMode, matching JNDIRealm and MemoryRealm which return an empty
    role set for a valid user when no role source is configured. The
    allRolesMode is interpreted centrally in RealmBase. The deliberate
    hardening of failing authentication when a configured role lookup fails
    is unchanged. The invalid statement is no longer constructed at all
    when no role store is defined.
    
    Add a Derby backed regression test for the strict mode with no role
    store configuration. It fails before the fix.
---
 .../org/apache/catalina/realm/DataSourceRealm.java | 31 ++++++++------
 .../apache/catalina/realm/TestDataSourceRealm.java | 49 ++++++++++++++++++++++
 2 files changed, 67 insertions(+), 13 deletions(-)

diff --git a/java/org/apache/catalina/realm/DataSourceRealm.java 
b/java/org/apache/catalina/realm/DataSourceRealm.java
index 8d574a9b6a..28e7b2cfdc 100644
--- a/java/org/apache/catalina/realm/DataSourceRealm.java
+++ b/java/org/apache/catalina/realm/DataSourceRealm.java
@@ -537,9 +537,9 @@ public class DataSourceRealm extends RealmBase {
      */
     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
+        if (!isRoleStoreDefined()) {
+            // No role store has been defined so there is nothing to look up.
+            // The all roles mode is interpreted centrally in RealmBase.
             return new ArrayList<>(0);
         }
 
@@ -591,18 +591,23 @@ public class DataSourceRealm extends RealmBase {
             throw new 
LifecycleException(sm.getString("dataSourceRealm.roleConfigMismatch"));
         }
 
-        // Create the roles PreparedStatement string
-        StringBuilder temp = new StringBuilder("SELECT ");
-        temp.append(roleNameCol);
-        temp.append(" FROM ");
-        temp.append(userRoleTable);
-        temp.append(" WHERE ");
-        temp.append(userNameCol);
-        temp.append(" = ?");
-        preparedRoles = temp.toString();
+        // Create the roles PreparedStatement string. When no role store is
+        // defined there is nothing to query and no statement to build.
+        if (isRoleStoreDefined()) {
+            StringBuilder temp = new StringBuilder("SELECT ");
+            temp.append(roleNameCol);
+            temp.append(" FROM ");
+            temp.append(userRoleTable);
+            temp.append(" WHERE ");
+            temp.append(userNameCol);
+            temp.append(" = ?");
+            preparedRoles = temp.toString();
+        } else {
+            preparedRoles = null;
+        }
 
         // Create the credentials PreparedStatement string
-        temp = new StringBuilder("SELECT ");
+        StringBuilder temp = new StringBuilder("SELECT ");
         temp.append(userCredCol);
         temp.append(" FROM ");
         temp.append(userTable);
diff --git a/test/org/apache/catalina/realm/TestDataSourceRealm.java 
b/test/org/apache/catalina/realm/TestDataSourceRealm.java
index cd1471a799..d0677fdd70 100644
--- a/test/org/apache/catalina/realm/TestDataSourceRealm.java
+++ b/test/org/apache/catalina/realm/TestDataSourceRealm.java
@@ -162,4 +162,53 @@ public class TestDataSourceRealm extends LoggingBaseTest {
 
         db.stop();
     }
+
+    @Test
+    public void testRealmWithoutRoleStore() throws Exception {
+
+        db = new DerbyDataSourceRealm("dsRealmNoRoles");
+        db.setUserTable("users");
+        db.setUserNameCol("user_name");
+        db.setUserCredCol("user_pass");
+
+        // Create only the users table, no role store is configured
+        Connection connection = db.open();
+        for (String sql: SIMPLE_SCHEMA.split(";")) {
+            if (sql.contains("user_roles")) {
+                continue;
+            }
+            try (Statement statement = connection.createStatement()) {
+                statement.execute(sql);
+            }
+        }
+
+        try (PreparedStatement stmt = 
connection.prepareStatement(USERS_INSERT)) {
+            stmt.setString(1, "tomcat");
+            stmt.setString(2, "password");
+            stmt.executeUpdate();
+        }
+
+        db.start();
+
+        // Default strict mode with no role store. Authentication of a valid
+        // user must succeed with an empty role list rather than fail.
+        Principal p = db.authenticate("tomcat", "bar");
+        Assert.assertNull(p);
+
+        p = db.authenticate("tomcat", "password");
+        Assert.assertTrue(p instanceof GenericPrincipal);
+        GenericPrincipal gp = (GenericPrincipal) p;
+        Assert.assertEquals(0, gp.getRoles().length);
+
+        p = db.getPrincipal("tomcat");
+        Assert.assertTrue(p instanceof GenericPrincipal);
+        gp = (GenericPrincipal) p;
+        Assert.assertEquals(0, gp.getRoles().length);
+
+        List<String> roles = db.getRoles("tomcat");
+        Assert.assertNotNull(roles);
+        Assert.assertEquals(0, roles.size());
+
+        db.stop();
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to