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
Collection<ObjectPermissionModel> implicitPermissions = getImplicitPermissions(user, model);
if (!implicitPermissions.isEmpty())
getPermissionMapper().insert(implicitPermissions);
getPermissionMapper().insert(implicitPermissions, getCaseSensitiveIdentifiers());
// Add any arbitrary attributes
if (model.hasArbitraryAttributes())

View File

@@ -42,7 +42,7 @@ import org.apache.guacamole.net.auth.permission.PermissionSet;
public abstract class AbstractPermissionService<PermissionSetType extends PermissionSet<PermissionType>,
PermissionType extends Permission>
implements PermissionService<PermissionSetType, PermissionType> {
/**
* Returns the ObjectPermissionSet related to the type of the given entity.
* If the given entity represents a user, then the ObjectPermissionSet

View File

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

View File

@@ -192,7 +192,10 @@ public abstract class ModeledPermissionService<PermissionSetType extends Permiss
// Retrieve permissions only if allowed
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
throw new GuacamoleSecurityException("Permission denied.");

View File

@@ -48,6 +48,10 @@ public interface ObjectPermissionMapper extends PermissionMapper<ObjectPermissio
* 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
* "true" if identifiers should be treated as case-sensitive, otherwise
* "false".
*
* @return
* The requested permission, or null if no such permission is granted
@@ -56,7 +60,8 @@ public interface ObjectPermissionMapper extends PermissionMapper<ObjectPermissio
ObjectPermissionModel selectOne(@Param("entity") EntityModel entity,
@Param("type") ObjectPermission.Type type,
@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
@@ -79,6 +84,10 @@ public interface ObjectPermissionMapper extends PermissionMapper<ObjectPermissio
* 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
* "true" if identifiers should be treated as case-sensitive, otherwise
* "false".
*
* @return
* A collection containing the subset of identifiers for which at least
@@ -87,6 +96,7 @@ public interface ObjectPermissionMapper extends PermissionMapper<ObjectPermissio
Collection<String> selectAccessibleIdentifiers(@Param("entity") EntityModel entity,
@Param("permissions") Collection<ObjectPermission.Type> permissions,
@Param("identifiers") Collection<String> identifiers,
@Param("effectiveGroups") Collection<String> effectiveGroups);
@Param("effectiveGroups") Collection<String> effectiveGroups,
@Param("caseSensitive") boolean caseSensitive);
}

View File

@@ -43,12 +43,17 @@ public interface PermissionMapper<PermissionType> {
* 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
* "true" if identifiers should be treated as case-sensitive, otherwise
* "false".
*
* @return
* All permissions associated with the given 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
@@ -56,11 +61,16 @@ public interface PermissionMapper<PermissionType> {
*
* @param permissions
* The permissions to insert.
*
* @param caseSensitive
* "true" if identifiers should be treated as case-sensitive, otherwise
* "false".
*
* @return
* 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
@@ -68,10 +78,15 @@ public interface PermissionMapper<PermissionType> {
*
* @param permissions
* The permissions to delete.
*
* @param caseSensitive
* "true" if identifiers should be treated as case-sensitive, otherwise
* "false".
*
* @return
* 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>,
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
* permissions of the given entity.

View File

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

View File

@@ -19,142 +19,7 @@
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 {
/**
* 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);
}
public interface UserPermissionMapper extends ObjectPermissionMapper {}

View File

@@ -24,6 +24,7 @@ import com.google.inject.Provider;
import java.util.Set;
import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
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.ModeledPermissions;
@@ -46,6 +47,17 @@ public class UserPermissionService extends ModeledObjectPermissionService {
@Inject
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
protected ObjectPermissionMapper getPermissionMapper() {
return userPermissionMapper;