GUAC-1101: Limit results of retrieval operations by read permissions, unless user is a sysadmin.

This commit is contained in:
Michael Jumper
2015-02-12 21:29:58 -08:00
parent b514fc910d
commit 0e38acbd59
4 changed files with 131 additions and 12 deletions

View File

@@ -29,6 +29,7 @@ import net.sourceforge.guacamole.net.auth.mysql.service.SaltService;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.User;
import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet;
import org.glyptodon.guacamole.net.auth.permission.SystemPermission;
import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet;
import org.glyptodon.guacamole.net.auth.simple.SimpleObjectPermissionSet;
import org.glyptodon.guacamole.net.auth.simple.SimpleSystemPermissionSet;
@@ -126,6 +127,22 @@ public class MySQLUser implements User, DirectoryObject<UserModel> {
}
/**
* Returns whether this user is a system administrator, and thus is not
* restricted by permissions.
*
* @return
* true if this user is a system administrator, false otherwise.
*
* @throws GuacamoleException
* If an error occurs while determining the user's system administrator
* status.
*/
public boolean isAdministrator() throws GuacamoleException {
SystemPermissionSet systemPermissionSet = getSystemPermissions();
return systemPermissionSet.hasPermission(SystemPermission.Type.ADMINISTER);
}
@Override
public SystemPermissionSet getSystemPermissions()
throws GuacamoleException {

View File

@@ -24,6 +24,7 @@ package net.sourceforge.guacamole.net.auth.mysql.dao;
import java.util.Collection;
import java.util.Set;
import net.sourceforge.guacamole.net.auth.mysql.model.UserModel;
import org.apache.ibatis.annotations.Param;
/**
@@ -32,23 +33,45 @@ import org.apache.ibatis.annotations.Param;
* to fulfill the needs of the Directory class.
*
* @author Michael Jumper
* @param <T>
* @param <ModelType>
* The type of object contained within the directory whose objects are
* mapped by this mapper.
*/
public interface DirectoryObjectMapper<T> {
public interface DirectoryObjectMapper<ModelType> {
/**
* Selects the identifiers of all objects.
* Selects the identifiers of all objects, regardless of whether they
* are readable by any particular user. This should only be called on
* behalf of a system administrator. If identifiers are needed by a non-
* administrative user who must have explicit read rights, use
* selectReadableIdentifiers() instead.
*
* @return
* A Set containing all identifiers of all objects.
*/
Set<String> selectIdentifiers();
/**
* Selects the identifiers of all objects that are explicitly readable by
* the given user. If identifiers are needed by a system administrator
* (who, by definition, does not need explicit read rights), use
* selectIdentifiers() instead.
*
* @param user
* The user whose permissions should determine whether an identifier
* is returned.
*
* @return
* A Set containing all identifiers of all readable objects.
*/
Set<String> selectReadableIdentifiers(@Param("user") UserModel user);
/**
* Selects all objects which have the given identifiers. If an identifier
* has no corresponding object, it will be ignored.
* has no corresponding object, it will be ignored. This should only be
* called on behalf of a system administrator. If objects are needed by a
* non-administrative user who must have explicit read rights, use
* selectReadable() instead.
*
* @param identifiers
* The identifiers of the objects to return.
@@ -56,7 +79,27 @@ public interface DirectoryObjectMapper<T> {
* @return
* A Collection of all objects having the given identifiers.
*/
Collection<T> select(@Param("identifiers") Collection<String> identifiers);
Collection<ModelType> select(@Param("identifiers") Collection<String> identifiers);
/**
* Selects all objects which have the given identifiers and are explicitly
* readably by the given user. If an identifier has no corresponding
* object, or the corresponding object is unreadable, it will be ignored.
* If objects 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 objects to return.
*
* @return
* A Collection of all objects having the given identifiers.
*/
Collection<ModelType> selectReadable(@Param("user") UserModel user,
@Param("identifiers") Collection<String> identifiers);
/**
* Inserts the given object into the database. If the object already
@@ -68,7 +111,7 @@ public interface DirectoryObjectMapper<T> {
* @return
* The number of rows inserted.
*/
int insert(@Param("object") T object);
int insert(@Param("object") ModelType object);
/**
* Deletes the given object into the database. If the object does not
@@ -92,6 +135,6 @@ public interface DirectoryObjectMapper<T> {
* @return
* The number of rows updated.
*/
int update(@Param("object") T object);
int update(@Param("object") ModelType object);
}

View File

@@ -103,9 +103,12 @@ public abstract class DirectoryObjectService<ObjectType extends DirectoryObject<
* @return
* The object having the given identifier, or null if no such object
* exists.
*
* @throws GuacamoleException
* If an error occurs while retrieving the requested object.
*/
public ObjectType retrieveObject(AuthenticatedUser user,
String identifier) {
String identifier) throws GuacamoleException {
// Pull objects having given identifier
Collection<ObjectType> objects = retrieveObjects(user, Collections.singleton(identifier));
@@ -135,16 +138,29 @@ public abstract class DirectoryObjectService<ObjectType extends DirectoryObject<
*
* @return
* The objects having the given identifiers.
*
* @throws GuacamoleException
* If an error occurs while retrieving the requested objects.
*/
public Collection<ObjectType> retrieveObjects(AuthenticatedUser user,
Collection<String> identifiers) {
Collection<String> identifiers) throws GuacamoleException {
// Do not query if no identifiers given
if (identifiers.isEmpty())
return Collections.EMPTY_LIST;
Collection<ModelType> objects;
// Bypass permission checks if the user is a system admin
if (user.getUser().isAdministrator())
objects = getObjectMapper().select(identifiers);
// Otherwise only return explicitly readable identifiers
else
objects = getObjectMapper().selectReadable(user.getUser().getModel(), identifiers);
// Return collection of requested objects
return getObjectInstances(getObjectMapper().select(identifiers));
return getObjectInstances(objects);
}
@@ -215,9 +231,21 @@ public abstract class DirectoryObjectService<ObjectType extends DirectoryObject<
*
* @return
* The set of all identifiers for all objects in the database.
*
* @throws GuacamoleException
* If an error occurs while reading identifiers.
*/
public Set<String> getIdentifiers(AuthenticatedUser user) {
return getObjectMapper().selectIdentifiers();
public Set<String> getIdentifiers(AuthenticatedUser user)
throws GuacamoleException {
// Bypass permission checks if the user is a system admin
if (user.getUser().isAdministrator())
return getObjectMapper().selectIdentifiers();
// Otherwise only return explicitly readable identifiers
else
return getObjectMapper().selectReadableIdentifiers(user.getUser().getModel());
}
}

View File

@@ -40,6 +40,16 @@
FROM guacamole_user
</select>
<!-- Select usernames of all readable users -->
<select id="selectReadableIdentifiers" resultType="string">
SELECT username
FROM guacamole_user
JOIN guacamole_user_permission ON affected_user_id = guacamole_user.user_id
WHERE
guacamole_user_permission.user_id = #{user.userID,jdbcType=INTEGER}
AND permission = 'read'
</select>
<!-- Select multiple users by username -->
<select id="select" resultMap="UserResultMap">
@@ -57,6 +67,27 @@
</select>
<!-- Select multiple users by username only if readable -->
<select id="selectReadable" resultMap="UserResultMap">
SELECT
guacamole_user.user_id,
username,
password_hash,
password_salt
FROM guacamole_user
JOIN guacamole_user_permission ON affected_user_id = guacamole_user.user_id
WHERE username IN
<foreach collection="identifiers" item="identifier"
open="(" separator="," close=")">
#{identifier,jdbcType=VARCHAR}
</foreach>
AND guacamole_user_permission.user_id = #{user.userID,jdbcType=INTEGER}
AND permission = 'read'
</select>
<select id="selectByCredentials" resultMap="UserResultMap">
SELECT
user_id,