Ticket #269: Connection implementation completed. Testing and styling remain.

This commit is contained in:
James Muehlner
2013-02-21 22:56:43 -08:00
parent 253636bb4f
commit 4bbe2c9863
12 changed files with 405 additions and 255 deletions

View File

@@ -35,7 +35,7 @@
* ***** END LICENSE BLOCK ***** */
package net.sourceforge.guacamole.net.auth.mysql;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.HashMap;
@@ -51,6 +51,7 @@ import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionParameterMapper;
import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionPermissionMapper;
import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionParameter;
import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionParameterExample;
import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionPermissionExample;
import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionPermissionKey;
import net.sourceforge.guacamole.net.auth.mysql.utility.PermissionCheckUtility;
import net.sourceforge.guacamole.net.auth.mysql.utility.ProviderUtility;
@@ -113,7 +114,6 @@ public class ConnectionDirectory implements Directory<String, Connection>{
@Transactional
@Override
public void add(Connection object) throws GuacamoleException {
Preconditions.checkNotNull(object);
permissionCheckUtility.verifyCreateConnectionPermission(this.user.getUserID());
MySQLConnection mySQLConnection = providerUtility.getNewMySQLConnection(object);
@@ -143,6 +143,7 @@ public class ConnectionDirectory implements Directory<String, Connection>{
GuacamoleConfiguration configuration = mySQLConnection.getConfiguration();
Map<String, String> existingConfiguration = new HashMap<String, String>();
ConnectionParameterExample example = new ConnectionParameterExample();
example.createCriteria().andConnection_idEqualTo(mySQLConnection.getConnectionID());
List<ConnectionParameter> connectionParameters = connectionParameterDAO.selectByExample(example);
for(ConnectionParameter parameter : connectionParameters)
existingConfiguration.put(parameter.getParameter_name(), parameter.getParameter_value());
@@ -153,20 +154,84 @@ public class ConnectionDirectory implements Directory<String, Connection>{
Set<String> parameterNames = configuration.getParameterNames();
for(String parameterName : parameterNames) {
String parameterValue = configuration.getParameter(parameterName);
if(existingConfiguration.containsKey(parameterName)) {
String existingValue = existingConfiguration.get(parameterName);
// the value is different; we'll have to update this one in the database
if(!parameterValue.equals(existingValue)) {
ConnectionParameter parameterToUpdate = new ConnectionParameter();
parameterToUpdate.setConnection_id(mySQLConnection.getConnectionID());
parameterToUpdate.setParameter_name(parameterName);
parameterToUpdate.setParameter_value(parameterValue);
parametersToUpdate.add(parameterToUpdate);
}
} else {
// the value is new, we need to insert it
ConnectionParameter parameterToInsert = new ConnectionParameter();
parameterToInsert.setConnection_id(mySQLConnection.getConnectionID());
parameterToInsert.setParameter_name(parameterName);
parameterToInsert.setParameter_value(parameterValue);
parametersToInsert.add(parameterToInsert);
}
}
// First, delete all parameters that are not in the new configuration.
example.clear();
example.createCriteria().
andConnection_idEqualTo(mySQLConnection.getConnectionID()).
andParameter_nameNotIn(Lists.newArrayList(existingConfiguration.keySet()));
//Second, update all the parameters that need to be modified.
for(ConnectionParameter parameter : parametersToUpdate) {
example.clear();
example.createCriteria().
andConnection_idEqualTo(mySQLConnection.getConnectionID()).
andParameter_nameEqualTo(parameter.getParameter_name());
connectionParameterDAO.updateByExample(parameter, example);
}
//Finally, insert any new parameters.
for(ConnectionParameter parameter : parametersToInsert) {
example.clear();
example.createCriteria().
andConnection_idEqualTo(mySQLConnection.getConnectionID()).
andParameter_nameEqualTo(parameter.getParameter_name());
connectionParameterDAO.insert(parameter);
}
}
@Transactional
@Override
public void update(Connection object) throws GuacamoleException {
throw new UnsupportedOperationException("Not supported yet.");
permissionCheckUtility.verifyConnectionUpdateAccess(this.user.getUserID(), object.getIdentifier());
MySQLConnection mySQLConnection = providerUtility.getExistingMySQLConnection(object);
connectionDAO.updateByPrimaryKey(mySQLConnection.getConnection());
updateConfigurationValues(mySQLConnection);
}
@Transactional
@Override
public void remove(String identifier) throws GuacamoleException {
throw new UnsupportedOperationException("Not supported yet.");
permissionCheckUtility.verifyConnectionDeleteAccess(this.user.getUserID(), identifier);
MySQLConnection mySQLConnection = providerUtility.getExistingMySQLConnection(identifier);
// delete all configuration values
ConnectionParameterExample connectionParameterExample = new ConnectionParameterExample();
connectionParameterExample.createCriteria().andConnection_idEqualTo(mySQLConnection.getConnectionID());
connectionParameterDAO.deleteByExample(connectionParameterExample);
// delete all permissions that refer to this connection
ConnectionPermissionExample connectionPermissionExample = new ConnectionPermissionExample();
connectionPermissionExample.createCriteria().andConnection_idEqualTo(mySQLConnection.getConnectionID());
connectionPermissionDAO.deleteByExample(connectionPermissionExample);
// delete the connection itself
connectionDAO.deleteByPrimaryKey(mySQLConnection.getConnectionID());
}
}

View File

@@ -52,6 +52,7 @@ import net.sourceforge.guacamole.net.auth.mysql.dao.SystemPermissionMapper;
import net.sourceforge.guacamole.net.auth.mysql.dao.UserMapper;
import net.sourceforge.guacamole.net.auth.mysql.dao.UserPermissionMapper;
import net.sourceforge.guacamole.net.auth.mysql.properties.MySQLGuacamoleProperties;
import net.sourceforge.guacamole.net.auth.mysql.utility.ConfigurationTranslationUtility;
import net.sourceforge.guacamole.net.auth.mysql.utility.PasswordEncryptionUtility;
import net.sourceforge.guacamole.net.auth.mysql.utility.PermissionCheckUtility;
import net.sourceforge.guacamole.net.auth.mysql.utility.ProviderUtility;
@@ -74,6 +75,8 @@ public class MySQLAuthenticationProvider implements AuthenticationProvider {
private Logger logger = LoggerFactory.getLogger(MySQLUserContext.class);
private ActiveConnectionSet activeConnectionSet = new ActiveConnectionSet();
private Injector injector;
@Override
@@ -85,6 +88,7 @@ public class MySQLAuthenticationProvider implements AuthenticationProvider {
public MySQLAuthenticationProvider() throws GuacamoleException {
final Properties myBatisProperties = new Properties();
//set the mysql properties for MyBatis.
myBatisProperties.setProperty("mybatis.environment.id", "guacamole");
myBatisProperties.setProperty("JDBC.host", GuacamoleProperties.getRequiredProperty(MySQLGuacamoleProperties.MYSQL_HOSTNAME));
myBatisProperties.setProperty("JDBC.port", String.valueOf(GuacamoleProperties.getRequiredProperty(MySQLGuacamoleProperties.MYSQL_PORT)));
@@ -93,6 +97,7 @@ public class MySQLAuthenticationProvider implements AuthenticationProvider {
myBatisProperties.setProperty("JDBC.password", GuacamoleProperties.getRequiredProperty(MySQLGuacamoleProperties.MYSQL_PASSWORD));
myBatisProperties.setProperty("JDBC.autoCommit", "false");
// Set up Guice injector.
injector = Guice.createInjector(
JdbcHelper.MySQL,
new Module() {
@@ -117,6 +122,8 @@ public class MySQLAuthenticationProvider implements AuthenticationProvider {
bind(PasswordEncryptionUtility.class).to(Sha256PasswordEncryptionUtility.class);
bind(PermissionCheckUtility.class);
bind(ProviderUtility.class);
bind(ConfigurationTranslationUtility.class);
bind(ActiveConnectionSet.class).toInstance(activeConnectionSet);
}
}
);

View File

@@ -39,11 +39,19 @@ import com.google.inject.Inject;
import java.util.List;
import net.sourceforge.guacamole.GuacamoleException;
import net.sourceforge.guacamole.net.GuacamoleSocket;
import net.sourceforge.guacamole.net.InetGuacamoleSocket;
import net.sourceforge.guacamole.net.auth.Connection;
import net.sourceforge.guacamole.net.auth.ConnectionRecord;
import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionMapper;
import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionParameterMapper;
import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionExample;
import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionParameter;
import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionParameterExample;
import net.sourceforge.guacamole.net.auth.mysql.properties.MySQLGuacamoleProperties;
import net.sourceforge.guacamole.net.auth.mysql.utility.ConfigurationTranslationUtility;
import net.sourceforge.guacamole.net.auth.mysql.utility.ProviderUtility;
import net.sourceforge.guacamole.properties.GuacamoleProperties;
import net.sourceforge.guacamole.protocol.ConfiguredGuacamoleSocket;
import net.sourceforge.guacamole.protocol.GuacamoleClientInformation;
import net.sourceforge.guacamole.protocol.GuacamoleConfiguration;
@@ -56,9 +64,18 @@ public class MySQLConnection implements Connection {
@Inject
ConnectionMapper connectionDAO;
@Inject
ConnectionParameterMapper connectionParameterDAO;
@Inject
ProviderUtility providerUtility;
@Inject
ActiveConnectionSet activeConnectionSet;
@Inject
ConfigurationTranslationUtility configurationTranslationUtility;
private net.sourceforge.guacamole.net.auth.mysql.model.Connection connection;
private GuacamoleConfiguration configuration;
@@ -68,6 +85,7 @@ public class MySQLConnection implements Connection {
*/
MySQLConnection() {
connection = new net.sourceforge.guacamole.net.auth.mysql.model.Connection();
configuration = new GuacamoleConfiguration();
}
/**
@@ -95,6 +113,18 @@ public class MySQLConnection implements Connection {
this.configuration = connection.getConfiguration();
}
/**
* Initializes the GuacamoleConfiguration based on the ConnectionParameter values in the database.
*/
private void initConfiguration() {
ConnectionParameterExample connectionParameterExample = new ConnectionParameterExample();
connectionParameterExample.createCriteria().andConnection_idEqualTo(connection.getConnection_id());
List<ConnectionParameter> connectionParameters = connectionParameterDAO.selectByExample(connectionParameterExample);
configuration = configurationTranslationUtility.getConfiguration(connection.getProtocol(), connectionParameters);
}
/**
* Load an existing connection by name.
* @param connectionName
@@ -110,14 +140,17 @@ public class MySQLConnection implements Connection {
throw new GuacamoleException("No connection found named '" + connectionName + "'.");
connection = connections.get(0);
initConfiguration();
}
/**
* Initialize from a database record.
* Initialize from a database record. This also initializes the configuration values.
* @param connection
*/
public void init(net.sourceforge.guacamole.net.auth.mysql.model.Connection connection) {
this.connection = connection;
initConfiguration();
}
@Override
@@ -138,11 +171,28 @@ public class MySQLConnection implements Connection {
@Override
public void setConfiguration(GuacamoleConfiguration config) {
this.configuration = config;
}
@Override
public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException {
throw new UnsupportedOperationException("Not supported yet.");
// If the current connection is active, and multiple simultaneous connections are not allowed.
if(GuacamoleProperties.getProperty(MySQLGuacamoleProperties.MYSQL_DISALLOW_SIMULTANEOUS_CONNECTIONS, false)
&& activeConnectionSet.contains(getConnectionID()))
throw new GuacamoleException("Cannot connect. This connection is in use.");
String host = GuacamoleProperties.getProperty(GuacamoleProperties.GUACD_HOSTNAME);
int port = GuacamoleProperties.getProperty(GuacamoleProperties.GUACD_PORT);
InetGuacamoleSocket inetSocket = new InetGuacamoleSocket(host, port);
ConfiguredGuacamoleSocket configuredSocket = new ConfiguredGuacamoleSocket(inetSocket, configuration);
MySQLGuacamoleSocket mySQLSocket = providerUtility.getMySQLGuacamoleSocket(configuredSocket, getConnectionID());
// mark this connection as active
activeConnectionSet.add(getConnectionID());
return mySQLSocket;
}
@Override

View File

@@ -411,5 +411,10 @@ public class UserDirectory implements Directory<String, User> {
SystemPermissionExample systemPermissionExample = new SystemPermissionExample();
systemPermissionExample.createCriteria().andUser_idEqualTo(user.getUserID());
systemPermissionDAO.deleteByExample(systemPermissionExample);
//delete all permissions that refer to this user
userPermissionExample.createCriteria();
userPermissionExample.createCriteria().andAffected_user_idEqualTo(user.getUserID());
userPermissionDAO.deleteByExample(userPermissionExample);
}
}

View File

@@ -35,6 +35,7 @@
* ***** END LICENSE BLOCK ***** */
package net.sourceforge.guacamole.net.auth.mysql.properties;
import net.sourceforge.guacamole.properties.BooleanGuacamoleProperty;
import net.sourceforge.guacamole.properties.IntegerGuacamoleProperty;
import net.sourceforge.guacamole.properties.StringGuacamoleProperty;
@@ -98,4 +99,14 @@ public class MySQLGuacamoleProperties {
public String getName() { return "mysql-password"; }
};
/**
* Whether or not multiple users accessing the same connection at the same time should be disallowed.
*/
public static final BooleanGuacamoleProperty MYSQL_DISALLOW_SIMULTANEOUS_CONNECTIONS = new BooleanGuacamoleProperty() {
@Override
public String getName() { return "mysql-disallow-simultaneous-connections"; }
};
}

View File

@@ -44,6 +44,7 @@ import net.sourceforge.guacamole.net.auth.Connection;
import net.sourceforge.guacamole.net.auth.User;
import net.sourceforge.guacamole.net.auth.mysql.MySQLConnection;
import net.sourceforge.guacamole.net.auth.mysql.MySQLConnectionRecord;
import net.sourceforge.guacamole.net.auth.mysql.MySQLGuacamoleSocket;
import net.sourceforge.guacamole.net.auth.mysql.MySQLUser;
import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionHistoryMapper;
import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionMapper;
@@ -53,9 +54,10 @@ import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionHistory;
import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionHistoryExample;
import net.sourceforge.guacamole.net.auth.mysql.model.UserExample;
import net.sourceforge.guacamole.net.auth.mysql.model.UserWithBLOBs;
import net.sourceforge.guacamole.protocol.ConfiguredGuacamoleSocket;
/**
* Provides convenient provider methods for MySQLUser, MySQLConnection, and MySQLConnctionRecord objects.
* Provides convenient provider methods for MySQL specific implementations.
* @author James Muehlner
*/
public class ProviderUtility {
@@ -77,6 +79,9 @@ public class ProviderUtility {
@Inject
Provider<MySQLConnectionRecord> mySQLConnectionRecordProvider;
@Inject
Provider<MySQLGuacamoleSocket> mySQLGuacamoleSocketProvider;
/**
* Create a new user based on the provided object.
* @param user
@@ -205,6 +210,8 @@ public class ProviderUtility {
public List<MySQLConnectionRecord> getExistingMySQLConnectionRecords(Integer connectionID) {
ConnectionHistoryExample example = new ConnectionHistoryExample();
example.createCriteria().andConnection_idEqualTo(connectionID);
// we want to return the newest records first
example.setOrderByClause("start_date DESC");
List<ConnectionHistory> connectionHistories = connectionHistoryDAO.selectByExample(example);
List<MySQLConnectionRecord> connectionRecords = new ArrayList<MySQLConnectionRecord>();
for(ConnectionHistory history : connectionHistories) {
@@ -223,4 +230,16 @@ public class ProviderUtility {
record.init(history);
return record;
}
/**
* Create a MySQLGuacamoleSocket using the provided ConfiguredGuacamoleSocket and connection ID.
* @param socket
* @param connectionID
* @return
*/
public MySQLGuacamoleSocket getMySQLGuacamoleSocket(ConfiguredGuacamoleSocket socket, int connectionID) {
MySQLGuacamoleSocket mySQLGuacamoleSocket = mySQLGuacamoleSocketProvider.get();
mySQLGuacamoleSocket.init(socket, connectionID);
return mySQLGuacamoleSocket;
}
}

View File

@@ -35,7 +35,6 @@
* ***** END LICENSE BLOCK ***** */
package net.sourceforge.guacamole.net.auth.mysql.utility;
import com.google.common.base.Preconditions;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -51,18 +50,12 @@ public class Sha256PasswordEncryptionUtility implements PasswordEncryptionUtilit
@Override
public boolean checkCredentials(Credentials credentials, byte[] dbPasswordHash, String dbUsername, byte[] dbSalt) {
Preconditions.checkNotNull(credentials);
Preconditions.checkNotNull(dbPasswordHash);
Preconditions.checkNotNull(dbUsername);
Preconditions.checkNotNull(dbSalt);
byte[] passwordBytes = createPasswordHash(credentials.getPassword(), dbSalt);
return Arrays.equals(passwordBytes, dbPasswordHash);
}
@Override
public byte[] createPasswordHash(String password, byte[] salt) {
Preconditions.checkNotNull(password);
Preconditions.checkNotNull(salt);
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");