GUACAMOLE-1239: Move caseSensitive parameter to the base mapper classes.

This commit is contained in:
Virtually Nick
2024-10-11 11:21:36 -04:00
parent 3d86026cb1
commit 76d37b4071
9 changed files with 97 additions and 138 deletions

View File

@@ -77,10 +77,15 @@ public interface ModeledDirectoryObjectMapper<ModelType> {
* @param identifiers * @param identifiers
* The identifiers of the objects to return. * The identifiers of the objects to return.
* *
* @param caseSensitive
* true if the query should evaluate identifiers in a case-sensitive
* manner, otherwise false.
*
* @return * @return
* A Collection of all objects having the given identifiers. * A Collection of all objects having the given identifiers.
*/ */
Collection<ModelType> select(@Param("identifiers") Collection<String> identifiers); Collection<ModelType> select(@Param("identifiers") Collection<String> identifiers,
@Param("caseSensitive") boolean caseSensitive);
/** /**
* Selects all objects which have the given identifiers and are explicitly * Selects all objects which have the given identifiers and are explicitly
@@ -100,12 +105,17 @@ public interface ModeledDirectoryObjectMapper<ModelType> {
* The identifiers of any known effective groups that should be taken * The identifiers of any known effective groups that should be taken
* into account, such as those defined externally to the database. * into account, such as those defined externally to the database.
* *
* @param caseSensitive
* true if the query should evaluate identifiers in a case-sensitive
* manner, otherwise false.
*
* @return * @return
* A Collection of all objects having the given identifiers. * A Collection of all objects having the given identifiers.
*/ */
Collection<ModelType> selectReadable(@Param("user") UserModel user, Collection<ModelType> selectReadable(@Param("user") UserModel user,
@Param("identifiers") Collection<String> identifiers, @Param("identifiers") Collection<String> identifiers,
@Param("effectiveGroups") Collection<String> effectiveGroups); @Param("effectiveGroups") Collection<String> effectiveGroups,
@Param("caseSensitive") boolean caseSensitive);
/** /**
* Inserts the given object into the database. If the object already * Inserts the given object into the database. If the object already
@@ -126,10 +136,15 @@ public interface ModeledDirectoryObjectMapper<ModelType> {
* @param identifier * @param identifier
* The identifier of the object to delete. * The identifier of the object to delete.
* *
* @param caseSensitive
* true if the query should evaluate the identifier in a
* case-sensitive manner, otherwise false.
*
* @return * @return
* The number of rows deleted. * The number of rows deleted.
*/ */
int delete(@Param("identifier") String identifier); int delete(@Param("identifier") String identifier,
@Param("caseSensitive") boolean caseSensitive);
/** /**
* Updates the given existing object in the database. If the object does * Updates the given existing object in the database. If the object does

View File

@@ -116,6 +116,24 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
protected abstract InternalType getObjectInstance(ModeledAuthenticatedUser currentUser, protected abstract InternalType getObjectInstance(ModeledAuthenticatedUser currentUser,
ModelType model) throws GuacamoleException; ModelType model) throws GuacamoleException;
/**
* Returns whether or not identifiers for objects provided by this service
* are handled in a case-sensitive manner or not.
*
* @return
* "true" if identifiers handled by this object service should be
* treated as case-sensitive, otherwise false.
*
* @throws GuacamoleException
* If an error occurs retrieving relevant configuration information.
*/
protected boolean getCaseSensitiveIdentifiers() throws GuacamoleException {
// By default identifiers are not case-sensitive.
return false;
}
/** /**
* Returns an instance of a model object which is based on the given * Returns an instance of a model object which is based on the given
* object. * object.
@@ -408,6 +426,8 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
boolean userIsPrivileged = user.isPrivileged(); boolean userIsPrivileged = user.isPrivileged();
boolean caseSensitive = getCaseSensitiveIdentifiers();
// Process the filteredIdentifiers in batches using Lists.partition() and flatMap // Process the filteredIdentifiers in batches using Lists.partition() and flatMap
Collection<ModelType> allObjects = Lists.partition(filteredIdentifiers, batchSize).stream() Collection<ModelType> allObjects = Lists.partition(filteredIdentifiers, batchSize).stream()
.flatMap(chunk -> { .flatMap(chunk -> {
@@ -415,12 +435,12 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
// Bypass permission checks if the user is privileged // Bypass permission checks if the user is privileged
if (userIsPrivileged) if (userIsPrivileged)
objects = getObjectMapper().select(chunk); objects = getObjectMapper().select(chunk, caseSensitive);
// Otherwise only return explicitly readable identifiers // Otherwise only return explicitly readable identifiers
else else
objects = getObjectMapper().selectReadable(user.getUser().getModel(), objects = getObjectMapper().selectReadable(user.getUser().getModel(),
chunk, user.getEffectiveUserGroups()); chunk, user.getEffectiveUserGroups(), caseSensitive);
return objects.stream(); return objects.stream();
}) })
@@ -510,7 +530,7 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
beforeDelete(user, identifier); beforeDelete(user, identifier);
// Delete object // Delete object
getObjectMapper().delete(identifier); getObjectMapper().delete(identifier, getCaseSensitiveIdentifiers());
} }

View File

@@ -49,11 +49,17 @@ public interface ObjectRelationMapper<ParentModelType extends ObjectModel> {
* The identifiers of the objects on the child side of the one-to-many * The identifiers of the objects on the child side of the one-to-many
* relationship represented by the RelatedObjectSet. * relationship represented by the RelatedObjectSet.
* *
* @param caseSensitive
* true if child identifiers should be treated as case-sensitive when
* performing lookups on them, or false if the queries should be done
* case-insensitively.
*
* @return * @return
* The number of rows inserted. * The number of rows inserted.
*/ */
int insert(@Param("parent") ParentModelType parent, int insert(@Param("parent") ParentModelType parent,
@Param("children") Collection<String> children); @Param("children") Collection<String> children,
@Param("caseSensitive") boolean caseSensitive);
/** /**
* Deletes rows as necessary to modify the one-to-many relationship * Deletes rows as necessary to modify the one-to-many relationship
@@ -70,11 +76,17 @@ public interface ObjectRelationMapper<ParentModelType extends ObjectModel> {
* The identifiers of the objects on the child side of the one-to-many * The identifiers of the objects on the child side of the one-to-many
* relationship represented by the RelatedObjectSet. * relationship represented by the RelatedObjectSet.
* *
* @param caseSensitive
* true if child identifiers should be treated as case-sensitive when
* performing lookups on them, or false if the queries should be done
* case-insensitively.
*
* @return * @return
* The number of rows deleted. * The number of rows deleted.
*/ */
int delete(@Param("parent") ParentModelType parent, int delete(@Param("parent") ParentModelType parent,
@Param("children") Collection<String> children); @Param("children") Collection<String> children,
@Param("caseSensitive") boolean caseSensitive);
/** /**
* Retrieves the identifiers of all objects on the child side of the * Retrieves the identifiers of all objects on the child side of the

View File

@@ -75,6 +75,24 @@ public abstract class RelatedObjectSet<ParentObjectType extends ModeledDirectory
this.parent = parent; this.parent = parent;
} }
/**
* Return "true" if identifiers within a related object set should be treated
* as case-sensitive, otherwise false.
*
* @return
* "true" if identifiers should be treated as case-sensitive, otherwise
* "false".
*
* @throws GuacamoleException
* If an error occurs retrieving configuration information on
* case-sensitivity.
*/
protected boolean getCaseSensitiveIdentifiers() throws GuacamoleException {
// Identifiers are not case-sensitive by default.
return false;
}
/** /**
* Returns the mapper which provides low-level access to the database * Returns the mapper which provides low-level access to the database
* models which drive the relation represented by this RelatedObjectSet. * models which drive the relation represented by this RelatedObjectSet.
@@ -184,7 +202,7 @@ public abstract class RelatedObjectSet<ParentObjectType extends ModeledDirectory
// Create relations only if permission is granted // Create relations only if permission is granted
if (canAlterRelation(identifiers)) if (canAlterRelation(identifiers))
getObjectRelationMapper().insert(parent.getModel(), identifiers); getObjectRelationMapper().insert(parent.getModel(), identifiers, getCaseSensitiveIdentifiers());
// User lacks permission to add user groups // User lacks permission to add user groups
else else
@@ -201,7 +219,7 @@ public abstract class RelatedObjectSet<ParentObjectType extends ModeledDirectory
// Delete relations only if permission is granted // Delete relations only if permission is granted
if (canAlterRelation(identifiers)) if (canAlterRelation(identifiers))
getObjectRelationMapper().delete(parent.getModel(), identifiers); getObjectRelationMapper().delete(parent.getModel(), identifiers, getCaseSensitiveIdentifiers());
// User lacks permission to remove user groups // User lacks permission to remove user groups
else else

View File

@@ -638,7 +638,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
identifiers = getPreferredConnections(user, identifiers); identifiers = getPreferredConnections(user, identifiers);
// Retrieve all children // Retrieve all children
Collection<ConnectionModel> models = connectionMapper.select(identifiers); Collection<ConnectionModel> models = connectionMapper.select(identifiers, false);
List<ModeledConnection> connections = new ArrayList<ModeledConnection>(models.size()); List<ModeledConnection> connections = new ArrayList<ModeledConnection>(models.size());
// Convert each retrieved model to a modeled connection // Convert each retrieved model to a modeled connection
@@ -679,7 +679,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
// Produce collection of readable connection identifiers // Produce collection of readable connection identifiers
Collection<ConnectionModel> connections = Collection<ConnectionModel> connections =
connectionMapper.selectReadable(user.getUser().getModel(), connectionMapper.selectReadable(user.getUser().getModel(),
identifiers, user.getEffectiveUserGroups()); identifiers, user.getEffectiveUserGroups(), false);
// Ensure set contains only identifiers of readable connections // Ensure set contains only identifiers of readable connections
identifiers.clear(); identifiers.clear();

View File

@@ -19,7 +19,6 @@
package org.apache.guacamole.auth.jdbc.user; package org.apache.guacamole.auth.jdbc.user;
import java.util.Collection;
import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper; import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@@ -36,7 +35,7 @@ public interface UserMapper extends ModeledDirectoryObjectMapper<UserModel> {
* The username of the user to return. * The username of the user to return.
* *
* @param caseSensitive * @param caseSensitive
* true if the search should evaluate usernames in a case-sensitive * true if the search should evaluate the username in a case-sensitive
* manner, otherwise false. * manner, otherwise false.
* *
* @return * @return
@@ -45,71 +44,4 @@ public interface UserMapper extends ModeledDirectoryObjectMapper<UserModel> {
UserModel selectOne(@Param("username") String username, UserModel selectOne(@Param("username") String username,
@Param("caseSensitive") boolean caseSensitive); @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<UserModel> select(@Param("identifiers") Collection<String> 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<UserModel> selectReadable(@Param("user") UserModel user,
@Param("identifiers") Collection<String> identifiers,
@Param("effectiveGroups") Collection<String> 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);
} }

View File

@@ -218,6 +218,11 @@ public class UserService extends ModeledDirectoryObjectService<ModeledUser, User
} }
@Override
protected boolean getCaseSensitiveIdentifiers() throws GuacamoleException {
return environment.getCaseSensitiveUsernames();
}
@Override @Override
protected boolean hasCreatePermission(ModeledAuthenticatedUser user) protected boolean hasCreatePermission(ModeledAuthenticatedUser user)
throws GuacamoleException { throws GuacamoleException {

View File

@@ -27,59 +27,4 @@ import org.apache.ibatis.annotations.Param;
* Mapper for the one-to-many relationship between a user group and its user * Mapper for the one-to-many relationship between a user group and its user
* members. * members.
*/ */
public interface UserGroupMemberUserMapper extends ObjectRelationMapper<UserGroupModel> { public interface UserGroupMemberUserMapper extends ObjectRelationMapper<UserGroupModel> {}
/**
* 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<String> 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<String> children,
@Param("caseSensitive") boolean caseSensitive);
}

View File

@@ -21,6 +21,7 @@ package org.apache.guacamole.auth.jdbc.usergroup;
import com.google.inject.Inject; import com.google.inject.Inject;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
import org.apache.guacamole.auth.jdbc.base.ObjectRelationMapper; import org.apache.guacamole.auth.jdbc.base.ObjectRelationMapper;
import org.apache.guacamole.auth.jdbc.base.RelatedObjectSet; import org.apache.guacamole.auth.jdbc.base.RelatedObjectSet;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
@@ -37,6 +38,17 @@ public class UserGroupMemberUserSet extends RelatedObjectSet<ModeledUserGroup, U
@Inject @Inject
private UserGroupMemberUserMapper userGroupMemberUserMapper; private UserGroupMemberUserMapper userGroupMemberUserMapper;
/**
* The server environment for retrieving configuration information.
*/
@Inject
private JDBCEnvironment environment;
@Override
protected boolean getCaseSensitiveIdentifiers() throws GuacamoleException {
return environment.getCaseSensitiveUsernames();
}
@Override @Override
protected ObjectRelationMapper<UserGroupModel> getObjectRelationMapper() { protected ObjectRelationMapper<UserGroupModel> getObjectRelationMapper() {
return userGroupMemberUserMapper; return userGroupMemberUserMapper;