GUACAMOLE-96: Add object- and model-level support for storage of arbitrary attributes.

This commit is contained in:
Michael Jumper
2017-11-18 16:42:13 -08:00
parent fff1411768
commit a3cee158cb
6 changed files with 175 additions and 18 deletions

View File

@@ -19,6 +19,11 @@
package org.apache.guacamole.auth.jdbc.base; package org.apache.guacamole.auth.jdbc.base;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.apache.guacamole.net.auth.Attributes;
import org.apache.guacamole.net.auth.Identifiable; import org.apache.guacamole.net.auth.Identifiable;
/** /**
@@ -31,7 +36,7 @@ import org.apache.guacamole.net.auth.Identifiable;
* The type of model object that corresponds to this object. * The type of model object that corresponds to this object.
*/ */
public abstract class ModeledDirectoryObject<ModelType extends ObjectModel> public abstract class ModeledDirectoryObject<ModelType extends ObjectModel>
extends ModeledObject<ModelType> implements Identifiable { extends ModeledObject<ModelType> implements Identifiable, Attributes {
@Override @Override
public String getIdentifier() { public String getIdentifier() {
@@ -43,4 +48,54 @@ public abstract class ModeledDirectoryObject<ModelType extends ObjectModel>
getModel().setIdentifier(identifier); getModel().setIdentifier(identifier);
} }
/**
* Returns the names of all attributes explicitly supported by this object.
* Attributes named here have associated mappings within the backing model
* object, and thus should not be included in the arbitrary attribute
* storage. Any attributes set which do not match these names, such as those
* set via other extensions, will be added to arbitrary attribute storage.
*
* @return
* A read-only Set of the names of all attributes explicitly supported
* (mapped to a property of the backing model) by this object.
*/
public Set<String> getSupportedAttributeNames() {
return Collections.<String>emptySet();
}
@Override
public Map<String, String> getAttributes() {
// If no arbitrary attributes are defined, just return an empty map
Map<String, String> arbitraryAttributes = getModel().getArbitraryAttributes();
if (arbitraryAttributes == null)
return new HashMap<String, String>();
// Otherwise include any defined arbitrary attributes
return new HashMap<String, String>(arbitraryAttributes);
}
@Override
public void setAttributes(Map<String, String> attributes) {
// Get set of all supported attribute names
Set<String> supportedAttributes = getSupportedAttributeNames();
// Initialize model with empty map if no such map is already present
Map<String, String> arbitraryAttributes = getModel().getArbitraryAttributes();
if (arbitraryAttributes == null) {
arbitraryAttributes = new HashMap<String, String>();
getModel().setArbitraryAttributes(arbitraryAttributes);
}
// Store remaining attributes only if not directly mapped to model
for (Map.Entry<String, String> attribute : attributes.entrySet()) {
String name = attribute.getKey();
if (!supportedAttributes.contains(name))
arbitraryAttributes.put(name, attribute.getValue());
}
}
} }

View File

@@ -19,6 +19,8 @@
package org.apache.guacamole.auth.jdbc.base; package org.apache.guacamole.auth.jdbc.base;
import java.util.Map;
/** /**
* Object representation of a Guacamole object, such as a user or connection, * Object representation of a Guacamole object, such as a user or connection,
* as represented in the database. * as represented in the database.
@@ -34,7 +36,13 @@ public abstract class ObjectModel {
* The unique identifier which identifies this object. * The unique identifier which identifies this object.
*/ */
private String identifier; private String identifier;
/**
* Map of all arbitrary attributes associated with this object but not
* directly mapped to a particular column.
*/
private Map<String, String> arbitraryAttributes;
/** /**
* Creates a new, empty object. * Creates a new, empty object.
*/ */
@@ -82,4 +90,35 @@ public abstract class ObjectModel {
this.objectID = objectID; this.objectID = objectID;
} }
/**
* Returns a map of attribute name/value pairs for all attributes associated
* with this model which do not have explicit mappings to actual model
* properties. All other attributes (those which are explicitly supported
* by the model) should instead be mapped to properties with corresponding
* and properly-typed columns.
*
* @return
* A map of attribute name/value pairs for all attributes associated
* with this model which do not otherwise have explicit mappings to
* properties.
*/
public Map<String, String> getArbitraryAttributes() {
return arbitraryAttributes;
}
/**
* Sets all arbitrary attribute name/value pairs associated with this
* model. The provided map should contain only attributes which are not
* explicitly supported by the model, as any explicitly-supported
* attributes should instead be mapped to corresponding properties.
*
* @param arbitraryAttributes
* A map of attribute name/value pairs for all attributes associated
* with this model which do not otherwise have explicit mappings to
* properties.
*/
public void setArbitraryAttributes(Map<String, String> arbitraryAttributes) {
this.arbitraryAttributes = arbitraryAttributes;
}
} }

View File

@@ -25,7 +25,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@@ -157,6 +157,21 @@ public class ModeledConnection extends ModeledChildDirectoryObject<ConnectionMod
GUACD_PARAMETERS GUACD_PARAMETERS
)); ));
/**
* The names of all attributes which are explicitly supported by this
* extension's Connection objects.
*/
public static final Set<String> ATTRIBUTE_NAMES =
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
GUACD_HOSTNAME_NAME,
GUACD_PORT_NAME,
GUACD_ENCRYPTION_NAME,
MAX_CONNECTIONS_NAME,
MAX_CONNECTIONS_PER_USER_NAME,
CONNECTION_WEIGHT,
FAILOVER_ONLY_NAME
)));
/** /**
* The environment of the Guacamole server. * The environment of the Guacamole server.
*/ */
@@ -253,10 +268,16 @@ public class ModeledConnection extends ModeledChildDirectoryObject<ConnectionMod
return tunnelService.getActiveConnections(this).size(); return tunnelService.getActiveConnections(this).size();
} }
@Override
public Set<String> getSupportedAttributeNames() {
return ATTRIBUTE_NAMES;
}
@Override @Override
public Map<String, String> getAttributes() { public Map<String, String> getAttributes() {
Map<String, String> attributes = new HashMap<String, String>(); // Include any defined arbitrary attributes
Map<String, String> attributes = super.getAttributes();
// Set connection limit attribute // Set connection limit attribute
attributes.put(MAX_CONNECTIONS_NAME, NumericField.format(getModel().getMaxConnections())); attributes.put(MAX_CONNECTIONS_NAME, NumericField.format(getModel().getMaxConnections()));
@@ -305,6 +326,9 @@ public class ModeledConnection extends ModeledChildDirectoryObject<ConnectionMod
@Override @Override
public void setAttributes(Map<String, String> attributes) { public void setAttributes(Map<String, String> attributes) {
// Set arbitrary attributes
super.setAttributes(attributes);
// Translate connection limit attribute // Translate connection limit attribute
try { getModel().setMaxConnections(NumericField.parse(attributes.get(MAX_CONNECTIONS_NAME))); } try { getModel().setMaxConnections(NumericField.parse(attributes.get(MAX_CONNECTIONS_NAME))); }
catch (NumberFormatException e) { catch (NumberFormatException e) {

View File

@@ -23,7 +23,7 @@ import com.google.inject.Inject;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
@@ -89,6 +89,17 @@ public class ModeledConnectionGroup extends ModeledChildDirectoryObject<Connecti
CONCURRENCY_LIMITS CONCURRENCY_LIMITS
)); ));
/**
* The names of all attributes which are explicitly supported by this
* extension's ConnectionGroup objects.
*/
public static final Set<String> ATTRIBUTE_NAMES =
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
MAX_CONNECTIONS_NAME,
MAX_CONNECTIONS_PER_USER_NAME,
ENABLE_SESSION_AFFINITY
)));
/** /**
* The environment of the Guacamole server. * The environment of the Guacamole server.
*/ */
@@ -156,10 +167,16 @@ public class ModeledConnectionGroup extends ModeledChildDirectoryObject<Connecti
return getModel().getConnectionGroupIdentifiers(); return getModel().getConnectionGroupIdentifiers();
} }
@Override
public Set<String> getSupportedAttributeNames() {
return ATTRIBUTE_NAMES;
}
@Override @Override
public Map<String, String> getAttributes() { public Map<String, String> getAttributes() {
Map<String, String> attributes = new HashMap<String, String>(); // Include any defined arbitrary attributes
Map<String, String> attributes = super.getAttributes();
// Set connection limit attribute // Set connection limit attribute
attributes.put(MAX_CONNECTIONS_NAME, NumericField.format(getModel().getMaxConnections())); attributes.put(MAX_CONNECTIONS_NAME, NumericField.format(getModel().getMaxConnections()));
@@ -177,6 +194,9 @@ public class ModeledConnectionGroup extends ModeledChildDirectoryObject<Connecti
@Override @Override
public void setAttributes(Map<String, String> attributes) { public void setAttributes(Map<String, String> attributes) {
// Set arbitrary attributes
super.setAttributes(attributes);
// Translate connection limit attribute // Translate connection limit attribute
try { getModel().setMaxConnections(NumericField.parse(attributes.get(MAX_CONNECTIONS_NAME))); } try { getModel().setMaxConnections(NumericField.parse(attributes.get(MAX_CONNECTIONS_NAME))); }
catch (NumberFormatException e) { catch (NumberFormatException e) {

View File

@@ -95,14 +95,4 @@ public class ModeledSharingProfile
this.parameters = parameters; this.parameters = parameters;
} }
@Override
public Map<String, String> getAttributes() {
return Collections.<String, String>emptyMap();
}
@Override
public void setAttributes(Map<String, String> attributes) {
// Do nothing - no attributes
}
} }

View File

@@ -28,9 +28,10 @@ import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObject; import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObject;
import org.apache.guacamole.auth.jdbc.security.PasswordEncryptionService; import org.apache.guacamole.auth.jdbc.security.PasswordEncryptionService;
@@ -144,6 +145,25 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
ACCOUNT_RESTRICTIONS ACCOUNT_RESTRICTIONS
)); ));
/**
* The names of all attributes which are explicitly supported by this
* extension's User objects.
*/
public static final Set<String> ATTRIBUTE_NAMES =
Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
User.Attribute.FULL_NAME,
User.Attribute.EMAIL_ADDRESS,
User.Attribute.ORGANIZATION,
User.Attribute.ORGANIZATIONAL_ROLE,
DISABLED_ATTRIBUTE_NAME,
EXPIRED_ATTRIBUTE_NAME,
ACCESS_WINDOW_START_ATTRIBUTE_NAME,
ACCESS_WINDOW_END_ATTRIBUTE_NAME,
VALID_FROM_ATTRIBUTE_NAME,
VALID_UNTIL_ATTRIBUTE_NAME,
TIMEZONE_ATTRIBUTE_NAME
)));
/** /**
* Service for managing users. * Service for managing users.
*/ */
@@ -547,10 +567,16 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
} }
@Override
public Set<String> getSupportedAttributeNames() {
return ATTRIBUTE_NAMES;
}
@Override @Override
public Map<String, String> getAttributes() { public Map<String, String> getAttributes() {
Map<String, String> attributes = new HashMap<String, String>(); // Include any defined arbitrary attributes
Map<String, String> attributes = super.getAttributes();
// Always include unrestricted attributes // Always include unrestricted attributes
putUnrestrictedAttributes(attributes); putUnrestrictedAttributes(attributes);
@@ -565,6 +591,9 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
@Override @Override
public void setAttributes(Map<String, String> attributes) { public void setAttributes(Map<String, String> attributes) {
// Set arbitrary attributes
super.setAttributes(attributes);
// Always assign unrestricted attributes // Always assign unrestricted attributes
setUnrestrictedAttributes(attributes); setUnrestrictedAttributes(attributes);