GUAC-1101: Separate database-specific concerns from Guice and MyBatis config.

This commit is contained in:
Michael Jumper
2015-02-27 17:36:09 -08:00
parent 883cc051da
commit bcb603a4b8
5 changed files with 360 additions and 209 deletions

View File

@@ -22,17 +22,31 @@
package net.sourceforge.guacamole.net.auth.mysql; package net.sourceforge.guacamole.net.auth.mysql;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.auth.jdbc.JDBCAuthenticationProvider; import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
import org.glyptodon.guacamole.net.auth.Credentials;
import org.glyptodon.guacamole.net.auth.UserContext;
import org.glyptodon.guacamole.auth.jdbc.JDBCAuthenticationProviderModule;
import org.glyptodon.guacamole.auth.jdbc.user.UserContextService;
import org.glyptodon.guacamole.environment.Environment;
import org.glyptodon.guacamole.environment.LocalEnvironment;
/** /**
* Provides a MySQL based implementation of the AuthenticationProvider * Provides a MySQL based implementation of the AuthenticationProvider
* functionality. * functionality.
* *
* @author James Muehlner * @author James Muehlner
* @author Michael Jumper
*/ */
public class MySQLAuthenticationProvider extends JDBCAuthenticationProvider { public class MySQLAuthenticationProvider implements AuthenticationProvider {
/**
* Injector which will manage the object graph of this authentication
* provider.
*/
private final Injector injector;
/** /**
* Creates a new MySQLAuthenticationProvider that reads and writes * Creates a new MySQLAuthenticationProvider that reads and writes
@@ -44,6 +58,40 @@ public class MySQLAuthenticationProvider extends JDBCAuthenticationProvider {
* a property. * a property.
*/ */
public MySQLAuthenticationProvider() throws GuacamoleException { public MySQLAuthenticationProvider() throws GuacamoleException {
// Get local environment
Environment environment = new LocalEnvironment();
// Set up Guice injector.
injector = Guice.createInjector(
// Configure MySQL-specific authentication
new MySQLAuthenticationProviderModule(environment),
// Configure JDBC authentication core
new JDBCAuthenticationProviderModule(environment)
);
}
@Override
public UserContext getUserContext(Credentials credentials)
throws GuacamoleException {
// Create UserContext based on credentials, if valid
UserContextService userContextService = injector.getInstance(UserContextService.class);
return userContextService.getUserContext(credentials);
}
@Override
public UserContext updateUserContext(UserContext context,
Credentials credentials) throws GuacamoleException {
// No need to update the context
return context;
} }
} }

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package net.sourceforge.guacamole.net.auth.mysql;
import com.google.inject.Binder;
import com.google.inject.Module;
import com.google.inject.name.Names;
import java.util.Properties;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.auth.jdbc.conf.MySQLGuacamoleProperties;
import org.glyptodon.guacamole.environment.Environment;
import org.mybatis.guice.datasource.helper.JdbcHelper;
/**
* Guice module which configures MySQL-specific injections.
*
* @author James Muehlner
*/
public class MySQLAuthenticationProviderModule implements Module {
/**
* MyBatis-specific configuration properties.
*/
private final Properties myBatisProperties = new Properties();
/**
* MySQL-specific driver configuration properties.
*/
private final Properties driverProperties = new Properties();
/**
* Creates a new MySQL authentication provider module that configures
* driver and MyBatis properties using the given environment.
*
* @param environment
* The environment to use when configuring MyBatis and the underlying
* JDBC driver.
*
* @throws GuacamoleException
* If a required property is missing, or an error occurs while parsing
* a property.
*/
public MySQLAuthenticationProviderModule(Environment environment)
throws GuacamoleException {
// Set the MySQL-specific properties for MyBatis.
myBatisProperties.setProperty("mybatis.environment.id", "guacamole");
myBatisProperties.setProperty("JDBC.host", environment.getRequiredProperty(MySQLGuacamoleProperties.MYSQL_HOSTNAME));
myBatisProperties.setProperty("JDBC.port", String.valueOf(environment.getRequiredProperty(MySQLGuacamoleProperties.MYSQL_PORT)));
myBatisProperties.setProperty("JDBC.schema", environment.getRequiredProperty(MySQLGuacamoleProperties.MYSQL_DATABASE));
myBatisProperties.setProperty("JDBC.username", environment.getRequiredProperty(MySQLGuacamoleProperties.MYSQL_USERNAME));
myBatisProperties.setProperty("JDBC.password", environment.getRequiredProperty(MySQLGuacamoleProperties.MYSQL_PASSWORD));
myBatisProperties.setProperty("JDBC.autoCommit", "false");
myBatisProperties.setProperty("mybatis.pooled.pingEnabled", "true");
myBatisProperties.setProperty("mybatis.pooled.pingQuery", "SELECT 1");
// Use UTF-8 in database
driverProperties.setProperty("characterEncoding","UTF-8");
}
@Override
public void configure(Binder binder) {
// Bind MySQL-specific properties
JdbcHelper.MySQL.configure(binder);
// Bind MyBatis properties
Names.bindProperties(binder, myBatisProperties);
// Bing JDBC driver properties
binder.bind(Properties.class)
.annotatedWith(Names.named("JDBC.driverProperties"))
.toInstance(driverProperties);
}
}

View File

@@ -1,205 +0,0 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.glyptodon.guacamole.auth.jdbc;
import org.glyptodon.guacamole.auth.jdbc.user.MySQLUserContext;
import org.glyptodon.guacamole.auth.jdbc.connectiongroup.MySQLRootConnectionGroup;
import org.glyptodon.guacamole.auth.jdbc.connectiongroup.MySQLConnectionGroup;
import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupDirectory;
import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionDirectory;
import org.glyptodon.guacamole.auth.jdbc.connection.MySQLGuacamoleConfiguration;
import org.glyptodon.guacamole.auth.jdbc.connection.MySQLConnection;
import org.glyptodon.guacamole.auth.jdbc.permission.MySQLSystemPermissionSet;
import org.glyptodon.guacamole.auth.jdbc.user.MySQLUser;
import org.glyptodon.guacamole.auth.jdbc.user.UserDirectory;
import com.google.inject.Binder;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.name.Names;
import java.util.Properties;
import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupMapper;
import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionMapper;
import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionRecordMapper;
import org.glyptodon.guacamole.auth.jdbc.connection.ParameterMapper;
import org.glyptodon.guacamole.auth.jdbc.permission.SystemPermissionMapper;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
import org.glyptodon.guacamole.net.auth.Credentials;
import org.glyptodon.guacamole.net.auth.UserContext;
import org.glyptodon.guacamole.auth.jdbc.user.UserMapper;
import org.glyptodon.guacamole.auth.jdbc.conf.MySQLGuacamoleProperties;
import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupService;
import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionService;
import org.glyptodon.guacamole.auth.jdbc.socket.GuacamoleSocketService;
import org.glyptodon.guacamole.auth.jdbc.security.PasswordEncryptionService;
import org.glyptodon.guacamole.auth.jdbc.security.SHA256PasswordEncryptionService;
import org.glyptodon.guacamole.auth.jdbc.security.SaltService;
import org.glyptodon.guacamole.auth.jdbc.security.SecureRandomSaltService;
import org.glyptodon.guacamole.auth.jdbc.permission.SystemPermissionService;
import org.glyptodon.guacamole.auth.jdbc.socket.UnrestrictedGuacamoleSocketService;
import org.glyptodon.guacamole.auth.jdbc.user.UserService;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.glyptodon.guacamole.environment.Environment;
import org.glyptodon.guacamole.environment.LocalEnvironment;
import org.mybatis.guice.MyBatisModule;
import org.mybatis.guice.datasource.builtin.PooledDataSourceProvider;
import org.mybatis.guice.datasource.helper.JdbcHelper;
/**
* Provides a MySQL based implementation of the AuthenticationProvider
* functionality.
*
* @author James Muehlner
*/
public class JDBCAuthenticationProvider implements AuthenticationProvider {
/**
* Injector which will manage the object graph of this authentication
* provider.
*/
private final Injector injector;
@Override
public UserContext getUserContext(Credentials credentials) throws GuacamoleException {
// Get user service
UserService userService = injector.getInstance(UserService.class);
// Authenticate user
MySQLUser user = userService.retrieveUser(credentials);
if (user != null) {
// Upon successful authentication, return new user context
MySQLUserContext context = injector.getInstance(MySQLUserContext.class);
context.init(user.getCurrentUser());
return context;
}
// Otherwise, unauthorized
return null;
}
/**
* Creates a new JDBCAuthenticationProvider that reads and writes
* authentication data to an arbitrary database defined by properties in
* guacamole.properties.
*
* @throws GuacamoleException
* If a required property is missing, or an error occurs while parsing
* a property.
*/
public JDBCAuthenticationProvider() throws GuacamoleException {
// Get local environment
final Environment environment = new LocalEnvironment();
final Properties myBatisProperties = new Properties();
final Properties driverProperties = new Properties();
// Set the mysql properties for MyBatis.
myBatisProperties.setProperty("mybatis.environment.id", "guacamole");
myBatisProperties.setProperty("JDBC.host", environment.getRequiredProperty(MySQLGuacamoleProperties.MYSQL_HOSTNAME));
myBatisProperties.setProperty("JDBC.port", String.valueOf(environment.getRequiredProperty(MySQLGuacamoleProperties.MYSQL_PORT)));
myBatisProperties.setProperty("JDBC.schema", environment.getRequiredProperty(MySQLGuacamoleProperties.MYSQL_DATABASE));
myBatisProperties.setProperty("JDBC.username", environment.getRequiredProperty(MySQLGuacamoleProperties.MYSQL_USERNAME));
myBatisProperties.setProperty("JDBC.password", environment.getRequiredProperty(MySQLGuacamoleProperties.MYSQL_PASSWORD));
myBatisProperties.setProperty("JDBC.autoCommit", "false");
myBatisProperties.setProperty("mybatis.pooled.pingEnabled", "true");
myBatisProperties.setProperty("mybatis.pooled.pingQuery", "SELECT 1");
driverProperties.setProperty("characterEncoding","UTF-8");
// Set up Guice injector.
injector = Guice.createInjector(
JdbcHelper.MySQL,
new Module() {
@Override
public void configure(Binder binder) {
Names.bindProperties(binder, myBatisProperties);
binder.bind(Properties.class)
.annotatedWith(Names.named("JDBC.driverProperties"))
.toInstance(driverProperties);
}
},
new MyBatisModule() {
@Override
protected void initialize() {
// Datasource
bindDataSourceProviderType(PooledDataSourceProvider.class);
// Transaction factory
bindTransactionFactoryType(JdbcTransactionFactory.class);
// Add MyBatis mappers
addMapperClass(ConnectionMapper.class);
addMapperClass(ConnectionGroupMapper.class);
addMapperClass(ConnectionRecordMapper.class);
addMapperClass(ParameterMapper.class);
addMapperClass(SystemPermissionMapper.class);
addMapperClass(UserMapper.class);
// Bind core implementations of guacamole-ext classes
bind(Environment.class).toInstance(environment);
bind(ConnectionDirectory.class);
bind(ConnectionGroupDirectory.class);
bind(MySQLConnection.class);
bind(MySQLConnectionGroup.class);
bind(MySQLGuacamoleConfiguration.class);
bind(MySQLUser.class);
bind(MySQLUserContext.class);
bind(MySQLRootConnectionGroup.class);
bind(MySQLSystemPermissionSet.class);
bind(UserDirectory.class);
// Bind services
bind(ConnectionService.class);
bind(ConnectionGroupService.class);
bind(PasswordEncryptionService.class).to(SHA256PasswordEncryptionService.class);
bind(SaltService.class).to(SecureRandomSaltService.class);
bind(SystemPermissionService.class);
bind(UserService.class);
// Bind appropriate socket service based on policy
bind(GuacamoleSocketService.class).to(UnrestrictedGuacamoleSocketService.class);
}
} // end of mybatis module
);
} // end of constructor
@Override
public UserContext updateUserContext(UserContext context,
Credentials credentials) throws GuacamoleException {
// No need to update the context
return context;
}
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.glyptodon.guacamole.auth.jdbc;
import org.glyptodon.guacamole.auth.jdbc.user.MySQLUserContext;
import org.glyptodon.guacamole.auth.jdbc.connectiongroup.MySQLRootConnectionGroup;
import org.glyptodon.guacamole.auth.jdbc.connectiongroup.MySQLConnectionGroup;
import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupDirectory;
import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionDirectory;
import org.glyptodon.guacamole.auth.jdbc.connection.MySQLGuacamoleConfiguration;
import org.glyptodon.guacamole.auth.jdbc.connection.MySQLConnection;
import org.glyptodon.guacamole.auth.jdbc.permission.MySQLSystemPermissionSet;
import org.glyptodon.guacamole.auth.jdbc.user.MySQLUser;
import org.glyptodon.guacamole.auth.jdbc.user.UserDirectory;
import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupMapper;
import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionMapper;
import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionRecordMapper;
import org.glyptodon.guacamole.auth.jdbc.connection.ParameterMapper;
import org.glyptodon.guacamole.auth.jdbc.permission.SystemPermissionMapper;
import org.glyptodon.guacamole.auth.jdbc.user.UserMapper;
import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupService;
import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionService;
import org.glyptodon.guacamole.auth.jdbc.socket.GuacamoleSocketService;
import org.glyptodon.guacamole.auth.jdbc.security.PasswordEncryptionService;
import org.glyptodon.guacamole.auth.jdbc.security.SHA256PasswordEncryptionService;
import org.glyptodon.guacamole.auth.jdbc.security.SaltService;
import org.glyptodon.guacamole.auth.jdbc.security.SecureRandomSaltService;
import org.glyptodon.guacamole.auth.jdbc.permission.SystemPermissionService;
import org.glyptodon.guacamole.auth.jdbc.socket.UnrestrictedGuacamoleSocketService;
import org.glyptodon.guacamole.auth.jdbc.user.UserService;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.glyptodon.guacamole.environment.Environment;
import org.mybatis.guice.MyBatisModule;
import org.mybatis.guice.datasource.builtin.PooledDataSourceProvider;
/**
* Guice module which configures the injections used by the JDBC authentication
* provider base. This module MUST be included in the Guice injector, or
* authentication providers based on JDBC will not function.
*
* @author Michael Jumper
* @author James Muehlner
*/
public class JDBCAuthenticationProviderModule extends MyBatisModule {
/**
* The environment of the Guacamole server.
*/
private final Environment environment;
/**
* Creates a new JDBC authentication provider module that configures the
* various injected base classes using the given environment.
*
* @param environment
* The environment to use to configure injected classes.
*/
public JDBCAuthenticationProviderModule(Environment environment) {
this.environment = environment;
}
@Override
protected void initialize() {
// Datasource
bindDataSourceProviderType(PooledDataSourceProvider.class);
// Transaction factory
bindTransactionFactoryType(JdbcTransactionFactory.class);
// Add MyBatis mappers
addMapperClass(ConnectionMapper.class);
addMapperClass(ConnectionGroupMapper.class);
addMapperClass(ConnectionRecordMapper.class);
addMapperClass(ParameterMapper.class);
addMapperClass(SystemPermissionMapper.class);
addMapperClass(UserMapper.class);
// Bind core implementations of guacamole-ext classes
bind(Environment.class).toInstance(environment);
bind(ConnectionDirectory.class);
bind(ConnectionGroupDirectory.class);
bind(MySQLConnection.class);
bind(MySQLConnectionGroup.class);
bind(MySQLGuacamoleConfiguration.class);
bind(MySQLUser.class);
bind(MySQLUserContext.class);
bind(MySQLRootConnectionGroup.class);
bind(MySQLSystemPermissionSet.class);
bind(UserDirectory.class);
// Bind services
bind(ConnectionService.class);
bind(ConnectionGroupService.class);
bind(PasswordEncryptionService.class).to(SHA256PasswordEncryptionService.class);
bind(SaltService.class).to(SecureRandomSaltService.class);
bind(SystemPermissionService.class);
bind(UserService.class);
// Bind appropriate socket service based on policy
bind(GuacamoleSocketService.class).to(UnrestrictedGuacamoleSocketService.class);
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.glyptodon.guacamole.auth.jdbc.user;
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.Credentials;
import org.glyptodon.guacamole.net.auth.UserContext;
/**
* Service which creates new UserContext instances for valid users based on
* credentials.
*
* @author Michael Jumper
*/
public class UserContextService {
/**
* Service for accessing users.
*/
@Inject
private UserService userService;
/**
* Provider for retrieving UserContext instances.
*/
@Inject
private Provider<MySQLUserContext> userContextProvider;
/**
* Authenticates the user having the given credentials, returning a new
* UserContext instance if the credentials are valid.
*
* @param credentials
* The credentials to use to produce the UserContext.
*
* @return
* A new UserContext instance for the user identified by the given
* credentials, or null if the credentials are not valid.
*
* @throws GuacamoleException
* If an error occurs during authentication.
*/
public UserContext getUserContext(Credentials credentials)
throws GuacamoleException {
// Authenticate user
MySQLUser user = userService.retrieveUser(credentials);
if (user != null) {
// Upon successful authentication, return new user context
MySQLUserContext context = userContextProvider.get();
context.init(user.getCurrentUser());
return context;
}
// Otherwise, unauthorized
return null;
}
}