mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-30 16:43:22 +00:00 
			
		
		
		
	GUAC-830: Add ConfigurableGuacamoleTunnelService.
This commit is contained in:
		
				
					committed by
					
						 Michael Jumper
						Michael Jumper
					
				
			
			
				
	
			
			
			
						parent
						
							2a88856787
						
					
				
				
					commit
					3b94f5d4b3
				
			| @@ -0,0 +1,276 @@ | ||||
| /* | ||||
|  * Copyright (C) 2015 Glyptodon LLC | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| package org.glyptodon.guacamole.auth.jdbc.tunnel; | ||||
|  | ||||
| import com.google.common.collect.ConcurrentHashMultiset; | ||||
| import com.google.inject.Singleton; | ||||
| import java.util.Arrays; | ||||
| import java.util.Comparator; | ||||
| import java.util.List; | ||||
| 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.connectiongroup.ModeledConnectionGroup; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * GuacamoleTunnelService implementation which restricts concurrency for each | ||||
|  * connection and group according to a maximum number of connections and | ||||
|  * maximum number of connections per user. | ||||
|  * | ||||
|  * @author James Muehlner | ||||
|  * @author Michael Jumper | ||||
|  */ | ||||
| @Singleton | ||||
| public class ConfigurableGuacamoleTunnelService | ||||
|     extends AbstractGuacamoleTunnelService { | ||||
|  | ||||
|     /** | ||||
|      * Set of all currently-active user/connection pairs (seats). | ||||
|      */ | ||||
|     private final ConcurrentHashMultiset<Seat> activeSeats = ConcurrentHashMultiset.<Seat>create(); | ||||
|  | ||||
|     /** | ||||
|      * Set of all currently-active connections. | ||||
|      */ | ||||
|     private final ConcurrentHashMultiset<String> activeConnections = ConcurrentHashMultiset.<String>create(); | ||||
|  | ||||
|     /** | ||||
|      * Set of all currently-active user/connection group pairs (seats). | ||||
|      */ | ||||
|     private final ConcurrentHashMultiset<Seat> activeGroupSeats = ConcurrentHashMultiset.<Seat>create(); | ||||
|  | ||||
|     /** | ||||
|      * Set of all currently-active connection groups. | ||||
|      */ | ||||
|     private final ConcurrentHashMultiset<String> activeGroups = ConcurrentHashMultiset.<String>create(); | ||||
|  | ||||
|     /** | ||||
|      * The maximum number of connections allowed per connection by default, or | ||||
|      * zero if no default limit applies. | ||||
|      */ | ||||
|     private final int connectionDefaultMaxConnections; | ||||
|  | ||||
|     /** | ||||
|      * The maximum number of connections a user may have to any one connection | ||||
|      * by default, or zero if no default limit applies. | ||||
|      */ | ||||
|     private final int connectionDefaultMaxConnectionsPerUser; | ||||
|  | ||||
|     /** | ||||
|      * The maximum number of connections allowed per connection group by | ||||
|      * default, or zero if no default limit applies. | ||||
|      */ | ||||
|     private final int connectionGroupDefaultMaxConnections; | ||||
|  | ||||
|     /** | ||||
|      * The maximum number of connections a user may have to any one connection | ||||
|      * group by default, or zero if no default limit applies. | ||||
|      */ | ||||
|     private final int connectionGroupDefaultMaxConnectionsPerUser; | ||||
|  | ||||
|     /** | ||||
|      * Creates a new ConfigurableGuacamoleTunnelService which applies the given | ||||
|      * limitations when new connections are acquired. | ||||
|      * | ||||
|      * @param connectionDefaultMaxConnections | ||||
|      *     The maximum number of connections allowed per connection by default, | ||||
|      *     or zero if no default limit applies. | ||||
|      * | ||||
|      * @param connectionDefaultMaxConnectionsPerUser | ||||
|      *     The maximum number of connections a user may have to any one | ||||
|      *     connection by default, or zero if no default limit applies. | ||||
|      * | ||||
|      * @param connectionGroupDefaultMaxConnections | ||||
|      *     The maximum number of connections allowed per connection group by | ||||
|      *     default, or zero if no default limit applies. | ||||
|      * | ||||
|      * @param connectionGroupDefaultMaxConnectionsPerUser | ||||
|      *     The maximum number of connections a user may have to any one | ||||
|      *     connection group by default, or zero if no default limit applies. | ||||
|      */ | ||||
|     public ConfigurableGuacamoleTunnelService( | ||||
|             int connectionDefaultMaxConnections, | ||||
|             int connectionDefaultMaxConnectionsPerUser, | ||||
|             int connectionGroupDefaultMaxConnections, | ||||
|             int connectionGroupDefaultMaxConnectionsPerUser) { | ||||
|  | ||||
|         // Set default connection limits | ||||
|         this.connectionDefaultMaxConnections        = connectionDefaultMaxConnections; | ||||
|         this.connectionDefaultMaxConnectionsPerUser = connectionDefaultMaxConnectionsPerUser; | ||||
|  | ||||
|         // Set default connection group limits | ||||
|         this.connectionGroupDefaultMaxConnections        = connectionGroupDefaultMaxConnections; | ||||
|         this.connectionGroupDefaultMaxConnectionsPerUser = connectionGroupDefaultMaxConnectionsPerUser; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Attempts to add a single instance of the given value to the given | ||||
|      * multiset without exceeding the specified maximum number of values. If | ||||
|      * the value cannot be added without exceeding the maximum, false is | ||||
|      * returned. | ||||
|      * | ||||
|      * @param <T> | ||||
|      *     The type of values contained within the multiset. | ||||
|      * | ||||
|      * @param multiset | ||||
|      *     The multiset to attempt to add a value to. | ||||
|      * | ||||
|      * @param value | ||||
|      *     The value to attempt to add. | ||||
|      * | ||||
|      * @param max | ||||
|      *     The maximum number of each distinct value that the given multiset | ||||
|      *     should hold, or zero if no limit applies. | ||||
|      * | ||||
|      * @return | ||||
|      *     true if the value was successfully added without exceeding the | ||||
|      *     specified maximum, false if the value could not be added. | ||||
|      */ | ||||
|     private <T> boolean tryAdd(ConcurrentHashMultiset<T> multiset, T value, int max) { | ||||
|  | ||||
|         // Repeatedly attempt to add a new value to the given multiset until we | ||||
|         // explicitly succeed or explicitly fail | ||||
|         while (true) { | ||||
|  | ||||
|             // Get current number of values | ||||
|             int count = multiset.count(value); | ||||
|  | ||||
|             // Bail out if the maximum has already been reached | ||||
|             if (count >= max || max == 0) | ||||
|                 return false; | ||||
|  | ||||
|             // Attempt to add one more value | ||||
|             if (multiset.setCount(value, count, count+1)) | ||||
|                 return true; | ||||
|  | ||||
|             // Try again if unsuccessful | ||||
|  | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected ModeledConnection acquire(AuthenticatedUser user, | ||||
|             List<ModeledConnection> connections) throws GuacamoleException { | ||||
|  | ||||
|         // Get username | ||||
|         String username = user.getUser().getIdentifier(); | ||||
|  | ||||
|         // Sort connections in ascending order of usage | ||||
|         ModeledConnection[] sortedConnections = connections.toArray(new ModeledConnection[connections.size()]); | ||||
|         Arrays.sort(sortedConnections, new Comparator<ModeledConnection>() { | ||||
|  | ||||
|             @Override | ||||
|             public int compare(ModeledConnection a, ModeledConnection b) { | ||||
|  | ||||
|                 return getActiveConnections(a).size() | ||||
|                      - getActiveConnections(b).size(); | ||||
|  | ||||
|             } | ||||
|  | ||||
|         }); | ||||
|  | ||||
|         // Track whether acquire fails due to user-specific limits | ||||
|         boolean userSpecificFailure = true; | ||||
|  | ||||
|         // Return the first unreserved connection | ||||
|         for (ModeledConnection connection : sortedConnections) { | ||||
|  | ||||
|             // Attempt to aquire connection according to per-user limits | ||||
|             Seat seat = new Seat(username, connection.getIdentifier()); | ||||
|             if (tryAdd(activeSeats, seat, | ||||
|                     connectionDefaultMaxConnectionsPerUser)) { | ||||
|  | ||||
|                 // Attempt to aquire connection according to overall limits | ||||
|                 if (tryAdd(activeConnections, connection.getIdentifier(), | ||||
|                         connectionDefaultMaxConnections)) | ||||
|                     return connection; | ||||
|  | ||||
|                 // Acquire failed - retry with next connection | ||||
|                 activeSeats.remove(seat); | ||||
|  | ||||
|                 // Failure to acquire is not user-specific | ||||
|                 userSpecificFailure = false; | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         // Too many connections by this user | ||||
|         if (userSpecificFailure) | ||||
|             throw new GuacamoleClientTooManyException("Cannot connect. Connection group already in use by this user."); | ||||
|  | ||||
|         // Too many connections, but not necessarily due purely to this user | ||||
|         else | ||||
|             throw new GuacamoleResourceConflictException("Cannot connect. This connection is in use."); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void release(AuthenticatedUser user, ModeledConnection connection) { | ||||
|         activeSeats.remove(new Seat(user.getUser().getIdentifier(), connection.getIdentifier())); | ||||
|         activeConnections.remove(connection.getIdentifier()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void acquire(AuthenticatedUser user, | ||||
|             ModeledConnectionGroup connectionGroup) throws GuacamoleException { | ||||
|  | ||||
|         // Get username | ||||
|         String username = user.getUser().getIdentifier(); | ||||
|  | ||||
|         // Attempt to aquire connection group according to per-user limits | ||||
|         Seat seat = new Seat(username, connectionGroup.getIdentifier()); | ||||
|         if (tryAdd(activeGroupSeats, seat, | ||||
|                 connectionGroupDefaultMaxConnectionsPerUser)) { | ||||
|  | ||||
|             // Attempt to aquire connection group according to overall limits | ||||
|             if (tryAdd(activeGroups, connectionGroup.getIdentifier(), | ||||
|                     connectionGroupDefaultMaxConnections)) | ||||
|                 return; | ||||
|  | ||||
|             // Acquire failed | ||||
|             activeGroupSeats.remove(seat); | ||||
|  | ||||
|             // Failure to acquire is not user-specific | ||||
|             throw new GuacamoleResourceConflictException("Cannot connect. This connection group is in use."); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         // Already in use by this user | ||||
|         throw new GuacamoleClientTooManyException("Cannot connect. Connection group already in use by this user."); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void release(AuthenticatedUser user, | ||||
|             ModeledConnectionGroup connectionGroup) { | ||||
|         activeGroupSeats.remove(new Seat(user.getUser().getIdentifier(), connectionGroup.getIdentifier())); | ||||
|         activeGroups.remove(connectionGroup.getIdentifier()); | ||||
|     } | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user