From 0810d082d3e7e95a9eb8f2412201e9f42039b853 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Sun, 3 Mar 2013 14:28:11 -0800 Subject: [PATCH] Ticket #269: Improvements made with regards to the connection history. --- .../net/auth/mysql/ActiveConnectionSet.java | 74 +++++++++++++++++- .../net/auth/mysql/ConnectionDirectory.java | 7 +- .../net/auth/mysql/MySQLConnection.java | 26 +++---- .../net/auth/mysql/MySQLConnectionRecord.java | 65 +++++++--------- .../net/auth/mysql/MySQLGuacamoleSocket.java | 14 +++- .../auth/mysql/service/ConnectionService.java | 78 +++++++++++-------- .../net/auth/mysql/service/UserService.java | 6 +- .../src/main/resources/generatorConfig.xml | 2 + 8 files changed, 178 insertions(+), 94 deletions(-) diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ActiveConnectionSet.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ActiveConnectionSet.java index d04f79871..38d651214 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ActiveConnectionSet.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ActiveConnectionSet.java @@ -37,7 +37,13 @@ package net.sourceforge.guacamole.net.auth.mysql; * * ***** END LICENSE BLOCK ***** */ +import com.google.inject.Inject; +import java.util.Date; import java.util.HashSet; +import java.util.Set; +import net.sourceforge.guacamole.GuacamoleException; +import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionHistoryMapper; +import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionHistory; /** * Represents the set of currently active Connections. Whenever a socket is @@ -46,4 +52,70 @@ import java.util.HashSet; * * @author James Muehlner */ -public class ActiveConnectionSet extends HashSet {} +public class ActiveConnectionSet { + + /** + * DAO for accessing connection history. + */ + @Inject + private ConnectionHistoryMapper connectionHistoryDAO; + + /** + * Set of all the connections that are currently active. + */ + private Set activeConnectionSet = new HashSet(); + + /** + * Check if a connection is currently in use. + * @param connectionID The connection to check the status of. + * @return true if the connection is currently in use. + */ + public boolean isActive(int connectionID) { + return activeConnectionSet.contains(connectionID); + } + + /** + * 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. + * @return The ID of the history record created for this open connection. + */ + public int openConnection(int connectionID, int userID) { + + // Create the connection history record + ConnectionHistory connectionHistory = new ConnectionHistory(); + connectionHistory.setConnection_id(connectionID); + connectionHistory.setUser_id(userID); + connectionHistory.setStart_date(new Date()); + connectionHistoryDAO.insert(connectionHistory); + + // Mark the connection as active + activeConnectionSet.add(connectionID); + + return connectionHistory.getHistory_id(); + } + + /** + * Set a connection as closed. + * @param connectionID The ID of the connection that is being opened. + * @param historyID The ID of the history record about the open connection. + * @throws GuacamoleException If the open connection history is not found. + */ + public void closeConnection(int connectionID, int historyID) + throws GuacamoleException { + + // Get the existing history record + ConnectionHistory connectionHistory = + connectionHistoryDAO.selectByPrimaryKey(historyID); + + if(connectionHistory == null) + throw new GuacamoleException("History record not found."); + + // Update the connection history record to mark that it is now closed + connectionHistory.setEnd_date(new Date()); + connectionHistoryDAO.updateByPrimaryKey(connectionHistory); + + // Remove the connection from the set of active connections. + activeConnectionSet.remove(connectionID); + } +} diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionDirectory.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionDirectory.java index 984aac7a0..2b4b597d2 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionDirectory.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionDirectory.java @@ -105,7 +105,7 @@ public class ConnectionDirectory implements Directory{ // Get connection MySQLConnection connection = - connectionService.retrieveConnection(identifier); + connectionService.retrieveConnection(identifier, user_id); // Verify access is granted permissionCheckService.verifyConnectionAccess( @@ -142,7 +142,8 @@ public class ConnectionDirectory implements Directory{ // Create connection MySQLConnection connection = connectionService.createConnection( - object.getIdentifier(), object.getConfiguration().getProtocol()); + object.getIdentifier(), object.getConfiguration().getProtocol(), + user_id); // Add connection parameters createConfigurationValues(connection.getConnectionID(), @@ -235,7 +236,7 @@ public class ConnectionDirectory implements Directory{ // Get connection MySQLConnection mySQLConnection = - connectionService.retrieveConnection(identifier); + connectionService.retrieveConnection(identifier, user_id); // Verify permission to delete permissionCheckService.verifyConnectionAccess(this.user_id, diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnection.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnection.java index 4f3068f86..3ce094ada 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnection.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnection.java @@ -44,7 +44,6 @@ import java.util.List; import net.sourceforge.guacamole.GuacamoleException; import net.sourceforge.guacamole.net.GuacamoleSocket; import net.sourceforge.guacamole.net.auth.AbstractConnection; -import net.sourceforge.guacamole.net.auth.Connection; import net.sourceforge.guacamole.net.auth.ConnectionRecord; import net.sourceforge.guacamole.net.auth.mysql.service.ConnectionService; import net.sourceforge.guacamole.protocol.GuacamoleClientInformation; @@ -60,6 +59,11 @@ public class MySQLConnection extends AbstractConnection { * The ID associated with this connection in the database. */ private Integer connectionID; + + /** + * The ID of the user who queried or created this connection. + */ + private int userID; /** * History of this connection. @@ -94,18 +98,6 @@ public class MySQLConnection extends AbstractConnection { this.connectionID = connectionID; } - /** - * Initialize a new MySQLConnection from this existing connection. - * - * @param connection The connection to use when populating the identifier - * and configuration of this connection. - * @throws GuacamoleException If permission to read the given connection's - * history is denied. - */ - public void init(Connection connection) throws GuacamoleException { - init(null, connection.getIdentifier(), connection.getConfiguration(), connection.getHistory()); - } - /** * Initialize from explicit values. * @@ -113,21 +105,23 @@ public class MySQLConnection extends AbstractConnection { * @param identifier The unique identifier associated with this connection. * @param config The GuacamoleConfiguration associated with this connection. * @param history All ConnectionRecords associated with this connection. + * @param userID The IID of the user who queried this connection. */ public void init(Integer connectionID, String identifier, GuacamoleConfiguration config, - List history) { + List history, int userID) { this.connectionID = connectionID; setIdentifier(identifier); setConfiguration(config); this.history.addAll(history); - + this.userID = userID; + } @Override public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException { - return connectionService.connect(this, info); + return connectionService.connect(this, info, userID); } @Override diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnectionRecord.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnectionRecord.java index 334614f59..77ee93af7 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnectionRecord.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnectionRecord.java @@ -37,14 +37,8 @@ package net.sourceforge.guacamole.net.auth.mysql; * * ***** END LICENSE BLOCK ***** */ -import com.google.inject.Inject; import java.util.Date; -import net.sourceforge.guacamole.net.auth.Connection; import net.sourceforge.guacamole.net.auth.ConnectionRecord; -import net.sourceforge.guacamole.net.auth.User; -import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionHistory; -import net.sourceforge.guacamole.net.auth.mysql.service.ConnectionService; -import net.sourceforge.guacamole.net.auth.mysql.service.UserService; /** * A ConnectionRecord which is based on data stored in MySQL. @@ -54,59 +48,54 @@ import net.sourceforge.guacamole.net.auth.mysql.service.UserService; public class MySQLConnectionRecord implements ConnectionRecord { /** - * The database record that this ConnectionRecord represents. + * The start date of the ConnectionRecord. */ - private ConnectionHistory connectionHistory; + private Date startDate; + + /** + * The end date of the ConnectionRecord. + */ + private Date endDate; + + /** + * The name of the user that is associated with this ConnectionRecord. + */ + private String username; /** - * Service for accessing users. - */ - @Inject - private UserService userService; - - /** - * Service for accessing connections. - */ - @Inject - private ConnectionService connectionService; - - /** - * Initialize this MySQLConnectionRecord with the database record it - * represents. + * Initialize this MySQLConnectionRecord with the start/end dates, + * and the name of the user it represents. * - * @param connectionHistory The ConnectionHistory entry from the database - * corresponding to this connection record. + * @param startDate The start date of the connection history. + * @param endDate The end date of the connection history. + * @param username The name of the user that used the connection. */ - public void init(ConnectionHistory connectionHistory) { - this.connectionHistory = connectionHistory; + public MySQLConnectionRecord(Date startDate, Date endDate, + String username) { + this.startDate = startDate; + this.endDate = endDate; + this.username = username; } @Override public Date getStartDate() { - return connectionHistory.getStart_date(); + return startDate; } @Override public Date getEndDate() { - return connectionHistory.getEnd_date(); + return endDate; } @Override - public User getUser() { - // FIXME: This will be SLOW - history is queried in bulk. When listed - // to the user in the webapp, this will result in one query per record. - return userService.retrieveUser(connectionHistory.getUser_id()); - } - - @Override - public Connection getConnection() { - return connectionService.retrieveConnection(connectionHistory.getConnection_id()); + public String getUsername() { + return username; } @Override public boolean isActive() { // If the end date hasn't been stored yet, the connection is still open. - return connectionHistory.getEnd_date() == null; + return endDate == null; } } diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLGuacamoleSocket.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLGuacamoleSocket.java index 5e0603049..084537d2b 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLGuacamoleSocket.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLGuacamoleSocket.java @@ -65,6 +65,12 @@ public class MySQLGuacamoleSocket implements GuacamoleSocket { * socket. */ private int connectionID; + + /** + * The ID of the history record associated with this instance of the + * connection. + */ + private int historyID; /** * Initialize this MySQLGuacamoleSocket with the provided GuacamoleSocket. @@ -72,10 +78,13 @@ public class MySQLGuacamoleSocket implements GuacamoleSocket { * @param socket The ConfiguredGuacamoleSocket to wrap. * @param connectionID The ID of the connection associated with the given * socket. + * @param historyID The ID of the history record associated with this + * instance of the connection. */ - public void init(GuacamoleSocket socket, int connectionID) { + public void init(GuacamoleSocket socket, int connectionID, int historyID) { this.socket = socket; this.connectionID = connectionID; + this.historyID = historyID; } @Override @@ -95,8 +104,7 @@ public class MySQLGuacamoleSocket implements GuacamoleSocket { socket.close(); // Mark this connection as inactive - activeConnectionSet.remove(connectionID); - + activeConnectionSet.closeConnection(connectionID, historyID); } @Override diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionService.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionService.java index 9dc1bade0..cae971d71 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionService.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionService.java @@ -37,13 +37,18 @@ package net.sourceforge.guacamole.net.auth.mysql.service; * * ***** END LICENSE BLOCK ***** */ +import com.google.common.collect.Lists; import com.google.inject.Inject; import com.google.inject.Provider; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import net.sourceforge.guacamole.GuacamoleException; import net.sourceforge.guacamole.net.GuacamoleSocket; import net.sourceforge.guacamole.net.InetGuacamoleSocket; @@ -98,12 +103,6 @@ public class ConnectionService { @Inject private Provider mySQLConnectionProvider; - /** - * Provider which creates MySQLConnectionRecords. - */ - @Inject - private Provider mySQLConnectionRecordProvider; - /** * Provider which creates MySQLGuacamoleSockets. */ @@ -115,15 +114,22 @@ public class ConnectionService { */ @Inject private ActiveConnectionSet activeConnectionSet; + + /** + * Service managing users. + */ + @Inject + private UserService userService; /** * Retrieves the connection having the given name from the database. * * @param name The name of the connection to return. + * @param userID The ID of the user who queried this connection. * @return The connection having the given name, or null if no such * connection could be found. */ - public MySQLConnection retrieveConnection(String name) { + public MySQLConnection retrieveConnection(String name, int userID) { // Query connection by connection identifier (name) ConnectionExample example = new ConnectionExample(); @@ -139,7 +145,7 @@ public class ConnectionService { assert connections.size() == 1 : "Multiple connections with same name."; // Otherwise, return found connection - return toMySQLConnection(connections.get(0)); + return toMySQLConnection(connections.get(0), userID); } @@ -147,10 +153,11 @@ public class ConnectionService { * Retrieves the connection having the given ID from the database. * * @param id The ID of the connection to retrieve. + * @param userID The ID of the user who queried this connection. * @return The connection having the given ID, or null if no such * connection was found. */ - public MySQLConnection retrieveConnection(int id) { + public MySQLConnection retrieveConnection(int id, int userID) { // Query connection by ID Connection connection = connectionDAO.selectByPrimaryKey(id); @@ -160,8 +167,7 @@ public class ConnectionService { return null; // Otherwise, return found connection - return toMySQLConnection(connection); - + return toMySQLConnection(connection, userID); } /** @@ -202,7 +208,7 @@ public class ConnectionService { * @return A map containing the names of all connections and their * corresponding IDs. */ - public Map retrieveNames(List ids) { + public Map retrieveNames(Collection ids) { // If no IDs given, just return empty map if (ids.isEmpty()) @@ -213,7 +219,7 @@ public class ConnectionService { // Get all connections having the given IDs ConnectionExample example = new ConnectionExample(); - example.createCriteria().andConnection_idIn(ids); + example.createCriteria().andConnection_idIn(Lists.newArrayList(ids)); List connections = connectionDAO.selectByExample(example); // Produce set of names @@ -231,10 +237,11 @@ public class ConnectionService { * MySQLConnection in the process. * * @param connection The connection to convert. + * @param userID The user who queried this connection. * @return A new MySQLConnection containing all data associated with the * specified connection. */ - private MySQLConnection toMySQLConnection(Connection connection) { + private MySQLConnection toMySQLConnection(Connection connection, int userID) { // Build configuration GuacamoleConfiguration config = new GuacamoleConfiguration(); @@ -259,7 +266,8 @@ public class ConnectionService { connection.getConnection_id(), connection.getConnection_name(), config, - retrieveHistory(connection.getConnection_id()) + retrieveHistory(connection.getConnection_id()), + userID ); return mySQLConnection; @@ -274,7 +282,7 @@ public class ConnectionService { * connection. */ public List retrieveHistory(int connectionID) { - + // Retrieve history records relating to given connection ID ConnectionHistoryExample example = new ConnectionHistoryExample(); example.createCriteria().andConnection_idEqualTo(connectionID); @@ -287,13 +295,21 @@ public class ConnectionService { // Convert history entries to connection records List connectionRecords = new ArrayList(); + Set userIDSet = new HashSet(); for(ConnectionHistory history : connectionHistories) { - - // Create connection record from history - MySQLConnectionRecord record = mySQLConnectionRecordProvider.get(); - record.init(history); - connectionRecords.add(record); - + userIDSet.add(history.getUser_id()); + } + + // Get all the usernames for the users who are in the history + Map usernameMap = userService.retrieveUsernames(userIDSet); + + // Create the new ConnectionRecords + for(ConnectionHistory history : connectionHistories) { + Date startDate = history.getStart_date(); + Date endDate = history.getEnd_date(); + String username = usernameMap.get(history.getUser_id()); + MySQLConnectionRecord connectionRecord = new MySQLConnectionRecord(startDate, endDate, username); + connectionRecords.add(connectionRecord); } return connectionRecords; @@ -305,19 +321,20 @@ public class ConnectionService { * @param connection The connection to use when connecting the socket. * @param info The information to use when performing the connection * handshake. + * @param userID The ID of the user who is connecting to the socket. * @return The connected socket. * @throws GuacamoleException If an error occurs while connecting the * socket. */ public MySQLGuacamoleSocket connect(MySQLConnection connection, - GuacamoleClientInformation info) + GuacamoleClientInformation info, int userID) throws GuacamoleException { // If the given connection is active, and multiple simultaneous // connections are not allowed, disallow connection if(GuacamoleProperties.getProperty( MySQLGuacamoleProperties.MYSQL_DISALLOW_SIMULTANEOUS_CONNECTIONS, false) - && activeConnectionSet.contains(connection.getConnectionID())) + && activeConnectionSet.isActive(connection.getConnectionID())) throw new GuacamoleException("Cannot connect. This connection is in use."); // Get guacd connection information @@ -331,13 +348,11 @@ public class ConnectionService { ); // Mark this connection as active - activeConnectionSet.add(connection.getConnectionID()); - - // TODO: Actually update history... - + int historyID = activeConnectionSet.openConnection(connection.getConnectionID(), userID); + // Return new MySQLGuacamoleSocket MySQLGuacamoleSocket mySQLGuacamoleSocket = mySQLGuacamoleSocketProvider.get(); - mySQLGuacamoleSocket.init(socket, connection.getConnectionID()); + mySQLGuacamoleSocket.init(socket, connection.getConnectionID(), historyID); return mySQLGuacamoleSocket; } @@ -347,10 +362,11 @@ public class ConnectionService { * * @param name The name to assign to the new connection. * @param protocol The protocol to assign to the new connection. + * @param userID The ID of the user who created this connection. * @return A new MySQLConnection containing the data of the newly created * connection. */ - public MySQLConnection createConnection(String name, String protocol) { + public MySQLConnection createConnection(String name, String protocol, int userID) { // Initialize database connection Connection connection = new Connection(); @@ -359,7 +375,7 @@ public class ConnectionService { // Create connection connectionDAO.insert(connection); - return toMySQLConnection(connection); + return toMySQLConnection(connection, userID); } diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/UserService.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/UserService.java index dc1e466dd..3526b7751 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/UserService.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/UserService.java @@ -37,8 +37,10 @@ package net.sourceforge.guacamole.net.auth.mysql.service; * * ***** END LICENSE BLOCK ***** */ +import com.google.common.collect.Lists; import com.google.inject.Inject; import com.google.inject.Provider; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -270,7 +272,7 @@ public class UserService { * @return A map containing the names of all users and their corresponding * IDs. */ - public Map retrieveUsernames(List ids) { + public Map retrieveUsernames(Collection ids) { // If no IDs given, just return empty map if (ids.isEmpty()) @@ -281,7 +283,7 @@ public class UserService { // Get all users having the given IDs UserExample example = new UserExample(); - example.createCriteria().andUser_idIn(ids); + example.createCriteria().andUser_idIn(Lists.newArrayList(ids)); List users = userDAO.selectByExample(example); diff --git a/extensions/guacamole-auth-mysql/src/main/resources/generatorConfig.xml b/extensions/guacamole-auth-mysql/src/main/resources/generatorConfig.xml index c15ad15ce..3a5faf956 100644 --- a/extensions/guacamole-auth-mysql/src/main/resources/generatorConfig.xml +++ b/extensions/guacamole-auth-mysql/src/main/resources/generatorConfig.xml @@ -84,6 +84,8 @@ domainObjectName="ConnectionHistory" > +