Ticket #269: Improvements made with regards to the connection history.

This commit is contained in:
James Muehlner
2013-03-03 14:28:11 -08:00
parent 3b459d7b8e
commit 0810d082d3
8 changed files with 178 additions and 94 deletions

View File

@@ -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<Integer> {}
public class ActiveConnectionSet {
/**
* DAO for accessing connection history.
*/
@Inject
private ConnectionHistoryMapper connectionHistoryDAO;
/**
* Set of all the connections that are currently active.
*/
private Set<Integer> activeConnectionSet = new HashSet<Integer>();
/**
* 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);
}
}

View File

@@ -105,7 +105,7 @@ public class ConnectionDirectory implements Directory<String, Connection>{
// 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<String, Connection>{
// 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<String, Connection>{
// Get connection
MySQLConnection mySQLConnection =
connectionService.retrieveConnection(identifier);
connectionService.retrieveConnection(identifier, user_id);
// Verify permission to delete
permissionCheckService.verifyConnectionAccess(this.user_id,

View File

@@ -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;
@@ -61,6 +60,11 @@ public class MySQLConnection extends AbstractConnection {
*/
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<? extends ConnectionRecord> history) {
List<? extends ConnectionRecord> 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

View File

@@ -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;
/**
* Service for accessing users.
* The end date of the ConnectionRecord.
*/
@Inject
private UserService userService;
private Date endDate;
/**
* Service for accessing connections.
* The name of the user that is associated with this ConnectionRecord.
*/
@Inject
private ConnectionService connectionService;
private String username;
/**
* 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;
}
}

View File

@@ -66,16 +66,25 @@ public class MySQLGuacamoleSocket implements GuacamoleSocket {
*/
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.
*
* @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

View File

@@ -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<MySQLConnection> mySQLConnectionProvider;
/**
* Provider which creates MySQLConnectionRecords.
*/
@Inject
private Provider<MySQLConnectionRecord> mySQLConnectionRecordProvider;
/**
* Provider which creates MySQLGuacamoleSockets.
*/
@@ -116,14 +115,21 @@ 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<Integer, String> retrieveNames(List<Integer> ids) {
public Map<Integer, String> retrieveNames(Collection<Integer> 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<Connection> 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;
@@ -287,13 +295,21 @@ public class ConnectionService {
// Convert history entries to connection records
List<MySQLConnectionRecord> connectionRecords = new ArrayList<MySQLConnectionRecord>();
Set<Integer> userIDSet = new HashSet<Integer>();
for(ConnectionHistory history : connectionHistories) {
userIDSet.add(history.getUser_id());
}
// Create connection record from history
MySQLConnectionRecord record = mySQLConnectionRecordProvider.get();
record.init(history);
connectionRecords.add(record);
// Get all the usernames for the users who are in the history
Map<Integer, String> 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);
}

View File

@@ -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<Integer, String> retrieveUsernames(List<Integer> ids) {
public Map<Integer, String> retrieveUsernames(Collection<Integer> 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<net.sourceforge.guacamole.net.auth.mysql.model.User> users =
userDAO.selectByExample(example);

View File

@@ -84,6 +84,8 @@
domainObjectName="ConnectionHistory" >
<property name="useActualColumnNames" value="true"/>
<property name="ignoreQualifiersAtRuntime" value="true"/>
<generatedKey column="history_id" identity="true"
sqlStatement="SELECT LAST_INSERT_ID()"/>
</table>
</context>