diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCEnvironment.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCEnvironment.java index f7a3a6fbe..fe944f9cd 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCEnvironment.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCEnvironment.java @@ -44,6 +44,20 @@ public abstract class JDBCEnvironment extends LocalEnvironment { super(); } + /** + * Returns the maximum number of concurrent connections to allow overall. + * As this limit applies globally (independent of which connection is in + * use or which user is using it), this setting cannot be overridden at the + * connection level. Zero denotes unlimited. + * + * @return + * The maximum allowable number of concurrent connections. + * + * @throws GuacamoleException + * If an error occurs while retrieving the property. + */ + public abstract int getAbsoluteMaxConnections() throws GuacamoleException; + /** * Returns the default maximum number of concurrent connections to allow to * any one connection, unless specified differently on an individual diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/RestrictedGuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/RestrictedGuacamoleTunnelService.java index 7f5f28338..77056a18b 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/RestrictedGuacamoleTunnelService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/RestrictedGuacamoleTunnelService.java @@ -23,15 +23,18 @@ package org.glyptodon.guacamole.auth.jdbc.tunnel; import com.google.common.collect.ConcurrentHashMultiset; +import com.google.inject.Inject; import com.google.inject.Singleton; import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; import org.glyptodon.guacamole.GuacamoleClientTooManyException; import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; import org.glyptodon.guacamole.auth.jdbc.connection.ModeledConnection; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleResourceConflictException; +import org.glyptodon.guacamole.auth.jdbc.JDBCEnvironment; import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; @@ -47,6 +50,12 @@ import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; public class RestrictedGuacamoleTunnelService extends AbstractGuacamoleTunnelService { + /** + * The environment of the Guacamole server. + */ + @Inject + private JDBCEnvironment environment; + /** * Set of all currently-active user/connection pairs (seats). */ @@ -67,6 +76,12 @@ public class RestrictedGuacamoleTunnelService */ private final ConcurrentHashMultiset activeGroups = ConcurrentHashMultiset.create(); + /** + * The total number of active connections within this instance of + * Guacamole. + */ + private final AtomicInteger totalActiveConnections = new AtomicInteger(0); + /** * Attempts to add a single instance of the given value to the given * multiset without exceeding the specified maximum number of values. If @@ -113,10 +128,54 @@ public class RestrictedGuacamoleTunnelService } + /** + * Attempts to increment the given AtomicInteger without exceeding the + * specified maximum value. If the AtomicInteger cannot be incremented + * without exceeding the maximum, false is returned. + * + * @param counter + * The AtomicInteger to attempt to increment. + * + * @param max + * The maximum value that the given AtomicInteger should contain, or + * zero if no limit applies. + * + * @return + * true if the AtomicInteger was successfully incremented without + * exceeding the specified maximum, false if the AtomicInteger could + * not be incremented. + */ + private boolean tryIncrement(AtomicInteger counter, int max) { + + // Repeatedly attempt to increment the given AtomicInteger until we + // explicitly succeed or explicitly fail + while (true) { + + // Get current value + int count = counter.get(); + + // Bail out if the maximum has already been reached + if (count >= max && max != 0) + return false; + + // Attempt to increment + if (counter.compareAndSet(count, count+1)) + return true; + + // Try again if unsuccessful + + } + + } + @Override protected ModeledConnection acquire(AuthenticatedUser user, List connections) throws GuacamoleException { + // Do not acquire connection unless within overall limits + if (!tryIncrement(totalActiveConnections, environment.getAbsoluteMaxConnections())) + throw new GuacamoleResourceConflictException("Cannot connect. Overall maximum connections reached."); + // Get username String username = user.getUser().getIdentifier(); @@ -160,6 +219,9 @@ public class RestrictedGuacamoleTunnelService } + // Acquire failed + totalActiveConnections.decrementAndGet(); + // Too many connections by this user if (userSpecificFailure) throw new GuacamoleClientTooManyException("Cannot connect. Connection already in use by this user."); @@ -174,6 +236,7 @@ public class RestrictedGuacamoleTunnelService protected void release(AuthenticatedUser user, ModeledConnection connection) { activeSeats.remove(new Seat(user.getUser().getIdentifier(), connection.getIdentifier())); activeConnections.remove(connection.getIdentifier()); + totalActiveConnections.decrementAndGet(); } @Override diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLEnvironment.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLEnvironment.java index 90a42afd9..e54f2a9de 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLEnvironment.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLEnvironment.java @@ -51,6 +51,12 @@ public class MySQLEnvironment extends JDBCEnvironment { */ private static final int DEFAULT_PORT = 3306; + /** + * The default value for the maximum number of connections to be + * allowed to the Guacamole server overall. + */ + private final int DEFAULT_ABSOLUTE_MAX_CONNECTIONS = 0; + /** * The default value for the default maximum number of connections to be * allowed per user to any one connection. Note that, as long as the @@ -164,6 +170,13 @@ public class MySQLEnvironment extends JDBCEnvironment { } + @Override + public int getAbsoluteMaxConnections() throws GuacamoleException { + return getProperty(MySQLGuacamoleProperties.MYSQL_ABSOLUTE_MAX_CONNECTIONS, + DEFAULT_ABSOLUTE_MAX_CONNECTIONS + ); + } + @Override public int getDefaultMaxConnections() throws GuacamoleException { return getProperty( diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLGuacamoleProperties.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLGuacamoleProperties.java index cb0c35bed..7a81daf2f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLGuacamoleProperties.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLGuacamoleProperties.java @@ -115,6 +115,19 @@ public class MySQLGuacamoleProperties { }; + /** + * The maximum number of concurrent connections to allow overall. Zero + * denotes unlimited. + */ + public static final IntegerGuacamoleProperty + MYSQL_ABSOLUTE_MAX_CONNECTIONS = + new IntegerGuacamoleProperty() { + + @Override + public String getName() { return "mysql-absolute-max-connections"; } + + }; + /** * The maximum number of concurrent connections to allow to any one * connection. Zero denotes unlimited. diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLEnvironment.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLEnvironment.java index 83c139ec5..eddc84e7b 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLEnvironment.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLEnvironment.java @@ -50,6 +50,12 @@ public class PostgreSQLEnvironment extends JDBCEnvironment { */ private static final int DEFAULT_PORT = 5432; + /** + * The default value for the maximum number of connections to be + * allowed to the Guacamole server overall. + */ + private final int DEFAULT_ABSOLUTE_MAX_CONNECTIONS = 0; + /** * The default value for the default maximum number of connections to be * allowed per user to any one connection. Note that, as long as the @@ -163,6 +169,13 @@ public class PostgreSQLEnvironment extends JDBCEnvironment { } + @Override + public int getAbsoluteMaxConnections() throws GuacamoleException { + return getProperty(PostgreSQLGuacamoleProperties.POSTGRESQL_ABSOLUTE_MAX_CONNECTIONS, + DEFAULT_ABSOLUTE_MAX_CONNECTIONS + ); + } + @Override public int getDefaultMaxConnections() throws GuacamoleException { return getProperty( diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLGuacamoleProperties.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLGuacamoleProperties.java index cf410386e..8b87ce091 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLGuacamoleProperties.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/glyptodon/guacamole/auth/postgresql/PostgreSQLGuacamoleProperties.java @@ -124,6 +124,19 @@ public class PostgreSQLGuacamoleProperties { }; + /** + * The maximum number of concurrent connections to allow overall. Zero + * denotes unlimited. + */ + public static final IntegerGuacamoleProperty + POSTGRESQL_ABSOLUTE_MAX_CONNECTIONS = + new IntegerGuacamoleProperty() { + + @Override + public String getName() { return "postgresql-absolute-max-connections"; } + + }; + /** * The maximum number of concurrent connections to allow to any one * connection. Zero denotes unlimited.