GUACAMOLE-96: Restrict submitted attributes to those explicitly declared by the UserContext.

This commit is contained in:
Michael Jumper
2017-10-29 12:59:50 -07:00
parent a050c16020
commit 77255652bf
13 changed files with 153 additions and 16 deletions

View File

@@ -22,6 +22,7 @@ package org.apache.guacamole.rest.activeconnection;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleUnsupportedException; import org.apache.guacamole.GuacamoleUnsupportedException;
import org.apache.guacamole.net.auth.ActiveConnection; import org.apache.guacamole.net.auth.ActiveConnection;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
/** /**
@@ -30,7 +31,7 @@ import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
* toExternalObject() is implemented here. * toExternalObject() is implemented here.
*/ */
public class ActiveConnectionObjectTranslator public class ActiveConnectionObjectTranslator
implements DirectoryObjectTranslator<ActiveConnection, APIActiveConnection> { extends DirectoryObjectTranslator<ActiveConnection, APIActiveConnection> {
@Override @Override
public APIActiveConnection toExternalObject(ActiveConnection object) public APIActiveConnection toExternalObject(ActiveConnection object)
@@ -56,4 +57,10 @@ public class ActiveConnectionObjectTranslator
} }
@Override
public void filterExternalObject(UserContext context,
APIActiveConnection object) throws GuacamoleException {
// Nothing to filter on ActiveConnections (no attributes)
}
} }

View File

@@ -89,7 +89,7 @@ public class ActiveConnectionResource
@Assisted Directory<ActiveConnection> directory, @Assisted Directory<ActiveConnection> directory,
@Assisted ActiveConnection activeConnection, @Assisted ActiveConnection activeConnection,
DirectoryObjectTranslator<ActiveConnection, APIActiveConnection> translator) { DirectoryObjectTranslator<ActiveConnection, APIActiveConnection> translator) {
super(directory, activeConnection, translator); super(userContext, directory, activeConnection, translator);
this.userContext = userContext; this.userContext = userContext;
this.activeConnection = activeConnection; this.activeConnection = activeConnection;
} }

View File

@@ -21,6 +21,7 @@ package org.apache.guacamole.rest.connection;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.protocol.GuacamoleConfiguration; import org.apache.guacamole.protocol.GuacamoleConfiguration;
import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
@@ -29,7 +30,7 @@ import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
* objects. * objects.
*/ */
public class ConnectionObjectTranslator public class ConnectionObjectTranslator
implements DirectoryObjectTranslator<Connection, APIConnection> { extends DirectoryObjectTranslator<Connection, APIConnection> {
@Override @Override
public APIConnection toExternalObject(Connection object) public APIConnection toExternalObject(Connection object)
@@ -59,4 +60,14 @@ public class ConnectionObjectTranslator
} }
@Override
public void filterExternalObject(UserContext userContext,
APIConnection object) throws GuacamoleException {
// Filter object attributes by defined schema
object.setAttributes(filterAttributes(
userContext.getConnectionAttributes(), object.getAttributes()));
}
} }

View File

@@ -100,7 +100,7 @@ public class ConnectionResource extends DirectoryObjectResource<Connection, APIC
@Assisted Directory<Connection> directory, @Assisted Directory<Connection> directory,
@Assisted Connection connection, @Assisted Connection connection,
DirectoryObjectTranslator<Connection, APIConnection> translator) { DirectoryObjectTranslator<Connection, APIConnection> translator) {
super(directory, connection, translator); super(userContext, directory, connection, translator);
this.userContext = userContext; this.userContext = userContext;
this.connection = connection; this.connection = connection;
} }

View File

@@ -21,6 +21,7 @@ package org.apache.guacamole.rest.connectiongroup;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.ConnectionGroup; import org.apache.guacamole.net.auth.ConnectionGroup;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
/** /**
@@ -28,7 +29,7 @@ import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
* APIConnectionGroup objects. * APIConnectionGroup objects.
*/ */
public class ConnectionGroupObjectTranslator public class ConnectionGroupObjectTranslator
implements DirectoryObjectTranslator<ConnectionGroup, APIConnectionGroup> { extends DirectoryObjectTranslator<ConnectionGroup, APIConnectionGroup> {
@Override @Override
public APIConnectionGroup toExternalObject(ConnectionGroup object) public APIConnectionGroup toExternalObject(ConnectionGroup object)
@@ -53,4 +54,15 @@ public class ConnectionGroupObjectTranslator
} }
@Override
public void filterExternalObject(UserContext userContext,
APIConnectionGroup object) throws GuacamoleException {
// Filter object attributes by defined schema
object.setAttributes(filterAttributes(
userContext.getConnectionGroupAttributes(),
object.getAttributes()));
}
} }

View File

@@ -79,7 +79,7 @@ public class ConnectionGroupResource
@Assisted Directory<ConnectionGroup> directory, @Assisted Directory<ConnectionGroup> directory,
@Assisted ConnectionGroup connectionGroup, @Assisted ConnectionGroup connectionGroup,
DirectoryObjectTranslator<ConnectionGroup, APIConnectionGroup> translator) { DirectoryObjectTranslator<ConnectionGroup, APIConnectionGroup> translator) {
super(directory, connectionGroup, translator); super(userContext, directory, connectionGroup, translator);
this.userContext = userContext; this.userContext = userContext;
this.connectionGroup = connectionGroup; this.connectionGroup = connectionGroup;
} }

View File

@@ -29,6 +29,7 @@ import org.apache.guacamole.GuacamoleClientException;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.Directory; import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.Identifiable; import org.apache.guacamole.net.auth.Identifiable;
import org.apache.guacamole.net.auth.UserContext;
/** /**
* A REST resource which abstracts the operations available on an existing * A REST resource which abstracts the operations available on an existing
@@ -50,6 +51,12 @@ import org.apache.guacamole.net.auth.Identifiable;
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
public abstract class DirectoryObjectResource<InternalType extends Identifiable, ExternalType> { public abstract class DirectoryObjectResource<InternalType extends Identifiable, ExternalType> {
/**
* The UserContext associated with the Directory containing the object
* represented by this DirectoryObjectResource.
*/
private final UserContext userContext;
/** /**
* The Directory which contains the object represented by this * The Directory which contains the object represented by this
* DirectoryObjectResource. * DirectoryObjectResource.
@@ -71,6 +78,9 @@ public abstract class DirectoryObjectResource<InternalType extends Identifiable,
* Creates a new DirectoryObjectResource which exposes the operations * Creates a new DirectoryObjectResource which exposes the operations
* available for the given object. * available for the given object.
* *
* @param userContext
* The UserContext associated with the given Directory.
*
* @param directory * @param directory
* The Directory which contains the given object. * The Directory which contains the given object.
* *
@@ -81,8 +91,10 @@ public abstract class DirectoryObjectResource<InternalType extends Identifiable,
* A DirectoryObjectTranslator implementation which handles the type of * A DirectoryObjectTranslator implementation which handles the type of
* object given. * object given.
*/ */
public DirectoryObjectResource(Directory<InternalType> directory, InternalType object, public DirectoryObjectResource(UserContext userContext,
Directory<InternalType> directory, InternalType object,
DirectoryObjectTranslator<InternalType, ExternalType> translator) { DirectoryObjectTranslator<InternalType, ExternalType> translator) {
this.userContext = userContext;
this.directory = directory; this.directory = directory;
this.object = object; this.object = object;
this.translator = translator; this.translator = translator;
@@ -121,6 +133,9 @@ public abstract class DirectoryObjectResource<InternalType extends Identifiable,
if (modifiedObject == null) if (modifiedObject == null)
throw new GuacamoleClientException("Data must be submitted when updating objects."); throw new GuacamoleClientException("Data must be submitted when updating objects.");
// Filter/sanitize object contents
translator.filterExternalObject(userContext, modifiedObject);
// Perform update // Perform update
translator.applyExternalChanges(object, modifiedObject); translator.applyExternalChanges(object, modifiedObject);
directory.update(object); directory.update(object);

View File

@@ -19,8 +19,14 @@
package org.apache.guacamole.rest.directory; package org.apache.guacamole.rest.directory;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.form.Field;
import org.apache.guacamole.form.Form;
import org.apache.guacamole.net.auth.Identifiable; import org.apache.guacamole.net.auth.Identifiable;
import org.apache.guacamole.net.auth.UserContext;
/** /**
* Provides bidirectional conversion between REST-specific objects and the * Provides bidirectional conversion between REST-specific objects and the
@@ -35,7 +41,7 @@ import org.apache.guacamole.net.auth.Identifiable;
* deserialized as JSON) between REST clients and resource implementations * deserialized as JSON) between REST clients and resource implementations
* when representing the InternalType. * when representing the InternalType.
*/ */
public interface DirectoryObjectTranslator<InternalType extends Identifiable, ExternalType> { public abstract class DirectoryObjectTranslator<InternalType extends Identifiable, ExternalType> {
/** /**
* Converts the given object to an object which is intended to be used in * Converts the given object to an object which is intended to be used in
@@ -51,7 +57,7 @@ public interface DirectoryObjectTranslator<InternalType extends Identifiable, Ex
* @throws GuacamoleException * @throws GuacamoleException
* If the provided object cannot be converted for any reason. * If the provided object cannot be converted for any reason.
*/ */
ExternalType toExternalObject(InternalType object) public abstract ExternalType toExternalObject(InternalType object)
throws GuacamoleException; throws GuacamoleException;
/** /**
@@ -69,7 +75,7 @@ public interface DirectoryObjectTranslator<InternalType extends Identifiable, Ex
* @throws GuacamoleException * @throws GuacamoleException
* If the provided object cannot be converted for any reason. * If the provided object cannot be converted for any reason.
*/ */
InternalType toInternalObject(ExternalType object) public abstract InternalType toInternalObject(ExternalType object)
throws GuacamoleException; throws GuacamoleException;
/** /**
@@ -87,7 +93,67 @@ public interface DirectoryObjectTranslator<InternalType extends Identifiable, Ex
* @throws GuacamoleException * @throws GuacamoleException
* If the provided modifications cannot be applied for any reason. * If the provided modifications cannot be applied for any reason.
*/ */
void applyExternalChanges(InternalType existingObject, ExternalType object) public abstract void applyExternalChanges(InternalType existingObject,
throws GuacamoleException; ExternalType object) throws GuacamoleException;
/**
* Applies filtering to the contents of the given external object which
* came from an untrusted source. Implementations MUST sanitize the
* contents of the external object as necessary to guarantee that the
* object conforms to declared schema, such as the attributes declared for
* each object type at the UserContext level.
*
* @param userContext
* The UserContext associated with the object being filtered.
*
* @param object
* The object to modify such that it strictly conforms to the declared
* schema.
*
* @throws GuacamoleException
* If the object cannot be filtered due to an error.
*/
public abstract void filterExternalObject(UserContext userContext,
ExternalType object) throws GuacamoleException;
/**
* Filters the given map of attribute name/value pairs, producing a new
* map containing only attributes defined as fields within the given schema.
*
* @param schema
* The schema whose fields should be used to filter the given map of
* attributes.
*
* @param attributes
* The map of attribute name/value pairs to filter.
*
* @return
* A new map containing only the attributes defined as fields within
* the given schema.
*/
public Map<String, String> filterAttributes(Collection<Form> schema,
Map<String, String> attributes) {
Map<String, String> filtered = new HashMap<String, String>();
// Grab all attribute value strictly for defined fields
for (Form form : schema) {
for (Field field : form.getFields()) {
// Pull the associated attribute value from given map
String attributeName = field.getName();
String attributeValue = attributes.get(attributeName);
// Include attribute value within filtered map only if
// (1) defined and (2) present within provided map
if (attributeValue != null)
filtered.put(attributeName, attributeValue);
}
}
return filtered;
}
} }

View File

@@ -222,6 +222,9 @@ public abstract class DirectoryResource<InternalType extends Identifiable, Exter
if (object == null) if (object == null)
throw new GuacamoleClientException("Data must be submitted when creating objects."); throw new GuacamoleClientException("Data must be submitted when creating objects.");
// Filter/sanitize object contents
translator.filterExternalObject(userContext, object);
// Create the new object within the directory // Create the new object within the directory
directory.add(translator.toInternalObject(object)); directory.add(translator.toInternalObject(object));

View File

@@ -21,6 +21,7 @@ package org.apache.guacamole.rest.sharingprofile;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.SharingProfile; import org.apache.guacamole.net.auth.SharingProfile;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
/** /**
@@ -28,7 +29,7 @@ import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
* APISharingProfile objects. * APISharingProfile objects.
*/ */
public class SharingProfileObjectTranslator public class SharingProfileObjectTranslator
implements DirectoryObjectTranslator<SharingProfile, APISharingProfile> { extends DirectoryObjectTranslator<SharingProfile, APISharingProfile> {
@Override @Override
public APISharingProfile toExternalObject(SharingProfile object) public APISharingProfile toExternalObject(SharingProfile object)
@@ -53,4 +54,15 @@ public class SharingProfileObjectTranslator
} }
@Override
public void filterExternalObject(UserContext userContext,
APISharingProfile object) throws GuacamoleException {
// Filter object attributes by defined schema
object.setAttributes(filterAttributes(
userContext.getSharingProfileAttributes(),
object.getAttributes()));
}
} }

View File

@@ -82,7 +82,7 @@ public class SharingProfileResource
@Assisted Directory<SharingProfile> directory, @Assisted Directory<SharingProfile> directory,
@Assisted SharingProfile sharingProfile, @Assisted SharingProfile sharingProfile,
DirectoryObjectTranslator<SharingProfile, APISharingProfile> translator) { DirectoryObjectTranslator<SharingProfile, APISharingProfile> translator) {
super(directory, sharingProfile, translator); super(userContext, directory, sharingProfile, translator);
this.userContext = userContext; this.userContext = userContext;
this.sharingProfile = sharingProfile; this.sharingProfile = sharingProfile;
} }

View File

@@ -21,13 +21,14 @@ package org.apache.guacamole.rest.user;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.User; import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
/** /**
* Translator which converts between User objects and APIUser objects. * Translator which converts between User objects and APIUser objects.
*/ */
public class UserObjectTranslator public class UserObjectTranslator
implements DirectoryObjectTranslator<User, APIUser> { extends DirectoryObjectTranslator<User, APIUser> {
@Override @Override
public APIUser toExternalObject(User object) public APIUser toExternalObject(User object)
@@ -54,4 +55,14 @@ public class UserObjectTranslator
} }
@Override
public void filterExternalObject(UserContext userContext, APIUser object)
throws GuacamoleException {
// Filter object attributes by defined schema
object.setAttributes(filterAttributes(userContext.getUserAttributes(),
object.getAttributes()));
}
} }

View File

@@ -92,7 +92,7 @@ public class UserResource
@Assisted Directory<User> directory, @Assisted Directory<User> directory,
@Assisted User user, @Assisted User user,
DirectoryObjectTranslator<User, APIUser> translator) { DirectoryObjectTranslator<User, APIUser> translator) {
super(directory, user, translator); super(userContext, directory, user, translator);
this.userContext = userContext; this.userContext = userContext;
this.directory = directory; this.directory = directory;
this.user = user; this.user = user;