Ticket #390: Added mysql-disallow-duplicate-connections property and enforced connection and connectionGroup access restrictions thereof.

This commit is contained in:
James Muehlner
2013-08-18 19:32:22 -07:00
parent 94647857ed
commit 681a0201ba
6 changed files with 298 additions and 11 deletions

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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"; }
};
}

View File

@@ -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());
}
/**

View File

@@ -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;
}