mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
Ticket #390: Added mysql-disallow-duplicate-connections property and enforced connection and connectionGroup access restrictions thereof.
This commit is contained in:
@@ -113,6 +113,76 @@ public class ActiveConnectionMap {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Represents a user connected to a connection or BALANCING connection group.
|
||||
*/
|
||||
public class ConnectionUser {
|
||||
/**
|
||||
* The ID of the connection or connection group that this ConnectionUser refers to.
|
||||
*/
|
||||
private int connectionID;
|
||||
|
||||
/**
|
||||
* The user that this ConnectionUser refers to.
|
||||
*/
|
||||
private int userID;
|
||||
|
||||
/**
|
||||
* Returns ID of the connection or connection group that this ConnectionUser refers to.
|
||||
* @return ID of the connection or connection group that this ConnectionUser refers to.
|
||||
*/
|
||||
public int getConnectionGroupID() {
|
||||
return connectionID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user ID that this ConnectionUser refers to.
|
||||
* @return the user ID that this ConnectionUser refers to.
|
||||
*/
|
||||
public int getUserID() {
|
||||
return userID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ConnectionUser with the given connection or connection group
|
||||
* ID and user ID.
|
||||
*
|
||||
* @param connectionID The connection or connection group ID that this
|
||||
* ConnectionUser refers to.
|
||||
* @param userID The user ID that this ConnectionUser refers to.
|
||||
*/
|
||||
public ConnectionUser(int connectionID, int userID) {
|
||||
this.connectionID = connectionID;
|
||||
this.userID = userID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
|
||||
// Only another ConnectionUser can equal this ConnectionUser
|
||||
if(!(other instanceof ConnectionUser))
|
||||
return false;
|
||||
|
||||
ConnectionUser otherConnectionGroupUser =
|
||||
(ConnectionUser)other;
|
||||
|
||||
/*
|
||||
* Two ConnectionGroupUsers are equal iff they represent the exact
|
||||
* same pairing of connection or connection group and user.
|
||||
*/
|
||||
return this.connectionID == otherConnectionGroupUser.connectionID
|
||||
&& this.userID == otherConnectionGroupUser.userID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
hash = 23 * hash + this.connectionID;
|
||||
hash = 23 * hash + this.userID;
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DAO for accessing connection history.
|
||||
*/
|
||||
@@ -120,12 +190,161 @@ public class ActiveConnectionMap {
|
||||
private ConnectionHistoryMapper connectionHistoryDAO;
|
||||
|
||||
/**
|
||||
* Map of all the connections that are currently active the
|
||||
* Map of all the connections that are currently active to the
|
||||
* count of current users.
|
||||
*/
|
||||
private Map<Integer, Connection> activeConnectionMap =
|
||||
new HashMap<Integer, Connection>();
|
||||
|
||||
/**
|
||||
* Map of all the connection group users to the count of current usages.
|
||||
*/
|
||||
private Map<ConnectionUser, Integer> activeConnectionGroupUserMap =
|
||||
new HashMap<ConnectionUser, Integer>();
|
||||
|
||||
/**
|
||||
* Map of all the connection users to the count of current usages.
|
||||
*/
|
||||
private Map<ConnectionUser, Integer> activeConnectionUserMap =
|
||||
new HashMap<ConnectionUser, Integer>();
|
||||
|
||||
/**
|
||||
* Returns the number of connectionGroups opened by the given user using
|
||||
* the given ConnectionGroup.
|
||||
*
|
||||
* @param connectionID The connection group ID that this
|
||||
* ConnectionUser refers to.
|
||||
* @param userID The user ID that this ConnectionUser refers to.
|
||||
*
|
||||
* @return The number of connections opened by the given user to the given
|
||||
* ConnectionGroup.
|
||||
*/
|
||||
public int getConnectionGroupUserCount(int connectionGroupID, int userID) {
|
||||
Integer count = activeConnectionGroupUserMap.get
|
||||
(new ConnectionUser(connectionGroupID, userID));
|
||||
|
||||
// No ConnectionUser found means this combination was never used
|
||||
if(count == null)
|
||||
return 0;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given user is currently connected to the given BALANCING
|
||||
* connection group.
|
||||
*
|
||||
* @param connectionGroupID The connection group ID that this
|
||||
* ConnectionUser refers to.
|
||||
* @param userID The user ID that this ConnectionUser refers to.
|
||||
*
|
||||
* @return True if the given user is currently connected to the given
|
||||
* BALANCING connection group, false otherwise.
|
||||
*/
|
||||
public boolean isConnectionGroupUserActive(int connectionGroupID, int userID) {
|
||||
Integer count = activeConnectionGroupUserMap.get
|
||||
(new ConnectionUser(connectionGroupID, userID));
|
||||
|
||||
// The connection group is in use if the ConnectionUser count > 0
|
||||
return count != null && count > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the count of the number of connections opened by the given user
|
||||
* to the given ConnectionGroup.
|
||||
*
|
||||
* @param connectionGroupID The connection group ID that this
|
||||
* ConnectionUser refers to.
|
||||
* @param userID The user ID that this ConnectionUser refers to.
|
||||
*/
|
||||
private void incrementConnectionGroupUserCount(int connectionGroupID, int userID) {
|
||||
int currentCount = getConnectionGroupUserCount(connectionGroupID, userID);
|
||||
|
||||
activeConnectionGroupUserMap.put
|
||||
(new ConnectionUser(connectionGroupID, userID), currentCount + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement the count of the number of connections opened by the given user
|
||||
* to the given ConnectionGroup.
|
||||
*
|
||||
* @param connectionGroupID The connection group ID that this
|
||||
* ConnectionUser refers to.
|
||||
* @param userID The user ID that this ConnectionUser refers to.
|
||||
*/
|
||||
private void decrementConnectionGroupUserCount(int connectionGroupID, int userID) {
|
||||
int currentCount = getConnectionGroupUserCount(connectionGroupID, userID);
|
||||
|
||||
activeConnectionGroupUserMap.put
|
||||
(new ConnectionUser(connectionGroupID, userID), currentCount - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of connections opened by the given user using
|
||||
* the given Connection.
|
||||
*
|
||||
* @param connectionID The connection ID that this ConnectionUser refers to.
|
||||
* @param userID The user ID that this ConnectionUser refers to.
|
||||
*
|
||||
* @return The number of connections opened by the given user to the given
|
||||
* connection.
|
||||
*/
|
||||
public int getConnectionUserCount(int connectionID, int userID) {
|
||||
Integer count = activeConnectionUserMap.get
|
||||
(new ConnectionUser(connectionID, userID));
|
||||
|
||||
// No ConnectionUser found means this combination was never used
|
||||
if(count == null)
|
||||
return 0;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given user is currently connected to the given connection.
|
||||
*
|
||||
* @param connectionID The connection ID that this ConnectionUser refers to.
|
||||
* @param userID The user ID that this ConnectionUser refers to.
|
||||
*
|
||||
* @return True if the given user is currently connected to the given
|
||||
* connection, false otherwise.
|
||||
*/
|
||||
public boolean isConnectionUserActive(int connectionID, int userID) {
|
||||
Integer count = activeConnectionUserMap.get
|
||||
(new ConnectionUser(connectionID, userID));
|
||||
|
||||
// The connection is in use if the ConnectionUser count > 0
|
||||
return count != null && count > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the count of the number of connections opened by the given user
|
||||
* to the given Connection.
|
||||
*
|
||||
* @param connectionID The connection ID that this ConnectionUser refers to.
|
||||
* @param userID The user ID that this ConnectionUser refers to.
|
||||
*/
|
||||
private void incrementConnectionUserCount(int connectionID, int userID) {
|
||||
int currentCount = getConnectionGroupUserCount(connectionID, userID);
|
||||
|
||||
activeConnectionUserMap.put
|
||||
(new ConnectionUser(connectionID, userID), currentCount + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement the count of the number of connections opened by the given user
|
||||
* to the given Connection.
|
||||
*
|
||||
* @param connectionID The connection ID that this ConnectionUser refers to.
|
||||
* @param userID The user ID that this ConnectionUser refers to.
|
||||
*/
|
||||
private void decrementConnectionUserCount(int connectionID, int userID) {
|
||||
int currentCount = getConnectionGroupUserCount(connectionID, userID);
|
||||
|
||||
activeConnectionUserMap.put
|
||||
(new ConnectionUser(connectionID, userID), currentCount - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of the connection with the lowest number of current
|
||||
* active users, if found.
|
||||
@@ -232,9 +451,11 @@ public class ActiveConnectionMap {
|
||||
* Set a connection as open.
|
||||
* @param connectionID The ID of the connection that is being opened.
|
||||
* @param userID The ID of the user who is opening the connection.
|
||||
* @param connectionID The ID of the BALANCING connection group that is
|
||||
* being connected to; null if not used.
|
||||
* @return The ID of the history record created for this open connection.
|
||||
*/
|
||||
public int openConnection(int connectionID, int userID) {
|
||||
public int openConnection(int connectionID, int userID, Integer connectionGroupID) {
|
||||
|
||||
// Create the connection history record
|
||||
ConnectionHistory connectionHistory = new ConnectionHistory();
|
||||
@@ -246,17 +467,27 @@ public class ActiveConnectionMap {
|
||||
// Increment the user count
|
||||
incrementUserCount(connectionID);
|
||||
|
||||
// Increment the connection user count
|
||||
incrementConnectionUserCount(connectionID, userID);
|
||||
|
||||
// If this is a connection to a BALANCING ConnectionGroup, increment the count
|
||||
if(connectionGroupID != null)
|
||||
incrementConnectionGroupUserCount(connectionGroupID, userID);
|
||||
|
||||
return connectionHistory.getHistory_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a connection as closed.
|
||||
* @param connectionID The ID of the connection that is being opened.
|
||||
* @param userID The ID of the user who is opening the connection.
|
||||
* @param historyID The ID of the history record about the open connection.
|
||||
* @param connectionID The ID of the BALANCING connection group that is
|
||||
* being connected to; null if not used.
|
||||
* @throws GuacamoleException If the open connection history is not found.
|
||||
*/
|
||||
public void closeConnection(int connectionID, int historyID)
|
||||
throws GuacamoleException {
|
||||
public void closeConnection(int connectionID, int userID, int historyID,
|
||||
Integer connectionGroupID) throws GuacamoleException {
|
||||
|
||||
// Get the existing history record
|
||||
ConnectionHistory connectionHistory =
|
||||
@@ -271,5 +502,12 @@ public class ActiveConnectionMap {
|
||||
|
||||
// Decrement the user count.
|
||||
decrementUserCount(connectionID);
|
||||
|
||||
// Decrement the connection user count
|
||||
decrementConnectionUserCount(connectionID, userID);
|
||||
|
||||
// If this is a connection to a BALANCING ConnectionGroup, decrement the count
|
||||
if(connectionGroupID != null)
|
||||
decrementConnectionGroupUserCount(connectionGroupID, userID);
|
||||
}
|
||||
}
|
||||
|
@@ -145,7 +145,7 @@ public class MySQLConnection extends AbstractConnection {
|
||||
|
||||
@Override
|
||||
public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException {
|
||||
return connectionService.connect(this, info, userID);
|
||||
return connectionService.connect(this, info, userID, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -66,25 +66,42 @@ public class MySQLGuacamoleSocket implements GuacamoleSocket {
|
||||
*/
|
||||
private int connectionID;
|
||||
|
||||
/**
|
||||
* The ID of the user who is connecting to the socket.
|
||||
*/
|
||||
private int userID;
|
||||
|
||||
/**
|
||||
* The ID of the history record associated with this instance of the
|
||||
* connection.
|
||||
*/
|
||||
private int historyID;
|
||||
|
||||
/**
|
||||
* The ID of the balancing connection group that is being connected to;
|
||||
* null if not used.
|
||||
*/
|
||||
private Integer connectionGroupID;
|
||||
|
||||
/**
|
||||
* Initialize this MySQLGuacamoleSocket with the provided GuacamoleSocket.
|
||||
*
|
||||
* @param socket The ConfiguredGuacamoleSocket to wrap.
|
||||
* @param connectionID The ID of the connection associated with the given
|
||||
* socket.
|
||||
* @param userID The ID of the user who is connecting to the socket.
|
||||
* @param historyID The ID of the history record associated with this
|
||||
* instance of the connection.
|
||||
* @param connectionGroupID The ID of the balancing connection group that is
|
||||
* being connected to; null if not used.
|
||||
*/
|
||||
public void init(GuacamoleSocket socket, int connectionID, int historyID) {
|
||||
public void init(GuacamoleSocket socket, int connectionID, int userID,
|
||||
int historyID, Integer connectionGroupID) {
|
||||
this.socket = socket;
|
||||
this.connectionID = connectionID;
|
||||
this.userID = userID;
|
||||
this.historyID = historyID;
|
||||
this.connectionGroupID = connectionGroupID;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -104,7 +121,8 @@ public class MySQLGuacamoleSocket implements GuacamoleSocket {
|
||||
socket.close();
|
||||
|
||||
// Mark this connection as inactive
|
||||
activeConnectionSet.closeConnection(connectionID, historyID);
|
||||
activeConnectionSet.closeConnection(connectionID, userID,
|
||||
historyID, connectionGroupID);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -109,4 +109,16 @@ public class MySQLGuacamoleProperties {
|
||||
public String getName() { return "mysql-disallow-simultaneous-connections"; }
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether or not the same user accessing the same connection or connection group at the same time should be disallowed.
|
||||
*/
|
||||
public static final BooleanGuacamoleProperty MYSQL_DISALLOW_DUPLICATE_CONNECTIONS = new BooleanGuacamoleProperty() {
|
||||
|
||||
@Override
|
||||
public String getName() { return "mysql-disallow-duplicate-connections"; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
@@ -218,12 +218,18 @@ public class ConnectionGroupService {
|
||||
throw new GuacamoleClientException
|
||||
("Cannot connect. All connections are in use.");
|
||||
|
||||
if(GuacamoleProperties.getProperty(
|
||||
MySQLGuacamoleProperties.MYSQL_DISALLOW_DUPLICATE_CONNECTIONS, true)
|
||||
&& activeConnectionMap.isConnectionGroupUserActive(group.getConnectionGroupID(), userID))
|
||||
throw new GuacamoleClientException
|
||||
("Cannot connect. Connection group already in use by this user.");
|
||||
|
||||
// Get the connection
|
||||
MySQLConnection connection = connectionService
|
||||
.retrieveConnection(leastUsedConnectionID, userID);
|
||||
|
||||
// Connect to the connection
|
||||
return connectionService.connect(connection, info, userID);
|
||||
return connectionService.connect(connection, info, userID, group.getConnectionGroupID());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -317,6 +317,8 @@ public class ConnectionService {
|
||||
return connectionRecords;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a MySQLGuacamoleSocket using the provided connection.
|
||||
*
|
||||
@@ -324,12 +326,14 @@ public class ConnectionService {
|
||||
* @param info The information to use when performing the connection
|
||||
* handshake.
|
||||
* @param userID The ID of the user who is connecting to the socket.
|
||||
* @param connectionGroupID The ID of the balancing connection group that is
|
||||
* being connected to; null if not used.
|
||||
* @return The connected socket.
|
||||
* @throws GuacamoleException If an error occurs while connecting the
|
||||
* socket.
|
||||
*/
|
||||
public MySQLGuacamoleSocket connect(MySQLConnection connection,
|
||||
GuacamoleClientInformation info, int userID)
|
||||
GuacamoleClientInformation info, int userID, Integer connectionGroupID)
|
||||
throws GuacamoleException {
|
||||
|
||||
// If the given connection is active, and multiple simultaneous
|
||||
@@ -339,6 +343,12 @@ public class ConnectionService {
|
||||
&& activeConnectionMap.isActive(connection.getConnectionID()))
|
||||
throw new GuacamoleClientException("Cannot connect. This connection is in use.");
|
||||
|
||||
if(GuacamoleProperties.getProperty(
|
||||
MySQLGuacamoleProperties.MYSQL_DISALLOW_DUPLICATE_CONNECTIONS, true)
|
||||
&& activeConnectionMap.isConnectionUserActive(connection.getConnectionID(), userID))
|
||||
throw new GuacamoleClientException
|
||||
("Cannot connect. Connection already in use by this user.");
|
||||
|
||||
// Get guacd connection information
|
||||
String host = GuacamoleProperties.getProperty(GuacamoleProperties.GUACD_HOSTNAME);
|
||||
int port = GuacamoleProperties.getProperty(GuacamoleProperties.GUACD_PORT);
|
||||
@@ -357,11 +367,14 @@ public class ConnectionService {
|
||||
);
|
||||
|
||||
// Mark this connection as active
|
||||
int historyID = activeConnectionMap.openConnection(connection.getConnectionID(), userID);
|
||||
int historyID = activeConnectionMap.openConnection(connection.getConnectionID(),
|
||||
userID, connectionGroupID);
|
||||
|
||||
// Return new MySQLGuacamoleSocket
|
||||
MySQLGuacamoleSocket mySQLGuacamoleSocket = mySQLGuacamoleSocketProvider.get();
|
||||
mySQLGuacamoleSocket.init(socket, connection.getConnectionID(), historyID);
|
||||
mySQLGuacamoleSocket.init(socket, connection.getConnectionID(), userID,
|
||||
historyID, connectionGroupID);
|
||||
|
||||
return mySQLGuacamoleSocket;
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user