From 93048ff27cc68d7716300fd6b927f295c42c2e64 Mon Sep 17 00:00:00 2001 From: Virtually Nick Date: Mon, 25 Mar 2024 15:38:28 -0400 Subject: [PATCH] GUACAMOLE-1239: Update JDBC queries to handle case-sensitivity. --- .../auth/jdbc/HistoryTrackingConnection.java | 10 +- .../guacamole/auth/jdbc/JDBCEnvironment.java | 13 -- .../auth/jdbc/base/ActivityRecordMapper.java | 21 ++- .../jdbc/connection/ConnectionService.java | 23 ++- .../jdbc/permission/UserPermissionMapper.java | 137 +++++++++++++++++- .../jdbc/security/PasswordPolicyService.java | 9 +- .../AbstractGuacamoleTunnelService.java | 11 +- .../guacamole/auth/jdbc/user/ModeledUser.java | 3 + .../auth/jdbc/user/ModeledUserContext.java | 8 +- .../auth/jdbc/user/PasswordRecordMapper.java | 7 +- .../guacamole/auth/jdbc/user/UserMapper.java | 75 +++++++++- .../guacamole/auth/jdbc/user/UserService.java | 36 ++++- .../usergroup/UserGroupMemberUserMapper.java | 59 +++++++- .../auth/mysql/conf/MySQLEnvironment.java | 16 +- .../mysql/conf/MySQLGuacamoleProperties.java | 6 + .../connection/ConnectionRecordMapper.xml | 28 +++- .../jdbc/permission/UserPermissionMapper.xml | 66 +++++++-- .../auth/jdbc/user/PasswordRecordMapper.xml | 9 +- .../guacamole/auth/jdbc/user/UserMapper.xml | 111 ++++++++++++-- .../auth/jdbc/user/UserRecordMapper.xml | 46 +++++- .../usergroup/UserGroupMemberUserMapper.xml | 39 ++++- .../conf/PostgreSQLEnvironment.java | 9 +- .../conf/PostgreSQLGuacamoleProperties.java | 4 +- .../connection/ConnectionRecordMapper.xml | 28 +++- .../jdbc/permission/UserPermissionMapper.xml | 69 +++++++-- .../auth/jdbc/user/PasswordRecordMapper.xml | 9 +- .../guacamole/auth/jdbc/user/UserMapper.xml | 114 +++++++++++++-- .../auth/jdbc/user/UserRecordMapper.xml | 46 +++++- .../usergroup/UserGroupMemberUserMapper.xml | 51 +++++-- .../sqlserver/conf/SQLServerEnvironment.java | 19 ++- .../conf/SQLServerGuacamoleProperties.java | 7 + .../connection/ConnectionRecordMapper.xml | 28 +++- .../jdbc/permission/UserPermissionMapper.xml | 51 ++++++- .../auth/jdbc/user/PasswordRecordMapper.xml | 9 +- .../guacamole/auth/jdbc/user/UserMapper.xml | 113 +++++++++++++-- .../auth/jdbc/user/UserRecordMapper.xml | 48 +++++- .../usergroup/UserGroupMemberUserMapper.xml | 51 +++++-- .../auth/sso/conf/SSOEnvironment.java | 62 -------- 38 files changed, 1210 insertions(+), 241 deletions(-) delete mode 100644 extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/conf/SSOEnvironment.java diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/HistoryTrackingConnection.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/HistoryTrackingConnection.java index f25620bda6..cdd50e3bc0 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/HistoryTrackingConnection.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/HistoryTrackingConnection.java @@ -19,6 +19,7 @@ package org.apache.guacamole.auth.jdbc; +import com.google.inject.Inject; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -55,6 +56,12 @@ public class HistoryTrackingConnection extends DelegatingConnection { * established connections. */ private final ConnectionRecordMapper connectionRecordMapper; + + /** + * The Guacamole server environment. + */ + @Inject + private JDBCEnvironment environment; /** * Creates a new HistoryConnection that wraps the given connection, @@ -98,7 +105,8 @@ public GuacamoleTunnel connect(GuacamoleClientInformation info, connectionRecordModel.setConnectionName(this.getDelegateConnection().getName()); // Insert the connection history record to mark the start of this connection - connectionRecordMapper.insert(connectionRecordModel); + connectionRecordMapper.insert(connectionRecordModel, + environment.getCaseSensitiveUsernames()); // Include history record UUID as token ModeledConnectionRecord modeledRecord = new ModeledConnectionRecord(connectionRecordModel); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCEnvironment.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCEnvironment.java index dcc7544518..27f3d0563d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCEnvironment.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCEnvironment.java @@ -271,18 +271,5 @@ public boolean shouldUseBatchExecutor() { return true; } - - /** - * Returns a boolean value that indicates whether or not usernames should - * be treated as case-sensitive. - * - * @return - * true if usernames should be treated as case-sensitive, or false if - * usernames should be treated as case-insensitive. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public abstract boolean getCaseSensitiveUsernames() throws GuacamoleException; } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordMapper.java index 52fbf9e640..17b4571ed8 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordMapper.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordMapper.java @@ -38,11 +38,16 @@ public interface ActivityRecordMapper { * * @param record * The activity record to insert. + * + * @param caseSensitive + * Whether or not string comparisons should be done in a case-sensitive + * manner. * * @return * The number of rows inserted. */ - int insert(@Param("record") ModelType record); + int insert(@Param("record") ModelType record, + @Param("caseSensitive") boolean caseSensitive); /** * Updates the given activity record in the database, assigning an end @@ -85,6 +90,10 @@ public interface ActivityRecordMapper { * * @param limit * The maximum number of records that should be returned. + * + * @param caseSensitive + * Whether or not string comparisons should be done in a case-sensitive + * manner. * * @return * The results of the search performed with the given parameters. @@ -93,7 +102,8 @@ List search(@Param("identifier") String identifier, @Param("recordIdentifier") String recordIdentifier, @Param("terms") Collection terms, @Param("sortPredicates") List sortPredicates, - @Param("limit") int limit); + @Param("limit") int limit, + @Param("caseSensitive") boolean caseSensitive); /** * Searches for up to limit activity records that contain @@ -132,6 +142,10 @@ List search(@Param("identifier") String identifier, * when determining the permissions effectively granted to the user. If * no groups are given, only permissions directly granted to the user * will be used. + * + * @param caseSensitive + * Whether or not string comparisons should be done in a case-sensitive + * manner. * * @return * The results of the search performed with the given parameters. @@ -142,6 +156,7 @@ List searchReadable(@Param("identifier") String identifier, @Param("terms") Collection terms, @Param("sortPredicates") List sortPredicates, @Param("limit") int limit, - @Param("effectiveGroups") Collection effectiveGroups); + @Param("effectiveGroups") Collection effectiveGroups, + @Param("caseSensitive") boolean caseSensitive); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionService.java index 31f4df49c7..e08d13b0cd 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionService.java @@ -28,19 +28,20 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; -import org.apache.guacamole.language.TranslatableGuacamoleClientOverrunException; -import org.apache.guacamole.language.TranslatableMessage; -import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper; -import org.apache.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; import org.apache.guacamole.GuacamoleClientException; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleSecurityException; +import org.apache.guacamole.auth.jdbc.JDBCEnvironment; import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm; import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate; +import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; import org.apache.guacamole.auth.jdbc.base.ModeledChildDirectoryObjectService; +import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper; import org.apache.guacamole.auth.jdbc.permission.ConnectionPermissionMapper; import org.apache.guacamole.auth.jdbc.permission.ObjectPermissionMapper; +import org.apache.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; +import org.apache.guacamole.language.TranslatableGuacamoleClientOverrunException; +import org.apache.guacamole.language.TranslatableMessage; import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.ConnectionRecord; @@ -85,6 +86,12 @@ public class ConnectionService extends ModeledChildDirectoryObjectService connectionProvider; + + /** + * The server environment for retrieving configuration. + */ + @Inject + private JDBCEnvironment environment; /** * Service for creating and tracking tunnels. @@ -486,14 +493,16 @@ public List retrieveHistory(String identifier, // Bypass permission checks if the user is privileged or has System-level audit permissions if (user.isPrivileged() || user.getUser().getEffectivePermissions().getSystemPermissions().hasPermission(SystemPermission.Type.AUDIT)) searchResults = connectionRecordMapper.search(identifier, - recordIdentifier, requiredContents, sortPredicates, limit); + recordIdentifier, requiredContents, sortPredicates, limit, + environment.getCaseSensitiveUsernames()); // Otherwise only return explicitly readable history records else searchResults = connectionRecordMapper.searchReadable(identifier, user.getUser().getModel(), recordIdentifier, requiredContents, sortPredicates, limit, - user.getEffectiveUserGroups()); + user.getEffectiveUserGroups(), + environment.getCaseSensitiveUsernames()); return getObjectInstances(searchResults); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.java index f7412a4fd0..c3489e5080 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.java @@ -19,7 +19,142 @@ package org.apache.guacamole.auth.jdbc.permission; +import java.util.Collection; +import org.apache.guacamole.auth.jdbc.base.EntityModel; +import org.apache.guacamole.net.auth.permission.ObjectPermission; +import org.apache.ibatis.annotations.Param; + /** * Mapper for user permissions. */ -public interface UserPermissionMapper extends ObjectPermissionMapper {} +public interface UserPermissionMapper extends ObjectPermissionMapper { + + /** + * Deletes the given permissions from the database. If any permissions do + * not exist, they will be ignored. + * + * @param permissions + * The permissions to delete. + * + * @param caseSensitive + * Whether or not string comparisons for usernames will be done in a + * case-sensitive manner. + * + * @return + * The number of rows deleted. + */ + int delete(@Param("permissions") Collection permissions, + @Param("caseSensitive") boolean caseSensitive); + + /** + * Inserts the given permissions into the database. If any permissions + * already exist, they will be ignored. + * + * @param permissions + * The permissions to insert. + * + * @param caseSensitive + * Whether or not string comparisons for usernames will be done in a + * case-sensitive manner. + * + * @return + * The number of rows inserted. + */ + int insert(@Param("permissions") Collection permissions, + @Param("caseSensitive") boolean caseSensitive); + + /** + * Retrieves all permissions associated with the given entity (user or user + * group). + * + * @param entity + * The entity to retrieve permissions for. + * + * @param effectiveGroups + * The identifiers of all groups that should be taken into account + * when determining the permissions effectively granted to the user. If + * no groups are given, only permissions directly granted to the user + * will be used. + * + * @param caseSensitive + * Whether or not string comparisons for usernames will be done in a + * case-sensitive manner. + * + * @return + * All permissions associated with the given entity. + */ + Collection select(@Param("entity") EntityModel entity, + @Param("effectiveGroups") Collection effectiveGroups, + @Param("caseSensitive") boolean caseSensitive); + + /** + * Retrieve the permission of the given type associated with the given + * entity and object, if it exists. If no such permission exists, null is + * returned. + * + * @param entity + * The entity to retrieve permissions for. + * + * @param type + * The type of permission to return. + * + * @param identifier + * The identifier of the object affected by the permission to return. + * + * @param effectiveGroups + * The identifiers of all groups that should be taken into account + * when determining the permissions effectively granted to the user. If + * no groups are given, only permissions directly granted to the user + * will be used. + * + * @param caseSensitive + * Whether or not string comparisons for usernames will be done in a + * case-sensitive manner. + * + * @return + * The requested permission, or null if no such permission is granted + * to the given entity for the given object. + */ + ObjectPermissionModel selectOne(@Param("entity") EntityModel entity, + @Param("type") ObjectPermission.Type type, + @Param("identifier") String identifier, + @Param("effectiveGroups") Collection effectiveGroups, + @Param("caseSensitive") boolean caseSensitive); + + /** + * Retrieves the subset of the given identifiers for which the given entity + * has at least one of the given permissions. + * + * @param entity + * The entity to check permissions of. + * + * @param permissions + * The permissions to check. An identifier will be included in the + * resulting collection if at least one of these permissions is granted + * for the associated object + * + * @param identifiers + * The identifiers of the objects affected by the permissions being + * checked. + * + * @param effectiveGroups + * The identifiers of all groups that should be taken into account + * when determining the permissions effectively granted to the user. If + * no groups are given, only permissions directly granted to the user + * will be used. + * + * @param caseSensitive + * Whether or not string comparisons for usernames will be done in a + * case-sensitive manner. + * + * @return + * A collection containing the subset of identifiers for which at least + * one of the specified permissions is granted. + */ + Collection selectAccessibleIdentifiers(@Param("entity") EntityModel entity, + @Param("permissions") Collection permissions, + @Param("identifiers") Collection identifiers, + @Param("effectiveGroups") Collection effectiveGroups, + @Param("caseSenstive") boolean caseSensitive); + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java index 148d5a2f94..639ed5e43c 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/security/PasswordPolicyService.java @@ -131,16 +131,21 @@ private boolean matches(String str, Pattern... patterns) { * @return * true if the given password matches any of the user's previous * passwords, up to the specified limit, false otherwise. + * + * @throws GuacamoleException + * If an error occurs accessing environment information used for + * retrieving configuration values. */ private boolean matchesPreviousPasswords(String password, String username, - int historySize) { + int historySize) throws GuacamoleException { // No need to compare if no history is relevant if (historySize <= 0) return false; // Check password against all recorded hashes - List history = passwordRecordMapper.select(username, historySize); + List history = passwordRecordMapper.select(username, + historySize, environment.getCaseSensitiveUsernames()); for (PasswordRecordModel record : history) { byte[] hash = encryptionService.createPasswordHash(password, record.getPasswordSalt()); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java index 236d184854..e0a6091b07 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java @@ -34,6 +34,7 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; +import org.apache.guacamole.auth.jdbc.JDBCEnvironment; import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; import org.apache.guacamole.auth.jdbc.connection.ModeledConnection; import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; @@ -167,6 +168,12 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS */ @Inject private SharedConnectionMap connectionMap; + + /** + * The Guacamole server environment. + */ + @Inject + private JDBCEnvironment environment; /** * All active connections through the tunnel having a given UUID. @@ -470,7 +477,9 @@ private GuacamoleTunnel assignGuacamoleTunnel(ActiveConnectionRecord activeConne // Record new active connection Runnable cleanupTask = new ConnectionCleanupTask(activeConnection); try { - connectionRecordMapper.insert(activeConnection.getModel()); // This MUST happen before getUUID() is invoked, to ensure the ID driving the UUID exists + // This MUST happen before getUUID() is invoked, to ensure the ID driving the UUID exists + connectionRecordMapper.insert(activeConnection.getModel(), + environment.getCaseSensitiveUsernames()); activeTunnels.put(activeConnection.getUUID().toString(), activeConnection); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java index 4ac0b95dca..3b0ae67f4f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java @@ -795,6 +795,9 @@ public boolean isCaseSensitive() { return environment.getCaseSensitiveUsernames(); } catch (GuacamoleException e) { + logger.error("Failed to retrieve the configuration for case-sensitive usernames: {}." + + " Usernames comparisons will be case-sensitive.", e.getMessage()); + logger.debug("Exception caught when attempting to read the configuration.", e); return true; } } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java index e5ad5d2eae..f90c158552 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java @@ -176,8 +176,12 @@ public void init(ModeledAuthenticatedUser currentUser) { * invocation has any effect. If this function is never invoked, no * activity record will be recorded, including when this UserContext is * invalidated. + * + * @throws GuacamoleException + * If an error occurs retrieving configuration information from the + * environment. */ - public void recordUserLogin() { + public void recordUserLogin() throws GuacamoleException { // Do nothing if invoked multiple times if (userRecord != null) @@ -190,7 +194,7 @@ public void recordUserLogin() { userRecord.setRemoteHost(getCurrentUser().getCredentials().getRemoteAddress()); // Insert record representing login - userRecordMapper.insert(userRecord); + userRecordMapper.insert(userRecord, environment.getCaseSensitiveUsernames()); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.java index 903ff6562d..c505afcc9e 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.java @@ -38,6 +38,10 @@ public interface PasswordRecordMapper extends ModeledDirectoryObjectMapper select(@Param("username") String username, - @Param("maxHistorySize") int maxHistorySize); + @Param("maxHistorySize") int maxHistorySize, + @Param("caseSensitive") boolean caseSensitive); /** * Inserts the given password record. Old records exceeding the maximum diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserMapper.java index cf829be588..243618c2b0 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserMapper.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserMapper.java @@ -19,6 +19,7 @@ package org.apache.guacamole.auth.jdbc.user; +import java.util.Collection; import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper; import org.apache.ibatis.annotations.Param; @@ -33,10 +34,82 @@ public interface UserMapper extends ModeledDirectoryObjectMapper { * * @param username * The username of the user to return. + * + * @param caseSensitive + * true if the search should evaluate usernames in a case-sensitive + * manner, otherwise false. * * @return * The user having the given username, or null if no such user exists. */ - UserModel selectOne(@Param("username") String username); + UserModel selectOne(@Param("username") String username, + @Param("caseSensitive") boolean caseSensitive); + + /** + * Selects all users which have the given identifiers. If an identifier + * has no corresponding object, it will be ignored. This should only be + * called on behalf of a system administrator. If users are needed by a + * non-administrative user who must have explicit read rights, use + * selectReadable() instead. + * + * @param identifiers + * The identifiers of the users to return. + * + * @param caseSensitive + * true if the query should evaluate username identifiers in a + * case-sensitive manner, otherwise false. + * + * @return + * A Collection of all objects having the given identifiers. + */ + Collection select(@Param("identifiers") Collection identifiers, + @Param("caseSensitive") boolean caseSensitive); + + /** + * Selects all users which have the given identifiers and are explicitly + * readable by the given user. If an identifier has no corresponding + * object, or the corresponding user is unreadable, it will be ignored. + * If users are needed by a system administrator (who, by definition, + * does not need explicit read rights), use select() instead. + * + * @param user + * The user whose permissions should determine whether an object + * is returned. + * + * @param identifiers + * The identifiers of the users to return. + * + * @param effectiveGroups + * The identifiers of any known effective groups that should be taken + * into account, such as those defined externally to the database. + * + * @param caseSensitive + * true if the query should evaluate username identifiers in a + * case-sensitive manner, otherwise false. + * + * @return + * A Collection of all objects having the given identifiers. + */ + Collection selectReadable(@Param("user") UserModel user, + @Param("identifiers") Collection identifiers, + @Param("effectiveGroups") Collection effectiveGroups, + @Param("caseSensitive") boolean caseSensitive); + + /** + * Deletes the given user from the database. If the user does not + * exist, this operation has no effect. + * + * @param identifier + * The identifier of the user to delete. + * + * @param caseSensitive + * true if the query should evaluate username identifiers in a + * case-sensitive manner, otherwise false. + * + * @return + * The number of rows deleted. + */ + int delete(@Param("identifier") String identifier, + @Param("caseSensitive") boolean caseSensitive); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java index 08acff2a65..9478fd3719 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java @@ -33,6 +33,7 @@ import org.apache.guacamole.GuacamoleClientException; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleUnsupportedException; +import org.apache.guacamole.auth.jdbc.JDBCEnvironment; import org.apache.guacamole.auth.jdbc.base.ActivityRecordModel; import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm; import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate; @@ -155,6 +156,12 @@ public class UserService extends ModeledDirectoryObjectService getObjectMapper() { @@ -241,7 +248,8 @@ protected void beforeCreate(ModeledAuthenticatedUser user, User object, throw new GuacamoleClientException("The username must not be blank."); // Do not create duplicate users - Collection existing = userMapper.select(Collections.singleton(model.getIdentifier())); + Collection existing = userMapper.select(Collections.singleton( + model.getIdentifier()), environment.getCaseSensitiveUsernames()); if (!existing.isEmpty()) throw new GuacamoleClientException("User \"" + model.getIdentifier() + "\" already exists."); @@ -277,7 +285,8 @@ protected void beforeUpdate(ModeledAuthenticatedUser user, throw new GuacamoleClientException("The username must not be blank."); // Check whether such a user is already present - UserModel existing = userMapper.selectOne(model.getIdentifier()); + UserModel existing = userMapper.selectOne(model.getIdentifier(), + environment.getCaseSensitiveUsernames()); if (existing != null) { // Do not rename to existing user @@ -337,6 +346,17 @@ protected void beforeDelete(ModeledAuthenticatedUser user, String identifier) th throw new GuacamoleUnsupportedException("Deleting your own user is not allowed."); } + + @Override + public void deleteObject(ModeledAuthenticatedUser user, String identifier) + throws GuacamoleException { + + beforeDelete(user, identifier); + + // Delete object + userMapper.delete(identifier, environment.getCaseSensitiveUsernames()); + + } @Override protected boolean isValidIdentifier(String identifier) { @@ -375,7 +395,8 @@ public ModeledAuthenticatedUser retrieveAuthenticatedUser(AuthenticationProvider String password = credentials.getPassword(); // Retrieve corresponding user model, if such a user exists - UserModel userModel = userMapper.selectOne(username); + UserModel userModel = userMapper.selectOne(username, + environment.getCaseSensitiveUsernames()); if (userModel == null) return null; @@ -416,7 +437,8 @@ public ModeledUser retrieveUser(AuthenticationProvider authenticationProvider, AuthenticatedUser authenticatedUser) throws GuacamoleException { // Retrieve corresponding user model, if such a user exists - UserModel userModel = userMapper.selectOne(authenticatedUser.getIdentifier()); + UserModel userModel = userMapper.selectOne(authenticatedUser.getIdentifier(), + environment.getCaseSensitiveUsernames()); if (userModel == null) return null; @@ -614,14 +636,16 @@ public List retrieveHistory(String username, // Bypass permission checks if the user is privileged or has System-level audit permissions if (user.isPrivileged() || user.getUser().getEffectivePermissions().getSystemPermissions().hasPermission(SystemPermission.Type.AUDIT)) searchResults = userRecordMapper.search(username, recordIdentifier, - requiredContents, sortPredicates, limit); + requiredContents, sortPredicates, limit, + environment.getCaseSensitiveUsernames()); // Otherwise only return explicitly readable history records else searchResults = userRecordMapper.searchReadable(username, user.getUser().getModel(), recordIdentifier, requiredContents, sortPredicates, limit, - user.getEffectiveUserGroups()); + user.getEffectiveUserGroups(), + environment.getCaseSensitiveUsernames()); return getObjectInstances(searchResults); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMemberUserMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMemberUserMapper.java index b668d07fec..3b2c3175b1 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMemberUserMapper.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMemberUserMapper.java @@ -19,10 +19,67 @@ package org.apache.guacamole.auth.jdbc.usergroup; +import java.util.Collection; import org.apache.guacamole.auth.jdbc.base.ObjectRelationMapper; +import org.apache.ibatis.annotations.Param; /** * Mapper for the one-to-many relationship between a user group and its user * members. */ -public interface UserGroupMemberUserMapper extends ObjectRelationMapper {} +public interface UserGroupMemberUserMapper extends ObjectRelationMapper { + + /** + * Inserts rows as necessary to establish the one-to-many relationship + * represented by the RelatedObjectSet between the given parent and + * children. If the relation for any parent/child pair is already present, + * no attempt is made to insert a new row for that relation. + * + * @param parent + * The model of the object on the parent side of the one-to-many + * relationship represented by the RelatedObjectSet. + * + * @param children + * The identifiers of the objects on the child side of the one-to-many + * relationship represented by the RelatedObjectSet. + * + * @param caseSensitive + * True if username case should be respected when looking up the username + * in the guacamole_entity table, or false if the query to the + * guacamole_entity table should be done case-insensitively. + * + * @return + * The number of rows inserted. + */ + int insert(@Param("parent") UserGroupModel parent, + @Param("children") Collection children, + @Param("caseSensitive") boolean caseSensitive); + + /** + * Deletes rows as necessary to modify the one-to-many relationship + * represented by the RelatedObjectSet between the given parent and + * children. If the relation for any parent/child pair does not exist, + * that specific relation is ignored, and deletion proceeds with the + * remaining relations. + * + * @param parent + * The model of the object on the parent side of the one-to-many + * relationship represented by the RelatedObjectSet. + * + * @param children + * The identifiers of the objects on the child side of the one-to-many + * relationship represented by the RelatedObjectSet. + * + * @param caseSensitive + * True if username case should be respected when looking up the username + * in the guacamole_entity table, or false if the query to the + * guacamole_entity table should be done case-insensitively. + * + * @return + * The number of rows deleted. + */ + int delete(@Param("parent") UserGroupModel parent, + @Param("children") Collection children, + @Param("caseSesitive") boolean caseSensitive); + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLEnvironment.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLEnvironment.java index c5c142ef37..08ccf54043 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLEnvironment.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLEnvironment.java @@ -446,11 +446,23 @@ public boolean enforceAccessWindowsForActiveSessions() throws GuacamoleException @Override public boolean getCaseSensitiveUsernames() throws GuacamoleException { - return getProperty( + // Get the configured value for the property. + boolean caseSensitiveUsernames = getProperty( MySQLGuacamoleProperties.MYSQL_CASE_SENSITIVE_USERNAMES, - false + super.getCaseSensitiveUsernames() ); + // If property has been set to true, warn the admin. + if (caseSensitiveUsernames) + logger.warn("You have enabled case-sensitive usernames; however, " + + "MySQL's default collations do not support case-sensitive " + + "string comparisons. If you really want case-sensitive " + + "usernames you will need to configure your database " + + "appropriately."); + + // Return the configured setting. + return caseSensitiveUsernames; + } } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLGuacamoleProperties.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLGuacamoleProperties.java index d2b80987a1..bbb1a29b01 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLGuacamoleProperties.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLGuacamoleProperties.java @@ -303,6 +303,12 @@ private MySQLGuacamoleProperties() {} }; + /** + * A property used to configure whether or not usernames within the MySQL + * JDBC module should be treated as case-sensitive. Be aware that MySQL's + * default database collations do not do case-sensitive comparisons, so in + * many cases they will effectively be case-insensitive. + */ public static final BooleanGuacamoleProperty MYSQL_CASE_SENSITIVE_USERNAMES = new BooleanGuacamoleProperty() { diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml index 11ca348ddc..f9e2e600ca 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -61,7 +61,14 @@ (SELECT user_id FROM guacamole_user JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id WHERE - guacamole_entity.name = #{record.username,jdbcType=VARCHAR} + + + guacamole_entity.name = #{record.username,jdbcType=VARCHAR} + + + LOWER(guacamole_entity.name) = LOWER(#{record.username,jdbcType=VARCHAR}) + + AND guacamole_entity.type = 'USER'), #{record.username,jdbcType=VARCHAR}, #{record.startDate,jdbcType=TIMESTAMP}, @@ -112,7 +119,15 @@ guacamole_connection_history.user_id IN ( SELECT user_id FROM guacamole_user - WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN username) > 0 + WHERE + + + POSITION(#{term.term,jdbcType=VARCHAR} IN username) > 0 + + + POSITION(LOWER(#{term.term,jdbcType=VARCHAR}) IN LOWER(username)) > 0 + + ) OR guacamole_connection_history.connection_id IN ( @@ -200,7 +215,14 @@ FROM guacamole_user JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id WHERE - POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + + + POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + + + POSITION(LOWER(#{term.term,jdbcType=VARCHAR}) IN LOWER(guacamole_entity.name)) > 0 + + AND guacamole_entity.type = 'USER' ) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.xml index 58fba1430a..f4cfab502c 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.xml @@ -68,7 +68,15 @@ AND permission = #{type,jdbcType=VARCHAR} - AND affected_entity.name = #{identifier,jdbcType=VARCHAR} + AND + + + affected_entity.name = #{identifier,jdbcType=VARCHAR} + + + LOWER(affected_entity.name) = LOWER(#{identifier,jdbcType=VARCHAR}) + + AND affected_entity.type = 'USER' @@ -86,11 +94,23 @@ - AND affected_entity.name IN - + + affected_entity.name IN + - #{identifier,jdbcType=VARCHAR} - + #{identifier,jdbcType=VARCHAR} + + + + LOWER(affected_entity.name) IN + + LOWER(#{identifier,jdbcType=VARCHAR}) + + + AND permission IN @@ -108,13 +128,26 @@ JOIN guacamole_user affected_user ON guacamole_user_permission.affected_user_id = affected_user.user_id JOIN guacamole_entity affected_entity ON affected_user.entity_id = affected_entity.entity_id WHERE - (guacamole_user_permission.entity_id, permission, affected_entity.name) IN - - (#{permission.entityID,jdbcType=INTEGER}, - #{permission.type,jdbcType=VARCHAR}, - #{permission.objectIdentifier,jdbcType=VARCHAR}) - + + + (guacamole_user_permission.entity_id, permission, affected_entity.name) IN + + (#{permission.entityID,jdbcType=INTEGER}, + #{permission.type,jdbcType=VARCHAR}, + #{permission.objectIdentifier,jdbcType=VARCHAR}) + + + + AND (guacamole_user_permission.entity_id, permission, LOWER(affected_entity.name)) IN + + (#{permission.entityID,jdbcType=INTEGER}, + #{permission.type,jdbcType=VARCHAR}, + LOWER(#{permission.objectIdentifier,jdbcType=VARCHAR})) + + + AND affected_entity.type = 'USER' @@ -140,7 +173,14 @@ AS permissions JOIN guacamole_entity affected_entity ON - affected_entity.name = permissions.affected_name + + + affected_entity.name = permissions.affected_name + + + LOWER(affected_entity.name) = LOWER(permissions.affected_name) + + AND affected_entity.type = 'USER' JOIN guacamole_user affected_user ON affected_user.entity_id = affected_entity.entity_id diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml index f3772d7f91..30f6bc6764 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml @@ -43,7 +43,14 @@ JOIN guacamole_user ON guacamole_user_password_history.user_id = guacamole_user.user_id JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id WHERE - guacamole_entity.name = #{username,jdbcType=VARCHAR} + + + guacamole_entity.name = #{username,jdbcType=VARCHAR} + + + LOWER(guacamole_entity.name) = LOWER(#{username,jdbcType=VARCHAR}) + + ORDER BY guacamole_user_password_history.password_date DESC LIMIT #{maxHistorySize} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml index 0dcfa2efcc..e6e613f4c9 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml @@ -130,10 +130,26 @@ FROM guacamole_user JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id LEFT JOIN guacamole_user_history ON guacamole_user_history.user_id = guacamole_user.user_id - WHERE guacamole_entity.name IN + WHERE + + + guacamole_entity.name + + + LOWER(guacamole_entity.name) + + + IN - #{identifier,jdbcType=VARCHAR} + open="(" separator="," close=")"> + + + #{identifier,jdbcType=VARCHAR} + + + LOWER(#{identifier,jdbcType=VARCHAR}) + + AND guacamole_entity.type = 'USER' GROUP BY guacamole_user.user_id, guacamole_entity.entity_id; @@ -145,10 +161,26 @@ FROM guacamole_user_attribute JOIN guacamole_user ON guacamole_user.user_id = guacamole_user_attribute.user_id JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id - WHERE guacamole_entity.name IN + WHERE + + + guacamole_entity.name + + + LOWER(guacamole_entity.name) + + + IN - #{identifier,jdbcType=VARCHAR} + open="(" separator="," close=")"> + + + #{identifier,jdbcType=VARCHAR} + + + LOWER(#{identifier,jdbcType=VARCHAR}) + + AND guacamole_entity.type = 'USER'; @@ -180,10 +212,26 @@ FROM guacamole_user JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id LEFT JOIN guacamole_user_history ON guacamole_user_history.user_id = guacamole_user.user_id - WHERE guacamole_entity.name IN + WHERE + + + guacamole_entity.name + + + LOWER(guacamole_entity.name) + + + IN - #{identifier,jdbcType=VARCHAR} + + + #{identifier,jdbcType=VARCHAR} + + + LOWER(#{identifier,jdbcType=VARCHAR}) + + AND guacamole_entity.type = 'USER' AND guacamole_user.user_id IN ( @@ -201,10 +249,26 @@ FROM guacamole_user_attribute JOIN guacamole_user ON guacamole_user.user_id = guacamole_user_attribute.user_id JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id - WHERE guacamole_entity.name IN + WHERE + + + guacamole_entity.name + + + LOWER(guacamole_entity.name) + + + IN - #{identifier,jdbcType=VARCHAR} + + + #{identifier,jdbcType=VARCHAR} + + + LOWER(#{identifier,jdbcType=VARCHAR}) + + AND guacamole_entity.type = 'USER' AND guacamole_user.user_id IN ( @@ -243,7 +307,14 @@ JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id LEFT JOIN guacamole_user_history ON guacamole_user_history.user_id = guacamole_user.user_id WHERE - guacamole_entity.name = #{username,jdbcType=VARCHAR} + + + guacamole_entity.name = #{username,jdbcType=VARCHAR} + + + LOWER(guacamole_entity.name) = LOWER(#{username,jdbcType=VARCHAR}) + + AND guacamole_entity.type = 'USER' GROUP BY guacamole_user.user_id, guacamole_entity.entity_id; @@ -255,7 +326,14 @@ JOIN guacamole_user ON guacamole_user.user_id = guacamole_user_attribute.user_id JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id WHERE - guacamole_entity.name = #{username,jdbcType=VARCHAR} + + + guacamole_entity.name = #{username,jdbcType=VARCHAR} + + + LOWER(guacamole_entity.name) = LOWER(#{username,jdbcType=VARCHAR}) + + AND guacamole_entity.type = 'USER' @@ -264,7 +342,14 @@ DELETE FROM guacamole_entity WHERE - name = #{identifier,jdbcType=VARCHAR} + + + name = #{identifier,jdbcType=VARCHAR} + + + LOWER(name) = LOWER(#{identifier,jdbcType=VARCHAR}) + + AND type = 'USER' diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml index 70742df4c1..69d6a01623 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml @@ -49,7 +49,14 @@ (SELECT user_id FROM guacamole_user JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id WHERE - guacamole_entity.name = #{record.username,jdbcType=VARCHAR} + + + guacamole_entity.name = #{record.username,jdbcType=VARCHAR} + + + LOWER(guacamole_entity.name) = LOWER(#{record.username,jdbcType=VARCHAR}) + + AND guacamole_entity.type = 'USER'), #{record.username,jdbcType=VARCHAR}, #{record.startDate,jdbcType=TIMESTAMP}, @@ -81,7 +88,14 @@ - guacamole_user_history.username = #{identifier,jdbcType=VARCHAR} + + + guacamole_user_history.username = #{identifier,jdbcType=VARCHAR} + + + LOWER(guacamole_user_history.username) = LOWER(#{identifier,jdbcType=VARCHAR}) + + @@ -92,7 +106,14 @@ FROM guacamole_user JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id WHERE - POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + + + POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + + + POSITION(LOWER(#{term.term,jdbcType=VARCHAR}) IN LOWER(guacamole_entity.name)) > 0 + + AND guacamole_entity.type = 'USER'), ) @@ -146,7 +167,15 @@ ) - AND guacamole_entity.name = #{identifier,jdbcType=VARCHAR} + AND + + + guacamole_entity.name = #{identifier,jdbcType=VARCHAR} + + + LOWER(guacamole_entity.name) = LOWER(#{identifier,jdbcType=VARCHAR}) + + @@ -157,7 +186,14 @@ FROM guacamole_user JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id WHERE - POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + + + POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + + + POSITION(LOWER(#{term.term,jdbcType=VARCHAR}) IN LOWER(guacamole_entity.name)) > 0 + + AND guacamole_entity.type = 'USER' ) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMemberUserMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMemberUserMapper.xml index 609d907f5a..c5f7030b92 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMemberUserMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMemberUserMapper.xml @@ -58,10 +58,26 @@ WHERE user_group_id = #{parent.objectID,jdbcType=INTEGER} AND guacamole_entity.type = 'USER' - AND guacamole_entity.name IN + AND + + + guacamole_entity.name + + + LOWER(guacamole_entity.name) + + + IN - #{identifier,jdbcType=VARCHAR} + + + #{identifier,jdbcType=VARCHAR} + + + LOWER(#{identifier,jdbcType=VARCHAR}) + + @@ -76,10 +92,25 @@ guacamole_entity.entity_id FROM guacamole_entity WHERE - guacamole_entity.name IN + + + guacamole_entity.name + + + LOWER(guacamole_entity.name) + + + IN - #{identifier} + + + #{identifier} + + + LOWER(#{identifier}) + + AND guacamole_entity.type = 'USER' AND guacamole_entity.entity_id NOT IN ( diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLEnvironment.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLEnvironment.java index 0955ab13e7..d620521fb5 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLEnvironment.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLEnvironment.java @@ -402,11 +402,14 @@ public boolean enforceAccessWindowsForActiveSessions() throws GuacamoleException @Override public boolean getCaseSensitiveUsernames() throws GuacamoleException { - // By default, PostgreSQL does use case-sensitive string searches, so - // we will honor case-sensitive usernames. + // By default, PostgreSQL does perform case-sensitive string comparisons. + // Even though usernames are generally not case-sensitive across + // most authenticaiton systems, we've elected to maintain case- + // sensitivity in this module in order to avoid surprising anyone who + // may be relying upon it. return getProperty( PostgreSQLGuacamoleProperties.POSTGRESQL_CASE_SENSITIVE_USERNAMES, - true + super.getCaseSensitiveUsernames() ); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLGuacamoleProperties.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLGuacamoleProperties.java index c3f64a1547..74db342f26 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLGuacamoleProperties.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLGuacamoleProperties.java @@ -316,8 +316,8 @@ private PostgreSQLGuacamoleProperties() {} }; /** - * A property that configures whether or not usernames should be treated as - * case-sensitive with the Postgres JDBC backend. + * A property used to configure whether or not usernames within the Postgres + * JDBC module should be treated as case-sensitive. */ public static final BooleanGuacamoleProperty POSTGRESQL_CASE_SENSITIVE_USERNAMES = new BooleanGuacamoleProperty() { diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml index 9857193b44..0889485f83 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -61,7 +61,14 @@ (SELECT user_id FROM guacamole_user JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id WHERE - guacamole_entity.name = #{record.username,jdbcType=VARCHAR} + + + guacamole_entity.name = #{record.username,jdbcType=VARCHAR} + + + LOWER(guacamole_entity.name) = LOWER(#{record.username,jdbcType=VARCHAR}) + + AND guacamole_entity.type = 'USER'::guacamole_entity_type), #{record.username,jdbcType=VARCHAR}, #{record.startDate,jdbcType=TIMESTAMP}, @@ -110,7 +117,15 @@ guacamole_connection_history.user_id IN ( SELECT user_id FROM guacamole_user - WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN username) > 0 + WHERE + + + POSITION(#{term.term,jdbcType=VARCHAR} IN username) > 0 + + + POSITION(LOWER(#{term.term,jdbcType=VARCHAR}) IN LOWER(username)) > 0 + + ) OR guacamole_connection_history.connection_id IN ( @@ -198,7 +213,14 @@ FROM guacamole_user JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id WHERE - POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + + + POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + + + POSITION(LOWER(#{term.term,jdbcType=VARCHAR}) IN LOWER(guacamole_entity.name)) > 0 + + AND guacamole_entity.type = 'USER'::guacamole_entity_type ) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.xml index a4b5f1b7c4..f96cc16a58 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.xml @@ -28,7 +28,7 @@ - + @@ -68,7 +68,16 @@ AND permission = #{type,jdbcType=VARCHAR}::guacamole_object_permission_type - AND affected_entity.name = #{identifier,jdbcType=VARCHAR} + AND + + + affected_entity.name = #{identifier,jdbcType=VARCHAR} + + + LOWER(affected_entity.name) = LOWER(#{identifier,jdbcType=VARCHAR}) + + + AND affected_entity.type = 'USER'::guacamole_entity_type @@ -86,11 +95,23 @@ - AND affected_entity.name IN - + + affected_entity.name IN + - #{identifier,jdbcType=VARCHAR} - + #{identifier,jdbcType=VARCHAR} + + + + LOWER(affected_entity.name) IN + + LOWER(#{identifier,jdbcType=VARCHAR}) + + + AND permission IN @@ -108,13 +129,26 @@ WHERE guacamole_user_permission.affected_user_id = affected_user.user_id AND affected_user.entity_id = affected_entity.entity_id - AND (guacamole_user_permission.entity_id, permission, affected_entity.name) IN - - (#{permission.entityID,jdbcType=INTEGER}, - #{permission.type,jdbcType=VARCHAR}::guacamole_object_permission_type, - #{permission.objectIdentifier,jdbcType=INTEGER}) - + + + AND (guacamole_user_permission.entity_id, permission, affected_entity.name) IN + + (#{permission.entityID,jdbcType=INTEGER}, + #{permission.type,jdbcType=VARCHAR}::guacamole_object_permission_type, + #{permission.objectIdentifier,jdbcType=VARCHAR}) + + + + AND (guacamole_user_permission.entity_id, permission, LOWER(affected_entity.name)) IN + + (#{permission.entityID,jdbcType=INTEGER}, + #{permission.type,jdbcType=VARCHAR}::guacamole_object_permission_type, + LOWER(#{permission.objectIdentifier,jdbcType=VARCHAR})) + + + AND affected_entity.type = 'USER'::guacamole_entity_type @@ -140,7 +174,14 @@ AS permissions JOIN guacamole_entity affected_entity ON - affected_entity.name = permissions.affected_name + + + affected_entity.name = permissions.affected_name + + + LOWER(affected_entity.name) = LOWER(permissions.affected_name) + + AND affected_entity.type = 'USER'::guacamole_entity_type JOIN guacamole_user affected_user ON affected_user.entity_id = affected_entity.entity_id WHERE (permissions.entity_id, permissions.permission, affected_user.user_id) NOT IN ( diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml index e9c857afd4..0e03a36169 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml @@ -43,7 +43,14 @@ JOIN guacamole_user ON guacamole_user_password_history.user_id = guacamole_user.user_id JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id WHERE - guacamole_entity.name = #{username,jdbcType=VARCHAR} + + + guacamole_entity.name = #{username,jdbcType=VARCHAR} + + + LOWER(guacamole_entity.name) = LOWER(#{username,jdbcType=VARCHAR}) + + ORDER BY guacamole_user_password_history.password_date DESC LIMIT #{maxHistorySize} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml index 94d607e337..d70a3cb9e1 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml @@ -130,10 +130,26 @@ FROM guacamole_user JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id LEFT JOIN guacamole_user_history ON guacamole_user_history.user_id = guacamole_user.user_id - WHERE guacamole_entity.name IN + WHERE + + + guacamole_entity.name + + + LOWER(guacamole_entity.name) + + + IN - #{identifier,jdbcType=VARCHAR} + open="(" separator="," close=")"> + + + #{identifier,jdbcType=VARCHAR} + + + LOWER(#{identifier,jdbcType=VARCHAR}) + + AND guacamole_entity.type = 'USER'::guacamole_entity_type GROUP BY guacamole_user.user_id, guacamole_entity.entity_id; @@ -145,10 +161,26 @@ FROM guacamole_user_attribute JOIN guacamole_user ON guacamole_user.user_id = guacamole_user_attribute.user_id JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id - WHERE guacamole_entity.name IN + WHERE + + + guacamole_entity.name + + + LOWER(guacamole_entity.name) + + + IN - #{identifier,jdbcType=VARCHAR} + open="(" separator="," close=")"> + + + #{identifier,jdbcType=VARCHAR} + + + LOWER(#{identifier,jdbcType=VARCHAR}) + + AND guacamole_entity.type = 'USER'::guacamole_entity_type; @@ -180,10 +212,26 @@ FROM guacamole_user JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id LEFT JOIN guacamole_user_history ON guacamole_user_history.user_id = guacamole_user.user_id - WHERE guacamole_entity.name IN + WHERE + + + guacamole_entity.name + + + LOWER(guacamole_entity.name) + + + IN - #{identifier,jdbcType=VARCHAR} + + + #{identifier,jdbcType=VARCHAR} + + + LOWER(#{identifier,jdbcType=VARCHAR}) + + AND guacamole_entity.type = 'USER'::guacamole_entity_type AND guacamole_user.user_id IN ( @@ -201,10 +249,26 @@ FROM guacamole_user_attribute JOIN guacamole_user ON guacamole_user.user_id = guacamole_user_attribute.user_id JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id - WHERE guacamole_entity.name IN + WHERE + + + guacamole_entity.name + + + LOWER(guacamole_entity.name) + + + IN - #{identifier,jdbcType=VARCHAR} + + + #{identifier,jdbcType=VARCHAR} + + + LOWER(#{identifier,jdbcType=VARCHAR}) + + AND guacamole_entity.type = 'USER'::guacamole_entity_type AND guacamole_user.user_id IN ( @@ -243,7 +307,14 @@ JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id LEFT JOIN guacamole_user_history ON guacamole_user_history.user_id = guacamole_user.user_id WHERE - guacamole_entity.name = #{username,jdbcType=VARCHAR} + + + guacamole_entity.name = #{username,jdbcType=VARCHAR} + + + LOWER(guacamole_entity.name) = LOWER(#{username,jdbcType=VARCHAR}) + + AND guacamole_entity.type = 'USER'::guacamole_entity_type GROUP BY guacamole_user.user_id, guacamole_entity.entity_id; @@ -255,16 +326,29 @@ JOIN guacamole_user ON guacamole_user.user_id = guacamole_user_attribute.user_id JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id WHERE - guacamole_entity.name = #{username,jdbcType=VARCHAR} + + + guacamole_entity.name = #{username,jdbcType=VARCHAR} + + + LOWER(guacamole_entity.name) = LOWER(#{username,jdbcType=VARCHAR}) + + AND guacamole_entity.type = 'USER'::guacamole_entity_type - DELETE FROM guacamole_entity WHERE - name = #{identifier,jdbcType=VARCHAR} + + + name = #{identifier,jdbcType=VARCHAR} + + + LOWER(name) = LOWER(#{identifier,jdbcType=VARCHAR}) + + AND type = 'USER'::guacamole_entity_type @@ -326,7 +410,7 @@ email_address = #{object.emailAddress,jdbcType=VARCHAR}, organization = #{object.organization,jdbcType=VARCHAR}, organizational_role = #{object.organizationalRole,jdbcType=VARCHAR} - WHERE user_id = #{object.objectID,jdbcType=VARCHAR} + WHERE user_id = #{object.objectID,jdbcType=INTEGER} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml index a372087ebd..e13a2d2a40 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml @@ -49,7 +49,14 @@ (SELECT user_id FROM guacamole_user JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id WHERE - guacamole_entity.name = #{record.username,jdbcType=VARCHAR} + + + guacamole_entity.name = #{record.username,jdbcType=VARCHAR} + + + LOWER(guacamole_entity.name) = LOWER(#{record.username,jdbcType=VARCHAR}) + + AND guacamole_entity.type = 'USER'::guacamole_entity_type), #{record.username,jdbcType=VARCHAR}, #{record.startDate,jdbcType=TIMESTAMP}, @@ -81,7 +88,14 @@ - guacamole_user_history.username = #{identifier,jdbcType=VARCHAR} + + + guacamole_user_history.username = #{identifier,jdbcType=VARCHAR} + + + LOWER(guacamole_user_history.username) = LOWER(#{identifier,jdbcType=VARCHAR}) + + @@ -92,7 +106,14 @@ FROM guacamole_user JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id WHERE - POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + + + POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + + + POSITION(LOWER(#{term.term,jdbcType=VARCHAR}) IN LOWER(guacamole_entity.name)) > 0 + + AND guacamole_entity.type = 'USER'::guacamole_entity_type), ) @@ -146,7 +167,15 @@ ) - AND guacamole_entity.name = #{identifier,jdbcType=VARCHAR} + AND + + + guacamole_entity.name = #{identifier,jdbcType=VARCHAR} + + + LOWER(guacamole_entity.name) = LOWER(#{identifier,jdbcType=VARCHAR}) + + @@ -157,7 +186,14 @@ FROM guacamole_user JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id WHERE - POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + + + POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + + + POSITION(LOWER(#{term.term,jdbcType=VARCHAR}) IN LOWER(guacamole_entity.name)) > 0 + + AND guacamole_entity.type = 'USER'::guacamole_entity_type ) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMemberUserMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMemberUserMapper.xml index a3ad5a78d1..5e74d4b8aa 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMemberUserMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMemberUserMapper.xml @@ -58,11 +58,27 @@ user_group_id = #{parent.objectID,jdbcType=INTEGER} AND guacamole_entity.entity_id = member_entity_id AND guacamole_entity.type = 'USER'::guacamole_entity_type - AND guacamole_entity.name IN - - #{identifier,jdbcType=VARCHAR} - + AND + + + guacamole_entity.name + + + LOWER(guacamole_entity.name) + + + IN + + + + #{identifier,jdbcType=VARCHAR} + + + LOWER(#{identifier,jdbcType=VARCHAR}) + + + @@ -76,11 +92,26 @@ guacamole_entity.entity_id FROM guacamole_entity WHERE - guacamole_entity.name IN - - #{identifier} - + + + guacamole_entity.name + + + LOWER(guacamole_entity.name) + + + IN + + + + #{identifier} + + + LOWER(#{identifier}) + + + AND guacamole_entity.type = 'USER'::guacamole_entity_type AND guacamole_entity.entity_id NOT IN ( SELECT guacamole_user_group_member.member_entity_id diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerEnvironment.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerEnvironment.java index 5b0f6969b9..9e2d540bbb 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerEnvironment.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerEnvironment.java @@ -332,13 +332,24 @@ public boolean trustAllServerCertificates() throws GuacamoleException { @Override public boolean getCaseSensitiveUsernames() throws GuacamoleException { - // SQL Server uses case-insensitive string searches by default, so - // we do not enforce case-sensitivity unless otherwise configured. - return getProperty( + // Get the configured or default value of the property. + boolean caseSensitiveUsernames = getProperty( SQLServerGuacamoleProperties.SQLSERVER_CASE_SENSITIVE_USERNAMES, - false + super.getCaseSensitiveUsernames() ); + // If property has been set to true, warn the admin. + if (caseSensitiveUsernames) + logger.warn("You have configured this extension for case-sensitive " + + "username comparisons, however, the default collations " + + "for SQL Server databases do not support case-sensitive " + + "string comparisons. Further database configuration may " + + "be required in order for case-sensitive username " + + "comparisons to function correctly."); + + // Return as configured + return caseSensitiveUsernames; + } } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerGuacamoleProperties.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerGuacamoleProperties.java index d978a53884..8a4e1ee28f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerGuacamoleProperties.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerGuacamoleProperties.java @@ -258,6 +258,13 @@ private SQLServerGuacamoleProperties() {} }; + /** + * A property used to configure whether or not usernames within the SQL + * Server JDBC module should be treated as case-sensitive. While Guacamole + * will treat usernames as case-sensitive by default, SQL Server's default + * database collations do not do case-sensitive string comparisons, so in + * many cases this will effectively result in case-insensitive usernames. + */ public static final BooleanGuacamoleProperty SQLSERVER_CASE_SENSITIVE_USERNAMES = new BooleanGuacamoleProperty() { diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml index 81f31d428b..0a67bf31cf 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -68,7 +68,14 @@ (SELECT user_id FROM [guacamole_user] JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id WHERE - [guacamole_entity].name = #{record.username,jdbcType=VARCHAR} + + + [guacamole_entity].name = #{record.username,jdbcType=VARCHAR} + + + LOWER([guacamole_entity].name) = LOWER(#{record.username,jdbcType=VARCHAR}) + + AND [guacamole_entity].type = 'USER'), #{record.username,jdbcType=VARCHAR}, #{record.startDate,jdbcType=TIMESTAMP}, @@ -110,7 +117,15 @@ [guacamole_connection_history].user_id IN ( SELECT user_id FROM [guacamole_user] - WHERE CHARINDEX(#{term.term,jdbcType=VARCHAR}, username) > 0 + WHERE + + + CHARINDEX(#{term.term,jdbcType=VARCHAR} IN username) > 0 + + + CHARINDEX(LOWER(#{term.term,jdbcType=VARCHAR}) IN LOWER(username)) > 0 + + ) OR [guacamole_connection_history].connection_id IN ( @@ -196,7 +211,14 @@ FROM [guacamole_user] JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id WHERE - CHARINDEX(#{term.term,jdbcType=VARCHAR}, [guacamole_entity].name) > 0 + + + CHARINDEX(#{term.term,jdbcType=VARCHAR} IN [guacamole_entity].name) > 0 + + + CHARINDEX(LOWER(#{term.term,jdbcType=VARCHAR}) IN LOWER(guacamole_entity.name)) > 0 + + AND [guacamole_entity].type = 'USER' ) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.xml index 5c088045eb..a2f9ff8adf 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/permission/UserPermissionMapper.xml @@ -68,7 +68,15 @@ AND permission = #{type,jdbcType=VARCHAR} - AND affected_entity.name = #{identifier,jdbcType=VARCHAR} + AND + + + affected_entity.name = #{identifier,jdbcType=VARCHAR} + + + LOWER(affected_entity.name) = LOWER(#{identifier,jdbcType=VARCHAR}) + + AND affected_entity.type = 'USER' @@ -86,11 +94,23 @@ - AND affected_entity.name IN - + + affected_entity.name IN + - #{identifier,jdbcType=VARCHAR} - + #{identifier,jdbcType=VARCHAR} + + + + LOWER(affected_entity.name) IN + + LOWER(#{identifier,jdbcType=VARCHAR}) + + + AND permission IN @@ -112,8 +132,16 @@ open="(" separator=" OR " close=")"> ([guacamole_user_permission].entity_id = #{permission.entityID,jdbcType=INTEGER} AND permission = #{permission.type,jdbcType=VARCHAR} AND - affected_entity.name = #{permission.objectIdentifier,jdbcType=VARCHAR} AND - affected_entity.type = 'USER') + + + affected_entity.name = #{permission.objectIdentifier,jdbcType=VARCHAR} + + + LOWER(affected_entity.name = LOWER(#{permission.objectIdentifier,jdbcType=VARCHAR}) + + + AND + affected_entity.type = 'USER') @@ -139,7 +167,14 @@ AS permissions JOIN [guacamole_entity] affected_entity ON - affected_entity.name = permissions.affected_name + + + affected_entity.name = permissions.affected_name + + + LOWER(affected_entity.name) = LOWER(permissions.affected_name) + + AND affected_entity.type = 'USER' JOIN [guacamole_user] affected_user ON affected_user.entity_id = affected_entity.entity_id WHERE NOT EXISTS (SELECT 1 FROM [guacamole_user_permission] diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml index 21fd986b18..da453d44d4 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/PasswordRecordMapper.xml @@ -43,7 +43,14 @@ JOIN [guacamole_user] ON [guacamole_user_password_history].user_id = [guacamole_user].user_id JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id WHERE - [guacamole_entity].name = #{username,jdbcType=VARCHAR} + + + [guacamole_entity].name = #{username,jdbcType=VARCHAR} + + + LOWER([guacamole_entity].name) = LOWER(#{username,jdbcType=VARCHAR}) + + ORDER BY [guacamole_user_password_history].password_date DESC diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml index 80344968bb..a4530335ad 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml @@ -133,10 +133,26 @@ ) AS last_active FROM [guacamole_user] JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id - WHERE [guacamole_entity].name IN + WHERE + + + [guacamole_entity].name + + + LOWER([guacamole_entity].name) + + + IN - #{identifier,jdbcType=VARCHAR} + open="(" separator="," close=")"> + + + #{identifier,jdbcType=VARCHAR} + + + LOWER(#{identifier,jdbcType=VARCHAR}) + + AND [guacamole_entity].type = 'USER'; @@ -147,10 +163,26 @@ FROM [guacamole_user_attribute] JOIN [guacamole_user] ON [guacamole_user].user_id = [guacamole_user_attribute].user_id JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id - WHERE [guacamole_entity].name IN + WHERE + + + [guacamole_entity].name + + + LOWER([guacamole_entity].name) + + + IN - #{identifier,jdbcType=VARCHAR} + open="(" separator="," close=")"> + + + #{identifier,jdbcType=VARCHAR} + + + LOWER(#{identifier,jdbcType=VARCHAR}) + + AND [guacamole_entity].type = 'USER'; @@ -185,10 +217,26 @@ ) AS last_active FROM [guacamole_user] JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id - WHERE [guacamole_entity].name IN + WHERE + + + [guacamole_entity].name + + + LOWER([guacamole_entity].name) + + + IN - #{identifier,jdbcType=VARCHAR} + + + #{identifier,jdbcType=VARCHAR} + + + LOWER(#{identifier,jdbcType=VARCHAR}) + + AND [guacamole_entity].type = 'USER' AND [guacamole_user].user_id IN ( @@ -205,10 +253,26 @@ FROM [guacamole_user_attribute] JOIN [guacamole_user] ON [guacamole_user].user_id = [guacamole_user_attribute].user_id JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id - WHERE [guacamole_entity].name IN + WHERE + + + [guacamole_entity].name + + + LOWER([guacamole_entity].name) + + + IN - #{identifier,jdbcType=VARCHAR} + + + #{identifier,jdbcType=VARCHAR} + + + LOWER(#{identifier,jdbcType=VARCHAR}) + + AND [guacamole_entity].type = 'USER' AND [guacamole_user].user_id IN ( @@ -250,7 +314,14 @@ FROM [guacamole_user] JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id WHERE - [guacamole_entity].name = #{username,jdbcType=VARCHAR} + + + [guacamole_entity].name = #{username,jdbcType=VARCHAR} + + + LOWER([guacamole_entity].name) = LOWER(#{username,jdbcType=VARCHAR}) + + AND [guacamole_entity].type = 'USER'; SELECT @@ -261,7 +332,14 @@ JOIN [guacamole_user] ON [guacamole_user].user_id = [guacamole_user_attribute].user_id JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id WHERE - [guacamole_entity].name = #{username,jdbcType=VARCHAR} + + + [guacamole_entity].name = #{username,jdbcType=VARCHAR} + + + LOWER([guacamole_entity].name) = LOWER(#{username,jdbcType=VARCHAR}) + + AND [guacamole_entity].type = 'USER' @@ -270,7 +348,14 @@ DELETE FROM [guacamole_entity] WHERE - name = #{identifier,jdbcType=VARCHAR} + + + name = #{identifier,jdbcType=VARCHAR} + + + LOWER(name) = LOWER(#{identifier,jdbcType=VARCHAR}) + + AND type = 'USER' @@ -332,7 +417,7 @@ email_address = #{object.emailAddress,jdbcType=VARCHAR}, organization = #{object.organization,jdbcType=VARCHAR}, organizational_role = #{object.organizationalRole,jdbcType=VARCHAR} - WHERE user_id = #{object.objectID,jdbcType=VARCHAR} + WHERE user_id = #{object.objectID,jdbcType=INTEGER} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml index 4fb64491ad..5bcc3b95f6 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml @@ -49,8 +49,15 @@ (SELECT user_id FROM [guacamole_user] JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id WHERE - [guacamole_entity].name = #{record.username,jdbcType=VARCHAR} - AND [guacamole_entity].type = 'USER'), + + + [guacamole_entity].name = #{record.username,jdbcType=VARCHAR} + + + LOWER([guacamole_entity].name) = LOWER(#{record.username,jdbcType=VARCHAR}) + + + AND [guacamole_entity].type = 'USER'), #{record.username,jdbcType=VARCHAR}, #{record.startDate,jdbcType=TIMESTAMP}, #{record.endDate,jdbcType=TIMESTAMP} @@ -81,7 +88,14 @@ - [guacamole_user_history].username = #{identifier,jdbcType=VARCHAR} + + + [guacamole_user_history].username = #{identifier,jdbcType=VARCHAR} + + + LOWER([guacamole_user_history].username) = LOWER(#{identifier,jdbcType=VARCHAR}) + + @@ -92,7 +106,14 @@ FROM [guacamole_user] JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id WHERE - CHARINDEX(#{term.term,jdbcType=VARCHAR}, [guacamole_entity].name) > 0 + + + CHARINDEX(#{term.term,jdbcType=VARCHAR} IN [guacamole_entity].name) > 0 + + + CHARINDEX(LOWER(#{term.term,jdbcType=VARCHAR}) IN LOWER([guacamole_entity].name)) > 0 + + AND [guacamole_entity].type = 'USER'), ) @@ -144,7 +165,15 @@ ) - AND [guacamole_entity].name = #{identifier,jdbcType=VARCHAR} + AND + + + [guacamole_entity].name = #{identifier,jdbcType=VARCHAR} + + + LOWER([guacamole_entity].name) = LOWER(#{identifier,jdbcType=VARCHAR}) + + @@ -155,7 +184,14 @@ FROM [guacamole_user] JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id WHERE - CHARINDEX(#{term.term,jdbcType=VARCHAR}, [guacamole_entity].name) > 0 + + + CHARINDEX(#{term.term,jdbcType=VARCHAR} IN [guacamole_entity].name) > 0 + + + CHARINDEX(LOWER(#{term.term,jdbcType=VARCHAR}) IN LOWER([guacamole_entity].name)) > 0 + + AND [guacamole_entity].type = 'USER' ) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMemberUserMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMemberUserMapper.xml index b0682b0411..70fe520da1 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMemberUserMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/usergroup/UserGroupMemberUserMapper.xml @@ -58,11 +58,27 @@ WHERE user_group_id = #{parent.objectID,jdbcType=INTEGER} AND [guacamole_entity].type = 'USER' - AND [guacamole_entity].name IN - - #{identifier,jdbcType=VARCHAR} - + AND + + + [guacamole_entity].name + + + LOWER([guacamole_entity].name) + + + IN + + + + #{identifier,jdbcType=VARCHAR} + + + LOWER(#{identifier,jdbcType=VARCHAR}) + + + @@ -76,11 +92,26 @@ [guacamole_entity].entity_id FROM [guacamole_entity] WHERE - [guacamole_entity].name IN - - #{identifier} - + + + [guacamole_entity].name + + + LOWER([guacamole_entity].name) + + + IN + + + + #{identifier} + + + LOWER(#{identifier}) + + + AND [guacamole_entity].type = 'USER' AND [guacamole_entity].entity_id NOT IN ( SELECT [guacamole_user_group_member].member_entity_id diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/conf/SSOEnvironment.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/conf/SSOEnvironment.java deleted file mode 100644 index b9fe0258d5..0000000000 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-base/src/main/java/org/apache/guacamole/auth/sso/conf/SSOEnvironment.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.guacamole.auth.sso.conf; - -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.environment.DelegatingEnvironment; -import org.apache.guacamole.environment.LocalEnvironment; - -/** - * An SSO-specific environment that defines generic properties that can be used - * with any of the implemented SSO providers. - */ -public abstract class SSOEnvironment extends DelegatingEnvironment { - - /** - * Create a new instance of the SSOEnvironment using the underlying - * LocalEnvironment to read configured properties. - */ - public SSOEnvironment() { - super(LocalEnvironment.getInstance()); - } - - /** - * Returns true if the usernames provided to the SSO authentication - * module should be treated as case-sensitive, or false if usernames - * should be treated as case-insensitive. The default is true, usernames - * will be case-sensitive in keeping with the past behavior of Guacamole - * prior to the addition of this option. - * - * @return - * true if usernames should be treated as case-sensitive, otherwise - * false. - * - * @throws GuacamoleException - * If guacamole.properties cannot be parsed. - */ - public boolean getCaseSensitiveUsernames() throws GuacamoleException { - - // While most SSO systems do not use case to differentiate between - // usernames, this currently defaults to true to avoid suddenly - // breaking any extensions that rely on case-sensitivity. - return true; - } - -}