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

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


The following commit(s) were added to refs/heads/main by this push:
     new 8120a12a0d Fixes from code review
8120a12a0d is described below

commit 8120a12a0dbbaafc104fa0d61a319d480b7ff92c
Author: remm <[email protected]>
AuthorDate: Fri Jul 10 14:17:44 2026 +0200

    Fixes from code review
    
    Take advantage of rollback to improve save reliability.
    Clear out transient memory changes only if save is successful,
---
 .../catalina/users/DataSourceUserDatabase.java     | 156 +++++++++++++--------
 java/org/apache/catalina/users/GenericGroup.java   |   2 +-
 java/org/apache/catalina/users/GenericRole.java    |   2 +-
 java/org/apache/catalina/users/GenericUser.java    |   2 +-
 .../apache/catalina/users/LocalStrings.properties  |   1 +
 .../catalina/users/TestDataSourceUserDatabase.java |   3 +
 webapps/docs/changelog.xml                         |   5 +
 7 files changed, 110 insertions(+), 61 deletions(-)

diff --git a/java/org/apache/catalina/users/DataSourceUserDatabase.java 
b/java/org/apache/catalina/users/DataSourceUserDatabase.java
index 119d36a5b4..6412161d18 100644
--- a/java/org/apache/catalina/users/DataSourceUserDatabase.java
+++ b/java/org/apache/catalina/users/DataSourceUserDatabase.java
@@ -153,13 +153,13 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
 
 
     /**
-     * The generated string for the groups PreparedStatement
+     * The generated string for the user groups relationship PreparedStatement
      */
     private String preparedUserGroups = null;
 
 
     /**
-     * The generated string for the groups PreparedStatement
+     * The generated string for the group roles relationship PreparedStatement
      */
     private String preparedGroupRoles = null;
 
@@ -231,13 +231,13 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
 
 
     /**
-     * The table that holds user data.
+     * The table that holds group data.
      */
     protected String groupTable = null;
 
 
     /**
-     * The table that holds user data.
+     * The table that holds role data.
      */
     protected String roleTable = null;
 
@@ -785,25 +785,22 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                     if (rs.getString(1) != null) {
                         String description = (roleAndGroupDescriptionCol != 
null) ? rs.getString(2) : null;
                         ArrayList<Role> groupRoles = new ArrayList<>();
-                        if (groupName != null) {
-                            groupName = groupName.trim();
-                            try (PreparedStatement stmt2 = 
dbConnection.prepareStatement(preparedGroupRoles)) {
-                                stmt2.setString(1, groupName);
-                                try (ResultSet rs2 = stmt2.executeQuery()) {
-                                    while (rs2.next()) {
-                                        String roleName = rs2.getString(1);
-                                        if (roleName != null) {
-                                            Role groupRole = 
findRoleInternal(dbConnection, roleName);
-                                            if (groupRole != null) {
-                                                groupRoles.add(groupRole);
-                                            }
+                        try (PreparedStatement stmt2 = 
dbConnection.prepareStatement(preparedGroupRoles)) {
+                            stmt2.setString(1, groupName);
+                            try (ResultSet rs2 = stmt2.executeQuery()) {
+                                while (rs2.next()) {
+                                    String roleName = rs2.getString(1);
+                                    if (roleName != null) {
+                                        Role groupRole = 
findRoleInternal(dbConnection, roleName);
+                                        if (groupRole != null) {
+                                            groupRoles.add(groupRole);
                                         }
                                     }
                                 }
-                            } catch (SQLException e) {
-                                
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
-                                return null;
                             }
+                        } catch (SQLException e) {
+                            
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                            return null;
                         }
                         group = new GenericGroup<>(this, groupName, 
description, groupRoles);
                     }
@@ -1059,9 +1056,17 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                     Boolean.toString(isRoleStoreDefined()), 
Boolean.toString(isGroupStoreDefined())));
         }
 
+        if (!isUserStoreDefined()) {
+            throw new 
IllegalArgumentException(sm.getString("dataSourceUserDatabase.noUserConfiguration",
+                    userTable, userNameCol, userCredCol, userRoleTable, 
roleNameCol));
+        }
+
         dbWriteLock.lock();
         try {
 
+            // Basic user functionality is mandatory
+            // This includes storing user name, credential, and a list of roles
+
             StringBuilder temp = new StringBuilder("SELECT ");
             temp.append(userCredCol);
             if (userFullNameCol != null) {
@@ -1232,9 +1237,42 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
         dbWriteLock.lock();
         try {
             try {
-                saveInternal(dbConnection);
+                dbConnection.setAutoCommit(false);
+                Exception exception = saveInternal(dbConnection);
+                if (exception.getSuppressed().length > 0) {
+                    // Some exception occurred so rollback everything
+                    try {
+                        dbConnection.rollback();
+                    } catch (SQLException e) {
+                        exception.addSuppressed(e);
+                    }
+                    
log.error(sm.getString("dataSourceUserDatabase.exception"), exception);
+                    throw exception;
+                }
+                // Commit all changes to the database
+                dbConnection.commit();
+                // Everything is committed successfully, we can now clear all 
un-persisted changes
+                removedRoles.clear();
+                if (isRoleStoreDefined()) {
+                    createdRoles.clear();
+                    modifiedRoles.clear();
+                }
+                if (isGroupStoreDefined()) {
+                    removedGroups.clear();
+                    createdGroups.clear();
+                    modifiedGroups.clear();
+                }
+                removedUsers.clear();
+                createdUsers.clear();
+                modifiedUsers.clear();
             } finally {
-                closeConnection(dbConnection);
+                try {
+                    dbConnection.setAutoCommit(true);
+                } catch (SQLException e) {
+                    // Ignore, this is for cleanup
+                } finally {
+                    closeConnection(dbConnection);
+                }
             }
         } finally {
             dbWriteLock.unlock();
@@ -1245,9 +1283,11 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
      * Save all pending changes to the database using the provided connection.
      *
      * @param dbConnection The database connection to use
+     * @return an Exception containing any error that occurred while saving 
the database as suppressed exception
      */
-    protected void saveInternal(Connection dbConnection) {
+    protected Exception saveInternal(Connection dbConnection) {
 
+        Exception mainException = new 
Exception(sm.getString("dataSourceUserDatabase.exception"));
         StringBuilder temp = null;
         StringBuilder tempRelation;
         StringBuilder tempRelationDelete = null;
@@ -1278,23 +1318,22 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                             stmt.setString(1, role.getRolename());
                             stmt.executeUpdate();
                         } catch (SQLException e) {
-                            
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                            mainException.addSuppressed(e);
                         }
                     }
                     try (PreparedStatement stmt = 
dbConnection.prepareStatement(tempRelationDelete2.toString())) {
                         stmt.setString(1, role.getRolename());
                         stmt.executeUpdate();
                     } catch (SQLException e) {
-                        
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                        mainException.addSuppressed(e);
                     }
                     try (PreparedStatement stmt = 
dbConnection.prepareStatement(temp.toString())) {
                         stmt.setString(1, role.getRolename());
                         stmt.executeUpdate();
                     } catch (SQLException e) {
-                        
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                        mainException.addSuppressed(e);
                     }
                 }
-                removedRoles.clear();
             }
 
             // Created roles
@@ -1318,10 +1357,9 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                         }
                         stmt.executeUpdate();
                     } catch (SQLException e) {
-                        
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                        mainException.addSuppressed(e);
                     }
                 }
-                createdRoles.clear();
             }
 
             // Modified roles
@@ -1337,13 +1375,12 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                         stmt.setString(2, role.getRolename());
                         stmt.executeUpdate();
                     } catch (SQLException e) {
-                        
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                        mainException.addSuppressed(e);
                     }
                 }
-                modifiedRoles.clear();
             }
 
-        } else if (userRoleTable != null && roleNameCol != null) {
+        } else {
             // Only remove role from users
             tempRelationDelete = new StringBuilder("DELETE FROM ");
             tempRelationDelete.append(userRoleTable);
@@ -1355,10 +1392,9 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                     stmt.setString(1, role.getRolename());
                     stmt.executeUpdate();
                 } catch (SQLException e) {
-                    
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                    mainException.addSuppressed(e);
                 }
             }
-            removedRoles.clear();
         }
 
         if (isGroupStoreDefined()) {
@@ -1393,22 +1429,21 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                         stmt.setString(1, group.getGroupname());
                         stmt.executeUpdate();
                     } catch (SQLException e) {
-                        
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                        mainException.addSuppressed(e);
                     }
                     try (PreparedStatement stmt = 
dbConnection.prepareStatement(tempRelationDelete2.toString())) {
                         stmt.setString(1, group.getGroupname());
                         stmt.executeUpdate();
                     } catch (SQLException e) {
-                        
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                        mainException.addSuppressed(e);
                     }
                     try (PreparedStatement stmt = 
dbConnection.prepareStatement(temp.toString())) {
                         stmt.setString(1, group.getGroupname());
                         stmt.executeUpdate();
                     } catch (SQLException e) {
-                        
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                        mainException.addSuppressed(e);
                     }
                 }
-                removedGroups.clear();
             }
 
             // Created groups
@@ -1432,7 +1467,7 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                         }
                         stmt.executeUpdate();
                     } catch (SQLException e) {
-                        
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                        mainException.addSuppressed(e);
                     }
                     Iterator<Role> roles = group.getRoles();
                     while (roles.hasNext()) {
@@ -1442,11 +1477,10 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                             stmt.setString(2, role.getRolename());
                             stmt.executeUpdate();
                         } catch (SQLException e) {
-                            
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                            mainException.addSuppressed(e);
                         }
                     }
                 }
-                createdGroups.clear();
             }
 
             // Modified groups
@@ -1465,14 +1499,14 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                             stmt.setString(2, group.getGroupname());
                             stmt.executeUpdate();
                         } catch (SQLException e) {
-                            
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                            mainException.addSuppressed(e);
                         }
                     }
                     try (PreparedStatement stmt = 
dbConnection.prepareStatement(groupRoleRelationDelete)) {
                         stmt.setString(1, group.getGroupname());
                         stmt.executeUpdate();
                     } catch (SQLException e) {
-                        
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                        mainException.addSuppressed(e);
                     }
                     Iterator<Role> roles = group.getRoles();
                     while (roles.hasNext()) {
@@ -1482,11 +1516,10 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                             stmt.setString(2, role.getRolename());
                             stmt.executeUpdate();
                         } catch (SQLException e) {
-                            
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                            mainException.addSuppressed(e);
                         }
                     }
                 }
-                modifiedGroups.clear();
             }
 
         }
@@ -1538,7 +1571,7 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                         stmt.setString(1, user.getUsername());
                         stmt.executeUpdate();
                     } catch (SQLException e) {
-                        
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                        mainException.addSuppressed(e);
                     }
                 }
                 if (userGroupRelationDelete != null) {
@@ -1546,17 +1579,16 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                         stmt.setString(1, user.getUsername());
                         stmt.executeUpdate();
                     } catch (SQLException e) {
-                        
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                        mainException.addSuppressed(e);
                     }
                 }
                 try (PreparedStatement stmt = 
dbConnection.prepareStatement(temp.toString())) {
                     stmt.setString(1, user.getUsername());
                     stmt.executeUpdate();
                 } catch (SQLException e) {
-                    
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                    mainException.addSuppressed(e);
                 }
             }
-            removedUsers.clear();
         }
 
         // Created users
@@ -1582,7 +1614,7 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                     }
                     stmt.executeUpdate();
                 } catch (SQLException e) {
-                    
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                    mainException.addSuppressed(e);
                 }
                 if (userRoleRelation != null) {
                     Iterator<Role> roles = user.getRoles();
@@ -1593,7 +1625,7 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                             stmt.setString(2, role.getRolename());
                             stmt.executeUpdate();
                         } catch (SQLException e) {
-                            
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                            mainException.addSuppressed(e);
                         }
                     }
                 }
@@ -1606,12 +1638,11 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                             stmt.setString(2, group.getGroupname());
                             stmt.executeUpdate();
                         } catch (SQLException e) {
-                            
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                            mainException.addSuppressed(e);
                         }
                     }
                 }
             }
-            createdUsers.clear();
         }
 
         // Modified users
@@ -1636,14 +1667,14 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                     }
                     stmt.executeUpdate();
                 } catch (SQLException e) {
-                    
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                    mainException.addSuppressed(e);
                 }
                 if (userRoleRelationDelete != null) {
                     try (PreparedStatement stmt = 
dbConnection.prepareStatement(userRoleRelationDelete)) {
                         stmt.setString(1, user.getUsername());
                         stmt.executeUpdate();
                     } catch (SQLException e) {
-                        
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                        mainException.addSuppressed(e);
                     }
                 }
                 if (userGroupRelationDelete != null) {
@@ -1651,7 +1682,7 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                         stmt.setString(1, user.getUsername());
                         stmt.executeUpdate();
                     } catch (SQLException e) {
-                        
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                        mainException.addSuppressed(e);
                     }
                 }
                 if (userRoleRelation != null) {
@@ -1663,7 +1694,7 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                             stmt.setString(2, role.getRolename());
                             stmt.executeUpdate();
                         } catch (SQLException e) {
-                            
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                            mainException.addSuppressed(e);
                         }
                     }
                 }
@@ -1676,14 +1707,14 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
                             stmt.setString(2, group.getGroupname());
                             stmt.executeUpdate();
                         } catch (SQLException e) {
-                            
log.error(sm.getString("dataSourceUserDatabase.exception"), e);
+                            mainException.addSuppressed(e);
                         }
                     }
                 }
             }
-            modifiedUsers.clear();
         }
 
+        return mainException;
     }
 
     @Override
@@ -1691,6 +1722,15 @@ public class DataSourceUserDatabase extends 
SparseUserDatabase {
         return connectionSuccess;
     }
 
+    /**
+     * User storage is mandatory.
+     *
+     * @return true when users are properly configured
+     */
+    protected boolean isUserStoreDefined() {
+        return userTable != null && userNameCol != null && userCredCol != null 
&& userRoleTable != null && roleNameCol != null;
+    }
+
     /**
      * Only use groups if the tables are fully defined.
      *
diff --git a/java/org/apache/catalina/users/GenericGroup.java 
b/java/org/apache/catalina/users/GenericGroup.java
index 1d314a14e0..e0b6c9b926 100644
--- a/java/org/apache/catalina/users/GenericGroup.java
+++ b/java/org/apache/catalina/users/GenericGroup.java
@@ -32,7 +32,7 @@ import org.apache.catalina.UserDatabase;
  * Concrete implementation of {@link org.apache.catalina.Group} for a {@link 
UserDatabase}.
  * </p>
  *
- * @param <UD> The specific type of UserDase with which this group is 
associated
+ * @param <UD> The specific type of UserDatabase with which this group is 
associated
  */
 public class GenericGroup<UD extends UserDatabase> extends AbstractGroup {
 
diff --git a/java/org/apache/catalina/users/GenericRole.java 
b/java/org/apache/catalina/users/GenericRole.java
index 793c5c480a..22f2f28d53 100644
--- a/java/org/apache/catalina/users/GenericRole.java
+++ b/java/org/apache/catalina/users/GenericRole.java
@@ -25,7 +25,7 @@ import org.apache.catalina.UserDatabase;
  * Concrete implementation of {@link org.apache.catalina.Role} for a {@link 
UserDatabase}.
  * </p>
  *
- * @param <UD> The specific type of UserDase with which this role is associated
+ * @param <UD> The specific type of UserDatabase with which this role is 
associated
  */
 public class GenericRole<UD extends UserDatabase> extends AbstractRole {
 
diff --git a/java/org/apache/catalina/users/GenericUser.java 
b/java/org/apache/catalina/users/GenericUser.java
index ca3724a213..6f0eee236b 100644
--- a/java/org/apache/catalina/users/GenericUser.java
+++ b/java/org/apache/catalina/users/GenericUser.java
@@ -30,7 +30,7 @@ import org.apache.catalina.UserDatabase;
  * Concrete implementation of {@link org.apache.catalina.User} for a {@link 
UserDatabase}.
  * </p>
  *
- * @param <UD> The specific type of UserDase with which this role is associated
+ * @param <UD> The specific type of UserDatabase with which this user is 
associated
  */
 public class GenericUser<UD extends UserDatabase> extends AbstractUser {
 
diff --git a/java/org/apache/catalina/users/LocalStrings.properties 
b/java/org/apache/catalina/users/LocalStrings.properties
index 56bea7c03f..902730208b 100644
--- a/java/org/apache/catalina/users/LocalStrings.properties
+++ b/java/org/apache/catalina/users/LocalStrings.properties
@@ -15,6 +15,7 @@
 
 dataSourceUserDatabase.exception=Exception accessing the database
 dataSourceUserDatabase.features=DataSource UserDatabase features: User <-> 
Role [{0}], Roles [{1}], Groups [{2}]
+dataSourceUserDatabase.noUserConfiguration=DataSource UserDatabase has no 
properly configured user storage defined: userTable [{0}] userNameCol [{1}] 
userCredCol [{2}] userRoleTable [{3}] roleNameCol [{4}]
 
 memoryUserDatabase.fileClose=Failed to close [{0}]
 memoryUserDatabase.fileDelete=Failed to delete [{0}]
diff --git a/test/org/apache/catalina/users/TestDataSourceUserDatabase.java 
b/test/org/apache/catalina/users/TestDataSourceUserDatabase.java
index d3908d9b51..326bf45bd2 100644
--- a/test/org/apache/catalina/users/TestDataSourceUserDatabase.java
+++ b/test/org/apache/catalina/users/TestDataSourceUserDatabase.java
@@ -228,6 +228,9 @@ public class TestDataSourceUserDatabase extends 
LoggingBaseTest {
         randomUser = db.findUser("random");
         Assert.assertTrue("No group for user", 
randomUser.isInGroup(userGroup));
 
+        Iterator<Role> roles = db.getRoles();
+        Assert.assertTrue("No roles found", roles.hasNext());
+
         db.close();
 
     }
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index a30d7270c8..54bceec742 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -243,6 +243,11 @@
         Extend <code>sessionAttributeValueClassNameFilter</code> to include
         filtering of dynamic proxy interface classes. (markt)
       </fix>
+      <fix>
+        Attempt to use rollback when persisting user data to the
+        <code>DataSourceUserDatabase</code> fails and improve error reporting.
+        (remm)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Coyote">


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

Reply via email to