Merge pull request #248 from glyptodon/concurrent-policy

GUAC-830: Implement concurrency policy attributes
This commit is contained in:
James Muehlner
2015-08-21 21:58:38 -07:00
21 changed files with 674 additions and 159 deletions

View File

@@ -42,6 +42,20 @@ public class ConnectionModel extends GroupedObjectModel {
*/ */
private String protocol; private String protocol;
/**
* The maximum number of connections that can be established to this
* connection concurrently, zero if no restriction applies, or null if the
* default restrictions should be applied.
*/
private Integer maxConnections;
/**
* The maximum number of connections that can be established to this
* connection concurrently by any one user, zero if no restriction applies,
* or null if the default restrictions should be applied.
*/
private Integer maxConnectionsPerUser;
/** /**
* Creates a new, empty connection. * Creates a new, empty connection.
*/ */
@@ -89,6 +103,58 @@ public class ConnectionModel extends GroupedObjectModel {
this.protocol = protocol; this.protocol = protocol;
} }
/**
* Returns the maximum number of connections that can be established to
* this connection concurrently.
*
* @return
* The maximum number of connections that can be established to this
* connection concurrently, zero if no restriction applies, or null if
* the default restrictions should be applied.
*/
public Integer getMaxConnections() {
return maxConnections;
}
/**
* Sets the maximum number of connections that can be established to this
* connection concurrently.
*
* @param maxConnections
* The maximum number of connections that can be established to this
* connection concurrently, zero if no restriction applies, or null if
* the default restrictions should be applied.
*/
public void setMaxConnections(Integer maxConnections) {
this.maxConnections = maxConnections;
}
/**
* Returns the maximum number of connections that can be established to
* this connection concurrently by any one user.
*
* @return
* The maximum number of connections that can be established to this
* connection concurrently by any one user, zero if no restriction
* applies, or null if the default restrictions should be applied.
*/
public Integer getMaxConnectionsPerUser() {
return maxConnectionsPerUser;
}
/**
* Sets the maximum number of connections that can be established to this
* connection concurrently by any one user.
*
* @param maxConnectionsPerUser
* The maximum number of connections that can be established to this
* connection concurrently by any one user, zero if no restriction
* applies, or null if the default restrictions should be applied.
*/
public void setMaxConnectionsPerUser(Integer maxConnectionsPerUser) {
this.maxConnectionsPerUser = maxConnectionsPerUser;
}
@Override @Override
public String getIdentifier() { public String getIdentifier() {

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2013 Glyptodon LLC * Copyright (C) 2015 Glyptodon LLC
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -24,17 +24,25 @@ package org.glyptodon.guacamole.auth.jdbc.connection;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.auth.jdbc.base.ModeledGroupedDirectoryObject; import org.glyptodon.guacamole.auth.jdbc.base.ModeledGroupedDirectoryObject;
import org.glyptodon.guacamole.form.Field;
import org.glyptodon.guacamole.form.Form;
import org.glyptodon.guacamole.form.NumericField;
import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.GuacamoleTunnel;
import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.Connection;
import org.glyptodon.guacamole.net.auth.ConnectionRecord; import org.glyptodon.guacamole.net.auth.ConnectionRecord;
import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; import org.glyptodon.guacamole.protocol.GuacamoleClientInformation;
import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* An implementation of the Connection object which is backed by a database * An implementation of the Connection object which is backed by a database
@@ -46,6 +54,40 @@ import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
public class ModeledConnection extends ModeledGroupedDirectoryObject<ConnectionModel> public class ModeledConnection extends ModeledGroupedDirectoryObject<ConnectionModel>
implements Connection { implements Connection {
/**
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(ModeledConnection.class);
/**
* The name of the attribute which controls the maximum number of
* concurrent connections.
*/
public static final String MAX_CONNECTIONS_NAME = "max-connections";
/**
* The name of the attribute which controls the maximum number of
* concurrent connections per user.
*/
public static final String MAX_CONNECTIONS_PER_USER_NAME = "max-connections-per-user";
/**
* All attributes related to restricting user accounts, within a logical
* form.
*/
public static final Form CONCURRENCY_LIMITS = new Form("concurrency", Arrays.<Field>asList(
new NumericField(MAX_CONNECTIONS_NAME),
new NumericField(MAX_CONNECTIONS_PER_USER_NAME)
));
/**
* All possible attributes of connection objects organized as individual,
* logical forms.
*/
public static final Collection<Form> ATTRIBUTES = Collections.unmodifiableCollection(Arrays.asList(
CONCURRENCY_LIMITS
));
/** /**
* Service for managing connections. * Service for managing connections.
*/ */
@@ -127,12 +169,35 @@ public class ModeledConnection extends ModeledGroupedDirectoryObject<ConnectionM
@Override @Override
public Map<String, String> getAttributes() { public Map<String, String> getAttributes() {
return Collections.<String, String>emptyMap();
Map<String, String> attributes = new HashMap<String, String>();
// Set connection limit attribute
attributes.put(MAX_CONNECTIONS_NAME, NumericField.format(getModel().getMaxConnections()));
// Set per-user connection limit attribute
attributes.put(MAX_CONNECTIONS_PER_USER_NAME, NumericField.format(getModel().getMaxConnectionsPerUser()));
return attributes;
} }
@Override @Override
public void setAttributes(Map<String, String> attributes) { public void setAttributes(Map<String, String> attributes) {
// Drop all attributes - none currently supported
// Translate connection limit attribute
try { getModel().setMaxConnections(NumericField.parse(attributes.get(MAX_CONNECTIONS_NAME))); }
catch (NumberFormatException e) {
logger.warn("Not setting maximum connections: {}", e.getMessage());
logger.debug("Unable to parse numeric attribute.", e);
}
// Translate per-user connection limit attribute
try { getModel().setMaxConnectionsPerUser(NumericField.parse(attributes.get(MAX_CONNECTIONS_PER_USER_NAME))); }
catch (NumberFormatException e) {
logger.warn("Not setting maximum connections per user: {}", e.getMessage());
logger.debug("Unable to parse numeric attribute.", e);
}
} }
} }

View File

@@ -43,6 +43,20 @@ public class ConnectionGroupModel extends GroupedObjectModel {
*/ */
private ConnectionGroup.Type type; private ConnectionGroup.Type type;
/**
* The maximum number of connections that can be established to this
* connection group concurrently, zero if no restriction applies, or
* null if the default restrictions should be applied.
*/
private Integer maxConnections;
/**
* The maximum number of connections that can be established to this
* connection group concurrently by any one user, zero if no restriction
* applies, or null if the default restrictions should be applied.
*/
private Integer maxConnectionsPerUser;
/** /**
* Creates a new, empty connection group. * Creates a new, empty connection group.
*/ */
@@ -91,6 +105,60 @@ public class ConnectionGroupModel extends GroupedObjectModel {
this.type = type; this.type = type;
} }
/**
* Returns the maximum number of connections that can be established to
* this connection group concurrently.
*
* @return
* The maximum number of connections that can be established to this
* connection group concurrently, zero if no restriction applies, or
* null if the default restrictions should be applied.
*/
public Integer getMaxConnections() {
return maxConnections;
}
/**
* Sets the maximum number of connections that can be established to this
* connection group concurrently.
*
* @param maxConnections
* The maximum number of connections that can be established to this
* connection group concurrently, zero if no restriction applies, or
* null if the default restrictions should be applied.
*/
public void setMaxConnections(Integer maxConnections) {
this.maxConnections = maxConnections;
}
/**
* Returns the maximum number of connections that can be established to
* this connection group concurrently by any one user.
*
* @return
* The maximum number of connections that can be established to this
* connection group concurrently by any one user, zero if no
* restriction applies, or null if the default restrictions should be
* applied.
*/
public Integer getMaxConnectionsPerUser() {
return maxConnectionsPerUser;
}
/**
* Sets the maximum number of connections that can be established to this
* connection group concurrently by any one user.
*
* @param maxConnectionsPerUser
* The maximum number of connections that can be established to this
* connection group concurrently by any one user, zero if no
* restriction applies, or null if the default restrictions should be
* applied.
*/
public void setMaxConnectionsPerUser(Integer maxConnectionsPerUser) {
this.maxConnectionsPerUser = maxConnectionsPerUser;
}
@Override @Override
public String getIdentifier() { public String getIdentifier() {

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2013 Glyptodon LLC * Copyright (C) 2015 Glyptodon LLC
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -23,26 +23,69 @@
package org.glyptodon.guacamole.auth.jdbc.connectiongroup; package org.glyptodon.guacamole.auth.jdbc.connectiongroup;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionService; import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionService;
import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.auth.jdbc.base.ModeledGroupedDirectoryObject; import org.glyptodon.guacamole.auth.jdbc.base.ModeledGroupedDirectoryObject;
import org.glyptodon.guacamole.form.Field;
import org.glyptodon.guacamole.form.Form;
import org.glyptodon.guacamole.form.NumericField;
import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.GuacamoleTunnel;
import org.glyptodon.guacamole.net.auth.ConnectionGroup; import org.glyptodon.guacamole.net.auth.ConnectionGroup;
import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; import org.glyptodon.guacamole.protocol.GuacamoleClientInformation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* An implementation of the ConnectionGroup object which is backed by a * An implementation of the ConnectionGroup object which is backed by a
* database model. * database model.
* *
* @author James Muehlner * @author James Muehlner
* @author Michael Jumper
*/ */
public class ModeledConnectionGroup extends ModeledGroupedDirectoryObject<ConnectionGroupModel> public class ModeledConnectionGroup extends ModeledGroupedDirectoryObject<ConnectionGroupModel>
implements ConnectionGroup { implements ConnectionGroup {
/**
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(ModeledConnectionGroup.class);
/**
* The name of the attribute which controls the maximum number of
* concurrent connections.
*/
public static final String MAX_CONNECTIONS_NAME = "max-connections";
/**
* The name of the attribute which controls the maximum number of
* concurrent connections per user.
*/
public static final String MAX_CONNECTIONS_PER_USER_NAME = "max-connections-per-user";
/**
* All attributes related to restricting user accounts, within a logical
* form.
*/
public static final Form CONCURRENCY_LIMITS = new Form("concurrency", Arrays.<Field>asList(
new NumericField(MAX_CONNECTIONS_NAME),
new NumericField(MAX_CONNECTIONS_PER_USER_NAME)
));
/**
* All possible attributes of connection group objects organized as
* individual, logical forms.
*/
public static final Collection<Form> ATTRIBUTES = Collections.unmodifiableCollection(Arrays.asList(
CONCURRENCY_LIMITS
));
/** /**
* Service for managing connections. * Service for managing connections.
*/ */
@@ -112,12 +155,35 @@ public class ModeledConnectionGroup extends ModeledGroupedDirectoryObject<Connec
@Override @Override
public Map<String, String> getAttributes() { public Map<String, String> getAttributes() {
return Collections.<String, String>emptyMap();
Map<String, String> attributes = new HashMap<String, String>();
// Set connection limit attribute
attributes.put(MAX_CONNECTIONS_NAME, NumericField.format(getModel().getMaxConnections()));
// Set per-user connection limit attribute
attributes.put(MAX_CONNECTIONS_PER_USER_NAME, NumericField.format(getModel().getMaxConnectionsPerUser()));
return attributes;
} }
@Override @Override
public void setAttributes(Map<String, String> attributes) { public void setAttributes(Map<String, String> attributes) {
// Drop all attributes - none currently supported
// Translate connection limit attribute
try { getModel().setMaxConnections(NumericField.parse(attributes.get(MAX_CONNECTIONS_NAME))); }
catch (NumberFormatException e) {
logger.warn("Not setting maximum connections: {}", e.getMessage());
logger.debug("Unable to parse numeric attribute.", e);
}
// Translate per-user connection limit attribute
try { getModel().setMaxConnectionsPerUser(NumericField.parse(attributes.get(MAX_CONNECTIONS_PER_USER_NAME))); }
catch (NumberFormatException e) {
logger.warn("Not setting maximum connections per user: {}", e.getMessage());
logger.debug("Unable to parse numeric attribute.", e);
}
} }
} }

View File

@@ -160,7 +160,7 @@ public class ConfigurableGuacamoleTunnelService
int count = multiset.count(value); int count = multiset.count(value);
// Bail out if the maximum has already been reached // Bail out if the maximum has already been reached
if (count >= max || max == 0) if (count >= max && max != 0)
return false; return false;
// Attempt to add one more value // Attempt to add one more value
@@ -200,14 +200,23 @@ public class ConfigurableGuacamoleTunnelService
// Return the first unreserved connection // Return the first unreserved connection
for (ModeledConnection connection : sortedConnections) { for (ModeledConnection connection : sortedConnections) {
// Determine per-user limits on this connection
Integer connectionMaxConnectionsPerUser = connection.getModel().getMaxConnectionsPerUser();
if (connectionMaxConnectionsPerUser == null)
connectionMaxConnectionsPerUser = connectionDefaultMaxConnectionsPerUser;
// Determine overall limits on this connection
Integer connectionMaxConnections = connection.getModel().getMaxConnections();
if (connectionMaxConnections == null)
connectionMaxConnections = connectionDefaultMaxConnections;
// Attempt to aquire connection according to per-user limits // Attempt to aquire connection according to per-user limits
Seat seat = new Seat(username, connection.getIdentifier()); Seat seat = new Seat(username, connection.getIdentifier());
if (tryAdd(activeSeats, seat, if (tryAdd(activeSeats, seat, connectionMaxConnectionsPerUser)) {
connectionDefaultMaxConnectionsPerUser)) {
// Attempt to aquire connection according to overall limits // Attempt to aquire connection according to overall limits
if (tryAdd(activeConnections, connection.getIdentifier(), if (tryAdd(activeConnections, connection.getIdentifier(),
connectionDefaultMaxConnections)) connectionMaxConnections))
return connection; return connection;
// Acquire failed - retry with next connection // Acquire failed - retry with next connection
@@ -222,7 +231,7 @@ public class ConfigurableGuacamoleTunnelService
// Too many connections by this user // Too many connections by this user
if (userSpecificFailure) if (userSpecificFailure)
throw new GuacamoleClientTooManyException("Cannot connect. Connection group already in use by this user."); throw new GuacamoleClientTooManyException("Cannot connect. Connection already in use by this user.");
// Too many connections, but not necessarily due purely to this user // Too many connections, but not necessarily due purely to this user
else else
@@ -243,14 +252,24 @@ public class ConfigurableGuacamoleTunnelService
// Get username // Get username
String username = user.getUser().getIdentifier(); String username = user.getUser().getIdentifier();
// Determine per-user limits on this connection group
Integer connectionGroupMaxConnectionsPerUser = connectionGroup.getModel().getMaxConnectionsPerUser();
if (connectionGroupMaxConnectionsPerUser == null)
connectionGroupMaxConnectionsPerUser = connectionGroupDefaultMaxConnectionsPerUser;
// Determine overall limits on this connection group
Integer connectionGroupMaxConnections = connectionGroup.getModel().getMaxConnections();
if (connectionGroupMaxConnections == null)
connectionGroupMaxConnections = connectionGroupDefaultMaxConnections;
// Attempt to aquire connection group according to per-user limits // Attempt to aquire connection group according to per-user limits
Seat seat = new Seat(username, connectionGroup.getIdentifier()); Seat seat = new Seat(username, connectionGroup.getIdentifier());
if (tryAdd(activeGroupSeats, seat, if (tryAdd(activeGroupSeats, seat,
connectionGroupDefaultMaxConnectionsPerUser)) { connectionGroupMaxConnectionsPerUser)) {
// Attempt to aquire connection group according to overall limits // Attempt to aquire connection group according to overall limits
if (tryAdd(activeGroups, connectionGroup.getIdentifier(), if (tryAdd(activeGroups, connectionGroup.getIdentifier(),
connectionGroupDefaultMaxConnections)) connectionGroupMaxConnections))
return; return;
// Acquire failed // Acquire failed

View File

@@ -25,9 +25,7 @@ package org.glyptodon.guacamole.auth.jdbc.user;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.sql.Date; import java.sql.Date;
import java.sql.Time; import java.sql.Time;
import java.text.DateFormat;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
@@ -268,40 +266,39 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
return userPermissionService.getPermissionSet(getCurrentUser(), this); return userPermissionService.getPermissionSet(getCurrentUser(), this);
} }
/** @Override
* Converts the given date into a string which follows the format used by public Map<String, String> getAttributes() {
* date attributes.
*
* @param date
* The date value to format, which may be null.
*
* @return
* The formatted date, or null if the provided time was null.
*/
private String formatDate(Date date) {
DateFormat dateFormat = new SimpleDateFormat(DateField.FORMAT);
return date == null ? null : dateFormat.format(date);
}
/** Map<String, String> attributes = new HashMap<String, String>();
* Converts the given time into a string which follows the format used by
* time attributes. // Set disabled attribute
* attributes.put(DISABLED_ATTRIBUTE_NAME, getModel().isDisabled() ? "true" : null);
* @param time
* The time value to format, which may be null. // Set password expired attribute
* attributes.put(EXPIRED_ATTRIBUTE_NAME, getModel().isExpired() ? "true" : null);
* @return
* The formatted time, or null if the provided time was null. // Set access window start time
*/ attributes.put(ACCESS_WINDOW_START_ATTRIBUTE_NAME, TimeField.format(getModel().getAccessWindowStart()));
private String formatTime(Time time) {
DateFormat timeFormat = new SimpleDateFormat(TimeField.FORMAT); // Set access window end time
return time == null ? null : timeFormat.format(time); attributes.put(ACCESS_WINDOW_END_ATTRIBUTE_NAME, TimeField.format(getModel().getAccessWindowEnd()));
// Set account validity start date
attributes.put(VALID_FROM_ATTRIBUTE_NAME, DateField.format(getModel().getValidFrom()));
// Set account validity end date
attributes.put(VALID_UNTIL_ATTRIBUTE_NAME, DateField.format(getModel().getValidUntil()));
// Set timezone attribute
attributes.put(TIMEZONE_ATTRIBUTE_NAME, getModel().getTimeZone());
return attributes;
} }
/** /**
* Parses the given string into a corresponding date. The string must * Parses the given string into a corresponding date. The string must
* follow the standard format used by date attributes, as defined by * follow the standard format used by date attributes, as defined by
* DATE_FORMAT and as would be produced by formatDate(). * DateField.FORMAT and as would be produced by DateField.format().
* *
* @param dateString * @param dateString
* The date string to parse, which may be null. * The date string to parse, which may be null.
@@ -318,19 +315,19 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
throws ParseException { throws ParseException {
// Return null if no date provided // Return null if no date provided
if (dateString == null || dateString.isEmpty()) java.util.Date parsedDate = DateField.parse(dateString);
if (parsedDate == null)
return null; return null;
// Parse date according to format // Convert to SQL Date
DateFormat dateFormat = new SimpleDateFormat(DateField.FORMAT); return new Date(parsedDate.getTime());
return new Date(dateFormat.parse(dateString).getTime());
} }
/** /**
* Parses the given string into a corresponding time. The string must * Parses the given string into a corresponding time. The string must
* follow the standard format used by time attributes, as defined by * follow the standard format used by time attributes, as defined by
* TIME_FORMAT and as would be produced by formatTime(). * TimeField.FORMAT and as would be produced by TimeField.format().
* *
* @param timeString * @param timeString
* The time string to parse, which may be null. * The time string to parse, which may be null.
@@ -347,67 +344,15 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
throws ParseException { throws ParseException {
// Return null if no time provided // Return null if no time provided
if (timeString == null || timeString.isEmpty()) java.util.Date parsedDate = TimeField.parse(timeString);
if (parsedDate == null)
return null; return null;
// Parse time according to format // Convert to SQL Time
DateFormat timeFormat = new SimpleDateFormat(TimeField.FORMAT); return new Time(parsedDate.getTime());
return new Time(timeFormat.parse(timeString).getTime());
} }
/**
* Parses the given string into a time zone ID string. As these strings are
* equivalent, the only transformation currently performed by this function
* is to ensure that a blank time zone string is parsed into null.
*
* @param timeZone
* The time zone string to parse, which may be null.
*
* @return
* The ID of the time zone corresponding to the given string, or null
* if the given time zone string was null or blank.
*/
private String parseTimeZone(String timeZone) {
// Return null if no time zone provided
if (timeZone == null || timeZone.isEmpty())
return null;
// Otherwise, assume time zone is valid
return timeZone;
}
@Override
public Map<String, String> getAttributes() {
Map<String, String> attributes = new HashMap<String, String>();
// Set disabled attribute
attributes.put(DISABLED_ATTRIBUTE_NAME, getModel().isDisabled() ? "true" : null);
// Set password expired attribute
attributes.put(EXPIRED_ATTRIBUTE_NAME, getModel().isExpired() ? "true" : null);
// Set access window start time
attributes.put(ACCESS_WINDOW_START_ATTRIBUTE_NAME, formatTime(getModel().getAccessWindowStart()));
// Set access window end time
attributes.put(ACCESS_WINDOW_END_ATTRIBUTE_NAME, formatTime(getModel().getAccessWindowEnd()));
// Set account validity start date
attributes.put(VALID_FROM_ATTRIBUTE_NAME, formatDate(getModel().getValidFrom()));
// Set account validity end date
attributes.put(VALID_UNTIL_ATTRIBUTE_NAME, formatDate(getModel().getValidUntil()));
// Set timezone attribute
attributes.put(TIMEZONE_ATTRIBUTE_NAME, getModel().getTimeZone());
return attributes;
}
@Override @Override
public void setAttributes(Map<String, String> attributes) { public void setAttributes(Map<String, String> attributes) {
@@ -446,7 +391,7 @@ public class ModeledUser extends ModeledDirectoryObject<UserModel> implements Us
} }
// Translate timezone attribute // Translate timezone attribute
getModel().setTimeZone(parseTimeZone(attributes.get(TIMEZONE_ATTRIBUTE_NAME))); getModel().setTimeZone(TimeZoneField.parse(attributes.get(TIMEZONE_ATTRIBUTE_NAME)));
} }

View File

@@ -29,10 +29,11 @@ import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionDirectory;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject; import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject;
import org.glyptodon.guacamole.auth.jdbc.activeconnection.ActiveConnectionDirectory; import org.glyptodon.guacamole.auth.jdbc.activeconnection.ActiveConnectionDirectory;
import org.glyptodon.guacamole.auth.jdbc.connection.ModeledConnection;
import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup;
import org.glyptodon.guacamole.form.Form; import org.glyptodon.guacamole.form.Form;
import org.glyptodon.guacamole.net.auth.ActiveConnection; import org.glyptodon.guacamole.net.auth.ActiveConnection;
import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.Connection;
@@ -140,12 +141,12 @@ public class UserContext extends RestrictedObject
@Override @Override
public Collection<Form> getConnectionAttributes() { public Collection<Form> getConnectionAttributes() {
return Collections.<Form>emptyList(); return ModeledConnection.ATTRIBUTES;
} }
@Override @Override
public Collection<Form> getConnectionGroupAttributes() { public Collection<Form> getConnectionGroupAttributes() {
return Collections.<Form>emptyList(); return ModeledConnectionGroup.ATTRIBUTES;
} }
} }

View File

@@ -15,6 +15,24 @@
}, },
"CONNECTION_ATTRIBUTES" : {
"FIELD_HEADER_MAX_CONNECTIONS" : "Maximum number of connections:",
"FIELD_HEADER_MAX_CONNECTIONS_PER_USER" : "Maximum number of connections per user:",
"SECTION_HEADER_CONCURRENCY" : "Concurrency Limits"
},
"CONNECTION_GROUP_ATTRIBUTES" : {
"FIELD_HEADER_MAX_CONNECTIONS" : "Maximum number of connections:",
"FIELD_HEADER_MAX_CONNECTIONS_PER_USER" : "Maximum number of connections per user:",
"SECTION_HEADER_CONCURRENCY" : "Concurrency Limits (Balancing Groups)"
},
"USER_ATTRIBUTES" : { "USER_ATTRIBUTES" : {
"FIELD_HEADER_DISABLED" : "Login disabled:", "FIELD_HEADER_DISABLED" : "Login disabled:",

View File

@@ -32,6 +32,10 @@ CREATE TABLE `guacamole_connection_group` (
`type` enum('ORGANIZATIONAL', `type` enum('ORGANIZATIONAL',
'BALANCING') NOT NULL DEFAULT 'ORGANIZATIONAL', 'BALANCING') NOT NULL DEFAULT 'ORGANIZATIONAL',
-- Concurrency limits
`max_connections` int(11),
`max_connections_per_user` int(11),
PRIMARY KEY (`connection_group_id`), PRIMARY KEY (`connection_group_id`),
UNIQUE KEY `connection_group_name_parent` (`connection_group_name`, `parent_id`), UNIQUE KEY `connection_group_name_parent` (`connection_group_name`, `parent_id`),
@@ -54,6 +58,10 @@ CREATE TABLE `guacamole_connection` (
`parent_id` int(11), `parent_id` int(11),
`protocol` varchar(32) NOT NULL, `protocol` varchar(32) NOT NULL,
-- Concurrency limits
`max_connections` int(11),
`max_connections_per_user` int(11),
PRIMARY KEY (`connection_id`), PRIMARY KEY (`connection_id`),
UNIQUE KEY `connection_name_parent` (`connection_name`, `parent_id`), UNIQUE KEY `connection_name_parent` (`connection_name`, `parent_id`),

View File

@@ -39,3 +39,17 @@ ALTER TABLE guacamole_user ADD COLUMN valid_until DATE;
-- --
ALTER TABLE guacamole_user ADD COLUMN timezone VARCHAR(64); ALTER TABLE guacamole_user ADD COLUMN timezone VARCHAR(64);
--
-- Add connection concurrency limits
--
ALTER TABLE guacamole_connection ADD COLUMN max_connections INT(11);
ALTER TABLE guacamole_connection ADD COLUMN max_connections_per_user INT(11);
--
-- Add connection group concurrency limits
--
ALTER TABLE guacamole_connection_group ADD COLUMN max_connections INT(11);
ALTER TABLE guacamole_connection_group ADD COLUMN max_connections_per_user INT(11);

View File

@@ -28,10 +28,12 @@
<!-- Result mapper for connection objects --> <!-- Result mapper for connection objects -->
<resultMap id="ConnectionResultMap" type="org.glyptodon.guacamole.auth.jdbc.connection.ConnectionModel" > <resultMap id="ConnectionResultMap" type="org.glyptodon.guacamole.auth.jdbc.connection.ConnectionModel" >
<id column="connection_id" property="objectID" jdbcType="INTEGER"/> <id column="connection_id" property="objectID" jdbcType="INTEGER"/>
<result column="connection_name" property="name" jdbcType="VARCHAR"/> <result column="connection_name" property="name" jdbcType="VARCHAR"/>
<result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/> <result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/>
<result column="protocol" property="protocol" jdbcType="VARCHAR"/> <result column="protocol" property="protocol" jdbcType="VARCHAR"/>
<result column="max_connections" property="maxConnections" jdbcType="INTEGER"/>
<result column="max_connections_per_user" property="maxConnectionsPerUser" jdbcType="INTEGER"/>
</resultMap> </resultMap>
<!-- Select all connection identifiers --> <!-- Select all connection identifiers -->
@@ -77,7 +79,9 @@
connection_id, connection_id,
connection_name, connection_name,
parent_id, parent_id,
protocol protocol,
max_connections,
max_connections_per_user
FROM guacamole_connection FROM guacamole_connection
WHERE connection_id IN WHERE connection_id IN
<foreach collection="identifiers" item="identifier" <foreach collection="identifiers" item="identifier"
@@ -94,7 +98,9 @@
guacamole_connection.connection_id, guacamole_connection.connection_id,
connection_name, connection_name,
parent_id, parent_id,
protocol protocol,
max_connections,
max_connections_per_user
FROM guacamole_connection FROM guacamole_connection
JOIN guacamole_connection_permission ON guacamole_connection_permission.connection_id = guacamole_connection.connection_id JOIN guacamole_connection_permission ON guacamole_connection_permission.connection_id = guacamole_connection.connection_id
WHERE guacamole_connection.connection_id IN WHERE guacamole_connection.connection_id IN
@@ -114,7 +120,9 @@
connection_id, connection_id,
connection_name, connection_name,
parent_id, parent_id,
protocol protocol,
max_connections,
max_connections_per_user
FROM guacamole_connection FROM guacamole_connection
WHERE WHERE
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=VARCHAR}</if> <if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=VARCHAR}</if>
@@ -136,12 +144,16 @@
INSERT INTO guacamole_connection ( INSERT INTO guacamole_connection (
connection_name, connection_name,
parent_id, parent_id,
protocol protocol,
max_connections,
max_connections_per_user
) )
VALUES ( VALUES (
#{object.name,jdbcType=VARCHAR}, #{object.name,jdbcType=VARCHAR},
#{object.parentIdentifier,jdbcType=VARCHAR}, #{object.parentIdentifier,jdbcType=VARCHAR},
#{object.protocol,jdbcType=VARCHAR} #{object.protocol,jdbcType=VARCHAR},
#{object.maxConnections,jdbcType=INTEGER},
#{object.maxConnectionsPerUser,jdbcType=INTEGER}
) )
</insert> </insert>
@@ -149,9 +161,11 @@
<!-- Update single connection --> <!-- Update single connection -->
<update id="update" parameterType="org.glyptodon.guacamole.auth.jdbc.connection.ConnectionModel"> <update id="update" parameterType="org.glyptodon.guacamole.auth.jdbc.connection.ConnectionModel">
UPDATE guacamole_connection UPDATE guacamole_connection
SET connection_name = #{object.name,jdbcType=VARCHAR}, SET connection_name = #{object.name,jdbcType=VARCHAR},
parent_id = #{object.parentIdentifier,jdbcType=VARCHAR}, parent_id = #{object.parentIdentifier,jdbcType=VARCHAR},
protocol = #{object.protocol,jdbcType=VARCHAR} protocol = #{object.protocol,jdbcType=VARCHAR},
max_connections = #{object.maxConnections,jdbcType=INTEGER},
max_connections_per_user = #{object.maxConnectionsPerUser,jdbcType=INTEGER}
WHERE connection_id = #{object.objectID,jdbcType=INTEGER} WHERE connection_id = #{object.objectID,jdbcType=INTEGER}
</update> </update>

View File

@@ -28,11 +28,13 @@
<!-- Result mapper for connection objects --> <!-- Result mapper for connection objects -->
<resultMap id="ConnectionGroupResultMap" type="org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupModel" > <resultMap id="ConnectionGroupResultMap" type="org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupModel" >
<id column="connection_group_id" property="objectID" jdbcType="INTEGER"/> <id column="connection_group_id" property="objectID" jdbcType="INTEGER"/>
<result column="connection_group_name" property="name" jdbcType="VARCHAR"/> <result column="connection_group_name" property="name" jdbcType="VARCHAR"/>
<result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/> <result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/>
<result column="type" property="type" jdbcType="VARCHAR" <result column="type" property="type" jdbcType="VARCHAR"
javaType="org.glyptodon.guacamole.net.auth.ConnectionGroup$Type"/> javaType="org.glyptodon.guacamole.net.auth.ConnectionGroup$Type"/>
<result column="max_connections" property="maxConnections" jdbcType="INTEGER"/>
<result column="max_connections_per_user" property="maxConnectionsPerUser" jdbcType="INTEGER"/>
</resultMap> </resultMap>
<!-- Select all connection group identifiers --> <!-- Select all connection group identifiers -->
@@ -78,7 +80,9 @@
connection_group_id, connection_group_id,
connection_group_name, connection_group_name,
parent_id, parent_id,
type type,
max_connections,
max_connections_per_user
FROM guacamole_connection_group FROM guacamole_connection_group
WHERE connection_group_id IN WHERE connection_group_id IN
<foreach collection="identifiers" item="identifier" <foreach collection="identifiers" item="identifier"
@@ -95,7 +99,9 @@
guacamole_connection_group.connection_group_id, guacamole_connection_group.connection_group_id,
connection_group_name, connection_group_name,
parent_id, parent_id,
type type,
max_connections,
max_connections_per_user
FROM guacamole_connection_group FROM guacamole_connection_group
JOIN guacamole_connection_group_permission ON guacamole_connection_group_permission.connection_group_id = guacamole_connection_group.connection_group_id JOIN guacamole_connection_group_permission ON guacamole_connection_group_permission.connection_group_id = guacamole_connection_group.connection_group_id
WHERE guacamole_connection_group.connection_group_id IN WHERE guacamole_connection_group.connection_group_id IN
@@ -115,7 +121,9 @@
connection_group_id, connection_group_id,
connection_group_name, connection_group_name,
parent_id, parent_id,
type type,
max_connections,
max_connections_per_user
FROM guacamole_connection_group FROM guacamole_connection_group
WHERE WHERE
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=VARCHAR}</if> <if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=VARCHAR}</if>
@@ -137,12 +145,16 @@
INSERT INTO guacamole_connection_group ( INSERT INTO guacamole_connection_group (
connection_group_name, connection_group_name,
parent_id, parent_id,
type type,
max_connections,
max_connections_per_user
) )
VALUES ( VALUES (
#{object.name,jdbcType=VARCHAR}, #{object.name,jdbcType=VARCHAR},
#{object.parentIdentifier,jdbcType=VARCHAR}, #{object.parentIdentifier,jdbcType=VARCHAR},
#{object.type,jdbcType=VARCHAR} #{object.type,jdbcType=VARCHAR},
#{object.maxConnections,jdbcType=INTEGER},
#{object.maxConnectionsPerUser,jdbcType=INTEGER}
) )
</insert> </insert>
@@ -150,9 +162,11 @@
<!-- Update single connection group --> <!-- Update single connection group -->
<update id="update" parameterType="org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupModel"> <update id="update" parameterType="org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupModel">
UPDATE guacamole_connection_group UPDATE guacamole_connection_group
SET connection_group_name = #{object.name,jdbcType=VARCHAR}, SET connection_group_name = #{object.name,jdbcType=VARCHAR},
parent_id = #{object.parentIdentifier,jdbcType=VARCHAR}, parent_id = #{object.parentIdentifier,jdbcType=VARCHAR},
type = #{object.type,jdbcType=VARCHAR} type = #{object.type,jdbcType=VARCHAR},
max_connections = #{object.maxConnections,jdbcType=INTEGER},
max_connections_per_user = #{object.maxConnectionsPerUser,jdbcType=INTEGER}
WHERE connection_group_id = #{object.objectID,jdbcType=INTEGER} WHERE connection_group_id = #{object.objectID,jdbcType=INTEGER}
</update> </update>

View File

@@ -63,6 +63,10 @@ CREATE TABLE guacamole_connection_group (
type guacamole_connection_group_type type guacamole_connection_group_type
NOT NULL DEFAULT 'ORGANIZATIONAL', NOT NULL DEFAULT 'ORGANIZATIONAL',
-- Concurrency limits
max_connections integer,
max_connections_per_user integer,
PRIMARY KEY (connection_group_id), PRIMARY KEY (connection_group_id),
CONSTRAINT connection_group_name_parent CONSTRAINT connection_group_name_parent
@@ -90,6 +94,10 @@ CREATE TABLE guacamole_connection (
parent_id integer, parent_id integer,
protocol varchar(32) NOT NULL, protocol varchar(32) NOT NULL,
-- Concurrency limits
max_connections integer,
max_connections_per_user integer,
PRIMARY KEY (connection_id), PRIMARY KEY (connection_id),
CONSTRAINT connection_name_parent CONSTRAINT connection_name_parent

View File

@@ -39,3 +39,17 @@ ALTER TABLE guacamole_user ADD COLUMN valid_until date;
-- --
ALTER TABLE guacamole_user ADD COLUMN timezone varchar(64); ALTER TABLE guacamole_user ADD COLUMN timezone varchar(64);
--
-- Add connection concurrency limits
--
ALTER TABLE guacamole_connection ADD COLUMN max_connections integer;
ALTER TABLE guacamole_connection ADD COLUMN max_connections_per_user integer;
--
-- Add connection group concurrency limits
--
ALTER TABLE guacamole_connection_group ADD COLUMN max_connections integer;
ALTER TABLE guacamole_connection_group ADD COLUMN max_connections_per_user integer;

View File

@@ -28,10 +28,12 @@
<!-- Result mapper for connection objects --> <!-- Result mapper for connection objects -->
<resultMap id="ConnectionResultMap" type="org.glyptodon.guacamole.auth.jdbc.connection.ConnectionModel" > <resultMap id="ConnectionResultMap" type="org.glyptodon.guacamole.auth.jdbc.connection.ConnectionModel" >
<id column="connection_id" property="objectID" jdbcType="INTEGER"/> <id column="connection_id" property="objectID" jdbcType="INTEGER"/>
<result column="connection_name" property="name" jdbcType="VARCHAR"/> <result column="connection_name" property="name" jdbcType="VARCHAR"/>
<result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/> <result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/>
<result column="protocol" property="protocol" jdbcType="VARCHAR"/> <result column="protocol" property="protocol" jdbcType="VARCHAR"/>
<result column="max_connections" property="maxConnections" jdbcType="INTEGER"/>
<result column="max_connections_per_user" property="maxConnectionsPerUser" jdbcType="INTEGER"/>
</resultMap> </resultMap>
<!-- Select all connection identifiers --> <!-- Select all connection identifiers -->
@@ -77,7 +79,9 @@
connection_id, connection_id,
connection_name, connection_name,
parent_id, parent_id,
protocol protocol,
max_connections,
max_connections_per_user
FROM guacamole_connection FROM guacamole_connection
WHERE connection_id IN WHERE connection_id IN
<foreach collection="identifiers" item="identifier" <foreach collection="identifiers" item="identifier"
@@ -94,7 +98,9 @@
guacamole_connection.connection_id, guacamole_connection.connection_id,
connection_name, connection_name,
parent_id, parent_id,
protocol protocol,
max_connections,
max_connections_per_user
FROM guacamole_connection FROM guacamole_connection
JOIN guacamole_connection_permission ON guacamole_connection_permission.connection_id = guacamole_connection.connection_id JOIN guacamole_connection_permission ON guacamole_connection_permission.connection_id = guacamole_connection.connection_id
WHERE guacamole_connection.connection_id IN WHERE guacamole_connection.connection_id IN
@@ -114,7 +120,9 @@
connection_id, connection_id,
connection_name, connection_name,
parent_id, parent_id,
protocol protocol,
max_connections,
max_connections_per_user
FROM guacamole_connection FROM guacamole_connection
WHERE WHERE
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=INTEGER}::integer</if> <if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=INTEGER}::integer</if>
@@ -136,12 +144,16 @@
INSERT INTO guacamole_connection ( INSERT INTO guacamole_connection (
connection_name, connection_name,
parent_id, parent_id,
protocol protocol,
max_connections,
max_connections_per_user
) )
VALUES ( VALUES (
#{object.name,jdbcType=VARCHAR}, #{object.name,jdbcType=VARCHAR},
#{object.parentIdentifier,jdbcType=INTEGER}::integer, #{object.parentIdentifier,jdbcType=INTEGER}::integer,
#{object.protocol,jdbcType=VARCHAR} #{object.protocol,jdbcType=VARCHAR},
#{object.maxConnections,jdbcType=INTEGER},
#{object.maxConnectionsPerUser,jdbcType=INTEGER}
) )
</insert> </insert>
@@ -149,9 +161,11 @@
<!-- Update single connection --> <!-- Update single connection -->
<update id="update" parameterType="org.glyptodon.guacamole.auth.jdbc.connection.ConnectionModel"> <update id="update" parameterType="org.glyptodon.guacamole.auth.jdbc.connection.ConnectionModel">
UPDATE guacamole_connection UPDATE guacamole_connection
SET connection_name = #{object.name,jdbcType=VARCHAR}, SET connection_name = #{object.name,jdbcType=VARCHAR},
parent_id = #{object.parentIdentifier,jdbcType=INTEGER}::integer, parent_id = #{object.parentIdentifier,jdbcType=INTEGER}::integer,
protocol = #{object.protocol,jdbcType=VARCHAR} protocol = #{object.protocol,jdbcType=VARCHAR},
max_connections = #{object.maxConnections,jdbcType=INTEGER},
max_connections_per_user = #{object.maxConnectionsPerUser,jdbcType=INTEGER}
WHERE connection_id = #{object.objectID,jdbcType=INTEGER}::integer WHERE connection_id = #{object.objectID,jdbcType=INTEGER}::integer
</update> </update>

View File

@@ -28,11 +28,13 @@
<!-- Result mapper for connection objects --> <!-- Result mapper for connection objects -->
<resultMap id="ConnectionGroupResultMap" type="org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupModel" > <resultMap id="ConnectionGroupResultMap" type="org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupModel" >
<id column="connection_group_id" property="objectID" jdbcType="INTEGER"/> <id column="connection_group_id" property="objectID" jdbcType="INTEGER"/>
<result column="connection_group_name" property="name" jdbcType="VARCHAR"/> <result column="connection_group_name" property="name" jdbcType="VARCHAR"/>
<result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/> <result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/>
<result column="type" property="type" jdbcType="VARCHAR" <result column="type" property="type" jdbcType="VARCHAR"
javaType="org.glyptodon.guacamole.net.auth.ConnectionGroup$Type"/> javaType="org.glyptodon.guacamole.net.auth.ConnectionGroup$Type"/>
<result column="max_connections" property="maxConnections" jdbcType="INTEGER"/>
<result column="max_connections_per_user" property="maxConnectionsPerUser" jdbcType="INTEGER"/>
</resultMap> </resultMap>
<!-- Select all connection group identifiers --> <!-- Select all connection group identifiers -->
@@ -78,7 +80,9 @@
connection_group_id, connection_group_id,
connection_group_name, connection_group_name,
parent_id, parent_id,
type type,
max_connections,
max_connections_per_user
FROM guacamole_connection_group FROM guacamole_connection_group
WHERE connection_group_id IN WHERE connection_group_id IN
<foreach collection="identifiers" item="identifier" <foreach collection="identifiers" item="identifier"
@@ -95,7 +99,9 @@
guacamole_connection_group.connection_group_id, guacamole_connection_group.connection_group_id,
connection_group_name, connection_group_name,
parent_id, parent_id,
type type,
max_connections,
max_connections_per_user
FROM guacamole_connection_group FROM guacamole_connection_group
JOIN guacamole_connection_group_permission ON guacamole_connection_group_permission.connection_group_id = guacamole_connection_group.connection_group_id JOIN guacamole_connection_group_permission ON guacamole_connection_group_permission.connection_group_id = guacamole_connection_group.connection_group_id
WHERE guacamole_connection_group.connection_group_id IN WHERE guacamole_connection_group.connection_group_id IN
@@ -115,7 +121,9 @@
connection_group_id, connection_group_id,
connection_group_name, connection_group_name,
parent_id, parent_id,
type type,
max_connections,
max_connections_per_user
FROM guacamole_connection_group FROM guacamole_connection_group
WHERE WHERE
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=INTEGER}::integer</if> <if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=INTEGER}::integer</if>
@@ -137,12 +145,16 @@
INSERT INTO guacamole_connection_group ( INSERT INTO guacamole_connection_group (
connection_group_name, connection_group_name,
parent_id, parent_id,
type type,
max_connections,
max_connections_per_user
) )
VALUES ( VALUES (
#{object.name,jdbcType=VARCHAR}, #{object.name,jdbcType=VARCHAR},
#{object.parentIdentifier,jdbcType=INTEGER}::integer, #{object.parentIdentifier,jdbcType=INTEGER}::integer,
#{object.type,jdbcType=VARCHAR}::guacamole_connection_group_type #{object.type,jdbcType=VARCHAR}::guacamole_connection_group_type,
#{object.maxConnections,jdbcType=INTEGER},
#{object.maxConnectionsPerUser,jdbcType=INTEGER}
) )
</insert> </insert>
@@ -150,9 +162,11 @@
<!-- Update single connection group --> <!-- Update single connection group -->
<update id="update" parameterType="org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupModel"> <update id="update" parameterType="org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupModel">
UPDATE guacamole_connection_group UPDATE guacamole_connection_group
SET connection_group_name = #{object.name,jdbcType=VARCHAR}, SET connection_group_name = #{object.name,jdbcType=VARCHAR},
parent_id = #{object.parentIdentifier,jdbcType=INTEGER}::integer, parent_id = #{object.parentIdentifier,jdbcType=INTEGER}::integer,
type = #{object.type,jdbcType=VARCHAR}::guacamole_connection_group_type type = #{object.type,jdbcType=VARCHAR}::guacamole_connection_group_type,
max_connections = #{object.maxConnections,jdbcType=INTEGER},
max_connections_per_user = #{object.maxConnectionsPerUser,jdbcType=INTEGER}
WHERE connection_group_id = #{object.objectID,jdbcType=INTEGER}::integer WHERE connection_group_id = #{object.objectID,jdbcType=INTEGER}::integer
</update> </update>

View File

@@ -22,6 +22,11 @@
package org.glyptodon.guacamole.form; package org.glyptodon.guacamole.form;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/** /**
* Represents a date field. The field may contain only date values which * Represents a date field. The field may contain only date values which
* conform to a standard pattern, defined by DateField.FORMAT. * conform to a standard pattern, defined by DateField.FORMAT.
@@ -45,4 +50,48 @@ public class DateField extends Field {
super(name, Field.Type.DATE); super(name, Field.Type.DATE);
} }
/**
* Converts the given date into a string which follows the format used by
* date fields.
*
* @param date
* The date value to format, which may be null.
*
* @return
* The formatted date, or null if the provided time was null.
*/
public static String format(Date date) {
DateFormat dateFormat = new SimpleDateFormat(DateField.FORMAT);
return date == null ? null : dateFormat.format(date);
}
/**
* Parses the given string into a corresponding date. The string must
* follow the standard format used by date fields, as defined by FORMAT
* and as would be produced by format().
*
* @param dateString
* The date string to parse, which may be null.
*
* @return
* The date corresponding to the given date string, or null if the
* provided date string was null or blank.
*
* @throws ParseException
* If the given date string does not conform to the standard format
* used by date fields.
*/
public static Date parse(String dateString)
throws ParseException {
// Return null if no date provided
if (dateString == null || dateString.isEmpty())
return null;
// Parse date according to format
DateFormat dateFormat = new SimpleDateFormat(DateField.FORMAT);
return dateFormat.parse(dateString);
}
} }

View File

@@ -39,4 +39,50 @@ public class NumericField extends Field {
super(name, Field.Type.NUMERIC); super(name, Field.Type.NUMERIC);
} }
/**
* Formats the given integer in the format required by a numeric field.
*
* @param i
* The integer to format, which may be null.
*
* @return
* A string representation of the given integer, or null if the given
* integer was null.
*/
public static String format(Integer i) {
// Return null if no value provided
if (i == null)
return null;
// Convert to string
return i.toString();
}
/**
* Parses the given string as an integer, where the given string is in the
* format required by a numeric field.
*
* @param str
* The string to parse as an integer, which may be null.
*
* @return
* The integer representation of the given string, or null if the given
* string was null.
*
* @throws NumberFormatException
* If the given string is not in a parseable format.
*/
public static Integer parse(String str) throws NumberFormatException {
// Return null if no value provided
if (str == null || str.isEmpty())
return null;
// Parse as integer
return new Integer(str);
}
} }

View File

@@ -22,6 +22,11 @@
package org.glyptodon.guacamole.form; package org.glyptodon.guacamole.form;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/** /**
* Represents a time field. The field may contain only time values which * Represents a time field. The field may contain only time values which
* conform to a standard pattern, defined by TimeField.FORMAT. * conform to a standard pattern, defined by TimeField.FORMAT.
@@ -45,4 +50,48 @@ public class TimeField extends Field {
super(name, Field.Type.TIME); super(name, Field.Type.TIME);
} }
/**
* Parses the given string into a corresponding time. The string must
* follow the standard format used by time fields, as defined by
* FORMAT and as would be produced by format().
*
* @param timeString
* The time string to parse, which may be null.
*
* @return
* The time corresponding to the given time string, or null if the
* provided time string was null or blank.
*
* @throws ParseException
* If the given time string does not conform to the standard format
* used by time fields.
*/
public static Date parse(String timeString)
throws ParseException {
// Return null if no time provided
if (timeString == null || timeString.isEmpty())
return null;
// Parse time according to format
DateFormat timeFormat = new SimpleDateFormat(TimeField.FORMAT);
return timeFormat.parse(timeString);
}
/**
* Converts the given time into a string which follows the format used by
* time fields.
*
* @param time
* The time value to format, which may be null.
*
* @return
* The formatted time, or null if the provided time was null.
*/
public static String format(Date time) {
DateFormat timeFormat = new SimpleDateFormat(TimeField.FORMAT);
return time == null ? null : timeFormat.format(time);
}
} }

View File

@@ -40,4 +40,27 @@ public class TimeZoneField extends Field {
super(name, Field.Type.TIMEZONE); super(name, Field.Type.TIMEZONE);
} }
/**
* Parses the given string into a time zone ID string. As these strings are
* equivalent, the only transformation currently performed by this function
* is to ensure that a blank time zone string is parsed into null.
*
* @param timeZone
* The time zone string to parse, which may be null.
*
* @return
* The ID of the time zone corresponding to the given string, or null
* if the given time zone string was null or blank.
*/
public static String parse(String timeZone) {
// Return null if no time zone provided
if (timeZone == null || timeZone.isEmpty())
return null;
// Otherwise, assume time zone is valid
return timeZone;
}
} }

View File

@@ -34,7 +34,7 @@ angular.module('form').controller('numberFieldController', ['$scope',
// Update string value in model when typed value is changed // Update string value in model when typed value is changed
$scope.$watch('typedValue', function typedValueChanged(typedValue) { $scope.$watch('typedValue', function typedValueChanged(typedValue) {
$scope.model = (typedValue ? typedValue.toString() : ''); $scope.model = ((typedValue || typedValue === 0) ? typedValue.toString() : '');
}); });
}]); }]);