GUACAMOLE-1239: Add case-sensitivity settings to permissions mappers and services.

This commit is contained in:
Virtually Nick
2024-10-12 16:16:39 -04:00
parent 3fcc59a8a5
commit 61f6c8ceb1
10 changed files with 83 additions and 150 deletions

View File

@@ -513,7 +513,7 @@ public abstract class ModeledDirectoryObjectService<InternalType extends Modeled
// Add implicit permissions // Add implicit permissions
Collection<ObjectPermissionModel> implicitPermissions = getImplicitPermissions(user, model); Collection<ObjectPermissionModel> implicitPermissions = getImplicitPermissions(user, model);
if (!implicitPermissions.isEmpty()) if (!implicitPermissions.isEmpty())
getPermissionMapper().insert(implicitPermissions); getPermissionMapper().insert(implicitPermissions, getCaseSensitiveIdentifiers());
// Add any arbitrary attributes // Add any arbitrary attributes
if (model.hasArbitraryAttributes()) if (model.hasArbitraryAttributes())

View File

@@ -133,10 +133,12 @@ public abstract class ModeledObjectPermissionService
// Create permissions only if user has permission to do so // Create permissions only if user has permission to do so
if (canAlterPermissions(user, targetEntity, permissions)) { if (canAlterPermissions(user, targetEntity, permissions)) {
boolean caseSensitive = getCaseSensitiveIdentifiers();
batchPermissionUpdates(permissions, permissionSubset -> { batchPermissionUpdates(permissions, permissionSubset -> {
Collection<ObjectPermissionModel> models = getModelInstances( Collection<ObjectPermissionModel> models = getModelInstances(
targetEntity, permissionSubset); targetEntity, permissionSubset);
getPermissionMapper().insert(models); getPermissionMapper().insert(models, caseSensitive);
}); });
return; return;
@@ -156,10 +158,12 @@ public abstract class ModeledObjectPermissionService
// Delete permissions only if user has permission to do so // Delete permissions only if user has permission to do so
if (canAlterPermissions(user, targetEntity, permissions)) { if (canAlterPermissions(user, targetEntity, permissions)) {
boolean caseSensitive = getCaseSensitiveIdentifiers();
batchPermissionUpdates(permissions, permissionSubset -> { batchPermissionUpdates(permissions, permissionSubset -> {
Collection<ObjectPermissionModel> models = getModelInstances( Collection<ObjectPermissionModel> models = getModelInstances(
targetEntity, permissionSubset); targetEntity, permissionSubset);
getPermissionMapper().delete(models); getPermissionMapper().delete(models, caseSensitive);
}); });
return; return;
@@ -179,7 +183,7 @@ public abstract class ModeledObjectPermissionService
// Retrieve permissions only if allowed // Retrieve permissions only if allowed
if (canReadPermissions(user, targetEntity)) if (canReadPermissions(user, targetEntity))
return getPermissionMapper().selectOne(targetEntity.getModel(), return getPermissionMapper().selectOne(targetEntity.getModel(),
type, identifier, effectiveGroups) != null; type, identifier, effectiveGroups, getCaseSensitiveIdentifiers()) != null;
// User cannot read this entity's permissions // User cannot read this entity's permissions
throw new GuacamoleSecurityException("Permission denied."); throw new GuacamoleSecurityException("Permission denied.");
@@ -205,7 +209,7 @@ public abstract class ModeledObjectPermissionService
if (canReadPermissions(user, targetEntity)) if (canReadPermissions(user, targetEntity))
return getPermissionMapper().selectAccessibleIdentifiers( return getPermissionMapper().selectAccessibleIdentifiers(
targetEntity.getModel(), permissions, identifiers, targetEntity.getModel(), permissions, identifiers,
effectiveGroups); effectiveGroups, getCaseSensitiveIdentifiers());
// User cannot read this entity's permissions // User cannot read this entity's permissions
throw new GuacamoleSecurityException("Permission denied."); throw new GuacamoleSecurityException("Permission denied.");

View File

@@ -192,7 +192,10 @@ public abstract class ModeledPermissionService<PermissionSetType extends Permiss
// Retrieve permissions only if allowed // Retrieve permissions only if allowed
if (canReadPermissions(user, targetEntity)) if (canReadPermissions(user, targetEntity))
return getPermissionInstances(getPermissionMapper().select(targetEntity.getModel(), effectiveGroups)); return getPermissionInstances(getPermissionMapper().select(
targetEntity.getModel(),
effectiveGroups,
getCaseSensitiveIdentifiers()));
// User cannot read this entity's permissions // User cannot read this entity's permissions
throw new GuacamoleSecurityException("Permission denied."); throw new GuacamoleSecurityException("Permission denied.");

View File

@@ -49,6 +49,10 @@ public interface ObjectPermissionMapper extends PermissionMapper<ObjectPermissio
* no groups are given, only permissions directly granted to the user * no groups are given, only permissions directly granted to the user
* will be used. * will be used.
* *
* @param caseSensitive
* "true" if identifiers should be treated as case-sensitive, otherwise
* "false".
*
* @return * @return
* The requested permission, or null if no such permission is granted * The requested permission, or null if no such permission is granted
* to the given entity for the given object. * to the given entity for the given object.
@@ -56,7 +60,8 @@ public interface ObjectPermissionMapper extends PermissionMapper<ObjectPermissio
ObjectPermissionModel selectOne(@Param("entity") EntityModel entity, ObjectPermissionModel selectOne(@Param("entity") EntityModel entity,
@Param("type") ObjectPermission.Type type, @Param("type") ObjectPermission.Type type,
@Param("identifier") String identifier, @Param("identifier") String identifier,
@Param("effectiveGroups") Collection<String> effectiveGroups); @Param("effectiveGroups") Collection<String> effectiveGroups,
@Param("caseSensitive") boolean caseSensitive);
/** /**
* Retrieves the subset of the given identifiers for which the given entity * Retrieves the subset of the given identifiers for which the given entity
@@ -80,6 +85,10 @@ public interface ObjectPermissionMapper extends PermissionMapper<ObjectPermissio
* no groups are given, only permissions directly granted to the user * no groups are given, only permissions directly granted to the user
* will be used. * will be used.
* *
* @param caseSensitive
* "true" if identifiers should be treated as case-sensitive, otherwise
* "false".
*
* @return * @return
* A collection containing the subset of identifiers for which at least * A collection containing the subset of identifiers for which at least
* one of the specified permissions is granted. * one of the specified permissions is granted.
@@ -87,6 +96,7 @@ public interface ObjectPermissionMapper extends PermissionMapper<ObjectPermissio
Collection<String> selectAccessibleIdentifiers(@Param("entity") EntityModel entity, Collection<String> selectAccessibleIdentifiers(@Param("entity") EntityModel entity,
@Param("permissions") Collection<ObjectPermission.Type> permissions, @Param("permissions") Collection<ObjectPermission.Type> permissions,
@Param("identifiers") Collection<String> identifiers, @Param("identifiers") Collection<String> identifiers,
@Param("effectiveGroups") Collection<String> effectiveGroups); @Param("effectiveGroups") Collection<String> effectiveGroups,
@Param("caseSensitive") boolean caseSensitive);
} }

View File

@@ -44,11 +44,16 @@ public interface PermissionMapper<PermissionType> {
* no groups are given, only permissions directly granted to the user * no groups are given, only permissions directly granted to the user
* will be used. * will be used.
* *
* @param caseSensitive
* "true" if identifiers should be treated as case-sensitive, otherwise
* "false".
*
* @return * @return
* All permissions associated with the given entity. * All permissions associated with the given entity.
*/ */
Collection<PermissionType> select(@Param("entity") EntityModel entity, Collection<PermissionType> select(@Param("entity") EntityModel entity,
@Param("effectiveGroups") Collection<String> effectiveGroups); @Param("effectiveGroups") Collection<String> effectiveGroups,
@Param("caseSensitive") boolean caseSensitive);
/** /**
* Inserts the given permissions into the database. If any permissions * Inserts the given permissions into the database. If any permissions
@@ -57,10 +62,15 @@ public interface PermissionMapper<PermissionType> {
* @param permissions * @param permissions
* The permissions to insert. * The permissions to insert.
* *
* @param caseSensitive
* "true" if identifiers should be treated as case-sensitive, otherwise
* "false".
*
* @return * @return
* The number of rows inserted. * The number of rows inserted.
*/ */
int insert(@Param("permissions") Collection<PermissionType> permissions); int insert(@Param("permissions") Collection<PermissionType> permissions,
@Param("caseSensitive") boolean caseSensitive);
/** /**
* Deletes the given permissions from the database. If any permissions do * Deletes the given permissions from the database. If any permissions do
@@ -69,9 +79,14 @@ public interface PermissionMapper<PermissionType> {
* @param permissions * @param permissions
* The permissions to delete. * The permissions to delete.
* *
* @param caseSensitive
* "true" if identifiers should be treated as case-sensitive, otherwise
* "false".
*
* @return * @return
* The number of rows deleted. * The number of rows deleted.
*/ */
int delete(@Param("permissions") Collection<PermissionType> permissions); int delete(@Param("permissions") Collection<PermissionType> permissions,
@Param("caseSensitive") boolean caseSensitive);
} }

View File

@@ -43,6 +43,24 @@ import org.apache.guacamole.net.auth.permission.PermissionSet;
public interface PermissionService<PermissionSetType extends PermissionSet<PermissionType>, public interface PermissionService<PermissionSetType extends PermissionSet<PermissionType>,
PermissionType extends Permission> { PermissionType extends Permission> {
/**
* Return "true" if identifiers 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 related to
* case-sensitivity.
*/
default boolean getCaseSensitiveIdentifiers() throws GuacamoleException {
// By default identifiers are case-insensitive.
return false;
}
/** /**
* Returns a permission set that can be used to retrieve and manipulate the * Returns a permission set that can be used to retrieve and manipulate the
* permissions of the given entity. * permissions of the given entity.

View File

@@ -98,10 +98,13 @@ public class SystemPermissionService
// system permissions // system permissions
if (user.isPrivileged()) { if (user.isPrivileged()) {
// Pull identifier case sensitivity
boolean caseSensitive = getCaseSensitiveIdentifiers();
batchPermissionUpdates(permissions, permissionSubset -> { batchPermissionUpdates(permissions, permissionSubset -> {
Collection<SystemPermissionModel> models = getModelInstances( Collection<SystemPermissionModel> models = getModelInstances(
targetEntity, permissionSubset); targetEntity, permissionSubset);
systemPermissionMapper.insert(models); systemPermissionMapper.insert(models, caseSensitive);
}); });
return; return;
@@ -125,10 +128,13 @@ public class SystemPermissionService
if (user.getUser().getIdentifier().equals(targetEntity.getIdentifier())) if (user.getUser().getIdentifier().equals(targetEntity.getIdentifier()))
throw new GuacamoleUnsupportedException("Removing your own administrative permissions is not allowed."); throw new GuacamoleUnsupportedException("Removing your own administrative permissions is not allowed.");
// Pull case sensitivity
boolean caseSensitive = getCaseSensitiveIdentifiers();
batchPermissionUpdates(permissions, permissionSubset -> { batchPermissionUpdates(permissions, permissionSubset -> {
Collection<SystemPermissionModel> models = getModelInstances( Collection<SystemPermissionModel> models = getModelInstances(
targetEntity, permissionSubset); targetEntity, permissionSubset);
systemPermissionMapper.delete(models); systemPermissionMapper.delete(models, caseSensitive);
}); });
return; return;

View File

@@ -19,142 +19,7 @@
package org.apache.guacamole.auth.jdbc.permission; 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. * 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<ObjectPermission.Type> 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<ObjectPermission.Type> 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<ObjectPermission.Type> select(@Param("entity") EntityModel entity,
@Param("effectiveGroups") Collection<String> 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<String> 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<String> selectAccessibleIdentifiers(@Param("entity") EntityModel entity,
@Param("permissions") Collection<ObjectPermission.Type> permissions,
@Param("identifiers") Collection<String> identifiers,
@Param("effectiveGroups") Collection<String> effectiveGroups,
@Param("caseSensitive") boolean caseSensitive);
}

View File

@@ -24,6 +24,7 @@ import com.google.inject.Provider;
import java.util.Set; import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
import org.apache.guacamole.auth.jdbc.base.EntityModel; import org.apache.guacamole.auth.jdbc.base.EntityModel;
import org.apache.guacamole.auth.jdbc.base.ModeledPermissions; import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
@@ -46,6 +47,17 @@ public class UserPermissionService extends ModeledObjectPermissionService {
@Inject @Inject
private Provider<UserPermissionSet> userPermissionSetProvider; private Provider<UserPermissionSet> userPermissionSetProvider;
/**
* The server environment for retrieving configuration data.
*/
@Inject
private JDBCEnvironment environment;
@Override
public boolean getCaseSensitiveIdentifiers() throws GuacamoleException {
return environment.getCaseSensitiveUsernames();
}
@Override @Override
protected ObjectPermissionMapper getPermissionMapper() { protected ObjectPermissionMapper getPermissionMapper() {
return userPermissionMapper; return userPermissionMapper;