diff --git a/extensions/guacamole-auth-mysql/LICENSE b/extensions/guacamole-auth-jdbc/LICENSE similarity index 100% rename from extensions/guacamole-auth-mysql/LICENSE rename to extensions/guacamole-auth-jdbc/LICENSE diff --git a/extensions/guacamole-auth-jdbc/README b/extensions/guacamole-auth-jdbc/README new file mode 100644 index 000000000..d4fa250aa --- /dev/null +++ b/extensions/guacamole-auth-jdbc/README @@ -0,0 +1,102 @@ + +------------------------------------------------------------ + About this README +------------------------------------------------------------ + +This README is intended to provide quick and to-the-point documentation for +technical users intending to compile parts of Guacamole themselves. + +Distribution-specific packages are available from the files section of the main +project page: + + http://sourceforge.net/projects/guacamole/files/ + +Distribution-specific documentation is provided on the Guacamole wiki: + + http://guac-dev.org/ + + +------------------------------------------------------------ + What is guacamole-auth-jdbc? +------------------------------------------------------------ + +guacamole-auth-jdbc is a Java library for use with the Guacamole web +application to provide database-driven authentication. + +guacamole-auth-jdbc provides multiple authentication provider implementations +which each provide a support for a different database. These authentication +providers can be set in guacamole.properties to allow authentication of +Guacamole users through that type of database. + +Schema files are provided to create the required tables in your database of +choice. + + +------------------------------------------------------------ + Compiling and installing guacamole-auth-jdbc +------------------------------------------------------------ + +guacamole-auth-jdbc is built using Maven. Building guacamole-auth-jdbc compiles +all classes and packages them into a redistributable .tar.gz archive. This +archive contains multiple .jar files, each of this corresponds to a +database-specific authentication provider implementation that can be installed +in the library directory configured in guacamole.properties. + +1) Run mvn package + + $ mvn package + + Maven will download any needed dependencies for building the .jar file. + Once all dependencies have been downloaded, the .jar file will be + created in the target/ subdirectory of the current directory. + +4) Extract the .tar.gz file now present in the target/ directory, and + place the .jar files in the extracted lib/ subdirectory in the library + directory specified in guacamole.properties. + + You will likely need to do this as root. + + If you do not have a library directory configured in your + guacamole.properties, you will need to specify one. The directory + is specified using the "lib-directory" property. + +5) Set up your MySQL database to authenticate Guacamole users + + A schema file is provided in the schema directory for creating + the guacamole authentication tables in your MySQL database. + + Additionally, a script is provided to create a default admin user + with username 'guacadmin' and password 'guacadmin'. This user can + be used to set up any other connections and users. + +6) Configure guacamole.properties for your database + + There are additional properties required by JDBC drivers which must + be added/changed in your guacamole.properties. These parameters are + specific to the database being used. + + For MySQL, the following properties are available: + + # Database connection configuration + mysql-hostname: database.host.name + mysql-port: 3306 + mysql-database: guacamole.database.name + mysql-username: user + mysql-password: pass + + Optionally, the authentication provider can be configured + not to allow multiple users to use the same connection + at the same time: + + mysql-disallow-simultaneous-connections: true + + +------------------------------------------------------------ + Reporting problems +------------------------------------------------------------ + +Please report any bugs encountered by opening a new issue in the JIRA system +hosted at: + + http://glyptodon.org/jira/ + diff --git a/extensions/guacamole-auth-mysql/.gitignore b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/.gitignore similarity index 100% rename from extensions/guacamole-auth-mysql/.gitignore rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/.gitignore diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml new file mode 100644 index 000000000..40ca67bee --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml @@ -0,0 +1,82 @@ + + + 4.0.0 + org.glyptodon.guacamole + guacamole-auth-jdbc-base + jar + guacamole-auth-jdbc-base + http://guac-dev.org/ + + + UTF-8 + + + + org.glyptodon.guacamole + guacamole-auth-jdbc + 0.9.5 + ../../ + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.6 + 1.6 + + + + + + + + + + + org.glyptodon.guacamole + guacamole-ext + provided + + + + + org.slf4j + slf4j-api + 1.7.7 + + + + + org.mybatis + mybatis + 3.2.8 + + + + + org.mybatis + mybatis-guice + 3.6 + + + + + com.google.inject + guice + 3.0 + + + com.google.inject.extensions + guice-multibindings + 3.0 + + + + + diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java new file mode 100644 index 000000000..31e9c6389 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java @@ -0,0 +1,143 @@ +/* + * 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.UserContext; +import org.glyptodon.guacamole.auth.jdbc.connectiongroup.RootConnectionGroup; +import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; +import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupDirectory; +import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionDirectory; +import org.glyptodon.guacamole.auth.jdbc.connection.ModeledGuacamoleConfiguration; +import org.glyptodon.guacamole.auth.jdbc.connection.ModeledConnection; +import org.glyptodon.guacamole.auth.jdbc.permission.SystemPermissionSet; +import org.glyptodon.guacamole.auth.jdbc.user.ModeledUser; +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.auth.jdbc.permission.ConnectionGroupPermissionMapper; +import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionGroupPermissionService; +import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionGroupPermissionSet; +import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionPermissionMapper; +import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionPermissionService; +import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionPermissionSet; +import org.glyptodon.guacamole.auth.jdbc.permission.UserPermissionMapper; +import org.glyptodon.guacamole.auth.jdbc.permission.UserPermissionService; +import org.glyptodon.guacamole.auth.jdbc.permission.UserPermissionSet; +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(ConnectionGroupPermissionMapper.class); + addMapperClass(ConnectionPermissionMapper.class); + addMapperClass(ConnectionRecordMapper.class); + addMapperClass(ParameterMapper.class); + addMapperClass(SystemPermissionMapper.class); + addMapperClass(UserMapper.class); + addMapperClass(UserPermissionMapper.class); + + // Bind core implementations of guacamole-ext classes + bind(Environment.class).toInstance(environment); + bind(ConnectionDirectory.class); + bind(ConnectionGroupDirectory.class); + bind(ConnectionGroupPermissionSet.class); + bind(ConnectionPermissionSet.class); + bind(ModeledConnection.class); + bind(ModeledConnectionGroup.class); + bind(ModeledGuacamoleConfiguration.class); + bind(ModeledUser.class); + bind(RootConnectionGroup.class); + bind(SystemPermissionSet.class); + bind(UserContext.class); + bind(UserDirectory.class); + bind(UserPermissionSet.class); + + // Bind services + bind(ConnectionGroupPermissionService.class); + bind(ConnectionGroupService.class); + bind(ConnectionPermissionService.class); + bind(ConnectionService.class); + bind(PasswordEncryptionService.class).to(SHA256PasswordEncryptionService.class); + bind(SaltService.class).to(SecureRandomSaltService.class); + bind(SystemPermissionService.class); + bind(UserPermissionService.class); + bind(UserService.class); + + // Bind appropriate socket service based on policy + bind(GuacamoleSocketService.class).to(UnrestrictedGuacamoleSocketService.class); + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObject.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObject.java new file mode 100644 index 000000000..8568d8995 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObject.java @@ -0,0 +1,49 @@ +/* + * 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.base; + +import org.glyptodon.guacamole.net.auth.Identifiable; + +/** + * Common base class for objects that will ultimately be made available through + * the Directory class. All such objects will need the same base set of queries + * to fulfill the needs of the Directory class. + * + * @author Michael Jumper + * @param + * The type of model object that corresponds to this object. + */ +public abstract class DirectoryObject + extends ModeledObject implements Identifiable { + + @Override + public String getIdentifier() { + return getModel().getIdentifier(); + } + + @Override + public void setIdentifier(String identifier) { + getModel().setIdentifier(identifier); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObjectMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObjectMapper.java new file mode 100644 index 000000000..d63cf3814 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObjectMapper.java @@ -0,0 +1,140 @@ +/* + * 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.base; + +import java.util.Collection; +import java.util.Set; +import org.glyptodon.guacamole.auth.jdbc.user.UserModel; +import org.apache.ibatis.annotations.Param; + +/** + * Common interface for objects that will ultimately be made available through + * the Directory class. All such objects will need the same base set of queries + * to fulfill the needs of the Directory class. + * + * @author Michael Jumper + * @param + * The type of object contained within the directory whose objects are + * mapped by this mapper. + */ +public interface DirectoryObjectMapper { + + /** + * Selects the identifiers of all objects, regardless of whether they + * are readable by any particular user. This should only be called on + * behalf of a system administrator. If identifiers are needed by a non- + * administrative user who must have explicit read rights, use + * selectReadableIdentifiers() instead. + * + * @return + * A Set containing all identifiers of all objects. + */ + Set selectIdentifiers(); + + /** + * Selects the identifiers of all objects that are explicitly readable by + * the given user. If identifiers are needed by a system administrator + * (who, by definition, does not need explicit read rights), use + * selectIdentifiers() instead. + * + * @param user + * The user whose permissions should determine whether an identifier + * is returned. + * + * @return + * A Set containing all identifiers of all readable objects. + */ + Set selectReadableIdentifiers(@Param("user") UserModel user); + + /** + * Selects all objects which have the given identifiers. If an identifier + * has no corresponding object, it will be ignored. This should only be + * called on behalf of a system administrator. If objects are needed by a + * non-administrative user who must have explicit read rights, use + * selectReadable() instead. + * + * @param identifiers + * The identifiers of the objects to return. + * + * @return + * A Collection of all objects having the given identifiers. + */ + Collection select(@Param("identifiers") Collection identifiers); + + /** + * Selects all objects which have the given identifiers and are explicitly + * readably by the given user. If an identifier has no corresponding + * object, or the corresponding object is unreadable, it will be ignored. + * If objects are needed by a system administrator (who, by definition, + * does not need explicit read rights), use select() instead. + * + * @param user + * The user whose permissions should determine whether an object + * is returned. + * + * @param identifiers + * The identifiers of the objects to return. + * + * @return + * A Collection of all objects having the given identifiers. + */ + Collection selectReadable(@Param("user") UserModel user, + @Param("identifiers") Collection identifiers); + + /** + * Inserts the given object into the database. If the object already + * exists, this will result in an error. + * + * @param object + * The object to insert. + * + * @return + * The number of rows inserted. + */ + int insert(@Param("object") ModelType object); + + /** + * Deletes the given object into the database. If the object does not + * exist, this operation has no effect. + * + * @param identifier + * The identifier of the object to delete. + * + * @return + * The number of rows deleted. + */ + int delete(@Param("identifier") String identifier); + + /** + * Updates the given existing object in the database. If the object does + * not actually exist, this operation has no effect. + * + * @param object + * The object to update. + * + * @return + * The number of rows updated. + */ + int update(@Param("object") ModelType object); + +} \ No newline at end of file diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObjectService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObjectService.java new file mode 100644 index 000000000..fc2bdb331 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObjectService.java @@ -0,0 +1,490 @@ +/* + * Copyright (C) 2013 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.base; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionMapper; +import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionModel; +import org.glyptodon.guacamole.auth.jdbc.user.UserModel; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; + +/** + * Service which provides convenience methods for creating, retrieving, and + * manipulating users. This service will automatically enforce the + * permissions of the current user. + * + * @author Michael Jumper + * @param + * The specific internal implementation of the type of object this service + * provides access to. + * + * @param + * The external interface or implementation of the type of object this + * service provides access to, as defined by the guacamole-ext API. + * + * @param + * The underlying model object used to represent InternalType in the + * database. + */ +public abstract class DirectoryObjectService, + ExternalType, ModelType extends ObjectModel> { + + /** + * All object permissions which are implicitly granted upon creation to the + * creator of the object. + */ + private static final ObjectPermission.Type[] IMPLICIT_OBJECT_PERMISSIONS = { + ObjectPermission.Type.READ, + ObjectPermission.Type.UPDATE, + ObjectPermission.Type.DELETE, + ObjectPermission.Type.ADMINISTER + }; + + /** + * Returns an instance of a mapper for the type of object used by this + * service. + * + * @return + * A mapper which provides access to the model objects associated with + * the objects used by this service. + */ + protected abstract DirectoryObjectMapper getObjectMapper(); + + /** + * Returns an instance of a mapper for the type of permissions that affect + * the type of object used by this service. + * + * @return + * A mapper which provides access to the model objects associated with + * the permissions that affect the objects used by this service. + */ + protected abstract ObjectPermissionMapper getPermissionMapper(); + + /** + * Returns an instance of an object which is backed by the given model + * object. + * + * @param currentUser + * The user for whom this object is being created. + * + * @param model + * The model object to use to back the returned object. + * + * @return + * An object which is backed by the given model object. + */ + protected abstract InternalType getObjectInstance(AuthenticatedUser currentUser, + ModelType model); + + /** + * Returns an instance of a model object which is based on the given + * object. + * + * @param currentUser + * The user for whom this model object is being created. + * + * @param object + * The object to use to produce the returned model object. + * + * @return + * A model object which is based on the given object. + */ + protected abstract ModelType getModelInstance(AuthenticatedUser currentUser, + ExternalType object); + + /** + * Returns whether the given user has permission to create the type of + * objects that this directory object service manages. + * + * @param user + * The user being checked. + * + * @return + * true if the user has object creation permission relevant to this + * directory object service, false otherwise. + * + * @throws GuacamoleException + * If permission to read the user's permissions is denied. + */ + protected abstract boolean hasCreatePermission(AuthenticatedUser user) + throws GuacamoleException; + + /** + * Returns whether the given user has permission to perform a certain + * action on a specific object managed by this directory object service. + * + * @param user + * The user being checked. + * + * @param identifier + * The identifier of the object to check. + * + * @param type + * The type of action that will be performed. + * + * @return + * true if the user has object permission relevant described, false + * otherwise. + * + * @throws GuacamoleException + * If permission to read the user's permissions is denied. + */ + protected boolean hasObjectPermission(AuthenticatedUser user, + String identifier, ObjectPermission.Type type) + throws GuacamoleException { + + // Get object permissions + ObjectPermissionSet permissionSet = getPermissionSet(user); + + // Return whether permission is granted + return user.getUser().isAdministrator() + || permissionSet.hasPermission(type, identifier); + + } + + /** + * Returns the permission set associated with the given user and related + * to the type of objects handled by this directory object service. + * + * @param user + * The user whose permissions are being retrieved. + * + * @return + * A permission set which contains the permissions associated with the + * given user and related to the type of objects handled by this + * directory object service. + * + * @throws GuacamoleException + * If permission to read the user's permissions is denied. + */ + protected abstract ObjectPermissionSet getPermissionSet(AuthenticatedUser user) + throws GuacamoleException; + + /** + * Returns a collection of objects which are backed by the models in the + * given collection. + * + * @param currentUser + * The user for whom these objects are being created. + * + * @param models + * The model objects to use to back the objects within the returned + * collection. + * + * @return + * A collection of objects which are backed by the models in the given + * collection. + */ + protected Collection getObjectInstances(AuthenticatedUser currentUser, + Collection models) { + + // Create new collection of objects by manually converting each model + Collection objects = new ArrayList(models.size()); + for (ModelType model : models) + objects.add(getObjectInstance(currentUser, model)); + + return objects; + + } + + /** + * Returns whether the contents of the given model are valid and can be + * used to create a new object as-is. The object does not yet exist in the + * database, but the user desires to create a new object with the given + * model. This function will be called prior to any creation operation, and + * provides a means for the implementation to abort prior to completion. The + * default implementation does nothing. + * + * @param user + * The user creating the object. + * + * @param model + * The model to validate. + * + * @throws GuacamoleException + * If the object is invalid, or an error prevents validating the given + * object. + */ + protected void validateNewModel(AuthenticatedUser user, + ModelType model) throws GuacamoleException { + + // By default, do nothing. + + } + + /** + * Returns whether the given model is valid and can be used to update an + * existing object as-is. The object already exists in the database, but the + * user desires to update the object to the given model. This function will + * be called prior to update operation, and provides a means for the + * implementation to abort prior to completion. The default implementation + * does nothing. + * + * @param user + * The user updating the existing object. + * + * @param model + * The model to validate. + * + * @throws GuacamoleException + * If the object is invalid, or an error prevents validating the given + * object. + */ + protected void validateExistingModel(AuthenticatedUser user, + ModelType model) throws GuacamoleException { + + // By default, do nothing. + + } + + /** + * Retrieves the single object that has the given identifier, if it exists + * and the user has permission to read it. + * + * @param user + * The user retrieving the object. + * + * @param identifier + * The identifier of the object to retrieve. + * + * @return + * The object having the given identifier, or null if no such object + * exists. + * + * @throws GuacamoleException + * If an error occurs while retrieving the requested object. + */ + public InternalType retrieveObject(AuthenticatedUser user, + String identifier) throws GuacamoleException { + + // Pull objects having given identifier + Collection objects = retrieveObjects(user, Collections.singleton(identifier)); + + // If no such object, return null + if (objects.isEmpty()) + return null; + + // The object collection will have exactly one element unless the + // database has seriously lost integrity + assert(objects.size() == 1); + + // Return first and only object + return objects.iterator().next(); + + } + + /** + * Retrieves all objects that have the identifiers in the given collection. + * Only objects that the user has permission to read will be returned. + * + * @param user + * The user retrieving the objects. + * + * @param identifiers + * The identifiers of the objects to retrieve. + * + * @return + * The objects having the given identifiers. + * + * @throws GuacamoleException + * If an error occurs while retrieving the requested objects. + */ + public Collection retrieveObjects(AuthenticatedUser user, + Collection identifiers) throws GuacamoleException { + + // Do not query if no identifiers given + if (identifiers.isEmpty()) + return Collections.EMPTY_LIST; + + Collection objects; + + // Bypass permission checks if the user is a system admin + if (user.getUser().isAdministrator()) + objects = getObjectMapper().select(identifiers); + + // Otherwise only return explicitly readable identifiers + else + objects = getObjectMapper().selectReadable(user.getUser().getModel(), identifiers); + + // Return collection of requested objects + return getObjectInstances(user, objects); + + } + + /** + * Creates the given object within the database. If the object already + * exists, an error will be thrown. The internal model object will be + * updated appropriately to contain the new database ID. + * + * @param user + * The user creating the object. + * + * @param object + * The object to create. + * + * @return + * The newly-created object. + * + * @throws GuacamoleException + * If the user lacks permission to create the object, or an error + * occurs while creating the object. + */ + public InternalType createObject(AuthenticatedUser user, ExternalType object) + throws GuacamoleException { + + // Only create object if user has permission to do so + if (user.getUser().isAdministrator() || hasCreatePermission(user)) { + + // Validate object prior to creation + ModelType model = getModelInstance(user, object); + validateNewModel(user, model); + + // Create object + getObjectMapper().insert(model); + + // Build list of implicit permissions + Collection implicitPermissions = + new ArrayList(IMPLICIT_OBJECT_PERMISSIONS.length); + + UserModel userModel = user.getUser().getModel(); + for (ObjectPermission.Type permission : IMPLICIT_OBJECT_PERMISSIONS) { + + // Create model which grants this permission to the current user + ObjectPermissionModel permissionModel = new ObjectPermissionModel(); + permissionModel.setUserID(userModel.getObjectID()); + permissionModel.setUsername(userModel.getIdentifier()); + permissionModel.setType(permission); + permissionModel.setObjectIdentifier(model.getIdentifier()); + + // Add permission + implicitPermissions.add(permissionModel); + + } + + // Add implicit permissions + getPermissionMapper().insert(implicitPermissions); + + return getObjectInstance(user, model); + } + + // User lacks permission to create + throw new GuacamoleSecurityException("Permission denied."); + + } + + /** + * Deletes the object having the given identifier. If no such object + * exists, this function has no effect. + * + * @param user + * The user deleting the object. + * + * @param identifier + * The identifier of the object to delete. + * + * @throws GuacamoleException + * If the user lacks permission to delete the object, or an error + * occurs while deleting the object. + */ + public void deleteObject(AuthenticatedUser user, String identifier) + throws GuacamoleException { + + // Only delete object if user has permission to do so + if (hasObjectPermission(user, identifier, ObjectPermission.Type.DELETE)) { + getObjectMapper().delete(identifier); + return; + } + + // User lacks permission to delete + throw new GuacamoleSecurityException("Permission denied."); + + } + + /** + * Updates the given object in the database, applying any changes that have + * been made. If no such object exists, this function has no effect. + * + * @param user + * The user updating the object. + * + * @param object + * The object to update. + * + * @throws GuacamoleException + * If the user lacks permission to update the object, or an error + * occurs while updating the object. + */ + public void updateObject(AuthenticatedUser user, InternalType object) + throws GuacamoleException { + + // Only update object if user has permission to do so + if (hasObjectPermission(user, object.getIdentifier(), ObjectPermission.Type.UPDATE)) { + + // Validate object prior to creation + ModelType model = object.getModel(); + validateExistingModel(user, model); + + // Update object + getObjectMapper().update(model); + return; + } + + // User lacks permission to update + throw new GuacamoleSecurityException("Permission denied."); + + } + + /** + * Returns the set of all identifiers for all objects in the database that + * the user has read access to. + * + * @param user + * The user retrieving the identifiers. + * + * @return + * The set of all identifiers for all objects in the database. + * + * @throws GuacamoleException + * If an error occurs while reading identifiers. + */ + public Set getIdentifiers(AuthenticatedUser user) + throws GuacamoleException { + + // Bypass permission checks if the user is a system admin + if (user.getUser().isAdministrator()) + return getObjectMapper().selectIdentifiers(); + + // Otherwise only return explicitly readable identifiers + else + return getObjectMapper().selectReadableIdentifiers(user.getUser().getModel()); + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledObject.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledObject.java new file mode 100644 index 000000000..276b0909e --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledObject.java @@ -0,0 +1,82 @@ +/* + * 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.base; + +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; + +/** + * Common base class for objects have an underlying model. For the purposes of + * JDBC-driven authentication providers, all modeled objects are also + * restricted. + * + * @author Michael Jumper + * @param + * The type of model object which corresponds to this object. + */ +public abstract class ModeledObject extends RestrictedObject { + + /** + * The internal model object containing the values which represent this + * object in the database. + */ + private ModelType model; + + /** + * Initializes this object, associating it with the current authenticated + * user and populating it with data from the given model object + * + * @param currentUser + * The user that created or retrieved this object. + * + * @param model + * The backing model object. + */ + public void init(AuthenticatedUser currentUser, ModelType model) { + super.init(currentUser); + setModel(model); + } + + /** + * Returns the backing model object. Changes to the model object will + * affect this object, and changes to this object will affect the model + * object. + * + * @return + * The backing model object. + */ + public ModelType getModel() { + return model; + } + + /** + * Sets the backing model object. This will effectively replace all data + * contained within this object. + * + * @param model + * The backing model object. + */ + public void setModel(ModelType model) { + this.model = model; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ObjectModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ObjectModel.java new file mode 100644 index 000000000..e936686cd --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ObjectModel.java @@ -0,0 +1,90 @@ +/* + * 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.base; + +/** + * Object representation of a Guacamole object, such as a user or connection, + * as represented in the database. + * + * @author Michael Jumper + */ +public abstract class ObjectModel { + + /** + * The ID of this object in the database, if any. + */ + private Integer objectID; + + /** + * The unique identifier which identifies this object. + */ + private String identifier; + + /** + * Creates a new, empty object. + */ + public ObjectModel() { + } + + /** + * Returns the identifier that uniquely identifies this object. + * + * @return + * The identifier that uniquely identifies this object. + */ + public String getIdentifier() { + return identifier; + } + + /** + * Sets the identifier that uniquely identifies this object. + * + * @param identifier + * The identifier that uniquely identifies this object. + */ + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + /** + * Returns the ID of this object in the database, if it exists. + * + * @return + * The ID of this object in the database, or null if this object was + * not retrieved from the database. + */ + public Integer getObjectID() { + return objectID; + } + + /** + * Sets the ID of this object to the given value. + * + * @param objectID + * The ID to assign to this object. + */ + public void setObjectID(Integer objectID) { + this.objectID = objectID; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/RestrictedObject.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/RestrictedObject.java new file mode 100644 index 000000000..d8828c43c --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/RestrictedObject.java @@ -0,0 +1,76 @@ +/* + * 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.base; + +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; + +/** + * Common base class for objects that are associated with the users that + * obtain them. + * + * @author Michael Jumper + */ +public abstract class RestrictedObject { + + /** + * The user this object belongs to. Access is based on his/her permission + * settings. + */ + private AuthenticatedUser currentUser; + + /** + * Initializes this object, associating it with the current authenticated + * user and populating it with data from the given model object + * + * @param currentUser + * The user that created or retrieved this object. + */ + public void init(AuthenticatedUser currentUser) { + setCurrentUser(currentUser); + } + + /** + * Returns the user that created or queried this object. This user's + * permissions dictate what operations can be performed on or through this + * object. + * + * @return + * The user that created or queried this object. + */ + public AuthenticatedUser getCurrentUser() { + return currentUser; + } + + /** + * Sets the user that created or queried this object. This user's + * permissions dictate what operations can be performed on or through this + * object. + * + * @param currentUser + * The user that created or queried this object. + */ + public void setCurrentUser(AuthenticatedUser currentUser) { + this.currentUser = currentUser; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/package-info.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/package-info.java new file mode 100644 index 000000000..122dc3ec9 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/package-info.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +/** + * Base classes supporting JDBC-driven authentication providers and defining + * the relationships between the model and the implementations of guacamole-ext + * classes. + */ +package org.glyptodon.guacamole.auth.jdbc.base; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionDirectory.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionDirectory.java new file mode 100644 index 000000000..2afc98b28 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionDirectory.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2013 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.connection; + + +import com.google.inject.Inject; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject; +import org.glyptodon.guacamole.net.auth.Connection; +import org.glyptodon.guacamole.net.auth.Directory; +import org.mybatis.guice.transactional.Transactional; + +/** + * Implementation of the Connection Directory which is driven by an underlying, + * arbitrary database. + * + * @author James Muehlner + * @author Michael Jumper + */ +public class ConnectionDirectory extends RestrictedObject + implements Directory { + + /** + * Service for managing connection objects. + */ + @Inject + private ConnectionService connectionService; + + @Override + public Connection get(String identifier) throws GuacamoleException { + return connectionService.retrieveObject(getCurrentUser(), identifier); + } + + @Override + @Transactional + public Collection getAll(Collection identifiers) throws GuacamoleException { + Collection objects = connectionService.retrieveObjects(getCurrentUser(), identifiers); + return Collections.unmodifiableCollection(objects); + } + + @Override + @Transactional + public Set getIdentifiers() throws GuacamoleException { + return connectionService.getIdentifiers(getCurrentUser()); + } + + @Override + @Transactional + public void add(Connection object) throws GuacamoleException { + connectionService.createObject(getCurrentUser(), object); + } + + @Override + @Transactional + public void update(Connection object) throws GuacamoleException { + ModeledConnection connection = (ModeledConnection) object; + connectionService.updateObject(getCurrentUser(), connection); + } + + @Override + @Transactional + public void remove(String identifier) throws GuacamoleException { + connectionService.deleteObject(getCurrentUser(), identifier); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionMapper.java new file mode 100644 index 000000000..faa7f21d1 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionMapper.java @@ -0,0 +1,92 @@ +/* + * 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.connection; + +import java.util.Set; +import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectMapper; +import org.glyptodon.guacamole.auth.jdbc.user.UserModel; +import org.apache.ibatis.annotations.Param; + +/** + * Mapper for connection objects. + * + * @author Michael Jumper + */ +public interface ConnectionMapper extends DirectoryObjectMapper { + + /** + * Selects the identifiers of all connections within the given parent + * connection group, regardless of whether they are readable by any + * particular user. This should only be called on behalf of a system + * administrator. If identifiers are needed by a non-administrative user + * who must have explicit read rights, use + * selectReadableIdentifiersWithin() instead. + * + * @param parentIdentifier + * The identifier of the parent connection group, or null if the root + * connection group is to be queried. + * + * @return + * A Set containing all identifiers of all objects. + */ + Set selectIdentifiersWithin(@Param("parentIdentifier") String parentIdentifier); + + /** + * Selects the identifiers of all connections within the given parent + * connection group that are explicitly readable by the given user. If + * identifiers are needed by a system administrator (who, by definition, + * does not need explicit read rights), use selectIdentifiersWithin() + * instead. + * + * @param user + * The user whose permissions should determine whether an identifier + * is returned. + * + * @param parentIdentifier + * The identifier of the parent connection group, or null if the root + * connection group is to be queried. + * + * @return + * A Set containing all identifiers of all readable objects. + */ + Set selectReadableIdentifiersWithin(@Param("user") UserModel user, + @Param("parentIdentifier") String parentIdentifier); + + /** + * Selects the connection within the given parent group and having the + * given name. If no such connection exists, null is returned. + * + * @param parentIdentifier + * The identifier of the parent group to search within. + * + * @param name + * The name of the connection to find. + * + * @return + * The connection having the given name within the given parent group, + * or null if no such connection exists. + */ + ConnectionModel selectOneByName(@Param("parentIdentifier") String parentIdentifier, + @Param("name") String name); + +} \ No newline at end of file diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionModel.java new file mode 100644 index 000000000..b4ef2e907 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionModel.java @@ -0,0 +1,139 @@ +/* + * 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.connection; + +import org.glyptodon.guacamole.auth.jdbc.base.ObjectModel; + +/** + * Object representation of a Guacamole connection, as represented in the + * database. + * + * @author Michael Jumper + */ +public class ConnectionModel extends ObjectModel { + + /** + * The identifier of the parent connection group in the database, or null + * if the parent connection group is the root group. + */ + private String parentIdentifier; + + /** + * The human-readable name associated with this connection. + */ + private String name; + + /** + * The name of the protocol to use when connecting to this connection. + */ + private String protocol; + + /** + * Creates a new, empty connection. + */ + public ConnectionModel() { + } + + /** + * Returns the name associated with this connection. + * + * @return + * The name associated with this connection. + */ + public String getName() { + return name; + } + + /** + * Sets the name associated with this connection. + * + * @param name + * The name to associate with this connection. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns the name of the protocol to use when connecting to this + * connection. + * + * @return + * The name of the protocol to use when connecting to this connection. + */ + public String getProtocol() { + return protocol; + } + + /** + * Sets the name of the protocol to use when connecting to this connection. + * + * @param protocol + * The name of the protocol to use when connecting to this connection. + */ + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + /** + * Returns the identifier of the parent connection group, or null if the + * parent connection group is the root connection group. + * + * @return + * The identifier of the parent connection group, or null if the parent + * connection group is the root connection group. + */ + public String getParentIdentifier() { + return parentIdentifier; + } + + /** + * Sets the identifier of the parent connection group. + * + * @param parentIdentifier + * The identifier of the parent connection group, or null if the parent + * connection group is the root connection group. + */ + public void setParentIdentifier(String parentIdentifier) { + this.parentIdentifier = parentIdentifier; + } + + @Override + public String getIdentifier() { + + // If no associated ID, then no associated identifier + Integer id = getObjectID(); + if (id == null) + return null; + + // Otherwise, the identifier is the ID as a string + return id.toString(); + + } + + @Override + public void setIdentifier(String identifier) { + throw new UnsupportedOperationException("Connection identifiers are derived from IDs. They cannot be set."); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.java new file mode 100644 index 000000000..8816fb69c --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.java @@ -0,0 +1,60 @@ +/* + * 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.connection; + +import java.util.List; +import org.apache.ibatis.annotations.Param; + +/** + * Mapper for connection record objects. + * + * @author Michael Jumper + */ +public interface ConnectionRecordMapper { + + /** + * Returns a collection of all connection records associated with the + * connection having the given identifier. + * + * @param identifier + * The identifier of the connection whose records are to be retrieved. + * + * @return + * A collection of all connection records associated with the + * connection having the given identifier. This collection will be + * empty if no such connection exists. + */ + List select(@Param("identifier") String identifier); + + /** + * Inserts the given connection record. + * + * @param record + * The connection record to insert. + * + * @return + * The number of rows inserted. + */ + int insert(@Param("record") ConnectionRecordModel record); + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordModel.java new file mode 100644 index 000000000..aa35fb27c --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordModel.java @@ -0,0 +1,170 @@ +/* + * 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.connection; + +import java.util.Date; + +/** + * A single connection record representing a past usage of a particular + * connection. + * + * @author Michael Jumper + */ +public class ConnectionRecordModel { + + /** + * The identifier of the connection associated with this connection record. + */ + private String connectionIdentifier; + + /** + * The database ID of the user associated with this connection record. + */ + private Integer userID; + + /** + * The username of the user associated with this connection record. + */ + private String username; + + /** + * The time the connection was initiated by the associated user. + */ + private Date startDate; + + /** + * The time the connection ended, or null if the end time is not known or + * the connection is still running. + */ + private Date endDate; + + /** + * Returns the identifier of the connection associated with this connection + * record. + * + * @return + * The identifier of the connection associated with this connection + * record. + */ + public String getConnectionIdentifier() { + return connectionIdentifier; + } + + /** + * Sets the identifier of the connection associated with this connection + * record. + * + * @param connectionIdentifier + * The identifier of the connection to associate with this connection + * record. + */ + public void setConnectionIdentifier(String connectionIdentifier) { + this.connectionIdentifier = connectionIdentifier; + } + + /** + * Returns the database ID of the user associated with this connection + * record. + * + * @return + * The database ID of the user associated with this connection record. + */ + public Integer getUserID() { + return userID; + } + + /** + * Sets the database ID of the user associated with this connection record. + * + * @param userID + * The database ID of the user to associate with this connection + * record. + */ + public void setUserID(Integer userID) { + this.userID = userID; + } + + /** + * Returns the username of the user associated with this connection record. + * + * @return + * The username of the user associated with this connection record. + */ + public String getUsername() { + return username; + } + + /** + * Sets the username of the user associated with this connection record. + * + * @param username + * The username of the user to associate with this connection record. + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * Returns the date that the associated connection was established. + * + * @return + * The date the associated connection was established. + */ + public Date getStartDate() { + return startDate; + } + + /** + * Sets the date that the associated connection was established. + * + * @param startDate + * The date that the associated connection was established. + */ + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + /** + * Returns the date that the associated connection ended, or null if no + * end date was recorded. The lack of an end date does not necessarily + * mean that the connection is still active. + * + * @return + * The date the associated connection ended, or null if no end date was + * recorded. + */ + public Date getEndDate() { + return endDate; + } + + /** + * Sets the date that the associated connection ended. + * + * @param endDate + * The date that the associated connection ended. + */ + public void setEndDate(Date endDate) { + this.endDate = endDate; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java new file mode 100644 index 000000000..f7d0b5ac5 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2013 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.connection; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectMapper; +import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectService; +import org.glyptodon.guacamole.auth.jdbc.socket.GuacamoleSocketService; +import org.glyptodon.guacamole.GuacamoleClientException; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionPermissionMapper; +import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionMapper; +import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.net.auth.Connection; +import org.glyptodon.guacamole.net.auth.ConnectionRecord; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; +import org.glyptodon.guacamole.net.auth.permission.SystemPermission; +import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; +import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; + +/** + * Service which provides convenience methods for creating, retrieving, and + * manipulating connections. + * + * @author Michael Jumper, James Muehlner + */ +public class ConnectionService extends DirectoryObjectService { + + /** + * Mapper for accessing connections. + */ + @Inject + private ConnectionMapper connectionMapper; + + /** + * Mapper for manipulating connection permissions. + */ + @Inject + private ConnectionPermissionMapper connectionPermissionMapper; + + /** + * Mapper for accessing connection parameters. + */ + @Inject + private ParameterMapper parameterMapper; + + /** + * Mapper for accessing connection history. + */ + @Inject + private ConnectionRecordMapper connectionRecordMapper; + + /** + * Provider for creating connections. + */ + @Inject + private Provider connectionProvider; + + /** + * Service for creating and tracking sockets. + */ + @Inject + private GuacamoleSocketService socketService; + + @Override + protected DirectoryObjectMapper getObjectMapper() { + return connectionMapper; + } + + @Override + protected ObjectPermissionMapper getPermissionMapper() { + return connectionPermissionMapper; + } + + @Override + protected ModeledConnection getObjectInstance(AuthenticatedUser currentUser, + ConnectionModel model) { + ModeledConnection connection = connectionProvider.get(); + connection.init(currentUser, model); + return connection; + } + + @Override + protected ConnectionModel getModelInstance(AuthenticatedUser currentUser, + final Connection object) { + + // Create new ModeledConnection backed by blank model + ConnectionModel model = new ConnectionModel(); + ModeledConnection connection = getObjectInstance(currentUser, model); + + // Set model contents through ModeledConnection, copying the provided connection + connection.setParentIdentifier(object.getParentIdentifier()); + connection.setName(object.getName()); + connection.setConfiguration(object.getConfiguration()); + + return model; + + } + + @Override + protected boolean hasCreatePermission(AuthenticatedUser user) + throws GuacamoleException { + + // Return whether user has explicit connection creation permission + SystemPermissionSet permissionSet = user.getUser().getSystemPermissions(); + return permissionSet.hasPermission(SystemPermission.Type.CREATE_CONNECTION); + + } + + @Override + protected ObjectPermissionSet getPermissionSet(AuthenticatedUser user) + throws GuacamoleException { + + // Return permissions related to connections + return user.getUser().getConnectionPermissions(); + + } + + @Override + protected void validateNewModel(AuthenticatedUser user, + ConnectionModel model) throws GuacamoleException { + + // Name must not be blank + if (model.getName().trim().isEmpty()) + throw new GuacamoleClientException("Connection names must not be blank."); + + // Do not attempt to create duplicate connections + ConnectionModel existing = connectionMapper.selectOneByName(model.getParentIdentifier(), model.getName()); + if (existing != null) + throw new GuacamoleClientException("The connection \"" + model.getName() + "\" already exists."); + + } + + @Override + protected void validateExistingModel(AuthenticatedUser user, + ConnectionModel model) throws GuacamoleException { + + // Name must not be blank + if (model.getName().trim().isEmpty()) + throw new GuacamoleClientException("Connection names must not be blank."); + + // Check whether such a connection is already present + ConnectionModel existing = connectionMapper.selectOneByName(model.getParentIdentifier(), model.getName()); + if (existing != null) { + + // If the specified name matches a DIFFERENT existing connection, the update cannot continue + if (!existing.getObjectID().equals(model.getObjectID())) + throw new GuacamoleClientException("The connection \"" + model.getName() + "\" already exists."); + + } + + } + + /** + * Given an arbitrary Guacamole connection, produces a collection of + * parameter model objects containing the name/value pairs of that + * connection's parameters. + * + * @param connection + * The connection whose configuration should be used to produce the + * collection of parameter models. + * + * @return + * A collection of parameter models containing the name/value pairs + * of the given connection's parameters. + */ + private Collection getParameterModels(ModeledConnection connection) { + + Map parameters = connection.getConfiguration().getParameters(); + + // Convert parameters to model objects + Collection parameterModels = new ArrayList(parameters.size()); + for (Map.Entry parameterEntry : parameters.entrySet()) { + + // Get parameter name and value + String name = parameterEntry.getKey(); + String value = parameterEntry.getValue(); + + // There is no need to insert empty parameters + if (value.isEmpty()) + continue; + + // Produce model object from parameter + ParameterModel model = new ParameterModel(); + model.setConnectionIdentifier(connection.getIdentifier()); + model.setName(name); + model.setValue(value); + + // Add model to list + parameterModels.add(model); + + } + + return parameterModels; + + } + + @Override + public ModeledConnection createObject(AuthenticatedUser user, Connection object) + throws GuacamoleException { + + // Create connection + ModeledConnection connection = super.createObject(user, object); + connection.setConfiguration(object.getConfiguration()); + + // Insert new parameters, if any + Collection parameterModels = getParameterModels(connection); + if (!parameterModels.isEmpty()) + parameterMapper.insert(parameterModels); + + return connection; + + } + + @Override + public void updateObject(AuthenticatedUser user, ModeledConnection object) + throws GuacamoleException { + + // Update connection + super.updateObject(user, object); + + // Replace existing parameters with new parameters, if any + Collection parameterModels = getParameterModels(object); + parameterMapper.delete(object.getIdentifier()); + if (!parameterModels.isEmpty()) + parameterMapper.insert(parameterModels); + + } + + /** + * Returns the set of all identifiers for all connections within the + * connection group having the given identifier. Only connections that the + * user has read access to will be returned. + * + * Permission to read the connection group having the given identifier is + * NOT checked. + * + * @param user + * The user retrieving the identifiers. + * + * @param identifier + * The identifier of the parent connection group, or null to check the + * root connection group. + * + * @return + * The set of all identifiers for all connections in the connection + * group having the given identifier that the user has read access to. + * + * @throws GuacamoleException + * If an error occurs while reading identifiers. + */ + public Set getIdentifiersWithin(AuthenticatedUser user, + String identifier) + throws GuacamoleException { + + // Bypass permission checks if the user is a system admin + if (user.getUser().isAdministrator()) + return connectionMapper.selectIdentifiersWithin(identifier); + + // Otherwise only return explicitly readable identifiers + else + return connectionMapper.selectReadableIdentifiersWithin(user.getUser().getModel(), identifier); + + } + + /** + * Retrieves all parameters visible to the given user and associated with + * the connection having the given identifier. If the given user has no + * access to such parameters, or no such connection exists, the returned + * map will be empty. + * + * @param user + * The user retrieving connection parameters. + * + * @param identifier + * The identifier of the connection whose parameters are being + * retrieved. + * + * @return + * A new map of all parameter name/value pairs that the given user has + * access to. + */ + public Map retrieveParameters(AuthenticatedUser user, + String identifier) { + + Map parameterMap = new HashMap(); + + // Determine whether we have permission to read parameters + boolean canRetrieveParameters; + try { + canRetrieveParameters = hasObjectPermission(user, identifier, + ObjectPermission.Type.UPDATE); + } + + // Provide empty (but mutable) map if unable to check permissions + catch (GuacamoleException e) { + return parameterMap; + } + + // Populate parameter map if we have permission to do so + if (canRetrieveParameters) { + for (ParameterModel parameter : parameterMapper.select(identifier)) + parameterMap.put(parameter.getName(), parameter.getValue()); + } + + return parameterMap; + + } + + /** + * Retrieves the connection history of the given connection, including any + * active connections. + * + * @param user + * The user retrieving the connection history. + * + * @param connection + * The connection whose history is being retrieved. + * + * @return + * The connection history of the given connection, including any + * active connections. + * + * @throws GuacamoleException + * If permission to read the connection history is denied. + */ + public List retrieveHistory(AuthenticatedUser user, + ModeledConnection connection) throws GuacamoleException { + + String identifier = connection.getIdentifier(); + + // Retrieve history only if READ permission is granted + if (hasObjectPermission(user, identifier, ObjectPermission.Type.READ)) { + + // Retrieve history + List models = connectionRecordMapper.select(identifier); + + // Get currently-active connections + List records = new ArrayList(socketService.getActiveConnections(connection)); + + // Add past connections from model objects + for (ConnectionRecordModel model : models) + records.add(new ModeledConnectionRecord(model)); + + // Return converted history list + return records; + + } + + // The user does not have permission to read the history + throw new GuacamoleSecurityException("Permission denied."); + + } + + /** + * Connects to the given connection as the given user, using the given + * client information. If the user does not have permission to read the + * connection, permission will be denied. + * + * @param user + * The user connecting to the connection. + * + * @param connection + * The connection being connected to. + * + * @param info + * Information associated with the connecting client. + * + * @return + * A connected GuacamoleSocket associated with a newly-established + * connection. + * + * @throws GuacamoleException + * If permission to connect to this connection is denied. + */ + public GuacamoleSocket connect(AuthenticatedUser user, + ModeledConnection connection, GuacamoleClientInformation info) + throws GuacamoleException { + + // Connect only if READ permission is granted + if (hasObjectPermission(user, connection.getIdentifier(), ObjectPermission.Type.READ)) + return socketService.getGuacamoleSocket(user, connection, info); + + // The user does not have permission to connect + throw new GuacamoleSecurityException("Permission denied."); + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnection.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnection.java new file mode 100644 index 000000000..64cb1232c --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnection.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2013 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.connection; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import java.util.List; +import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObject; +import org.glyptodon.guacamole.auth.jdbc.connectiongroup.RootConnectionGroup; +import org.glyptodon.guacamole.auth.jdbc.socket.GuacamoleSocketService; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.net.auth.Connection; +import org.glyptodon.guacamole.net.auth.ConnectionRecord; +import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; +import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; + +/** + * An implementation of the Connection object which is backed by a database + * model. + * + * @author James Muehlner + * @author Michael Jumper + */ +public class ModeledConnection extends DirectoryObject + implements Connection { + + /** + * Service for managing connections. + */ + @Inject + private ConnectionService connectionService; + + /** + * Service for creating and tracking sockets. + */ + @Inject + private GuacamoleSocketService socketService; + + /** + * Provider for lazy-loaded, permission-controlled configurations. + */ + @Inject + private Provider configProvider; + + /** + * The manually-set GuacamoleConfiguration, if any. + */ + private GuacamoleConfiguration config = null; + + /** + * Creates a new, empty ModeledConnection. + */ + public ModeledConnection() { + } + + @Override + public String getName() { + return getModel().getName(); + } + + @Override + public void setName(String name) { + getModel().setName(name); + } + + @Override + public String getParentIdentifier() { + + // Translate null parent to proper identifier + String parentIdentifier = getModel().getParentIdentifier(); + if (parentIdentifier == null) + return RootConnectionGroup.IDENTIFIER; + + return parentIdentifier; + + } + + @Override + public void setParentIdentifier(String parentIdentifier) { + + // Translate root identifier back into null + if (parentIdentifier != null + && parentIdentifier.equals(RootConnectionGroup.IDENTIFIER)) + parentIdentifier = null; + + getModel().setParentIdentifier(parentIdentifier); + + } + + @Override + public GuacamoleConfiguration getConfiguration() { + + // If configuration has been manually set, return that + if (config != null) + return config; + + // Otherwise, return permission-controlled configuration + ModeledGuacamoleConfiguration restrictedConfig = configProvider.get(); + restrictedConfig.init(getCurrentUser(), getModel()); + return restrictedConfig; + + } + + @Override + public void setConfiguration(GuacamoleConfiguration config) { + + // Store manually-set configuration internally + this.config = config; + + // Update model + getModel().setProtocol(config.getProtocol()); + + } + + @Override + public List getHistory() throws GuacamoleException { + return connectionService.retrieveHistory(getCurrentUser(), this); + } + + @Override + public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException { + return connectionService.connect(getCurrentUser(), this, info); + } + + @Override + public int getActiveConnections() { + return socketService.getActiveConnections(this).size(); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java new file mode 100644 index 000000000..c86e46588 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2013 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.connection; + + +import java.util.Date; +import org.glyptodon.guacamole.net.auth.ConnectionRecord; + +/** + * A ConnectionRecord which is backed by a database model. + * + * @author James Muehlner + * @author Michael Jumper + */ +public class ModeledConnectionRecord implements ConnectionRecord { + + /** + * The model object backing this connection record. + */ + private final ConnectionRecordModel model; + + /** + * Creates a new ModeledConnectionRecord backed by the given model object. + * Changes to this record will affect the backing model object, and changes + * to the backing model object will affect this record. + * + * @param model + * The model object to use to back this connection record. + */ + public ModeledConnectionRecord(ConnectionRecordModel model) { + this.model = model; + } + + @Override + public Date getStartDate() { + return model.getStartDate(); + } + + @Override + public Date getEndDate() { + return model.getEndDate(); + } + + @Override + public String getUsername() { + return model.getUsername(); + } + + @Override + public boolean isActive() { + return false; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledGuacamoleConfiguration.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledGuacamoleConfiguration.java new file mode 100644 index 000000000..b88a936e7 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledGuacamoleConfiguration.java @@ -0,0 +1,120 @@ +/* + * 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.connection; + +import com.google.inject.Inject; +import java.util.Map; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; + +/** + * Implementation of GuacamoleConfiguration which loads parameter values only + * if necessary, and only if allowed. + * + * @author Michael Jumper + */ +public class ModeledGuacamoleConfiguration extends GuacamoleConfiguration { + + /** + * The user this configuration belongs to. Access is based on his/her + * permission settings. + */ + private AuthenticatedUser currentUser; + + /** + * The internal model object containing the values which represent the + * connection associated with this configuration. + */ + private ConnectionModel connectionModel; + + /** + * Service for managing connection parameters. + */ + @Inject + private ConnectionService connectionService; + + /** + * The manually-set parameter map, if any. + */ + private Map parameters = null; + + /** + * Creates a new, empty ModelGuacamoleConfiguration. + */ + public ModeledGuacamoleConfiguration() { + } + + /** + * Initializes this configuration, associating it with the current + * authenticated user and populating it with data from the given model + * object. + * + * @param currentUser + * The user that created or retrieved this configuration. + * + * @param connectionModel + * The model object backing this configuration. + */ + public void init(AuthenticatedUser currentUser, ConnectionModel connectionModel) { + this.currentUser = currentUser; + this.connectionModel = connectionModel; + } + + @Override + public String getProtocol() { + return connectionModel.getProtocol(); + } + + @Override + public void setProtocol(String protocol) { + super.setProtocol(protocol); + connectionModel.setProtocol(protocol); + } + + + @Override + public void setParameters(Map parameters) { + this.parameters = parameters; + super.setParameters(parameters); + } + + @Override + public Map getParameters() { + + // Retrieve visible parameters, if not overridden by setParameters() + if (parameters == null) { + + // Retrieve all visible parameters + Map visibleParameters = + connectionService.retrieveParameters(currentUser, connectionModel.getIdentifier()); + + // Use retrieved parameters to back future operations + super.setParameters(visibleParameters); + + } + + return super.getParameters(); + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ParameterMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ParameterMapper.java new file mode 100644 index 000000000..b8bb26fb5 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ParameterMapper.java @@ -0,0 +1,75 @@ +/* + * 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.connection; + +import java.util.Collection; +import org.apache.ibatis.annotations.Param; + +/** + * Mapper for connection parameter objects. + * + * @author Michael Jumper + */ +public interface ParameterMapper { + + /** + * Returns a collection of all parameters associated with the connection + * having the given identifier. + * + * @param identifier + * The identifier of the connection whose parameters are to be + * retrieved. + * + * @return + * A collection of all parameters associated with the connection + * having the given identifier. This collection will be empty if no + * such connection exists. + */ + Collection select(@Param("identifier") String identifier); + + /** + * Inserts each of the parameter model objects in the given collection as + * new connection parameters. + * + * @param parameters + * The connection parameters to insert. + * + * @return + * The number of rows inserted. + */ + int insert(@Param("parameters") Collection parameters); + + /** + * Deletes all parameters associated with the connection having the given + * identifier. + * + * @param identifier + * The identifier of the connection whose parameters should be + * deleted. + * + * @return + * The number of rows deleted. + */ + int delete(@Param("identifier") String identifier); + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ParameterModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ParameterModel.java new file mode 100644 index 000000000..103bdae49 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ParameterModel.java @@ -0,0 +1,107 @@ +/* + * 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.connection; + +/** + * A single parameter name/value pair belonging to a connection. + * + * @author Michael Jumper + */ +public class ParameterModel { + + /** + * The identifier of the connection associated with this parameter. + */ + private String connectionIdentifier; + + /** + * The name of the parameter. + */ + private String name; + + /** + * The value the parameter is set to. + */ + private String value; + + /** + * Returns the identifier of the connection associated with this parameter. + * + * @return + * The identifier of the connection associated with this parameter. + */ + public String getConnectionIdentifier() { + return connectionIdentifier; + } + + /** + * Sets the identifier of the connection associated with this parameter. + * + * @param connectionIdentifier + * The identifier of the connection to associate with this parameter. + */ + public void setConnectionIdentifier(String connectionIdentifier) { + this.connectionIdentifier = connectionIdentifier; + } + + /** + * Returns the name of this parameter. + * + * @return + * The name of this parameter. + */ + public String getName() { + return name; + } + + /** + * Sets the name of this parameter. + * + * @param name + * The name of this parameter. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns the value of this parameter. + * + * @return + * The value of this parameter. + */ + public String getValue() { + return value; + } + + /** + * Sets the value of this parameter. + * + * @param value + * The value of this parameter. + */ + public void setValue(String value) { + this.value = value; + } + +} diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/package-info.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/package-info.java similarity index 85% rename from extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/package-info.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/package-info.java index d665bbc7d..6507c59b9 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/package-info.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Glyptodon LLC + * 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 @@ -21,8 +21,6 @@ */ /** - * Service classes which help fill the needs of the MySQL authentication - * provider. + * Classes related to connections and their parameters and history. */ -package net.sourceforge.guacamole.net.auth.mysql.service; - +package org.glyptodon.guacamole.auth.jdbc.connection; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupDirectory.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupDirectory.java new file mode 100644 index 000000000..6f76dd7c6 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupDirectory.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2013 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.connectiongroup; + + +import com.google.inject.Inject; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject; +import org.glyptodon.guacamole.net.auth.ConnectionGroup; +import org.glyptodon.guacamole.net.auth.Directory; +import org.mybatis.guice.transactional.Transactional; + +/** + * Implementation of the ConnectionGroup Directory which is driven by an + * underlying, arbitrary database. + * + * @author James Muehlner + * @author Michael Jumper + */ +public class ConnectionGroupDirectory extends RestrictedObject + implements Directory { + + /** + * Service for managing connection group objects. + */ + @Inject + private ConnectionGroupService connectionGroupService; + + @Override + public ConnectionGroup get(String identifier) throws GuacamoleException { + return connectionGroupService.retrieveObject(getCurrentUser(), identifier); + } + + @Override + @Transactional + public Collection getAll(Collection identifiers) throws GuacamoleException { + Collection objects = connectionGroupService.retrieveObjects(getCurrentUser(), identifiers); + return Collections.unmodifiableCollection(objects); + } + + @Override + @Transactional + public Set getIdentifiers() throws GuacamoleException { + return connectionGroupService.getIdentifiers(getCurrentUser()); + } + + @Override + @Transactional + public void add(ConnectionGroup object) throws GuacamoleException { + connectionGroupService.createObject(getCurrentUser(), object); + } + + @Override + @Transactional + public void update(ConnectionGroup object) throws GuacamoleException { + ModeledConnectionGroup connectionGroup = (ModeledConnectionGroup) object; + connectionGroupService.updateObject(getCurrentUser(), connectionGroup); + } + + @Override + @Transactional + public void remove(String identifier) throws GuacamoleException { + connectionGroupService.deleteObject(getCurrentUser(), identifier); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupMapper.java new file mode 100644 index 000000000..a08ef7c20 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupMapper.java @@ -0,0 +1,92 @@ +/* + * 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.connectiongroup; + +import java.util.Set; +import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectMapper; +import org.glyptodon.guacamole.auth.jdbc.user.UserModel; +import org.apache.ibatis.annotations.Param; + +/** + * Mapper for connection group objects. + * + * @author Michael Jumper + */ +public interface ConnectionGroupMapper extends DirectoryObjectMapper { + + /** + * Selects the identifiers of all connection groups within the given parent + * connection group, regardless of whether they are readable by any + * particular user. This should only be called on behalf of a system + * administrator. If identifiers are needed by a non-administrative user + * who must have explicit read rights, use + * selectReadableIdentifiersWithin() instead. + * + * @param parentIdentifier + * The identifier of the parent connection group, or null if the root + * connection group is to be queried. + * + * @return + * A Set containing all identifiers of all objects. + */ + Set selectIdentifiersWithin(@Param("parentIdentifier") String parentIdentifier); + + /** + * Selects the identifiers of all connection groups within the given parent + * connection group that are explicitly readable by the given user. If + * identifiers are needed by a system administrator (who, by definition, + * does not need explicit read rights), use selectIdentifiersWithin() + * instead. + * + * @param user + * The user whose permissions should determine whether an identifier + * is returned. + * + * @param parentIdentifier + * The identifier of the parent connection group, or null if the root + * connection group is to be queried. + * + * @return + * A Set containing all identifiers of all readable objects. + */ + Set selectReadableIdentifiersWithin(@Param("user") UserModel user, + @Param("parentIdentifier") String parentIdentifier); + + /** + * Selects the connection group within the given parent group and having + * the given name. If no such connection group exists, null is returned. + * + * @param parentIdentifier + * The identifier of the parent group to search within. + * + * @param name + * The name of the connection group to find. + * + * @return + * The connection group having the given name within the given parent + * group, or null if no such connection group exists. + */ + ConnectionGroupModel selectOneByName(@Param("parentIdentifier") String parentIdentifier, + @Param("name") String name); + +} \ No newline at end of file diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupModel.java new file mode 100644 index 000000000..68845df9c --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupModel.java @@ -0,0 +1,141 @@ +/* + * 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.connectiongroup; + +import org.glyptodon.guacamole.auth.jdbc.base.ObjectModel; +import org.glyptodon.guacamole.net.auth.ConnectionGroup; + +/** + * Object representation of a Guacamole connection group, as represented in the + * database. + * + * @author Michael Jumper + */ +public class ConnectionGroupModel extends ObjectModel { + + /** + * The identifier of the parent connection group in the database, or null + * if the parent connection group is the root group. + */ + private String parentIdentifier; + + /** + * The human-readable name associated with this connection group. + */ + private String name; + + /** + * The type of this connection group, such as organizational or balancing. + */ + private ConnectionGroup.Type type; + + /** + * Creates a new, empty connection group. + */ + public ConnectionGroupModel() { + } + + /** + * Returns the name associated with this connection group. + * + * @return + * The name associated with this connection group. + */ + public String getName() { + return name; + } + + /** + * Sets the name associated with this connection group. + * + * @param name + * The name to associate with this connection group. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns the identifier of the parent connection group, or null if the + * parent connection group is the root connection group. + * + * @return + * The identifier of the parent connection group, or null if the parent + * connection group is the root connection group. + */ + public String getParentIdentifier() { + return parentIdentifier; + } + + /** + * Sets the identifier of the parent connection group. + * + * @param parentIdentifier + * The identifier of the parent connection group, or null if the parent + * connection group is the root connection group. + */ + public void setParentIdentifier(String parentIdentifier) { + this.parentIdentifier = parentIdentifier; + } + + /** + * Returns the type of this connection group, such as organizational or + * balancing. + * + * @return + * The type of this connection group. + */ + public ConnectionGroup.Type getType() { + return type; + } + + /** + * Sets the type of this connection group, such as organizational or + * balancing. + * + * @param type + * The type of this connection group. + */ + public void setType(ConnectionGroup.Type type) { + this.type = type; + } + + @Override + public String getIdentifier() { + + // If no associated ID, then no associated identifier + Integer id = getObjectID(); + if (id == null) + return null; + + // Otherwise, the identifier is the ID as a string + return id.toString(); + + } + + @Override + public void setIdentifier(String identifier) { + throw new UnsupportedOperationException("Connection group identifiers are derived from IDs. They cannot be set."); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java new file mode 100644 index 000000000..cfed2edd0 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2013 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.connectiongroup; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import java.util.Set; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectMapper; +import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectService; +import org.glyptodon.guacamole.auth.jdbc.socket.GuacamoleSocketService; +import org.glyptodon.guacamole.GuacamoleClientException; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionGroupPermissionMapper; +import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionMapper; +import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.net.auth.ConnectionGroup; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; +import org.glyptodon.guacamole.net.auth.permission.SystemPermission; +import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; +import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; + +/** + * Service which provides convenience methods for creating, retrieving, and + * manipulating connection groups. + * + * @author Michael Jumper, James Muehlner + */ +public class ConnectionGroupService extends DirectoryObjectService { + + /** + * Mapper for accessing connection groups. + */ + @Inject + private ConnectionGroupMapper connectionGroupMapper; + + /** + * Mapper for manipulating connection group permissions. + */ + @Inject + private ConnectionGroupPermissionMapper connectionGroupPermissionMapper; + + /** + * Provider for creating connection groups. + */ + @Inject + private Provider connectionGroupProvider; + + /** + * Service for creating and tracking sockets. + */ + @Inject + private GuacamoleSocketService socketService; + + @Override + protected DirectoryObjectMapper getObjectMapper() { + return connectionGroupMapper; + } + + @Override + protected ObjectPermissionMapper getPermissionMapper() { + return connectionGroupPermissionMapper; + } + + @Override + protected ModeledConnectionGroup getObjectInstance(AuthenticatedUser currentUser, + ConnectionGroupModel model) { + ModeledConnectionGroup connectionGroup = connectionGroupProvider.get(); + connectionGroup.init(currentUser, model); + return connectionGroup; + } + + @Override + protected ConnectionGroupModel getModelInstance(AuthenticatedUser currentUser, + final ConnectionGroup object) { + + // Create new ModeledConnectionGroup backed by blank model + ConnectionGroupModel model = new ConnectionGroupModel(); + ModeledConnectionGroup connectionGroup = getObjectInstance(currentUser, model); + + // Set model contents through ModeledConnectionGroup, copying the provided connection group + connectionGroup.setParentIdentifier(object.getParentIdentifier()); + connectionGroup.setName(object.getName()); + connectionGroup.setType(object.getType()); + + return model; + + } + + @Override + protected boolean hasCreatePermission(AuthenticatedUser user) + throws GuacamoleException { + + // Return whether user has explicit connection group creation permission + SystemPermissionSet permissionSet = user.getUser().getSystemPermissions(); + return permissionSet.hasPermission(SystemPermission.Type.CREATE_CONNECTION_GROUP); + + } + + @Override + protected ObjectPermissionSet getPermissionSet(AuthenticatedUser user) + throws GuacamoleException { + + // Return permissions related to connection groups + return user.getUser().getConnectionGroupPermissions(); + + } + + @Override + protected void validateNewModel(AuthenticatedUser user, + ConnectionGroupModel model) throws GuacamoleException { + + // Name must not be blank + if (model.getName().trim().isEmpty()) + throw new GuacamoleClientException("Connection group names must not be blank."); + + // Do not attempt to create duplicate connection groups + ConnectionGroupModel existing = connectionGroupMapper.selectOneByName(model.getParentIdentifier(), model.getName()); + if (existing != null) + throw new GuacamoleClientException("The connection group \"" + model.getName() + "\" already exists."); + + } + + @Override + protected void validateExistingModel(AuthenticatedUser user, + ConnectionGroupModel model) throws GuacamoleException { + + // Name must not be blank + if (model.getName().trim().isEmpty()) + throw new GuacamoleClientException("Connection group names must not be blank."); + + // Check whether such a connection group is already present + ConnectionGroupModel existing = connectionGroupMapper.selectOneByName(model.getParentIdentifier(), model.getName()); + if (existing != null) { + + // If the specified name matches a DIFFERENT existing connection group, the update cannot continue + if (!existing.getObjectID().equals(model.getObjectID())) + throw new GuacamoleClientException("The connection group \"" + model.getName() + "\" already exists."); + + } + + } + + /** + * Returns the set of all identifiers for all connection groups within the + * connection group having the given identifier. Only connection groups + * that the user has read access to will be returned. + * + * Permission to read the connection group having the given identifier is + * NOT checked. + * + * @param user + * The user retrieving the identifiers. + * + * @param identifier + * The identifier of the parent connection group, or null to check the + * root connection group. + * + * @return + * The set of all identifiers for all connection groups in the + * connection group having the given identifier that the user has read + * access to. + * + * @throws GuacamoleException + * If an error occurs while reading identifiers. + */ + public Set getIdentifiersWithin(AuthenticatedUser user, + String identifier) + throws GuacamoleException { + + // Bypass permission checks if the user is a system admin + if (user.getUser().isAdministrator()) + return connectionGroupMapper.selectIdentifiersWithin(identifier); + + // Otherwise only return explicitly readable identifiers + else + return connectionGroupMapper.selectReadableIdentifiersWithin(user.getUser().getModel(), identifier); + + } + + /** + * Connects to the given connection group as the given user, using the + * given client information. If the user does not have permission to read + * the connection group, permission will be denied. + * + * @param user + * The user connecting to the connection group. + * + * @param connectionGroup + * The connectionGroup being connected to. + * + * @param info + * Information associated with the connecting client. + * + * @return + * A connected GuacamoleSocket associated with a newly-established + * connection. + * + * @throws GuacamoleException + * If permission to connect to this connection is denied. + */ + public GuacamoleSocket connect(AuthenticatedUser user, + ModeledConnectionGroup connectionGroup, GuacamoleClientInformation info) + throws GuacamoleException { + + // Connect only if READ permission is granted + if (hasObjectPermission(user, connectionGroup.getIdentifier(), ObjectPermission.Type.READ)) + return socketService.getGuacamoleSocket(user, connectionGroup, info); + + // The user does not have permission to connect + throw new GuacamoleSecurityException("Permission denied."); + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ModeledConnectionGroup.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ModeledConnectionGroup.java new file mode 100644 index 000000000..cad15286c --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ModeledConnectionGroup.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2013 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.connectiongroup; + +import com.google.inject.Inject; +import java.util.Set; +import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObject; +import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionService; +import org.glyptodon.guacamole.auth.jdbc.socket.GuacamoleSocketService; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.net.auth.ConnectionGroup; +import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; + +/** + * An implementation of the ConnectionGroup object which is backed by a + * database model. + * + * @author James Muehlner + */ +public class ModeledConnectionGroup extends DirectoryObject + implements ConnectionGroup { + + /** + * Service for managing connections. + */ + @Inject + private ConnectionService connectionService; + + /** + * Service for managing connection groups. + */ + @Inject + private ConnectionGroupService connectionGroupService; + + /** + * Service for creating and tracking sockets. + */ + @Inject + private GuacamoleSocketService socketService; + + /** + * Creates a new, empty ModeledConnectionGroup. + */ + public ModeledConnectionGroup() { + } + + @Override + public String getName() { + return getModel().getName(); + } + + @Override + public void setName(String name) { + getModel().setName(name); + } + + @Override + public String getParentIdentifier() { + + // Translate null parent to proper identifier + String parentIdentifier = getModel().getParentIdentifier(); + if (parentIdentifier == null) + return RootConnectionGroup.IDENTIFIER; + + return parentIdentifier; + + } + + @Override + public void setParentIdentifier(String parentIdentifier) { + + // Translate root identifier back into null + if (parentIdentifier != null + && parentIdentifier.equals(RootConnectionGroup.IDENTIFIER)) + parentIdentifier = null; + + getModel().setParentIdentifier(parentIdentifier); + + } + + @Override + public GuacamoleSocket connect(GuacamoleClientInformation info) + throws GuacamoleException { + return connectionGroupService.connect(getCurrentUser(), this, info); + } + + @Override + public int getActiveConnections() { + return socketService.getActiveConnections(this).size(); + } + + @Override + public void setType(Type type) { + getModel().setType(type); + } + + @Override + public Type getType() { + return getModel().getType(); + } + + @Override + public Set getConnectionIdentifiers() + throws GuacamoleException { + return connectionService.getIdentifiersWithin(getCurrentUser(), getIdentifier()); + } + + @Override + public Set getConnectionGroupIdentifiers() + throws GuacamoleException { + return connectionGroupService.getIdentifiersWithin(getCurrentUser(), getIdentifier()); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/RootConnectionGroup.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/RootConnectionGroup.java new file mode 100644 index 000000000..93b61275b --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/RootConnectionGroup.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2013 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.connectiongroup; + +import com.google.inject.Inject; +import java.util.Set; +import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionService; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject; +import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.net.auth.ConnectionGroup; +import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; + +/** + * The root connection group, here represented as its own dedicated object as + * the database does not contain an actual root group. + * + * @author Michael Jumper + */ +public class RootConnectionGroup extends RestrictedObject + implements ConnectionGroup { + + /** + * The identifier used to represent the root connection group. There is no + * corresponding entry in the database, thus a reserved identifier that + * cannot collide with database-generated identifiers is needed. + */ + public static final String IDENTIFIER = "ROOT"; + + /** + * The human-readable name of this connection group. The name of the root + * group is not normally visible, and may even be replaced by the web + * interface for the sake of translation. + */ + public static final String NAME = "ROOT"; + + /** + * Service for managing connection objects. + */ + @Inject + private ConnectionService connectionService; + + /** + * Service for managing connection group objects. + */ + @Inject + private ConnectionGroupService connectionGroupService; + + /** + * Creates a new, empty RootConnectionGroup. + */ + public RootConnectionGroup() { + } + + @Override + public String getName() { + return NAME; + } + + @Override + public void setName(String name) { + throw new UnsupportedOperationException("The root connection group cannot be modified."); + } + + @Override + public String getParentIdentifier() { + return null; + } + + @Override + public void setParentIdentifier(String parentIdentifier) { + throw new UnsupportedOperationException("The root connection group cannot be modified."); + } + + @Override + public Type getType() { + return ConnectionGroup.Type.ORGANIZATIONAL; + } + + @Override + public void setType(Type type) { + throw new UnsupportedOperationException("The root connection group cannot be modified."); + } + + @Override + public Set getConnectionIdentifiers() throws GuacamoleException { + return connectionService.getIdentifiersWithin(getCurrentUser(), null); + } + + @Override + public Set getConnectionGroupIdentifiers() + throws GuacamoleException { + return connectionGroupService.getIdentifiersWithin(getCurrentUser(), null); + } + + @Override + public String getIdentifier() { + return IDENTIFIER; + } + + @Override + public void setIdentifier(String identifier) { + throw new UnsupportedOperationException("The root connection group cannot be modified."); + } + + @Override + public GuacamoleSocket connect(GuacamoleClientInformation info) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public int getActiveConnections() { + return 0; + } + +} diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/properties/package-info.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/package-info.java similarity index 85% rename from extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/properties/package-info.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/package-info.java index cf3a32372..a1d0bd250 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/properties/package-info.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Glyptodon LLC + * 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 @@ -21,8 +21,6 @@ */ /** - * Properties which control the configuration of the MySQL authentication - * provider. + * Classes related to connection groups. */ -package net.sourceforge.guacamole.net.auth.mysql.properties; - +package org.glyptodon.guacamole.auth.jdbc.connectiongroup; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/package-info.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/package-info.java new file mode 100644 index 000000000..e4d12fca9 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2013 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. + */ + +/** + * The base JDBC authentication provider. This authentication provider serves + * as a basis for other JDBC authentication provider implementations which are + * driven by relatively-common schemas. The only difference between such + * implementations are maintained within database-specific MyBatis mappings. + */ +package org.glyptodon.guacamole.auth.jdbc; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionMapper.java new file mode 100644 index 000000000..cc791d628 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionMapper.java @@ -0,0 +1,30 @@ +/* + * 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.permission; + +/** + * Mapper for connection group permissions. + * + * @author Michael Jumper + */ +public interface ConnectionGroupPermissionMapper extends ObjectPermissionMapper {} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionService.java new file mode 100644 index 000000000..e64d6c71f --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionService.java @@ -0,0 +1,69 @@ +/* + * 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.permission; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.auth.jdbc.user.ModeledUser; + +/** + * Service which provides convenience methods for creating, retrieving, and + * deleting connection group permissions. This service will automatically + * enforce the permissions of the current user. + * + * @author Michael Jumper + */ +public class ConnectionGroupPermissionService extends ObjectPermissionService { + + /** + * Mapper for connection group permissions. + */ + @Inject + private ConnectionGroupPermissionMapper connectionGroupPermissionMapper; + + /** + * Provider for connection group permission sets. + */ + @Inject + private Provider connectionGroupPermissionSetProvider; + + @Override + protected ObjectPermissionMapper getPermissionMapper() { + return connectionGroupPermissionMapper; + } + + @Override + public ObjectPermissionSet getPermissionSet(AuthenticatedUser user, + ModeledUser targetUser) throws GuacamoleException { + + // Create permission set for requested user + ObjectPermissionSet permissionSet = connectionGroupPermissionSetProvider.get(); + permissionSet.init(user, targetUser); + + return permissionSet; + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionSet.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionSet.java new file mode 100644 index 000000000..5f057dc2d --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionSet.java @@ -0,0 +1,47 @@ +/* + * 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.permission; + +import com.google.inject.Inject; + +/** + * A database implementation of ObjectPermissionSet which uses an injected + * service to query and manipulate the connection group permissions associated + * with a particular user. + * + * @author Michael Jumper + */ +public class ConnectionGroupPermissionSet extends ObjectPermissionSet { + + /** + * Service for querying and manipulating connection group permissions. + */ + @Inject + private ConnectionGroupPermissionService connectionGroupPermissionService; + + @Override + protected ObjectPermissionService getObjectPermissionService() { + return connectionGroupPermissionService; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionMapper.java new file mode 100644 index 000000000..5dfdd07cd --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionMapper.java @@ -0,0 +1,30 @@ +/* + * 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.permission; + +/** + * Mapper for connection permissions. + * + * @author Michael Jumper + */ +public interface ConnectionPermissionMapper extends ObjectPermissionMapper {} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionService.java new file mode 100644 index 000000000..0903f71ab --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionService.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2013 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.permission; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.auth.jdbc.user.ModeledUser; + +/** + * Service which provides convenience methods for creating, retrieving, and + * deleting connection permissions. This service will automatically enforce the + * permissions of the current user. + * + * @author Michael Jumper + */ +public class ConnectionPermissionService extends ObjectPermissionService { + + /** + * Mapper for connection permissions. + */ + @Inject + private ConnectionPermissionMapper connectionPermissionMapper; + + /** + * Provider for connection permission sets. + */ + @Inject + private Provider connectionPermissionSetProvider; + + @Override + protected ObjectPermissionMapper getPermissionMapper() { + return connectionPermissionMapper; + } + + @Override + public ObjectPermissionSet getPermissionSet(AuthenticatedUser user, + ModeledUser targetUser) throws GuacamoleException { + + // Create permission set for requested user + ObjectPermissionSet permissionSet = connectionPermissionSetProvider.get(); + permissionSet.init(user, targetUser); + + return permissionSet; + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionSet.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionSet.java new file mode 100644 index 000000000..6ed57ebbd --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionSet.java @@ -0,0 +1,47 @@ +/* + * 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.permission; + +import com.google.inject.Inject; + +/** + * A database implementation of ObjectPermissionSet which uses an injected + * service to query and manipulate the connection permissions associated with + * a particular user. + * + * @author Michael Jumper + */ +public class ConnectionPermissionSet extends ObjectPermissionSet { + + /** + * Service for querying and manipulating connection permissions. + */ + @Inject + private ConnectionPermissionService connectionPermissionService; + + @Override + protected ObjectPermissionService getObjectPermissionService() { + return connectionPermissionService; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ObjectPermissionMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ObjectPermissionMapper.java new file mode 100644 index 000000000..fcd54b17f --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ObjectPermissionMapper.java @@ -0,0 +1,83 @@ +/* + * 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.permission; + +import java.util.Collection; +import org.apache.ibatis.annotations.Param; +import org.glyptodon.guacamole.auth.jdbc.user.UserModel; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; + +/** + * Mapper for object-related permissions. + * + * @author Michael Jumper + */ +public interface ObjectPermissionMapper extends PermissionMapper { + + /** + * Retrieve the permission of the given type associated with the given + * user and object, if it exists. If no such permission exists, null is + * returned. + * + * @param user + * The user to retrieve permissions for. + * + * @param type + * The type of permission to return. + * + * @param identifier + * The identifier of the object affected by the permission to return. + * + * @return + * The requested permission, or null if no such permission is granted + * to the given user for the given object. + */ + ObjectPermissionModel selectOne(@Param("user") UserModel user, + @Param("type") ObjectPermission.Type type, + @Param("identifier") String identifier); + + /** + * Retrieves the subset of the given identifiers for which the given user + * has at least one of the given permissions. + * + * @param user + * The user to check permissions of. + * + * @param permissions + * The permissions to check. An identifier will be included in the + * resulting collection if at least one of these permissions is granted + * for the associated object + * + * @param identifiers + * The identifiers of the objects affected by the permissions being + * checked. + * + * @return + * A collection containing the subset of identifiers for which at least + * one of the specified permissions is granted. + */ + Collection selectAccessibleIdentifiers(@Param("user") UserModel user, + @Param("permissions") Collection permissions, + @Param("identifiers") Collection identifiers); + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ObjectPermissionModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ObjectPermissionModel.java new file mode 100644 index 000000000..0a00081ab --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ObjectPermissionModel.java @@ -0,0 +1,66 @@ +/* + * 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.permission; + +import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; + +/** + * Object representation of an object-related Guacamole permission, as + * represented in the database. + * + * @author Michael Jumper + */ +public class ObjectPermissionModel extends PermissionModel { + + /** + * The unique identifier of the object affected by this permission. + */ + private String objectIdentifier; + + /** + * Creates a new, empty object permission. + */ + public ObjectPermissionModel() { + } + + /** + * Returns the unique identifier of the object affected by this permission. + * + * @return + * The unique identifier of the object affected by this permission. + */ + public String getObjectIdentifier() { + return objectIdentifier; + } + + /** + * Sets the unique identifier of the object affected by this permission. + * + * @param objectIdentifier + * The unique identifier of the object affected by this permission. + */ + public void setObjectIdentifier(String objectIdentifier) { + this.objectIdentifier = objectIdentifier; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ObjectPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ObjectPermissionService.java new file mode 100644 index 000000000..79ce6407e --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ObjectPermissionService.java @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2013 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.permission; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.auth.jdbc.user.ModeledUser; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; + +/** + * Service which provides convenience methods for creating, retrieving, and + * deleting object permissions. This service will automatically enforce the + * permissions of the current user. + * + * @author Michael Jumper + */ +public abstract class ObjectPermissionService + extends PermissionService { + + @Override + protected abstract ObjectPermissionMapper getPermissionMapper(); + + @Override + protected ObjectPermission getPermissionInstance(ObjectPermissionModel model) { + return new ObjectPermission(model.getType(), model.getObjectIdentifier()); + } + + @Override + protected ObjectPermissionModel getModelInstance(ModeledUser targetUser, + ObjectPermission permission) { + + ObjectPermissionModel model = new ObjectPermissionModel(); + + // Populate model object with data from user and permission + model.setUserID(targetUser.getModel().getObjectID()); + model.setUsername(targetUser.getModel().getIdentifier()); + model.setType(permission.getType()); + model.setObjectIdentifier(permission.getObjectIdentifier()); + + return model; + + } + + /** + * Determines whether the current user has permission to update the given + * target user, adding or removing the given permissions. Such permission + * depends on whether the current user is a system administrator, whether + * they have explicit UPDATE permission on the target user, and whether + * they have explicit ADMINISTER permission on all affected objects. + * + * @param user + * The user who is changing permissions. + * + * @param targetUser + * The user whose permissions are being changed. + * + * @param permissions + * The permissions that are being added or removed from the target + * user. + * + * @return + * true if the user has permission to change the target users + * permissions as specified, false otherwise. + * + * @throws GuacamoleException + * If an error occurs while checking permission status, or if + * permission is denied to read the current user's permissions. + */ + protected boolean canAlterPermissions(AuthenticatedUser user, ModeledUser targetUser, + Collection permissions) + throws GuacamoleException { + + // A system adminstrator can do anything + if (user.getUser().isAdministrator()) + return true; + + // Verify user has update permission on the target user + ObjectPermissionSet userPermissionSet = user.getUser().getUserPermissions(); + if (!userPermissionSet.hasPermission(ObjectPermission.Type.UPDATE, targetUser.getIdentifier())) + return false; + + // Produce collection of affected identifiers + Collection affectedIdentifiers = new HashSet(permissions.size()); + for (ObjectPermission permission : permissions) + affectedIdentifiers.add(permission.getObjectIdentifier()); + + // Determine subset of affected identifiers that we have admin access to + ObjectPermissionSet affectedPermissionSet = getPermissionSet(user, user.getUser()); + Collection allowedSubset = affectedPermissionSet.getAccessibleObjects( + Collections.singleton(ObjectPermission.Type.ADMINISTER), + affectedIdentifiers + ); + + // The permissions can be altered if and only if the set of objects we + // are allowed to administer is equal to the set of objects we will be + // affecting. + + return affectedIdentifiers.size() == allowedSubset.size(); + + } + + @Override + public void createPermissions(AuthenticatedUser user, ModeledUser targetUser, + Collection permissions) + throws GuacamoleException { + + // Create permissions only if user has permission to do so + if (canAlterPermissions(user, targetUser, permissions)) { + Collection models = getModelInstances(targetUser, permissions); + getPermissionMapper().insert(models); + return; + } + + // User lacks permission to create object permissions + throw new GuacamoleSecurityException("Permission denied."); + + } + + @Override + public void deletePermissions(AuthenticatedUser user, ModeledUser targetUser, + Collection permissions) + throws GuacamoleException { + + // Delete permissions only if user has permission to do so + if (canAlterPermissions(user, targetUser, permissions)) { + Collection models = getModelInstances(targetUser, permissions); + getPermissionMapper().delete(models); + return; + } + + // User lacks permission to delete object permissions + throw new GuacamoleSecurityException("Permission denied."); + + } + + /** + * Retrieves the permission of the given type associated with the given + * user and object, if it exists. If no such permission exists, null is + * + * @param user + * The user retrieving the permission. + * + * @param targetUser + * The user associated with the permission to be retrieved. + * + * @param type + * The type of permission to retrieve. + * + * @param identifier + * The identifier of the object affected by the permission to return. + * + * @return + * The permission of the given type associated with the given user and + * object, or null if no such permission exists. + * + * @throws GuacamoleException + * If an error occurs while retrieving the requested permission. + */ + public ObjectPermission retrievePermission(AuthenticatedUser user, + ModeledUser targetUser, ObjectPermission.Type type, + String identifier) throws GuacamoleException { + + // Only an admin can read permissions that aren't his own + if (user.getUser().getIdentifier().equals(targetUser.getIdentifier()) + || user.getUser().isAdministrator()) { + + // Read permission from database, return null if not found + ObjectPermissionModel model = getPermissionMapper().selectOne(targetUser.getModel(), type, identifier); + if (model == null) + return null; + + return getPermissionInstance(model); + + } + + // User cannot read this user's permissions + throw new GuacamoleSecurityException("Permission denied."); + + } + + /** + * Retrieves the subset of the given identifiers for which the given user + * has at least one of the given permissions. + * + * @param user + * The user checking the permissions. + * + * @param targetUser + * The user to check permissions of. + * + * @param permissions + * The permissions to check. An identifier will be included in the + * resulting collection if at least one of these permissions is granted + * for the associated object + * + * @param identifiers + * The identifiers of the objects affected by the permissions being + * checked. + * + * @return + * A collection containing the subset of identifiers for which at least + * one of the specified permissions is granted. + * + * @throws GuacamoleException + * If an error occurs while retrieving permissions. + */ + public Collection retrieveAccessibleIdentifiers(AuthenticatedUser user, + ModeledUser targetUser, Collection permissions, + Collection identifiers) throws GuacamoleException { + + // Nothing is always accessible + if (identifiers.isEmpty()) + return identifiers; + + // Determine whether the user is an admin + boolean isAdmin = user.getUser().isAdministrator(); + + // Only an admin can read permissions that aren't his own + if (isAdmin || user.getUser().getIdentifier().equals(targetUser.getIdentifier())) { + + // If user is an admin, everything is accessible + if (isAdmin) + return identifiers; + + // Otherwise, return explicitly-retrievable identifiers + return getPermissionMapper().selectAccessibleIdentifiers(targetUser.getModel(), permissions, identifiers); + + } + + // User cannot read this user's permissions + throw new GuacamoleSecurityException("Permission denied."); + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ObjectPermissionSet.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ObjectPermissionSet.java new file mode 100644 index 000000000..3806f046c --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ObjectPermissionSet.java @@ -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.permission; + +import org.glyptodon.guacamole.auth.jdbc.user.ModeledUser; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; + +/** + * A database implementation of ObjectPermissionSet which uses an injected + * service to query and manipulate the object-level permissions associated with + * a particular user. + * + * @author Michael Jumper + */ +public abstract class ObjectPermissionSet extends RestrictedObject + implements org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet { + + /** + * The user associated with this permission set. Each of the permissions in + * this permission set is granted to this user. + */ + private ModeledUser user; + + /** + * Creates a new ObjectPermissionSet. The resulting permission set + * must still be initialized by a call to init(), or the information + * necessary to read and modify this set will be missing. + */ + public ObjectPermissionSet() { + } + + /** + * Initializes this permission set with the current user and the user + * to whom the permissions in this set are granted. + * + * @param currentUser + * The user who queried this permission set, and whose permissions + * dictate the access level of all operations performed on this set. + * + * @param user + * The user to whom the permissions in this set are granted. + */ + public void init(AuthenticatedUser currentUser, ModeledUser user) { + super.init(currentUser); + this.user = user; + } + + /** + * Returns an ObjectPermissionService implementation for manipulating the + * type of permissions contained within this permission set. + * + * @return + * An object permission service for manipulating the type of + * permissions contained within this permission set. + */ + protected abstract ObjectPermissionService getObjectPermissionService(); + + @Override + public Set getPermissions() throws GuacamoleException { + return getObjectPermissionService().retrievePermissions(getCurrentUser(), user); + } + + @Override + public boolean hasPermission(ObjectPermission.Type permission, + String identifier) throws GuacamoleException { + return getObjectPermissionService().retrievePermission(getCurrentUser(), user, permission, identifier) != null; + } + + @Override + public void addPermission(ObjectPermission.Type permission, + String identifier) throws GuacamoleException { + addPermissions(Collections.singleton(new ObjectPermission(permission, identifier))); + } + + @Override + public void removePermission(ObjectPermission.Type permission, + String identifier) throws GuacamoleException { + removePermissions(Collections.singleton(new ObjectPermission(permission, identifier))); + } + + @Override + public Collection getAccessibleObjects(Collection permissions, + Collection identifiers) throws GuacamoleException { + return getObjectPermissionService().retrieveAccessibleIdentifiers(getCurrentUser(), user, permissions, identifiers); + } + + @Override + public void addPermissions(Set permissions) + throws GuacamoleException { + getObjectPermissionService().createPermissions(getCurrentUser(), user, permissions); + } + + @Override + public void removePermissions(Set permissions) + throws GuacamoleException { + getObjectPermissionService().deletePermissions(getCurrentUser(), user, permissions); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/PermissionMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/PermissionMapper.java new file mode 100644 index 000000000..eff35a1ac --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/PermissionMapper.java @@ -0,0 +1,73 @@ +/* + * 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.permission; + +import java.util.Collection; +import org.glyptodon.guacamole.auth.jdbc.user.UserModel; +import org.apache.ibatis.annotations.Param; + +/** + * Generic base for mappers which handle permissions. + * + * @author Michael Jumper + * @param + * The type of permission model object handled by this mapper. + */ +public interface PermissionMapper { + + /** + * Retrieves all permissions associated with the given user. + * + * @param user + * The user to retrieve permissions for. + * + * @return + * All permissions associated with the given user. + */ + Collection select(@Param("user") UserModel user); + + /** + * Inserts the given permissions into the database. If any permissions + * already exist, they will be ignored. + * + * @param permissions + * The permissions to insert. + * + * @return + * The number of rows inserted. + */ + int insert(@Param("permissions") Collection permissions); + + /** + * Deletes the given permissions from the database. If any permissions do + * not exist, they will be ignored. + * + * @param permissions + * The permissions to delete. + * + * @return + * The number of rows deleted. + */ + int delete(@Param("permissions") Collection permissions); + +} \ No newline at end of file diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/PermissionModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/PermissionModel.java new file mode 100644 index 000000000..d50c9704f --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/PermissionModel.java @@ -0,0 +1,110 @@ +/* + * 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.permission; + +/** + * Generic base permission model which grants a permission of a particular type + * to a specific user. + * + * @author Michael Jumper + * @param + * The type of permissions allowed within this model. + */ +public abstract class PermissionModel { + + /** + * The database ID of the user to whom this permission is granted. + */ + private Integer userID; + + /** + * The username of the user to whom this permission is granted. + */ + private String username; + + /** + * The type of action granted by this permission. + */ + private PermissionType type; + + /** + * Returns the database ID of the user to whom this permission is granted. + * + * @return + * The database ID of the user to whom this permission is granted. + */ + public Integer getUserID() { + return userID; + } + + /** + * Sets the database ID of the user to whom this permission is granted. + * + * @param userID + * The database ID of the user to whom this permission is granted. + */ + public void setUserID(Integer userID) { + this.userID = userID; + } + + /** + * Returns the username of the user to whom this permission is granted. + * + * @return + * The username of the user to whom this permission is granted. + */ + public String getUsername() { + return username; + } + + /** + * Sets the username of the user to whom this permission is granted. + * + * @param username + * The username of the user to whom this permission is granted. + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * Returns the type of action granted by this permission. + * + * @return + * The type of action granted by this permission. + */ + public PermissionType getType() { + return type; + } + + /** + * Sets the type of action granted by this permission. + * + * @param type + * The type of action granted by this permission. + */ + public void setType(PermissionType type) { + this.type = type; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/PermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/PermissionService.java new file mode 100644 index 000000000..3207ffc8d --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/PermissionService.java @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2013 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.permission; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.auth.jdbc.user.ModeledUser; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +import org.glyptodon.guacamole.net.auth.permission.Permission; +import org.glyptodon.guacamole.net.auth.permission.PermissionSet; + +/** + * Service which provides convenience methods for creating, retrieving, and + * deleting permissions, and for obtaining the permission sets that contain + * these permissions. This service will automatically enforce the permissions + * of the current user. + * + * @author Michael Jumper + * @param + * The type of permission sets this service provides access to. + * + * @param + * The type of permission this service provides access to. + * + * @param + * The underlying model object used to represent PermissionType in the + * database. + */ +public abstract class PermissionService, + PermissionType extends Permission, ModelType> { + + /** + * Returns an instance of a mapper for the type of permission used by this + * service. + * + * @return + * A mapper which provides access to the model objects associated with + * the permissions used by this service. + */ + protected abstract PermissionMapper getPermissionMapper(); + + /** + * Returns an instance of a permission which is based on the given model + * object. + * + * @param model + * The model object to use to produce the returned permission. + * + * @return + * A permission which is based on the given model object. + */ + protected abstract PermissionType getPermissionInstance(ModelType model); + + /** + * Returns a collection of permissions which are based on the models in + * the given collection. + * + * @param models + * The model objects to use to produce the permissions within the + * returned set. + * + * @return + * A set of permissions which are based on the models in the given + * collection. + */ + protected Set getPermissionInstances(Collection models) { + + // Create new collection of permissions by manually converting each model + Set permissions = new HashSet(models.size()); + for (ModelType model : models) + permissions.add(getPermissionInstance(model)); + + return permissions; + + } + + /** + * Returns an instance of a model object which is based on the given + * permission and target user. + * + * @param targetUser + * The user to whom this permission is granted. + * + * @param permission + * The permission to use to produce the returned model object. + * + * @return + * A model object which is based on the given permission and target + * user. + */ + protected abstract ModelType getModelInstance(ModeledUser targetUser, + PermissionType permission); + + /** + * Returns a collection of model objects which are based on the given + * permissions and target user. + * + * @param targetUser + * The user to whom this permission is granted. + * + * @param permissions + * The permissions to use to produce the returned model objects. + * + * @return + * A collection of model objects which are based on the given + * permissions and target user. + */ + protected Collection getModelInstances(ModeledUser targetUser, + Collection permissions) { + + // Create new collection of models by manually converting each permission + Collection models = new ArrayList(permissions.size()); + for (PermissionType permission : permissions) + models.add(getModelInstance(targetUser, permission)); + + return models; + + } + + /** + * Returns a permission set that can be used to retrieve and manipulate the + * permissions of the given user. + * + * @param user + * The user who will be retrieving or manipulating permissions through + * the returned permission set. + * + * @param targetUser + * The user to whom the permissions in the returned permission set are + * granted. + * + * @return + * A permission set that contains all permissions associated with the + * given user, and can be used to manipulate that user's permissions. + * + * @throws GuacamoleException + * If an error occurs while retrieving the permissions of the given + * user, or if permission to retrieve the permissions of the given + * user is denied. + */ + public abstract PermissionSetType getPermissionSet(AuthenticatedUser user, + ModeledUser targetUser) throws GuacamoleException; + + /** + * Retrieves all permissions associated with the given user. + * + * @param user + * The user retrieving the permissions. + * + * @param targetUser + * The user associated with the permissions to be retrieved. + * + * @return + * The permissions associated with the given user. + * + * @throws GuacamoleException + * If an error occurs while retrieving the requested permissions. + */ + public Set retrievePermissions(AuthenticatedUser user, + ModeledUser targetUser) throws GuacamoleException { + + // Only an admin can read permissions that aren't his own + if (user.getUser().getIdentifier().equals(targetUser.getIdentifier()) + || user.getUser().isAdministrator()) + return getPermissionInstances(getPermissionMapper().select(targetUser.getModel())); + + // User cannot read this user's permissions + throw new GuacamoleSecurityException("Permission denied."); + + } + + /** + * Creates the given permissions within the database. If any permissions + * already exist, they will be ignored. + * + * @param user + * The user creating the permissions. + * + * @param targetUser + * The user associated with the permissions to be created. + * + * @param permissions + * The permissions to create. + * + * @throws GuacamoleException + * If the user lacks permission to create the permissions, or an error + * occurs while creating the permissions. + */ + public abstract void createPermissions(AuthenticatedUser user, + ModeledUser targetUser, + Collection permissions) throws GuacamoleException; + + /** + * Deletes the given permissions. If any permissions do not exist, they + * will be ignored. + * + * @param user + * The user deleting the permissions. + * + * @param targetUser + * The user associated with the permissions to be deleted. + * + * @param permissions + * The permissions to delete. + * + * @throws GuacamoleException + * If the user lacks permission to delete the permissions, or an error + * occurs while deleting the permissions. + */ + public abstract void deletePermissions(AuthenticatedUser user, + ModeledUser targetUser, + Collection permissions) throws GuacamoleException; + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionMapper.java new file mode 100644 index 000000000..fdcb63ce9 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionMapper.java @@ -0,0 +1,53 @@ +/* + * 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.permission; + +import org.glyptodon.guacamole.auth.jdbc.user.UserModel; +import org.apache.ibatis.annotations.Param; +import org.glyptodon.guacamole.net.auth.permission.SystemPermission; + +/** + * Mapper for system-level permissions. + * + * @author Michael Jumper + */ +public interface SystemPermissionMapper extends PermissionMapper { + + /** + * Retrieve the permission of the given type associated with the given + * user, if it exists. If no such permission exists, null is returned. + * + * @param user + * The user to retrieve permissions for. + * + * @param type + * The type of permission to return. + * + * @return + * The requested permission, or null if no such permission is granted + * to the given user. + */ + SystemPermissionModel selectOne(@Param("user") UserModel user, + @Param("type") SystemPermission.Type type); + +} \ No newline at end of file diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionModel.java new file mode 100644 index 000000000..8c1c13ac9 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionModel.java @@ -0,0 +1,41 @@ +/* + * 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.permission; + +import org.glyptodon.guacamole.net.auth.permission.SystemPermission; + +/** + * Object representation of an system-level Guacamole permission, as + * represented in the database. + * + * @author Michael Jumper + */ +public class SystemPermissionModel extends PermissionModel { + + /** + * Creates a new, empty System permission. + */ + public SystemPermissionModel() { + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionService.java new file mode 100644 index 000000000..52edda6d0 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionService.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2013 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.permission; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import java.util.Collection; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.auth.jdbc.user.ModeledUser; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +import org.glyptodon.guacamole.net.auth.permission.SystemPermission; + +/** + * Service which provides convenience methods for creating, retrieving, and + * deleting system permissions. This service will automatically enforce + * the permissions of the current user. + * + * @author Michael Jumper + */ +public class SystemPermissionService + extends PermissionService { + + /** + * Mapper for system-level permissions. + */ + @Inject + private SystemPermissionMapper systemPermissionMapper; + + /** + * Provider for creating system permission sets. + */ + @Inject + private Provider systemPermissionSetProvider; + + @Override + protected SystemPermissionMapper getPermissionMapper() { + return systemPermissionMapper; + } + + @Override + protected SystemPermission getPermissionInstance(SystemPermissionModel model) { + return new SystemPermission(model.getType()); + } + + @Override + protected SystemPermissionModel getModelInstance(final ModeledUser targetUser, + final SystemPermission permission) { + + SystemPermissionModel model = new SystemPermissionModel(); + + // Populate model object with data from user and permission + model.setUserID(targetUser.getModel().getObjectID()); + model.setUsername(targetUser.getModel().getIdentifier()); + model.setType(permission.getType()); + + return model; + + } + + @Override + public SystemPermissionSet getPermissionSet(AuthenticatedUser user, + ModeledUser targetUser) throws GuacamoleException { + + // Create permission set for requested user + SystemPermissionSet permissionSet = systemPermissionSetProvider.get(); + permissionSet.init(user, targetUser); + + return permissionSet; + + } + + @Override + public void createPermissions(AuthenticatedUser user, ModeledUser targetUser, + Collection permissions) throws GuacamoleException { + + // Only an admin can create system permissions + if (user.getUser().isAdministrator()) { + Collection models = getModelInstances(targetUser, permissions); + systemPermissionMapper.insert(models); + return; + } + + // User lacks permission to create system permissions + throw new GuacamoleSecurityException("Permission denied."); + + } + + @Override + public void deletePermissions(AuthenticatedUser user, ModeledUser targetUser, + Collection permissions) throws GuacamoleException { + + // Only an admin can delete system permissions + if (user.getUser().isAdministrator()) { + Collection models = getModelInstances(targetUser, permissions); + systemPermissionMapper.delete(models); + return; + } + + // User lacks permission to delete system permissions + throw new GuacamoleSecurityException("Permission denied."); + + } + + /** + * Retrieves the permission of the given type associated with the given + * user, if it exists. If no such permission exists, null is returned. + * + * @param user + * The user retrieving the permission. + * + * @param targetUser + * The user associated with the permission to be retrieved. + * + * @param type + * The type of permission to retrieve. + * + * @return + * The permission of the given type associated with the given user, or + * null if no such permission exists. + * + * @throws GuacamoleException + * If an error occurs while retrieving the requested permission. + */ + public SystemPermission retrievePermission(AuthenticatedUser user, + ModeledUser targetUser, SystemPermission.Type type) throws GuacamoleException { + + // Only an admin can read permissions that aren't his own + if (user.getUser().getIdentifier().equals(targetUser.getIdentifier()) + || user.getUser().isAdministrator()) { + + // Read permission from database, return null if not found + SystemPermissionModel model = getPermissionMapper().selectOne(targetUser.getModel(), type); + if (model == null) + return null; + + return getPermissionInstance(model); + + } + + // User cannot read this user's permissions + throw new GuacamoleSecurityException("Permission denied."); + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionSet.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionSet.java new file mode 100644 index 000000000..485eaecfe --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionSet.java @@ -0,0 +1,115 @@ +/* + * 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.permission; + +import org.glyptodon.guacamole.auth.jdbc.user.ModeledUser; +import com.google.inject.Inject; +import java.util.Collections; +import java.util.Set; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject; +import org.glyptodon.guacamole.net.auth.permission.SystemPermission; + +/** + * A database implementation of SystemPermissionSet which uses an injected + * service to query and manipulate the system permissions associated with a + * particular user. + * + * @author Michael Jumper + */ +public class SystemPermissionSet extends RestrictedObject + implements org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet { + + /** + * The user associated with this permission set. Each of the permissions in + * this permission set is granted to this user. + */ + private ModeledUser user; + + /** + * Service for reading and manipulating system permissions. + */ + @Inject + private SystemPermissionService systemPermissionService; + + /** + * Creates a new SystemPermissionSet. The resulting permission set + * must still be initialized by a call to init(), or the information + * necessary to read and modify this set will be missing. + */ + public SystemPermissionSet() { + } + + /** + * Initializes this permission set with the current user and the user + * to whom the permissions in this set are granted. + * + * @param currentUser + * The user who queried this permission set, and whose permissions + * dictate the access level of all operations performed on this set. + * + * @param user + * The user to whom the permissions in this set are granted. + */ + public void init(AuthenticatedUser currentUser, ModeledUser user) { + super.init(currentUser); + this.user = user; + } + + @Override + public Set getPermissions() throws GuacamoleException { + return systemPermissionService.retrievePermissions(getCurrentUser(), user); + } + + @Override + public boolean hasPermission(SystemPermission.Type permission) + throws GuacamoleException { + return systemPermissionService.retrievePermission(getCurrentUser(), user, permission) != null; + } + + @Override + public void addPermission(SystemPermission.Type permission) + throws GuacamoleException { + addPermissions(Collections.singleton(new SystemPermission(permission))); + } + + @Override + public void removePermission(SystemPermission.Type permission) + throws GuacamoleException { + removePermissions(Collections.singleton(new SystemPermission(permission))); + } + + @Override + public void addPermissions(Set permissions) + throws GuacamoleException { + systemPermissionService.createPermissions(getCurrentUser(), user, permissions); + } + + @Override + public void removePermissions(Set permissions) + throws GuacamoleException { + systemPermissionService.deletePermissions(getCurrentUser(), user, permissions); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionMapper.java new file mode 100644 index 000000000..a6c3275d9 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionMapper.java @@ -0,0 +1,30 @@ +/* + * 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.permission; + +/** + * Mapper for user permissions. + * + * @author Michael Jumper + */ +public interface UserPermissionMapper extends ObjectPermissionMapper {} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionService.java new file mode 100644 index 000000000..33497496a --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionService.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2013 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.permission; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.auth.jdbc.user.ModeledUser; + +/** + * Service which provides convenience methods for creating, retrieving, and + * deleting user permissions. This service will automatically enforce the + * permissions of the current user. + * + * @author Michael Jumper + */ +public class UserPermissionService extends ObjectPermissionService { + + /** + * Mapper for user permissions. + */ + @Inject + private UserPermissionMapper userPermissionMapper; + + /** + * Provider for user permission sets. + */ + @Inject + private Provider userPermissionSetProvider; + + @Override + protected ObjectPermissionMapper getPermissionMapper() { + return userPermissionMapper; + } + + @Override + public ObjectPermissionSet getPermissionSet(AuthenticatedUser user, + ModeledUser targetUser) throws GuacamoleException { + + // Create permission set for requested user + ObjectPermissionSet permissionSet = userPermissionSetProvider.get(); + permissionSet.init(user, targetUser); + + return permissionSet; + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionSet.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionSet.java new file mode 100644 index 000000000..ca99be759 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionSet.java @@ -0,0 +1,47 @@ +/* + * 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.permission; + +import com.google.inject.Inject; + +/** + * A database implementation of ObjectPermissionSet which uses an injected + * service to query and manipulate the user permissions associated with a + * particular user. + * + * @author Michael Jumper + */ +public class UserPermissionSet extends ObjectPermissionSet { + + /** + * Service for querying and manipulating user permissions. + */ + @Inject + private UserPermissionService userPermissionService; + + @Override + protected ObjectPermissionService getObjectPermissionService() { + return userPermissionService; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/package-info.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/package-info.java new file mode 100644 index 000000000..01b820ac1 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/package-info.java @@ -0,0 +1,26 @@ +/* + * 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. + */ + +/** + * Classes related to object- and system-level permissions. + */ +package org.glyptodon.guacamole.auth.jdbc.permission; diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/PasswordEncryptionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/security/PasswordEncryptionService.java similarity index 71% rename from extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/PasswordEncryptionService.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/security/PasswordEncryptionService.java index 67ea8bab0..ef3099468 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/PasswordEncryptionService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/security/PasswordEncryptionService.java @@ -20,8 +20,7 @@ * THE SOFTWARE. */ -package net.sourceforge.guacamole.net.auth.mysql.service; - +package org.glyptodon.guacamole.auth.jdbc.security; /** * A service to perform password encryption and checking. @@ -29,20 +28,6 @@ package net.sourceforge.guacamole.net.auth.mysql.service; */ public interface PasswordEncryptionService { - /** - * Checks whether the provided, unhashed password matches the given - * hash/salt pair. - * - * @param password The unhashed password to validate. - * @param hashedPassword The hashed password to compare the given password - * against. - * @param salt The salt used when the hashed password given was created. - * @return true if the provided credentials match the values given, false - * otherwise. - */ - public boolean checkPassword(String password, byte[] hashedPassword, - byte[] salt); - /** * Creates a password hash based on the provided username, password, and * salt. @@ -52,4 +37,5 @@ public interface PasswordEncryptionService { * @return The generated password hash. */ public byte[] createPasswordHash(String password, byte[] salt); + } diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/SHA256PasswordEncryptionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/security/SHA256PasswordEncryptionService.java similarity index 84% rename from extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/SHA256PasswordEncryptionService.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/security/SHA256PasswordEncryptionService.java index aa3043805..cfe5bc45f 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/SHA256PasswordEncryptionService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/security/SHA256PasswordEncryptionService.java @@ -20,12 +20,11 @@ * THE SOFTWARE. */ -package net.sourceforge.guacamole.net.auth.mysql.service; +package org.glyptodon.guacamole.auth.jdbc.security; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.Arrays; import javax.xml.bind.DatatypeConverter; /** @@ -34,16 +33,6 @@ import javax.xml.bind.DatatypeConverter; */ public class SHA256PasswordEncryptionService implements PasswordEncryptionService { - @Override - public boolean checkPassword(String password, byte[] hashedPassword, - byte[] salt) { - - // Compare bytes of password in credentials against hashed password - byte[] passwordBytes = createPasswordHash(password, salt); - return Arrays.equals(passwordBytes, hashedPassword); - - } - @Override public byte[] createPasswordHash(String password, byte[] salt) { @@ -72,4 +61,5 @@ public class SHA256PasswordEncryptionService implements PasswordEncryptionServic } } + } diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/SaltService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/security/SaltService.java similarity index 96% rename from extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/SaltService.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/security/SaltService.java index 823fa93da..7badde447 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/SaltService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/security/SaltService.java @@ -20,7 +20,7 @@ * THE SOFTWARE. */ -package net.sourceforge.guacamole.net.auth.mysql.service; +package org.glyptodon.guacamole.auth.jdbc.security; /** * A service to generate password salts. diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/SecureRandomSaltService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/security/SecureRandomSaltService.java similarity index 96% rename from extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/SecureRandomSaltService.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/security/SecureRandomSaltService.java index e119eb881..608733b5b 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/SecureRandomSaltService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/security/SecureRandomSaltService.java @@ -20,7 +20,7 @@ * THE SOFTWARE. */ -package net.sourceforge.guacamole.net.auth.mysql.service; +package org.glyptodon.guacamole.auth.jdbc.security; import java.security.SecureRandom; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/security/package-info.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/security/package-info.java new file mode 100644 index 000000000..3f1d8b4f7 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/security/package-info.java @@ -0,0 +1,26 @@ +/* + * 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. + */ + +/** + * Classes related to hashing or encryption. + */ +package org.glyptodon.guacamole.auth.jdbc.security; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java new file mode 100644 index 000000000..5f1b623e6 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/AbstractGuacamoleSocketService.java @@ -0,0 +1,288 @@ +/* + * 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.socket; + +import com.google.inject.Inject; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.auth.jdbc.connection.ModeledConnection; +import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; +import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionRecordMapper; +import org.glyptodon.guacamole.auth.jdbc.connection.ParameterMapper; +import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionModel; +import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionRecordModel; +import org.glyptodon.guacamole.auth.jdbc.connection.ParameterModel; +import org.glyptodon.guacamole.auth.jdbc.user.UserModel; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.environment.Environment; +import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.net.InetGuacamoleSocket; +import org.glyptodon.guacamole.net.auth.Connection; +import org.glyptodon.guacamole.net.auth.ConnectionGroup; +import org.glyptodon.guacamole.net.auth.ConnectionRecord; +import org.glyptodon.guacamole.protocol.ConfiguredGuacamoleSocket; +import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; +import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; +import org.glyptodon.guacamole.token.StandardTokens; +import org.glyptodon.guacamole.token.TokenFilter; + + +/** + * Base implementation of the GuacamoleSocketService, handling retrieval of + * connection parameters, load balancing, and connection usage counts. The + * implementation of concurrency rules is up to policy-specific subclasses. + * + * @author Michael Jumper + */ +public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketService { + + /** + * The environment of the Guacamole server. + */ + @Inject + private Environment environment; + + /** + * Mapper for accessing connection parameters. + */ + @Inject + private ParameterMapper parameterMapper; + + /** + * Mapper for accessing connection history. + */ + @Inject + private ConnectionRecordMapper connectionRecordMapper; + + /** + * The current number of concurrent uses of the connection having a given + * identifier. + */ + private final Map> activeConnections = + new HashMap>(); + + /** + * Atomically increments the current usage count for the given connection. + * + * @param connection + * The connection which is being used. + */ + private void addActiveConnection(Connection connection, ConnectionRecord record) { + synchronized (activeConnections) { + + String identifier = connection.getIdentifier(); + + // Get set of active connection records, creating if necessary + LinkedList connections = activeConnections.get(identifier); + if (connections == null) { + connections = new LinkedList(); + activeConnections.put(identifier, connections); + } + + // Add active connection + connections.addFirst(record); + + } + } + + /** + * Atomically decrements the current usage count for the given connection. + * If a combination of incrementUsage() and decrementUsage() calls result + * in the usage counter being reduced to zero, it is guaranteed that one + * of those decrementUsage() calls will remove the value from the map. + * + * @param connection + * The connection which is no longer being used. + */ + private void removeActiveConnection(Connection connection, ConnectionRecord record) { + synchronized (activeConnections) { + + String identifier = connection.getIdentifier(); + + // Get set of active connection records + LinkedList connections = activeConnections.get(identifier); + assert(connections != null); + + // Remove old record + connections.remove(record); + + // If now empty, clean the tracking entry + if (connections.isEmpty()) + activeConnections.remove(identifier); + + } + } + + /** + * Acquires possibly-exclusive access to the given connection on behalf of + * the given user. If access is denied for any reason, an exception is + * thrown. + * + * @param user + * The user acquiring access. + * + * @param connection + * The connection being accessed. + * + * @throws GuacamoleException + * If access is denied to the given user for any reason. + */ + protected abstract void acquire(AuthenticatedUser user, + ModeledConnection connection) throws GuacamoleException; + + /** + * Releases possibly-exclusive access to the given connection on behalf of + * the given user. If the given user did not already have access, the + * behavior of this function is undefined. + * + * @param user + * The user releasing access. + * + * @param connection + * The connection being released. + */ + protected abstract void release(AuthenticatedUser user, + ModeledConnection connection); + + @Override + public GuacamoleSocket getGuacamoleSocket(final AuthenticatedUser user, + final ModeledConnection connection, GuacamoleClientInformation info) + throws GuacamoleException { + + // Create record for active connection + final ActiveConnectionRecord activeConnection = new ActiveConnectionRecord(user); + + // Generate configuration from available data + GuacamoleConfiguration config = new GuacamoleConfiguration(); + + // Set protocol from connection + ConnectionModel model = connection.getModel(); + config.setProtocol(model.getProtocol()); + + // Set parameters from associated data + Collection parameters = parameterMapper.select(connection.getIdentifier()); + for (ParameterModel parameter : parameters) + config.setParameter(parameter.getName(), parameter.getValue()); + + // Build token filter containing credential tokens + TokenFilter tokenFilter = new TokenFilter(); + StandardTokens.addStandardTokens(tokenFilter, user.getCredentials()); + + // Filter the configuration + tokenFilter.filterValues(config.getParameters()); + + // Return new socket + try { + + // Atomically gain access to connection + acquire(user, connection); + addActiveConnection(connection, activeConnection); + + // Return newly-reserved connection + return new ConfiguredGuacamoleSocket( + new InetGuacamoleSocket( + environment.getRequiredProperty(Environment.GUACD_HOSTNAME), + environment.getRequiredProperty(Environment.GUACD_PORT) + ), + config + ) { + + @Override + public void close() throws GuacamoleException { + + // Attempt to close connection + super.close(); + + // Release connection upon close + removeActiveConnection(connection, activeConnection); + release(user, connection); + + UserModel userModel = user.getUser().getModel(); + ConnectionRecordModel recordModel = new ConnectionRecordModel(); + + // Copy user information and timestamps into new record + recordModel.setUserID(userModel.getObjectID()); + recordModel.setUsername(userModel.getIdentifier()); + recordModel.setConnectionIdentifier(connection.getIdentifier()); + recordModel.setStartDate(activeConnection.getStartDate()); + recordModel.setEndDate(new Date()); + + // Insert connection record + connectionRecordMapper.insert(recordModel); + + } + + }; + + } + + // Release connection in case of error + catch (GuacamoleException e) { + + // Atomically release access to connection + removeActiveConnection(connection, activeConnection); + release(user, connection); + + throw e; + + } + + } + + @Override + public List getActiveConnections(Connection connection) { + synchronized (activeConnections) { + + String identifier = connection.getIdentifier(); + + // Get set of active connection records + LinkedList connections = activeConnections.get(identifier); + if (connections != null) + return Collections.unmodifiableList(connections); + + return Collections.EMPTY_LIST; + + } + } + + @Override + public GuacamoleSocket getGuacamoleSocket(AuthenticatedUser user, + ModeledConnectionGroup connectionGroup, + GuacamoleClientInformation info) throws GuacamoleException { + // STUB + throw new UnsupportedOperationException("STUB"); + } + + @Override + public List getActiveConnections(ConnectionGroup connectionGroup) { + // STUB + return Collections.EMPTY_LIST; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java new file mode 100644 index 000000000..f32669446 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/ActiveConnectionRecord.java @@ -0,0 +1,89 @@ +/* + * 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.socket; + +import java.util.Date; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.net.auth.ConnectionRecord; + + +/** + * A connection record implementation that describes an active connection. As + * the associated connection has not yet ended, getEndDate() will always return + * null, and isActive() will always return true. The associated start date will + * be the time of this objects creation. + * + * @author Michael Jumper + */ +public class ActiveConnectionRecord implements ConnectionRecord { + + /** + * The user that connected to the connection associated with this connection + * record. + */ + private final AuthenticatedUser user; + + /** + * The time this connection record was created. + */ + private final Date startDate = new Date(); + + /** + * Creates a new connection record associated with the given user. The + * start date of this connection record will be the time of its creation. + * + * @param user + * The user that connected to the connection associated with this + * connection record. + */ + public ActiveConnectionRecord(AuthenticatedUser user) { + this.user = user; + } + + @Override + public Date getStartDate() { + return startDate; + } + + @Override + public Date getEndDate() { + + // Active connections have not yet ended + return null; + + } + + @Override + public String getUsername() { + return user.getUser().getIdentifier(); + } + + @Override + public boolean isActive() { + + // Active connections are active by definition + return true; + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/GuacamoleSocketService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/GuacamoleSocketService.java new file mode 100644 index 000000000..31e240ba2 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/GuacamoleSocketService.java @@ -0,0 +1,132 @@ +/* + * 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.socket; + +import java.util.List; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.auth.jdbc.connection.ModeledConnection; +import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.net.auth.Connection; +import org.glyptodon.guacamole.net.auth.ConnectionGroup; +import org.glyptodon.guacamole.net.auth.ConnectionRecord; +import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; + + +/** + * Service which creates pre-configured GuacamoleSocket instances for + * connections and balancing groups, applying concurrent usage rules. + * + * @author Michael Jumper + */ +public interface GuacamoleSocketService { + + /** + * Creates a socket for the given user which connects to the given + * connection. The given client information will be passed to guacd when + * the connection is established. This function will apply any concurrent + * usage rules in effect, but will NOT test object- or system-level + * permissions. + * + * @param user + * The user for whom the connection is being established. + * + * @param connection + * The connection the user is connecting to. + * + * @param info + * Information describing the Guacamole client connecting to the given + * connection. + * + * @return + * A new GuacamoleSocket which is configured and connected to the given + * connection. + * + * @throws GuacamoleException + * If the connection cannot be established due to concurrent usage + * rules. + */ + GuacamoleSocket getGuacamoleSocket(AuthenticatedUser user, + ModeledConnection connection, GuacamoleClientInformation info) + throws GuacamoleException; + + /** + * Returns a list containing connection records representing all currently- + * active connections using the given connection. These records will have + * usernames and start dates, but no end date. + * + * @param connection + * The connection to check. + * + * @return + * A list containing connection records representing all currently- + * active connections. + */ + public List getActiveConnections(Connection connection); + + /** + * Creates a socket for the given user which connects to the given + * connection group. The given client information will be passed to guacd + * when the connection is established. This function will apply any + * concurrent usage rules in effect, but will NOT test object- or + * system-level permissions. + * + * @param user + * The user for whom the connection is being established. + * + * @param connectionGroup + * The connection group the user is connecting to. + * + * @param info + * Information describing the Guacamole client connecting to the given + * connection group. + * + * @return + * A new GuacamoleSocket which is configured and connected to the given + * connection group. + * + * @throws GuacamoleException + * If the connection cannot be established due to concurrent usage + * rules, or if the connection group is not balancing. + */ + GuacamoleSocket getGuacamoleSocket(AuthenticatedUser user, + ModeledConnectionGroup connectionGroup, + GuacamoleClientInformation info) + throws GuacamoleException; + + /** + * Returns a list containing connection records representing all currently- + * active connections using the given connection group. These records will + * have usernames and start dates, but no end date. + * + * @param connectionGroup + * The connection group to check. + * + * @return + * A list containing connection records representing all currently- + * active connections. + */ + public List getActiveConnections(ConnectionGroup connectionGroup); + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/UnrestrictedGuacamoleSocketService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/UnrestrictedGuacamoleSocketService.java new file mode 100644 index 000000000..a1d619378 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/UnrestrictedGuacamoleSocketService.java @@ -0,0 +1,52 @@ +/* + * 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.socket; + +import com.google.inject.Singleton; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.auth.jdbc.connection.ModeledConnection; +import org.glyptodon.guacamole.GuacamoleException; + + +/** + * GuacamoleSocketService implementation which imposes no restrictions + * whatsoever on the number of concurrent or duplicate connections. + * + * @author Michael Jumper + */ +@Singleton +public class UnrestrictedGuacamoleSocketService + extends AbstractGuacamoleSocketService { + + @Override + protected void acquire(AuthenticatedUser user, ModeledConnection connection) + throws GuacamoleException { + // Do nothing + } + + @Override + protected void release(AuthenticatedUser user, ModeledConnection connection) { + // Do nothing + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/package-info.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/package-info.java new file mode 100644 index 000000000..42c8cc335 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/socket/package-info.java @@ -0,0 +1,27 @@ +/* + * 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. + */ + +/** + * Classes related to obtaining/configuring Guacamole sockets, and restricting + * access to those sockets. + */ +package org.glyptodon.guacamole.auth.jdbc.socket; diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/AuthenticatedUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/AuthenticatedUser.java similarity index 75% rename from extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/AuthenticatedUser.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/AuthenticatedUser.java index a6aab2b15..7b01d4598 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/AuthenticatedUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/AuthenticatedUser.java @@ -20,22 +20,21 @@ * THE SOFTWARE. */ -package net.sourceforge.guacamole.net.auth.mysql; +package org.glyptodon.guacamole.auth.jdbc.user; import org.glyptodon.guacamole.net.auth.Credentials; /** - * Represents an authenticated user via their database ID and corresponding - * credentials. + * Associates a user with the credentials they used to authenticate. * * @author Michael Jumper */ public class AuthenticatedUser { /** - * The database ID of this user. + * The user that authenticated. */ - private final int userID; + private final ModeledUser user; /** * The credentials given when this user authenticated. @@ -43,28 +42,28 @@ public class AuthenticatedUser { private final Credentials credentials; /** - * Creates a new AuthenticatedUser associated with the given database ID - * and credentials. + * Creates a new AuthenticatedUser associating the given user with their + * corresponding credentials. * - * @param userID - * The database ID of the user this object should represent. + * @param user + * The user this object should represent. * * @param credentials * The credentials given by the user when they authenticated. */ - public AuthenticatedUser(int userID, Credentials credentials) { - this.userID = userID; + public AuthenticatedUser(ModeledUser user, Credentials credentials) { + this.user = user; this.credentials = credentials; } /** - * Returns the ID of this user. + * Returns the user that authenticated. * * @return - * The ID of this user. + * The user that authenticated. */ - public int getUserID() { - return userID; + public ModeledUser getUser() { + return user; } /** diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/ModeledUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/ModeledUser.java new file mode 100644 index 000000000..e9f79d2fd --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/ModeledUser.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2013 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 org.glyptodon.guacamole.auth.jdbc.base.DirectoryObject; +import org.glyptodon.guacamole.auth.jdbc.security.PasswordEncryptionService; +import org.glyptodon.guacamole.auth.jdbc.security.SaltService; +import org.glyptodon.guacamole.auth.jdbc.permission.SystemPermissionService; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionGroupPermissionService; +import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionPermissionService; +import org.glyptodon.guacamole.auth.jdbc.permission.UserPermissionService; +import org.glyptodon.guacamole.net.auth.User; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; +import org.glyptodon.guacamole.net.auth.permission.SystemPermission; +import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; + +/** + * An implementation of the User object which is backed by a database model. + * + * @author James Muehlner + * @author Michael Jumper + */ +public class ModeledUser extends DirectoryObject implements User { + + /** + * Service for hashing passwords. + */ + @Inject + private PasswordEncryptionService encryptionService; + + /** + * Service for providing secure, random salts. + */ + @Inject + private SaltService saltService; + + /** + * Service for retrieving system permissions. + */ + @Inject + private SystemPermissionService systemPermissionService; + + /** + * Service for retrieving connection permissions. + */ + @Inject + private ConnectionPermissionService connectionPermissionService; + + /** + * Service for retrieving connection group permissions. + */ + @Inject + private ConnectionGroupPermissionService connectionGroupPermissionService; + + /** + * Service for retrieving user permissions. + */ + @Inject + private UserPermissionService userPermissionService; + + /** + * The plaintext password previously set by a call to setPassword(), if + * any. The password of a user cannot be retrieved once saved into the + * database, so this serves to ensure getPassword() returns a reasonable + * value if setPassword() is called. If no password has been set, or the + * user was retrieved from the database, this will be null. + */ + private String password = null; + + /** + * Creates a new, empty ModeledUser. + */ + public ModeledUser() { + } + + @Override + public String getPassword() { + return password; + } + + @Override + public void setPassword(String password) { + + UserModel userModel = getModel(); + + // Store plaintext password internally + this.password = password; + + // If no password provided, clear password salt and hash + if (password == null) { + userModel.setPasswordSalt(null); + userModel.setPasswordHash(null); + } + + // Otherwise generate new salt and hash given password using newly-generated salt + else { + byte[] salt = saltService.generateSalt(); + byte[] hash = encryptionService.createPasswordHash(password, salt); + + // Set stored salt and hash + userModel.setPasswordSalt(salt); + userModel.setPasswordHash(hash); + } + + } + + /** + * Returns whether this user is a system administrator, and thus is not + * restricted by permissions. + * + * @return + * true if this user is a system administrator, false otherwise. + * + * @throws GuacamoleException + * If an error occurs while determining the user's system administrator + * status. + */ + public boolean isAdministrator() throws GuacamoleException { + SystemPermissionSet systemPermissionSet = getSystemPermissions(); + return systemPermissionSet.hasPermission(SystemPermission.Type.ADMINISTER); + } + + @Override + public SystemPermissionSet getSystemPermissions() + throws GuacamoleException { + return systemPermissionService.getPermissionSet(getCurrentUser(), this); + } + + @Override + public ObjectPermissionSet getConnectionPermissions() + throws GuacamoleException { + return connectionPermissionService.getPermissionSet(getCurrentUser(), this); + } + + @Override + public ObjectPermissionSet getConnectionGroupPermissions() + throws GuacamoleException { + return connectionGroupPermissionService.getPermissionSet(getCurrentUser(), this); + } + + @Override + public ObjectPermissionSet getUserPermissions() + throws GuacamoleException { + return userPermissionService.getPermissionSet(getCurrentUser(), this); + } + +} diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java similarity index 50% rename from extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUserContext.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java index 43f0728bb..fac5b9af9 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUserContext.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java @@ -20,29 +20,30 @@ * THE SOFTWARE. */ -package net.sourceforge.guacamole.net.auth.mysql; +package org.glyptodon.guacamole.auth.jdbc.user; +import org.glyptodon.guacamole.auth.jdbc.connectiongroup.RootConnectionGroup; +import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupDirectory; +import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionDirectory; import com.google.inject.Inject; +import com.google.inject.Provider; import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject; +import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionGroup; import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.User; -import org.glyptodon.guacamole.net.auth.UserContext; -import net.sourceforge.guacamole.net.auth.mysql.service.UserService; -import org.glyptodon.guacamole.net.auth.Credentials; /** - * The MySQL representation of a UserContext. + * UserContext implementation which is driven by an arbitrary, underlying + * database. + * * @author James Muehlner + * @author Michael Jumper */ -public class MySQLUserContext implements UserContext { - - /** - * The the user owning this context. The permissions of this user dictate - * the access given via the user and connection directories. - */ - private AuthenticatedUser currentUser; +public class UserContext extends RestrictedObject + implements org.glyptodon.guacamole.net.auth.UserContext { /** * User directory restricted by the permissions of the user associated @@ -50,47 +51,67 @@ public class MySQLUserContext implements UserContext { */ @Inject private UserDirectory userDirectory; - + /** - * The root connection group. + * Connection directory restricted by the permissions of the user + * associated with this context. */ @Inject - private MySQLConnectionGroup rootConnectionGroup; + private ConnectionDirectory connectionDirectory; /** - * Service for accessing users. + * Connection group directory restricted by the permissions of the user + * associated with this context. */ @Inject - private UserService userService; + private ConnectionGroupDirectory connectionGroupDirectory; /** - * Initializes the user and directories associated with this context. - * - * @param currentUser - * The user owning this context. + * Provider for creating the root group. */ + @Inject + private Provider rootGroupProvider; + + @Override public void init(AuthenticatedUser currentUser) { - this.currentUser = currentUser; + + super.init(currentUser); + + // Init directories userDirectory.init(currentUser); - rootConnectionGroup.init(null, null, - MySQLConstants.CONNECTION_GROUP_ROOT_IDENTIFIER, - MySQLConstants.CONNECTION_GROUP_ROOT_IDENTIFIER, - ConnectionGroup.Type.ORGANIZATIONAL, currentUser); + connectionDirectory.init(currentUser); + connectionGroupDirectory.init(currentUser); + } @Override public User self() { - return userService.retrieveUser(currentUser.getUserID()); + return getCurrentUser().getUser(); } @Override - public Directory getUserDirectory() throws GuacamoleException { + public Directory getUserDirectory() throws GuacamoleException { return userDirectory; } + @Override + public Directory getConnectionDirectory() throws GuacamoleException { + return connectionDirectory; + } + + @Override + public Directory getConnectionGroupDirectory() throws GuacamoleException { + return connectionGroupDirectory; + } + @Override public ConnectionGroup getRootConnectionGroup() throws GuacamoleException { - return rootConnectionGroup; + + // Build and return a root group for the current user + RootConnectionGroup rootGroup = rootGroupProvider.get(); + rootGroup.init(getCurrentUser()); + return rootGroup; + } } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContextService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContextService.java new file mode 100644 index 000000000..c0a31074f --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContextService.java @@ -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; + +/** + * 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 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 org.glyptodon.guacamole.net.auth.UserContext + getUserContext(Credentials credentials) + throws GuacamoleException { + + // Authenticate user + ModeledUser user = userService.retrieveUser(credentials); + if (user != null) { + + // Upon successful authentication, return new user context + UserContext context = userContextProvider.get(); + context.init(user.getCurrentUser()); + return context; + + } + + // Otherwise, unauthorized + return null; + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserDirectory.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserDirectory.java new file mode 100644 index 000000000..826957b89 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserDirectory.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2013 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 java.util.Collection; +import java.util.Collections; +import java.util.Set; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject; +import org.glyptodon.guacamole.net.auth.Directory; +import org.glyptodon.guacamole.net.auth.User; +import org.mybatis.guice.transactional.Transactional; + +/** + * Implementation of the User Directory which is driven by an underlying, + * arbitrary database. + * + * @author James Muehlner + * @author Michael Jumper + */ +public class UserDirectory extends RestrictedObject + implements Directory { + + /** + * Service for managing user objects. + */ + @Inject + private UserService userService; + + @Override + public User get(String identifier) throws GuacamoleException { + return userService.retrieveObject(getCurrentUser(), identifier); + } + + @Override + @Transactional + public Collection getAll(Collection identifiers) throws GuacamoleException { + Collection objects = userService.retrieveObjects(getCurrentUser(), identifiers); + return Collections.unmodifiableCollection(objects); + } + + @Override + @Transactional + public Set getIdentifiers() throws GuacamoleException { + return userService.getIdentifiers(getCurrentUser()); + } + + @Override + @Transactional + public void add(User object) throws GuacamoleException { + userService.createObject(getCurrentUser(), object); + } + + @Override + @Transactional + public void update(User object) throws GuacamoleException { + ModeledUser user = (ModeledUser) object; + userService.updateObject(getCurrentUser(), user); + } + + @Override + @Transactional + public void remove(String identifier) throws GuacamoleException { + userService.deleteObject(getCurrentUser(), identifier); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.java new file mode 100644 index 000000000..0563196de --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.java @@ -0,0 +1,64 @@ +/* + * 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 org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectMapper; +import org.apache.ibatis.annotations.Param; + +/** + * Mapper for user objects. + * + * @author Michael Jumper + */ +public interface UserMapper extends DirectoryObjectMapper { + + /** + * Returns the user having the given username and password, if any. If no + * such user exists, null is returned. + * + * @param username + * The username of the user to return. + * + * @param password + * The password of the user to return. + * + * @return + * The user having the given username and password, or null if no such + * user exists. + */ + UserModel selectOneByCredentials(@Param("username") String username, + @Param("password") String password); + + /** + * Returns the user having the given username, if any. If no such user + * exists, null is returned. + * + * @param username + * The username of the user to return. + * + * @return + * The user having the given username, or null if no such user exists. + */ + UserModel selectOne(@Param("username") String username); + +} \ No newline at end of file diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserModel.java new file mode 100644 index 000000000..546b1927a --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserModel.java @@ -0,0 +1,100 @@ +/* + * 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 org.glyptodon.guacamole.auth.jdbc.base.ObjectModel; + +/** + * Object representation of a Guacamole user, as represented in the database. + * + * @author Michael Jumper + */ +public class UserModel extends ObjectModel { + + /** + * The SHA-256 hash of the password and salt. + */ + private byte[] passwordHash; + + /** + * The 32-byte random binary password salt that was appended to the + * password prior to hashing. + */ + private byte[] passwordSalt; + + /** + * Creates a new, empty user. + */ + public UserModel() { + } + + /** + * Returns the hash of this user's password and password salt. This may be + * null if the user was not retrieved from the database, and setPassword() + * has not yet been called. + * + * @return + * The hash of this user's password and password salt. + */ + public byte[] getPasswordHash() { + return passwordHash; + } + + /** + * Sets the hash of this user's password and password salt. This is + * normally only set upon retrieval from the database, or through a call + * to the higher-level setPassword() function. + * + * @param passwordHash + * The hash of this user's password and password salt. + */ + public void setPasswordHash(byte[] passwordHash) { + this.passwordHash = passwordHash; + } + + /** + * Returns the random salt that was used when generating this user's + * password hash. This may be null if the user was not retrieved from the + * database, and setPassword() has not yet been called. + * + * @return + * The random salt that was used when generating this user's password + * hash. + */ + public byte[] getPasswordSalt() { + return passwordSalt; + } + + /** + * Sets the random salt that was used when generating this user's password + * hash. This is normally only set upon retrieval from the database, or + * through a call to the higher-level setPassword() function. + * + * @param passwordSalt + * The random salt used when generating this user's password hash. + */ + public void setPasswordSalt(byte[] passwordSalt) { + this.passwordSalt = passwordSalt; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserService.java new file mode 100644 index 000000000..1fd24dc54 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserService.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2013 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 java.util.Collection; +import java.util.Collections; +import org.glyptodon.guacamole.net.auth.Credentials; +import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectMapper; +import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectService; +import org.glyptodon.guacamole.GuacamoleClientException; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionMapper; +import org.glyptodon.guacamole.auth.jdbc.permission.UserPermissionMapper; +import org.glyptodon.guacamole.net.auth.User; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; +import org.glyptodon.guacamole.net.auth.permission.SystemPermission; +import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; + +/** + * Service which provides convenience methods for creating, retrieving, and + * manipulating users. + * + * @author Michael Jumper, James Muehlner + */ +public class UserService extends DirectoryObjectService { + + /** + * Mapper for accessing users. + */ + @Inject + private UserMapper userMapper; + + /** + * Mapper for manipulating user permissions. + */ + @Inject + private UserPermissionMapper userPermissionMapper; + + /** + * Provider for creating users. + */ + @Inject + private Provider userProvider; + + @Override + protected DirectoryObjectMapper getObjectMapper() { + return userMapper; + } + + @Override + protected ObjectPermissionMapper getPermissionMapper() { + return userPermissionMapper; + } + + @Override + protected ModeledUser getObjectInstance(AuthenticatedUser currentUser, + UserModel model) { + ModeledUser user = userProvider.get(); + user.init(currentUser, model); + return user; + } + + @Override + protected UserModel getModelInstance(AuthenticatedUser currentUser, + final User object) { + + // Create new ModeledUser backed by blank model + UserModel model = new UserModel(); + ModeledUser user = getObjectInstance(currentUser, model); + + // Set model contents through ModeledUser, copying the provided user + user.setIdentifier(object.getIdentifier()); + user.setPassword(object.getPassword()); + + return model; + + } + + @Override + protected boolean hasCreatePermission(AuthenticatedUser user) + throws GuacamoleException { + + // Return whether user has explicit user creation permission + SystemPermissionSet permissionSet = user.getUser().getSystemPermissions(); + return permissionSet.hasPermission(SystemPermission.Type.CREATE_USER); + + } + + @Override + protected ObjectPermissionSet getPermissionSet(AuthenticatedUser user) + throws GuacamoleException { + + // Return permissions related to users + return user.getUser().getUserPermissions(); + + } + + @Override + protected void validateNewModel(AuthenticatedUser user, UserModel model) + throws GuacamoleException { + + // Username must not be blank + if (model.getIdentifier().trim().isEmpty()) + throw new GuacamoleClientException("The username must not be blank."); + + // Do not create duplicate users + Collection existing = userMapper.select(Collections.singleton(model.getIdentifier())); + if (!existing.isEmpty()) + throw new GuacamoleClientException("User \"" + model.getIdentifier() + "\" already exists."); + + } + + @Override + protected void validateExistingModel(AuthenticatedUser user, + UserModel model) throws GuacamoleException { + + // Username must not be blank + if (model.getIdentifier().trim().isEmpty()) + throw new GuacamoleClientException("The username must not be blank."); + + // Check whether such a user is already present + UserModel existing = userMapper.selectOne(model.getIdentifier()); + if (existing != null) { + + // Do not rename to existing user + if (!existing.getObjectID().equals(model.getObjectID())) + throw new GuacamoleClientException("User \"" + model.getIdentifier() + "\" already exists."); + + } + + } + + /** + * Retrieves the user corresponding to the given credentials from the + * database. + * + * @param credentials + * The credentials to use when locating the user. + * + * @return + * The existing ModeledUser object if the credentials given are valid, + * null otherwise. + */ + public ModeledUser retrieveUser(Credentials credentials) { + + // Get username and password + String username = credentials.getUsername(); + String password = credentials.getPassword(); + + // Retrieve user model, if the user exists + UserModel userModel = userMapper.selectOneByCredentials(username, password); + if (userModel == null) + return null; + + // Return corresponding user, set up cyclic reference + ModeledUser user = getObjectInstance(null, userModel); + user.setCurrentUser(new AuthenticatedUser(user, credentials)); + return user; + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/package-info.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/package-info.java new file mode 100644 index 000000000..e5c15703f --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/package-info.java @@ -0,0 +1,26 @@ +/* + * 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. + */ + +/** + * Classes related to Guacamole users. + */ +package org.glyptodon.guacamole.auth.jdbc.user; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/.gitignore b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/.gitignore new file mode 100644 index 000000000..42f4a1a64 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/.gitignore @@ -0,0 +1,2 @@ +target/ +*~ diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/pom.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/pom.xml new file mode 100644 index 000000000..47dbf1797 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/pom.xml @@ -0,0 +1,78 @@ + + + 4.0.0 + org.glyptodon.guacamole + guacamole-auth-jdbc-mysql + jar + guacamole-auth-jdbc-mysql + http://guac-dev.org/ + + + UTF-8 + + + + org.glyptodon.guacamole + guacamole-auth-jdbc + 0.9.5 + ../../ + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.6 + 1.6 + + + + + + maven-assembly-plugin + 2.2-beta-5 + + + jar-with-dependencies + package + + single + + + extension/${project.artifactId}-${project.version} + false + + jar-with-dependencies + + + + + + + + + + + + + + org.glyptodon.guacamole + guacamole-ext + provided + + + + + org.glyptodon.guacamole + guacamole-auth-jdbc-base + 0.9.5 + + + + + diff --git a/extensions/guacamole-auth-mysql/schema/001-create-schema.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/001-create-schema.sql similarity index 100% rename from extensions/guacamole-auth-mysql/schema/001-create-schema.sql rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/001-create-schema.sql diff --git a/extensions/guacamole-auth-mysql/schema/002-create-admin-user.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/002-create-admin-user.sql similarity index 100% rename from extensions/guacamole-auth-mysql/schema/002-create-admin-user.sql rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/002-create-admin-user.sql diff --git a/extensions/guacamole-auth-mysql/schema/upgrade/upgrade-pre-0.8.2.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/upgrade/upgrade-pre-0.8.2.sql similarity index 100% rename from extensions/guacamole-auth-mysql/schema/upgrade/upgrade-pre-0.8.2.sql rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/upgrade/upgrade-pre-0.8.2.sql diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java new file mode 100644 index 000000000..8547fcf3d --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2013 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.Guice; +import com.google.inject.Injector; +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.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 + * functionality. + * + * @author James Muehlner + * @author Michael Jumper + */ +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 + * authentication data to a MySQL database defined by properties in + * guacamole.properties. + * + * @throws GuacamoleException + * If a required property is missing, or an error occurs while parsing + * a property. + */ + 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; + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProviderModule.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProviderModule.java new file mode 100644 index 000000000..e5af46928 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProviderModule.java @@ -0,0 +1,98 @@ +/* + * 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.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); + + } + +} diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/properties/MySQLGuacamoleProperties.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLGuacamoleProperties.java similarity index 98% rename from extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/properties/MySQLGuacamoleProperties.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLGuacamoleProperties.java index a4e7ef044..3a599fc93 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/properties/MySQLGuacamoleProperties.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLGuacamoleProperties.java @@ -20,7 +20,7 @@ * THE SOFTWARE. */ -package net.sourceforge.guacamole.net.auth.mysql.properties; +package net.sourceforge.guacamole.net.auth.mysql; import org.glyptodon.guacamole.properties.BooleanGuacamoleProperty; import org.glyptodon.guacamole.properties.IntegerGuacamoleProperty; diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/package-info.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/package-info.java similarity index 91% rename from extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/package-info.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/package-info.java index b21abfcae..65dc294e6 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/package-info.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/package-info.java @@ -21,8 +21,7 @@ */ /** - * Base classes which support the MySQL authentication provider, including - * the authentication provider itself. + * The MySQL authentication provider. This package exists outside of + * org.glyptodon for backwards-compatibility. */ package net.sourceforge.guacamole.net.auth.mysql; - diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionMapper.xml new file mode 100644 index 000000000..2211da069 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionMapper.xml @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DELETE FROM guacamole_connection + WHERE connection_id = #{identifier,jdbcType=VARCHAR} + + + + + + INSERT INTO guacamole_connection ( + connection_name, + parent_id, + protocol + ) + VALUES ( + #{object.name,jdbcType=VARCHAR}, + #{object.parentIdentifier,jdbcType=VARCHAR}, + #{object.protocol,jdbcType=VARCHAR} + ) + + + + + + UPDATE guacamole_connection + SET connection_name = #{object.name,jdbcType=VARCHAR}, + parent_id = #{object.parentIdentifier,jdbcType=VARCHAR}, + protocol = #{object.protocol,jdbcType=VARCHAR} + WHERE connection_id = #{object.objectID,jdbcType=INTEGER} + + + \ No newline at end of file diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml new file mode 100644 index 000000000..b5775f607 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + INSERT INTO guacamole_connection_history ( + connection_id, + user_id, + start_date, + end_date + ) + VALUES ( + #{record.connectionIdentifier,jdbcType=VARCHAR}, + #{record.userID,jdbcType=INTEGER}, + #{record.startDate,jdbcType=TIMESTAMP}, + #{record.endDate,jdbcType=TIMESTAMP} + ) + + + + \ No newline at end of file diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ParameterMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ParameterMapper.xml new file mode 100644 index 000000000..ccd386c14 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ParameterMapper.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + DELETE FROM guacamole_connection_parameter + WHERE connection_id = #{identifier,jdbcType=VARCHAR} + + + + + + INSERT INTO guacamole_connection_parameter ( + connection_id, + parameter_name, + parameter_value + ) + VALUES + + (#{parameter.connectionIdentifier,jdbcType=VARCHAR}, + #{parameter.name,jdbcType=VARCHAR}, + #{parameter.value,jdbcType=VARCHAR}) + + + + + + \ No newline at end of file diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupMapper.xml new file mode 100644 index 000000000..4eb20da1c --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupMapper.xml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DELETE FROM guacamole_connection_group + WHERE connection_group_id = #{identifier,jdbcType=VARCHAR} + + + + + + INSERT INTO guacamole_connection_group ( + connection_group_name, + parent_id, + type + ) + VALUES ( + #{object.name,jdbcType=VARCHAR}, + #{object.parentIdentifier,jdbcType=VARCHAR}, + #{object.type,jdbcType=VARCHAR} + ) + + + + + + UPDATE guacamole_connection_group + SET connection_group_name = #{object.name,jdbcType=VARCHAR}, + parent_id = #{object.parentIdentifier,jdbcType=VARCHAR}, + type = #{object.type,jdbcType=VARCHAR} + WHERE connection_group_id = #{object.objectID,jdbcType=INTEGER} + + + \ No newline at end of file diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionMapper.xml new file mode 100644 index 000000000..40ada12a5 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionMapper.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + DELETE FROM guacamole_connection_group_permission + WHERE (user_id, permission, connection_group_id) IN + + (#{permission.userID,jdbcType=INTEGER}, + #{permission.type,jdbcType=VARCHAR}, + #{permission.objectIdentifier,jdbcType=VARCHAR}) + + + + + + + + INSERT IGNORE INTO guacamole_connection_group_permission ( + user_id, + permission, + connection_group_id + ) + VALUES + + (#{permission.userID,jdbcType=INTEGER}, + #{permission.type,jdbcType=VARCHAR}, + #{permission.objectIdentifier,jdbcType=VARCHAR}) + + + + + \ No newline at end of file diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionMapper.xml new file mode 100644 index 000000000..9935f3cfd --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionMapper.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + DELETE FROM guacamole_connection_permission + WHERE (user_id, permission, connection_id) IN + + (#{permission.userID,jdbcType=INTEGER}, + #{permission.type,jdbcType=VARCHAR}, + #{permission.objectIdentifier,jdbcType=VARCHAR}) + + + + + + + + INSERT IGNORE INTO guacamole_connection_permission ( + user_id, + permission, + connection_id + ) + VALUES + + (#{permission.userID,jdbcType=INTEGER}, + #{permission.type,jdbcType=VARCHAR}, + #{permission.objectIdentifier,jdbcType=VARCHAR}) + + + + + \ No newline at end of file diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionMapper.xml new file mode 100644 index 000000000..55eacd072 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionMapper.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + DELETE FROM guacamole_system_permission + WHERE (user_id, permission) IN + + (#{permission.userID,jdbcType=INTEGER}, + #{permission.type,jdbcType=VARCHAR}) + + + + + + + + INSERT IGNORE INTO guacamole_system_permission ( + user_id, + permission + ) + VALUES + + (#{permission.userID,jdbcType=INTEGER}, + #{permission.type,jdbcType=VARCHAR}) + + + + + \ No newline at end of file diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionMapper.xml new file mode 100644 index 000000000..038bb814f --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionMapper.xml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + DELETE FROM guacamole_user_permission + USING guacamole_user_permission + JOIN guacamole_user affected ON guacamole_user_permission.affected_user_id = affected.user_id + WHERE + (guacamole_user_permission.user_id, permission, affected.username) IN + + (#{permission.userID,jdbcType=INTEGER}, + #{permission.type,jdbcType=VARCHAR}, + #{permission.objectIdentifier,jdbcType=VARCHAR}) + + + + + + + + INSERT IGNORE INTO guacamole_user_permission ( + user_id, + permission, + affected_user_id + ) + SELECT permissions.user_id, permissions.permission, guacamole_user.user_id FROM + + SELECT #{permission.userID,jdbcType=INTEGER} AS user_id, + #{permission.type,jdbcType=VARCHAR} AS permission, + #{permission.objectIdentifier,jdbcType=VARCHAR} AS username + + AS permissions + JOIN guacamole_user ON guacamole_user.username = permissions.username; + + + + \ No newline at end of file diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.xml new file mode 100644 index 000000000..fb7e6ff55 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.xml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DELETE FROM guacamole_user + WHERE username = #{identifier,jdbcType=VARCHAR} + + + + + + INSERT INTO guacamole_user ( + username, + password_hash, + password_salt + ) + VALUES ( + #{object.identifier,jdbcType=VARCHAR}, + #{object.passwordHash,jdbcType=BINARY}, + #{object.passwordSalt,jdbcType=BINARY} + ) + + + + + + UPDATE guacamole_user + SET password_hash = #{object.passwordHash,jdbcType=BINARY}, + password_salt = #{object.passwordSalt,jdbcType=BINARY} + WHERE user_id = #{object.objectID,jdbcType=VARCHAR} + + + \ No newline at end of file diff --git a/extensions/guacamole-auth-jdbc/pom.xml b/extensions/guacamole-auth-jdbc/pom.xml new file mode 100644 index 000000000..c6c622994 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + org.glyptodon.guacamole + guacamole-auth-jdbc + pom + 0.9.5 + guacamole-auth-jdbc + http://guac-dev.org/ + + + UTF-8 + + + + + + modules/guacamole-auth-jdbc-base + + + modules/guacamole-auth-jdbc-mysql + + + + + + + + + maven-assembly-plugin + 2.2-beta-5 + false + + + make-dist-archive + package + + single + + + ${project.artifactId}-${project.version} + false + + src/main/assembly/dist.xml + + + + + + + + + + + + + + + org.glyptodon.guacamole + guacamole-ext + 0.9.5 + provided + + + + + + diff --git a/extensions/guacamole-auth-jdbc/src/main/assembly/dist.xml b/extensions/guacamole-auth-jdbc/src/main/assembly/dist.xml new file mode 100644 index 000000000..2d20b63a0 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/src/main/assembly/dist.xml @@ -0,0 +1,32 @@ + + + dist + ${project.artifactId}-${project.version} + + + + tar.gz + + + + + + + + /mysql/schema + modules/guacamole-auth-jdbc-mysql/schema + + + modules/guacamole-auth-jdbc-mysql/target/extension + /mysql + + *.jar + + + + + + diff --git a/extensions/guacamole-auth-mysql/README b/extensions/guacamole-auth-mysql/README deleted file mode 100644 index 5543c124f..000000000 --- a/extensions/guacamole-auth-mysql/README +++ /dev/null @@ -1,171 +0,0 @@ - ------------------------------------------------------------- - About this README ------------------------------------------------------------- - -This README is intended to provide quick and to-the-point documentation for -technical users intending to compile parts of Guacamole themselves. - -Distribution-specific packages are available from the files section of the main -project page: - - http://sourceforge.net/projects/guacamole/files/ - -Distribution-specific documentation is provided on the Guacamole wiki: - - http://guac-dev.org/ - - ------------------------------------------------------------- - What is guacamole-auth-mysql? ------------------------------------------------------------- - -guacamole-auth-ldap is a Java library for use with the Guacamole web -application to provide MySQL based authentication. - -guacamole-auth-mysql provides an authentication provider which can be -set in guacamole.properties to allow MySQL authentication of Guacamole -users. Additional properties are required to configure the mysql -connection parameters. - -A schema file are provided to create the required tables in your -mysql database. - - ------------------------------------------------------------- - Compiling and installing guacamole-auth-mysql ------------------------------------------------------------- - -guacamole-auth-mysql is built using Maven. Building guacamole-auth-mysql -compiles all classes and packages them into a redistributable .jar file. This -.jar file can be installed in the library directory configured in -guacamole.properties such that the authentication provider is available. - -1) Set up a MySQL database with the Guacamole schema. - - When guacamole-auth-mysql is compiling, it needs to generate source - based on a database schema. Because the source generator uses a - connection to an actual database to do this, you must have a MySQL - database running with the Guacamole schema set up. - - First, create a database. For the sake of these instructions, we will - call the database "guacamole", and will run all scripts as the root user: - - $ mysql -u root -p - Enter password: - mysql> CREATE DATABASE guacamole; - Query OK, 1 row affected (0.00 sec) - - mysql> exit - Bye - - The schema files are in the schema/ subdirectory of the source. If run - in order, they will create the schema and a default user: - - $ cat schema/*.sql | mysql -u root -p guacamole - -2) Set up your ~/.m2/settings.xml - - Once the database is set up, Maven will need to have the credentials - required to connect to it and query the schema. This information is - specified in properties inside your ~/.m2/settings.xml file. If this - file does not exist yet, simply create it. - - For ease of compilation, we've included an example settings.xml - defining the required properties in doc/example/settings.xml. You can - simply copy this file into ~/.m2 and edit as necessary. - - If you wish to write the file yourself, the file should look like this in - general: - - - - ...profiles... - - - - We need to add a profile which defines the required properties by - placing a section like the following within the "profiles" section of your - settings.xml: - - - guacamole-mybatis - - DATABASE - USERNAME - PASSWORD - - - - Obviously, the DATABASE, USERNAME, and PASSWORD placeholders above must - be replaced with the appropriate values for your system. - - Finally, to make the profile available to the build, it must be activated. - Place a section like the following at the bottom of your settings.xml, - right after the profiles section: - - - guacamole-mybatis - - - Maven's documentation has more details on writing the settings.xml file - if you have different needs or the above directions are not clear. - -3) Run mvn package - - $ mvn package - - Maven will download any needed dependencies for building the .jar file. - Once all dependencies have been downloaded, the .jar file will be - created in the target/ subdirectory of the current directory. - - If this process fails, check the build errors, and verify that the - contents of your settings.xml file is correct. - -4) Extract the .tar.gz file now present in the target/ directory, and - place the .jar files in the extracted lib/ subdirectory in the library - directory specified in guacamole.properties. - - You will likely need to do this as root. - - If you do not have a library directory configured in your - guacamole.properties, you will need to specify one. The directory - is specified using the "lib-directory" property. - -5) Set up your MySQL database to authenticate Guacamole users - - A schema file is provided in the schema directory for creating - the guacamole authentication tables in your MySQL database. - - Additionally, a script is provided to create a default admin user - with username 'guacadmin' and password 'guacadmin'. This user can - be used to set up any other connections and users. - -6) Configure guacamole.properties for MySQL - - There are additional properties required by the MySQL JDBC driver - which must be added/changed in your guacamole.properties: - - # Configuration for MySQL connection - mysql-hostname: mysql.host.name - mysql-port: 3306 - mysql-database: guacamole.database.name - mysql-username: user - mysql-password: pass - - Optionally, the authentication provider can be configured - not to allow multiple users to use the same connection - at the same time: - - mysql-disallow-simultaneous-connections: true - - ------------------------------------------------------------- - Reporting problems ------------------------------------------------------------- - -Please report any bugs encountered by opening a new ticket at the Trac system -hosted at: - - http://guac-dev.org/trac/ - diff --git a/extensions/guacamole-auth-mysql/doc/example/settings.xml b/extensions/guacamole-auth-mysql/doc/example/settings.xml deleted file mode 100644 index d0fb6d5bd..000000000 --- a/extensions/guacamole-auth-mysql/doc/example/settings.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - guacamole-mybatis - - SCHEMA - DATABASE - USER - PASS - - - - - - - guacamole-mybatis - - - diff --git a/extensions/guacamole-auth-mysql/pom.xml b/extensions/guacamole-auth-mysql/pom.xml deleted file mode 100644 index e4a4f6544..000000000 --- a/extensions/guacamole-auth-mysql/pom.xml +++ /dev/null @@ -1,125 +0,0 @@ - - - 4.0.0 - org.glyptodon.guacamole - guacamole-auth-mysql - jar - 0.9.5 - guacamole-auth-mysql - http://guac-dev.org/ - - - UTF-8 - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.6 - 1.6 - - - - - - maven-assembly-plugin - 2.2-beta-5 - - ${project.artifactId}-${project.version} - false - - src/main/assembly/dist.xml - - - - - make-dist-archive - package - - single - - - - - - - - org.mybatis.generator - mybatis-generator-maven-plugin - 1.3.2 - - - - Generate MyBatis Artifacts - - generate - - - - - - - - mysql - mysql-connector-java - 5.1.23 - - - - - - - - - - - - - org.glyptodon.guacamole - guacamole-common - 0.9.4 - - - - - org.glyptodon.guacamole - guacamole-ext - 0.9.5 - - - - - org.slf4j - slf4j-api - 1.7.7 - - - - - org.mybatis - mybatis - 3.1.1 - - - - - org.mybatis - mybatis-guice - 3.2 - - - - - com.google.collections - google-collections - 1.0 - - - - diff --git a/extensions/guacamole-auth-mysql/src/main/assembly/dist.xml b/extensions/guacamole-auth-mysql/src/main/assembly/dist.xml deleted file mode 100644 index 0628ad61c..000000000 --- a/extensions/guacamole-auth-mysql/src/main/assembly/dist.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - dist - ${project.artifactId}-${project.version} - - - - tar.gz - - - - - - - - / - doc - - - - - /schema - schema - - - - - - - - - /lib - runtime - false - true - true - - - - - org.glyptodon.guacamole:guacamole-common - - - org.glyptodon.guacamole:guacamole-ext - - - - - - diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ActiveConnectionMap.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ActiveConnectionMap.java deleted file mode 100644 index 272434aa9..000000000 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ActiveConnectionMap.java +++ /dev/null @@ -1,502 +0,0 @@ -/* - * Copyright (C) 2013 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.Inject; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import org.glyptodon.guacamole.GuacamoleException; -import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionHistoryMapper; -import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionHistory; -import org.glyptodon.guacamole.GuacamoleResourceNotFoundException; - -/** - * Represents the map of currently active Connections to the count of the number - * of current users. Whenever a socket is opened, the connection count should be - * incremented, and whenever a socket is closed, the connection count should be - * decremented. - * - * @author James Muehlner - */ -public class ActiveConnectionMap { - - /** - * Represents the count of users currently using a MySQL connection. - */ - public class Connection { - - /** - * The ID of the MySQL connection that this Connection represents. - */ - private int connectionID; - - /** - * The number of users currently using this connection. - */ - private int currentUserCount; - - /** - * Returns the ID of the MySQL connection that this Connection - * represents. - * - * @return the ID of the MySQL connection that this Connection - * represents. - */ - public int getConnectionID() { - return connectionID; - } - - /** - * Returns the number of users currently using this connection. - * - * @return the number of users currently using this connection. - */ - public int getCurrentUserCount() { - return currentUserCount; - } - - /** - * Set the current user count for this connection. - * - * @param currentUserCount The new user count for this Connection. - */ - public void setCurrentUserCount(int currentUserCount) { - this.currentUserCount = currentUserCount; - } - - /** - * Create a new Connection for the given connectionID with a zero - * current user count. - * - * @param connectionID The ID of the MySQL connection that this - * Connection represents. - */ - public Connection(int connectionID) { - this.connectionID = connectionID; - this.currentUserCount = 0; - } - } - - /* - * 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 identifier; - - /** - * 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 getIdentifier() { - return identifier; - } - - /** - * 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 identifier The connection or connection group ID that this - * ConnectionUser refers to. - * @param userID The user ID that this ConnectionUser refers to. - */ - public ConnectionUser(int identifier, int userID) { - this.identifier = identifier; - 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.identifier == otherConnectionGroupUser.identifier - && this.userID == otherConnectionGroupUser.userID; - } - - @Override - public int hashCode() { - int hash = 3; - hash = 23 * hash + this.identifier; - hash = 23 * hash + this.userID; - return hash; - } - } - - /** - * DAO for accessing connection history. - */ - @Inject - private ConnectionHistoryMapper connectionHistoryDAO; - - /** - * Map of all the connections that are currently active to the - * count of current users. - */ - private Map activeConnectionMap = - new HashMap(); - - /** - * Map of all the connection group users to the count of current usages. - */ - private Map activeConnectionGroupUserMap = - new HashMap(); - - /** - * Map of all the connection users to the count of current usages. - */ - private Map activeConnectionUserMap = - new HashMap(); - - /** - * Returns the number of connections opened by the given user using - * the given ConnectionGroup. - * - * @param connectionGroupID 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 = getConnectionUserCount(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 = getConnectionUserCount(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. - * - * @param connectionIDs The subset of connection IDs to find the least - * used connection within. - * - * @return The ID of the connection with the lowest number of current - * active users, if found. - */ - public Integer getLeastUsedConnection(Collection connectionIDs) { - - if(connectionIDs.isEmpty()) - return null; - - int minUserCount = Integer.MAX_VALUE; - Integer minConnectionID = null; - - for(Integer connectionID : connectionIDs) { - Connection connection = activeConnectionMap.get(connectionID); - - /* - * If the connection is not found in the map, it has not been used, - * and therefore will be count 0. - */ - if(connection == null) { - minUserCount = 0; - minConnectionID = connectionID; - } - // If this is the least active connection - else if(connection.getCurrentUserCount() < minUserCount) { - minUserCount = connection.getCurrentUserCount(); - minConnectionID = connection.getConnectionID(); - } - } - - return minConnectionID; - } - - /** - * Returns the count of currently active users for the given connectionID. - * @return the count of currently active users for the given connectionID. - */ - public int getCurrentUserCount(int connectionID) { - Connection connection = activeConnectionMap.get(connectionID); - - if(connection == null) - return 0; - - return connection.getCurrentUserCount(); - } - - /** - * Decrement the current user count for this Connection. - * - * @param connectionID The ID of the MySQL connection that this - * Connection represents. - * - * @throws GuacamoleException If the connection is not found. - */ - private void decrementUserCount(int connectionID) - throws GuacamoleException { - Connection connection = activeConnectionMap.get(connectionID); - - if(connection == null) - throw new GuacamoleResourceNotFoundException - ("Connection to decrement does not exist."); - - // Decrement the current user count - connection.setCurrentUserCount(connection.getCurrentUserCount() - 1); - } - - /** - * Increment the current user count for this Connection. - * - * @param connectionID The ID of the MySQL connection that this - * Connection represents. - * - * @throws GuacamoleException If the connection is not found. - */ - private void incrementUserCount(int connectionID) { - Connection connection = activeConnectionMap.get(connectionID); - - // If the Connection does not exist, it should be created - if(connection == null) { - connection = new Connection(connectionID); - activeConnectionMap.put(connectionID, connection); - } - - // Increment the current user count - connection.setCurrentUserCount(connection.getCurrentUserCount() + 1); - } - - /** - * 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 getCurrentUserCount(connectionID) > 0; - } - - /** - * 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 connectionGroupID 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, Integer connectionGroupID) { - - // 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); - - // 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 historyID The ID of the history record about the open connection. - * @param connectionGroupID 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 historyID, Integer connectionGroupID) - throws GuacamoleException { - - // Get the existing history record - ConnectionHistory connectionHistory = - connectionHistoryDAO.selectByPrimaryKey(historyID); - - if(connectionHistory == null) - throw new GuacamoleResourceNotFoundException("History record not found."); - - // Get the connection and user IDs - int connectionID = connectionHistory.getConnection_id(); - int userID = connectionHistory.getUser_id(); - - // Update the connection history record to mark that it is now closed - connectionHistory.setEnd_date(new Date()); - connectionHistoryDAO.updateByPrimaryKey(connectionHistory); - - // 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); - } -} 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 deleted file mode 100644 index e9fde29dc..000000000 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionDirectory.java +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) 2013 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.Inject; -import java.util.Set; -import org.glyptodon.guacamole.GuacamoleClientException; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.auth.Connection; -import org.glyptodon.guacamole.net.auth.Directory; -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.ConnectionPermissionKey; -import net.sourceforge.guacamole.net.auth.mysql.service.ConnectionGroupService; -import net.sourceforge.guacamole.net.auth.mysql.service.ConnectionService; -import net.sourceforge.guacamole.net.auth.mysql.service.PermissionCheckService; -import org.glyptodon.guacamole.GuacamoleResourceNotFoundException; -import org.glyptodon.guacamole.GuacamoleUnsupportedException; -import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; -import org.mybatis.guice.transactional.Transactional; - -/** - * A MySQL-based implementation of the connection directory. - * - * @author James Muehlner - */ -public class ConnectionDirectory implements Directory{ - - /** - * The user who this connection directory belongs to. Access is based on - * his/her permission settings. - */ - private AuthenticatedUser currentUser; - - /** - * The ID of the parent connection group. - */ - private Integer parentID; - - /** - * Service for checking permissions. - */ - @Inject - private PermissionCheckService permissionCheckService; - - /** - * Service managing connections. - */ - @Inject - private ConnectionService connectionService; - - /** - * Service managing connection groups. - */ - @Inject - private ConnectionGroupService connectionGroupService; - - /** - * Service for manipulating connection permissions in the database. - */ - @Inject - private ConnectionPermissionMapper connectionPermissionDAO; - - /** - * Service for manipulating connection parameters in the database. - */ - @Inject - private ConnectionParameterMapper connectionParameterDAO; - - /** - * Set the user and parentID for this directory. - * - * @param currentUser - * The user owning this connection directory. - * - * @param parentID - * The ID of the parent connection group. - */ - public void init(AuthenticatedUser currentUser, Integer parentID) { - this.currentUser = currentUser; - this.parentID = parentID; - } - - @Transactional - @Override - public Connection get(String identifier) throws GuacamoleException { - - // Get connection - MySQLConnection connection = - connectionService.retrieveConnection(identifier, currentUser); - - if(connection == null) - return null; - - // Verify permission to use the parent connection group for organizational purposes - permissionCheckService.verifyConnectionGroupUsageAccess - (connection.getParentID(), currentUser, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL); - - // Verify access is granted - permissionCheckService.verifyConnectionAccess( - currentUser, - connection.getConnectionID(), - MySQLConstants.CONNECTION_READ); - - // Return connection - return connection; - - } - - @Transactional - @Override - public Set getIdentifiers() throws GuacamoleException { - - // Verify permission to use the connection group for organizational purposes - permissionCheckService.verifyConnectionGroupUsageAccess - (parentID, currentUser, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL); - - return permissionCheckService.retrieveConnectionIdentifiers(currentUser, - parentID, MySQLConstants.CONNECTION_READ); - } - - @Transactional - @Override - public void add(Connection object) throws GuacamoleException { - - String name = object.getName().trim(); - if(name.isEmpty()) - throw new GuacamoleClientException("The connection name cannot be blank."); - - // Verify permission to create - permissionCheckService.verifySystemAccess(currentUser, - MySQLConstants.SYSTEM_CONNECTION_CREATE); - - // Verify permission to edit the connection group - permissionCheckService.verifyConnectionGroupAccess(currentUser, - this.parentID, MySQLConstants.CONNECTION_GROUP_UPDATE); - - // Verify permission to use the connection group for organizational purposes - permissionCheckService.verifyConnectionGroupUsageAccess - (parentID, currentUser, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL); - - // Verify that no connection already exists with this name. - MySQLConnection previousConnection = - connectionService.retrieveConnection(name, parentID, currentUser); - if(previousConnection != null) - throw new GuacamoleClientException("That connection name is already in use."); - - // Create connection - MySQLConnection connection = connectionService.createConnection( - name, object.getConfiguration().getProtocol(), currentUser, parentID); - - // Set the connection ID - object.setIdentifier(connection.getIdentifier()); - - // Add connection parameters - createConfigurationValues(connection.getConnectionID(), - object.getConfiguration()); - - // Finally, give the current user full access to the newly created - // connection. - ConnectionPermissionKey newConnectionPermission = new ConnectionPermissionKey(); - newConnectionPermission.setUser_id(currentUser.getUserID()); - newConnectionPermission.setConnection_id(connection.getConnectionID()); - - // Read permission - newConnectionPermission.setPermission(MySQLConstants.CONNECTION_READ); - connectionPermissionDAO.insert(newConnectionPermission); - - // Update permission - newConnectionPermission.setPermission(MySQLConstants.CONNECTION_UPDATE); - connectionPermissionDAO.insert(newConnectionPermission); - - // Delete permission - newConnectionPermission.setPermission(MySQLConstants.CONNECTION_DELETE); - connectionPermissionDAO.insert(newConnectionPermission); - - // Administer permission - newConnectionPermission.setPermission(MySQLConstants.CONNECTION_ADMINISTER); - connectionPermissionDAO.insert(newConnectionPermission); - - } - - /** - * Inserts all parameter values from the given configuration into the - * database, associating them with the connection having the givenID. - * - * @param connection_id The ID of the connection to associate all - * parameters with. - * @param config The GuacamoleConfiguration to read parameters from. - */ - private void createConfigurationValues(int connection_id, - GuacamoleConfiguration config) { - - // Insert new parameters for each parameter in the config - for (String name : config.getParameterNames()) { - - // Create a ConnectionParameter based on the current parameter - ConnectionParameter parameter = new ConnectionParameter(); - parameter.setConnection_id(connection_id); - parameter.setParameter_name(name); - parameter.setParameter_value(config.getParameter(name)); - - // Insert connection parameter - connectionParameterDAO.insert(parameter); - } - - } - - @Transactional - @Override - public void update(Connection object) throws GuacamoleException { - - // If connection not actually from this auth provider, we can't handle - // the update - if (!(object instanceof MySQLConnection)) - throw new GuacamoleUnsupportedException("Connection not from database."); - - MySQLConnection mySQLConnection = (MySQLConnection) object; - - // Verify permission to update - permissionCheckService.verifyConnectionAccess(currentUser, - mySQLConnection.getConnectionID(), - MySQLConstants.CONNECTION_UPDATE); - - // Perform update - connectionService.updateConnection(mySQLConnection); - - // Delete old connection parameters - ConnectionParameterExample parameterExample = new ConnectionParameterExample(); - parameterExample.createCriteria().andConnection_idEqualTo(mySQLConnection.getConnectionID()); - connectionParameterDAO.deleteByExample(parameterExample); - - // Add connection parameters - createConfigurationValues(mySQLConnection.getConnectionID(), - object.getConfiguration()); - - } - - @Transactional - @Override - public void remove(String identifier) throws GuacamoleException { - - // Get connection - MySQLConnection mySQLConnection = - connectionService.retrieveConnection(identifier, currentUser); - - if(mySQLConnection == null) - throw new GuacamoleResourceNotFoundException("Connection not found."); - - // Verify permission to use the parent connection group for organizational purposes - permissionCheckService.verifyConnectionGroupUsageAccess - (mySQLConnection.getParentID(), currentUser, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL); - - // Verify permission to delete - permissionCheckService.verifyConnectionAccess(currentUser, - mySQLConnection.getConnectionID(), - MySQLConstants.CONNECTION_DELETE); - - // Delete the connection itself - connectionService.deleteConnection(mySQLConnection.getConnectionID()); - - } - - @Override - public void move(String identifier, Directory directory) - throws GuacamoleException { - - if(!(directory instanceof ConnectionDirectory)) - throw new GuacamoleUnsupportedException("Directory not from database"); - - Integer toConnectionGroupID = ((ConnectionDirectory)directory).parentID; - - // Get connection - MySQLConnection mySQLConnection = - connectionService.retrieveConnection(identifier, currentUser); - - if(mySQLConnection == null) - throw new GuacamoleResourceNotFoundException("Connection not found."); - - // Verify permission to update the connection - permissionCheckService.verifyConnectionAccess(currentUser, - mySQLConnection.getConnectionID(), - MySQLConstants.CONNECTION_UPDATE); - - // Verify permission to use the from connection group for organizational purposes - permissionCheckService.verifyConnectionGroupUsageAccess - (mySQLConnection.getParentID(), currentUser, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL); - - // Verify permission to update the from connection group - permissionCheckService.verifyConnectionGroupAccess(currentUser, - mySQLConnection.getParentID(), MySQLConstants.CONNECTION_GROUP_UPDATE); - - // Verify permission to use the to connection group for organizational purposes - permissionCheckService.verifyConnectionGroupUsageAccess - (toConnectionGroupID, currentUser, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL); - - // Verify permission to update the to connection group - permissionCheckService.verifyConnectionGroupAccess(currentUser, - toConnectionGroupID, MySQLConstants.CONNECTION_GROUP_UPDATE); - - // Verify that no connection already exists with this name. - MySQLConnection previousConnection = - connectionService.retrieveConnection(mySQLConnection.getName(), - toConnectionGroupID, currentUser); - if(previousConnection != null) - throw new GuacamoleClientException("That connection name is already in use."); - - // Update the connection - mySQLConnection.setParentID(toConnectionGroupID); - connectionService.updateConnection(mySQLConnection); - } - -} diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionGroupDirectory.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionGroupDirectory.java deleted file mode 100644 index 0064b7708..000000000 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionGroupDirectory.java +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright (C) 2013 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.Inject; -import java.util.Set; -import org.glyptodon.guacamole.GuacamoleClientException; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.auth.ConnectionGroup; -import org.glyptodon.guacamole.net.auth.ConnectionGroup.Type; -import org.glyptodon.guacamole.net.auth.Directory; -import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionGroupPermissionMapper; -import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionGroupPermissionKey; -import net.sourceforge.guacamole.net.auth.mysql.service.ConnectionGroupService; -import net.sourceforge.guacamole.net.auth.mysql.service.PermissionCheckService; -import org.glyptodon.guacamole.GuacamoleResourceNotFoundException; -import org.glyptodon.guacamole.GuacamoleUnsupportedException; -import org.mybatis.guice.transactional.Transactional; - -/** - * A MySQL-based implementation of the connection group directory. - * - * @author James Muehlner - */ -public class ConnectionGroupDirectory implements Directory{ - - /** - * The user who this connection directory belongs to. Access is based on - * his/her permission settings. - */ - private AuthenticatedUser currentUser; - - /** - * The ID of the parent connection group. - */ - private Integer parentID; - - /** - * Service for checking permissions. - */ - @Inject - private PermissionCheckService permissionCheckService; - - /** - * Service managing connection groups. - */ - @Inject - private ConnectionGroupService connectionGroupService; - - /** - * Service for manipulating connection group permissions in the database. - */ - @Inject - private ConnectionGroupPermissionMapper connectionGroupPermissionDAO; - - /** - * Set the user and parentID for this directory. - * - * @param currentUser - * The user owning this connection group directory. - * - * @param parentID - * The ID of the parent connection group. - */ - public void init(AuthenticatedUser currentUser, Integer parentID) { - this.parentID = parentID; - this.currentUser = currentUser; - } - - @Transactional - @Override - public ConnectionGroup get(String identifier) throws GuacamoleException { - - // Get connection - MySQLConnectionGroup connectionGroup = - connectionGroupService.retrieveConnectionGroup(identifier, currentUser); - - if(connectionGroup == null) - return null; - - // Verify permission to use the parent connection group for organizational purposes - permissionCheckService.verifyConnectionGroupUsageAccess - (connectionGroup.getParentID(), currentUser, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL); - - // Verify access is granted - permissionCheckService.verifyConnectionGroupAccess( - currentUser, - connectionGroup.getConnectionGroupID(), - MySQLConstants.CONNECTION_GROUP_READ); - - // Return connection group - return connectionGroup; - - } - - @Transactional - @Override - public Set getIdentifiers() throws GuacamoleException { - - // Verify permission to use the connection group for organizational purposes - permissionCheckService.verifyConnectionGroupUsageAccess - (parentID, currentUser, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL); - - return permissionCheckService.retrieveConnectionGroupIdentifiers(currentUser, - parentID, MySQLConstants.CONNECTION_GROUP_READ); - } - - @Transactional - @Override - public void add(ConnectionGroup object) throws GuacamoleException { - - String name = object.getName().trim(); - if(name.isEmpty()) - throw new GuacamoleClientException("The connection group name cannot be blank."); - - Type type = object.getType(); - - String mySQLType = MySQLConstants.getConnectionGroupTypeConstant(type); - - // Verify permission to create - permissionCheckService.verifySystemAccess(currentUser, - MySQLConstants.SYSTEM_CONNECTION_GROUP_CREATE); - - // Verify permission to edit the parent connection group - permissionCheckService.verifyConnectionGroupAccess(currentUser, - this.parentID, MySQLConstants.CONNECTION_GROUP_UPDATE); - - // Verify permission to use the parent connection group for organizational purposes - permissionCheckService.verifyConnectionGroupUsageAccess - (parentID, currentUser, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL); - - // Verify that no connection already exists with this name. - MySQLConnectionGroup previousConnectionGroup = - connectionGroupService.retrieveConnectionGroup(name, parentID, currentUser); - if(previousConnectionGroup != null) - throw new GuacamoleClientException("That connection group name is already in use."); - - // Create connection group - MySQLConnectionGroup connectionGroup = connectionGroupService - .createConnectionGroup(name, currentUser, parentID, mySQLType); - - // Set the connection group ID - object.setIdentifier(connectionGroup.getIdentifier()); - - // Finally, give the current user full access to the newly created - // connection group. - ConnectionGroupPermissionKey newConnectionGroupPermission = new ConnectionGroupPermissionKey(); - newConnectionGroupPermission.setUser_id(currentUser.getUserID()); - newConnectionGroupPermission.setConnection_group_id(connectionGroup.getConnectionGroupID()); - - // Read permission - newConnectionGroupPermission.setPermission(MySQLConstants.CONNECTION_GROUP_READ); - connectionGroupPermissionDAO.insert(newConnectionGroupPermission); - - // Update permission - newConnectionGroupPermission.setPermission(MySQLConstants.CONNECTION_GROUP_UPDATE); - connectionGroupPermissionDAO.insert(newConnectionGroupPermission); - - // Delete permission - newConnectionGroupPermission.setPermission(MySQLConstants.CONNECTION_GROUP_DELETE); - connectionGroupPermissionDAO.insert(newConnectionGroupPermission); - - // Administer permission - newConnectionGroupPermission.setPermission(MySQLConstants.CONNECTION_GROUP_ADMINISTER); - connectionGroupPermissionDAO.insert(newConnectionGroupPermission); - - } - - @Transactional - @Override - public void update(ConnectionGroup object) throws GuacamoleException { - - // If connection not actually from this auth provider, we can't handle - // the update - if (!(object instanceof MySQLConnectionGroup)) - throw new GuacamoleUnsupportedException("Connection not from database."); - - MySQLConnectionGroup mySQLConnectionGroup = (MySQLConnectionGroup) object; - - // Verify permission to update - permissionCheckService.verifyConnectionGroupAccess(currentUser, - mySQLConnectionGroup.getConnectionGroupID(), - MySQLConstants.CONNECTION_GROUP_UPDATE); - - // Perform update - connectionGroupService.updateConnectionGroup(mySQLConnectionGroup); - } - - @Transactional - @Override - public void remove(String identifier) throws GuacamoleException { - - // Get connection - MySQLConnectionGroup mySQLConnectionGroup = - connectionGroupService.retrieveConnectionGroup(identifier, currentUser); - - if(mySQLConnectionGroup == null) - throw new GuacamoleResourceNotFoundException("Connection group not found."); - - // Verify permission to use the parent connection group for organizational purposes - permissionCheckService.verifyConnectionGroupUsageAccess - (mySQLConnectionGroup.getParentID(), currentUser, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL); - - // Verify permission to delete - permissionCheckService.verifyConnectionGroupAccess(currentUser, - mySQLConnectionGroup.getConnectionGroupID(), - MySQLConstants.CONNECTION_GROUP_DELETE); - - // Delete the connection group itself - connectionGroupService.deleteConnectionGroup - (mySQLConnectionGroup.getConnectionGroupID()); - - } - - @Override - public void move(String identifier, Directory directory) - throws GuacamoleException { - - if(MySQLConstants.CONNECTION_GROUP_ROOT_IDENTIFIER.equals(identifier)) - throw new GuacamoleUnsupportedException("The root connection group cannot be moved."); - - if(!(directory instanceof ConnectionGroupDirectory)) - throw new GuacamoleUnsupportedException("Directory not from database"); - - Integer toConnectionGroupID = ((ConnectionGroupDirectory)directory).parentID; - - // Get connection group - MySQLConnectionGroup mySQLConnectionGroup = - connectionGroupService.retrieveConnectionGroup(identifier, currentUser); - - if(mySQLConnectionGroup == null) - throw new GuacamoleResourceNotFoundException("Connection group not found."); - - // Verify permission to update the connection group - permissionCheckService.verifyConnectionGroupAccess(currentUser, - mySQLConnectionGroup.getConnectionGroupID(), - MySQLConstants.CONNECTION_GROUP_UPDATE); - - // Verify permission to use the from connection group for organizational purposes - permissionCheckService.verifyConnectionGroupUsageAccess - (mySQLConnectionGroup.getParentID(), currentUser, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL); - - // Verify permission to update the from connection group - permissionCheckService.verifyConnectionGroupAccess(currentUser, - mySQLConnectionGroup.getParentID(), MySQLConstants.CONNECTION_GROUP_UPDATE); - - // Verify permission to use the to connection group for organizational purposes - permissionCheckService.verifyConnectionGroupUsageAccess - (toConnectionGroupID, currentUser, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL); - - // Verify permission to update the to connection group - permissionCheckService.verifyConnectionGroupAccess(currentUser, - toConnectionGroupID, MySQLConstants.CONNECTION_GROUP_UPDATE); - - // Verify that no connection already exists with this name. - MySQLConnectionGroup previousConnectionGroup = - connectionGroupService.retrieveConnectionGroup(mySQLConnectionGroup.getName(), - toConnectionGroupID, currentUser); - if(previousConnectionGroup != null) - throw new GuacamoleClientException("That connection group name is already in use."); - - // Verify that moving this connectionGroup would not cause a cycle - Integer relativeParentID = toConnectionGroupID; - while(relativeParentID != null) { - if(relativeParentID == mySQLConnectionGroup.getConnectionGroupID()) - throw new GuacamoleUnsupportedException("Connection group cycle detected."); - - MySQLConnectionGroup relativeParentGroup = connectionGroupService. - retrieveConnectionGroup(relativeParentID, currentUser); - - relativeParentID = relativeParentGroup.getParentID(); - } - - // Update the connection - mySQLConnectionGroup.setParentID(toConnectionGroupID); - connectionGroupService.updateConnectionGroup(mySQLConnectionGroup); - } - -} diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java deleted file mode 100644 index 2a0080325..000000000 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2013 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.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.GuacamoleException; -import org.glyptodon.guacamole.net.auth.AuthenticationProvider; -import org.glyptodon.guacamole.net.auth.Credentials; -import org.glyptodon.guacamole.net.auth.UserContext; -import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionGroupMapper; -import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionGroupPermissionMapper; -import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionHistoryMapper; -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.dao.ConnectionPermissionMapper; -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.service.ConnectionGroupService; -import net.sourceforge.guacamole.net.auth.mysql.service.ConnectionService; -import net.sourceforge.guacamole.net.auth.mysql.service.PasswordEncryptionService; -import net.sourceforge.guacamole.net.auth.mysql.service.PermissionCheckService; -import net.sourceforge.guacamole.net.auth.mysql.service.SHA256PasswordEncryptionService; -import net.sourceforge.guacamole.net.auth.mysql.service.SaltService; -import net.sourceforge.guacamole.net.auth.mysql.service.SecureRandomSaltService; -import net.sourceforge.guacamole.net.auth.mysql.service.UserService; -import org.glyptodon.guacamole.properties.GuacamoleProperties; -import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; -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 MySQLAuthenticationProvider implements AuthenticationProvider { - - /** - * Set of all active connections. - */ - private ActiveConnectionMap activeConnectionMap = new ActiveConnectionMap(); - - /** - * Injector which will manage the object graph of this authentication - * provider. - */ - private Injector injector; - - @Override - public UserContext getUserContext(Credentials credentials) throws GuacamoleException { - - // Get user service - UserService userService = injector.getInstance(UserService.class); - - // Get user - MySQLUser authenticatedUser = userService.retrieveUser(credentials); - if (authenticatedUser != null) { - MySQLUserContext context = injector.getInstance(MySQLUserContext.class); - context.init(new AuthenticatedUser(authenticatedUser.getUserID(), credentials)); - return context; - } - - // Otherwise, unauthorized - return null; - - } - - /** - * Creates a new MySQLAuthenticationProvider that reads and writes - * authentication data to a MySQL database defined by properties in - * guacamole.properties. - * - * @throws GuacamoleException If a required property is missing, or - * an error occurs while parsing a property. - */ - public MySQLAuthenticationProvider() throws GuacamoleException { - - 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", GuacamoleProperties.getRequiredProperty(MySQLGuacamoleProperties.MYSQL_HOSTNAME)); - myBatisProperties.setProperty("JDBC.port", String.valueOf(GuacamoleProperties.getRequiredProperty(MySQLGuacamoleProperties.MYSQL_PORT))); - myBatisProperties.setProperty("JDBC.schema", GuacamoleProperties.getRequiredProperty(MySQLGuacamoleProperties.MYSQL_DATABASE)); - myBatisProperties.setProperty("JDBC.username", GuacamoleProperties.getRequiredProperty(MySQLGuacamoleProperties.MYSQL_USERNAME)); - myBatisProperties.setProperty("JDBC.password", GuacamoleProperties.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(ConnectionHistoryMapper.class); - addMapperClass(ConnectionMapper.class); - addMapperClass(ConnectionGroupMapper.class); - addMapperClass(ConnectionGroupPermissionMapper.class); - addMapperClass(ConnectionParameterMapper.class); - addMapperClass(ConnectionPermissionMapper.class); - addMapperClass(SystemPermissionMapper.class); - addMapperClass(UserMapper.class); - addMapperClass(UserPermissionMapper.class); - - // Bind interfaces - bind(MySQLUserContext.class); - bind(UserDirectory.class); - bind(MySQLUser.class); - bind(SaltService.class).to(SecureRandomSaltService.class); - bind(PasswordEncryptionService.class).to(SHA256PasswordEncryptionService.class); - bind(PermissionCheckService.class); - bind(ConnectionService.class); - bind(ConnectionGroupService.class); - bind(UserService.class); - bind(ActiveConnectionMap.class).toInstance(activeConnectionMap); - - } - } // 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; - - } - -} 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 deleted file mode 100644 index c685f7eaa..000000000 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnection.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2013 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.Inject; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.GuacamoleSocket; -import org.glyptodon.guacamole.net.auth.AbstractConnection; -import org.glyptodon.guacamole.net.auth.ConnectionRecord; -import net.sourceforge.guacamole.net.auth.mysql.service.ConnectionService; -import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; -import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; - -/** - * A MySQL based implementation of the Connection object. - * @author James Muehlner - */ -public class MySQLConnection extends AbstractConnection { - - /** - * The ID associated with this connection in the database. - */ - private Integer connectionID; - - /** - * The ID of the parent connection group for this connection. - */ - private Integer parentID; - - /** - * The user who queried or created this connection. - */ - private AuthenticatedUser currentUser; - - /** - * History of this connection. - */ - private List history = new ArrayList(); - - /** - * Service for managing connections. - */ - @Inject - private ConnectionService connectionService; - - /** - * Create a default, empty connection. - */ - public MySQLConnection() { - } - - /** - * Get the ID of the corresponding connection record. - * @return The ID of the corresponding connection, if any. - */ - public Integer getConnectionID() { - return connectionID; - } - - /** - * Sets the ID of the corresponding connection record. - * @param connectionID The ID to assign to this connection. - */ - public void setConnectionID(Integer connectionID) { - this.connectionID = connectionID; - } - - /** - * Get the ID of the parent connection group for this connection, if any. - * @return The ID of the parent connection group for this connection, if any. - */ - public Integer getParentID() { - return parentID; - } - - /** - * Sets the ID of the parent connection group for this connection. - * @param parentID The ID of the parent connection group for this connection. - */ - public void setParentID(Integer parentID) { - this.parentID = parentID; - - // Translate to string identifier - if (parentID != null) - this.setParentIdentifier(String.valueOf(parentID)); - else - this.setParentIdentifier(MySQLConstants.CONNECTION_GROUP_ROOT_IDENTIFIER); - - } - - /** - * Initialize from explicit values. - * - * @param connectionID - * The ID of the associated database record, if any. - * - * @param parentID - * The ID of the parent connection group for this connection, if any. - * - * @param name - * The human-readable name associated with this connection. - * - * @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 currentUser - * The user who queried this connection. - */ - public void init(Integer connectionID, Integer parentID, String name, - String identifier, GuacamoleConfiguration config, - List history, - AuthenticatedUser currentUser) { - - this.connectionID = connectionID; - this.setParentID(parentID); - setName(name); - setIdentifier(identifier); - setConfiguration(config); - this.history.addAll(history); - this.currentUser = currentUser; - - } - - @Override - public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException { - return connectionService.connect(this, info, currentUser, null); - } - - @Override - public List getHistory() throws GuacamoleException { - return Collections.unmodifiableList(history); - } - -} diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnectionGroup.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnectionGroup.java deleted file mode 100644 index 53380bb5c..000000000 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnectionGroup.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2013 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.Inject; -import com.google.inject.Provider; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.GuacamoleSocket; -import org.glyptodon.guacamole.net.auth.AbstractConnectionGroup; -import org.glyptodon.guacamole.net.auth.Connection; -import org.glyptodon.guacamole.net.auth.ConnectionGroup; -import org.glyptodon.guacamole.net.auth.Directory; -import net.sourceforge.guacamole.net.auth.mysql.service.ConnectionGroupService; -import net.sourceforge.guacamole.net.auth.mysql.service.PermissionCheckService; -import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; - -/** - * A MySQL based implementation of the ConnectionGroup object. - * @author James Muehlner - */ -public class MySQLConnectionGroup extends AbstractConnectionGroup { - - /** - * The ID associated with this connection group in the database. - */ - private Integer connectionGroupID; - - /** - * The ID of the parent connection group for this connection group. - */ - private Integer parentID; - - /** - * The user who queried or created this connection group. - */ - private AuthenticatedUser currentUser; - - /** - * A Directory of connections that have this connection group as a parent. - */ - private ConnectionDirectory connectionDirectory = null; - - /** - * A Directory of connection groups that have this connection group as a parent. - */ - private ConnectionGroupDirectory connectionGroupDirectory = null; - - /** - * Service managing connection groups. - */ - @Inject - private ConnectionGroupService connectionGroupService; - - /** - * Service for checking permissions. - */ - @Inject - private PermissionCheckService permissionCheckService; - - /** - * Service for creating new ConnectionDirectory objects. - */ - @Inject Provider connectionDirectoryProvider; - - /** - * Service for creating new ConnectionGroupDirectory objects. - */ - @Inject Provider connectionGroupDirectoryProvider; - - /** - * Create a default, empty connection group. - */ - public MySQLConnectionGroup() { - } - - /** - * Get the ID of the corresponding connection group record. - * @return The ID of the corresponding connection group, if any. - */ - public Integer getConnectionGroupID() { - return connectionGroupID; - } - - /** - * Sets the ID of the corresponding connection group record. - * @param connectionGroupID The ID to assign to this connection group. - */ - public void setConnectionID(Integer connectionGroupID) { - this.connectionGroupID = connectionGroupID; - } - - /** - * Get the ID of the parent connection group for this connection group, if any. - * @return The ID of the parent connection group for this connection group, if any. - */ - public Integer getParentID() { - return parentID; - } - - /** - * Sets the ID of the parent connection group for this connection group. - * @param parentID The ID of the parent connection group for this connection group. - */ - public void setParentID(Integer parentID) { - this.parentID = parentID; - - // Translate to string identifier - if (parentID != null) - this.setParentIdentifier(String.valueOf(parentID)); - else - this.setParentIdentifier(MySQLConstants.CONNECTION_GROUP_ROOT_IDENTIFIER); - - } - - /** - * Initialize from explicit values. - * - * @param connectionGroupID - * The ID of the associated database record, if any. - * - * @param parentID - * The ID of the parent connection group for this connection group, if - * any. - * - * @param name - * The name of this connection group. - * - * @param identifier - * The unique identifier associated with this connection group. - * - * @param type - * The type of this connection group. - * - * @param currentUser - * The user who queried this connection. - */ - public void init(Integer connectionGroupID, Integer parentID, String name, - String identifier, ConnectionGroup.Type type, AuthenticatedUser currentUser) { - this.connectionGroupID = connectionGroupID; - this.setParentID(parentID); - setName(name); - setIdentifier(identifier); - setType(type); - this.currentUser = currentUser; - - connectionDirectory = connectionDirectoryProvider.get(); - connectionDirectory.init(currentUser, connectionGroupID); - - connectionGroupDirectory = connectionGroupDirectoryProvider.get(); - connectionGroupDirectory.init(currentUser, connectionGroupID); - } - - @Override - public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException { - - // Verify permission to use the connection group for balancing purposes - permissionCheckService.verifyConnectionGroupUsageAccess - (this.connectionGroupID, currentUser, MySQLConstants.CONNECTION_GROUP_BALANCING); - - // Verify permission to delete - permissionCheckService.verifyConnectionGroupAccess(currentUser, - this.connectionGroupID, - MySQLConstants.CONNECTION_GROUP_READ); - - return connectionGroupService.connect(this, info, currentUser); - } - - @Override - public Directory getConnectionDirectory() throws GuacamoleException { - return connectionDirectory; - } - - @Override - public Directory getConnectionGroupDirectory() throws GuacamoleException { - return connectionGroupDirectory; - } - -} 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 deleted file mode 100644 index e723f89b6..000000000 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnectionRecord.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2013 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 java.util.Date; -import org.glyptodon.guacamole.net.auth.ConnectionRecord; - -/** - * A ConnectionRecord which is based on data stored in MySQL. - * - * @author James Muehlner - */ -public class MySQLConnectionRecord implements ConnectionRecord { - - /** - * The start date of the ConnectionRecord. - */ - 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; - - /** - * Whether this connection is currently active. - */ - private boolean active; - - /** - * Initialize this MySQLConnectionRecord with the start/end dates, - * and the name of the user it represents. - * - * @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. - * @param active Whether the connection is currently active. - */ - public MySQLConnectionRecord(Date startDate, Date endDate, - String username, boolean active) { - if (startDate != null) this.startDate = new Date(startDate.getTime()); - if (endDate != null) this.endDate = new Date(endDate.getTime()); - this.username = username; - this.active = active; - } - - @Override - public Date getStartDate() { - if (startDate == null) return null; - return new Date(startDate.getTime()); - } - - @Override - public Date getEndDate() { - if (endDate == null) return null; - return new Date(endDate.getTime()); - } - - @Override - public String getUsername() { - return username; - } - - @Override - public boolean isActive() { - return active; - } - -} diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConstants.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConstants.java deleted file mode 100644 index 532c49f29..000000000 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConstants.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (C) 2013 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 org.glyptodon.guacamole.net.auth.ConnectionGroup; -import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; -import org.glyptodon.guacamole.net.auth.permission.SystemPermission; - - -/** - * A set of constants that are useful for the MySQL-based authentication provider. - * @author James Muehlner - */ -public final class MySQLConstants { - - /** - * This class should not be instantiated. - */ - private MySQLConstants() {} - - /** - * The string stored in the database to represent READ access to a user. - */ - public static final String USER_READ = "READ"; - - /** - * The string stored in the database to represent UPDATE access to a user. - */ - public static final String USER_UPDATE = "UPDATE"; - - /** - * The string stored in the database to represent DELETE access to a user. - */ - public static final String USER_DELETE = "DELETE"; - - /** - * The string stored in the database to represent ADMINISTER access to a - * user. - */ - public static final String USER_ADMINISTER = "ADMINISTER"; - - /** - * The string stored in the database to represent READ access to a - * connection. - */ - public static final String CONNECTION_READ = "READ"; - - /** - * The string stored in the database to represent UPDATE access to a - * connection. - */ - public static final String CONNECTION_UPDATE = "UPDATE"; - - /** - * The string stored in the database to represent DELETE access to a - * connection. - */ - public static final String CONNECTION_DELETE = "DELETE"; - - /** - * The string stored in the database to represent ADMINISTER access to a - * connection. - */ - public static final String CONNECTION_ADMINISTER = "ADMINISTER"; - - /** - * The string stored in the database to represent READ access to a - * connection. - */ - public static final String CONNECTION_GROUP_READ = "READ"; - - /** - * The string stored in the database to represent UPDATE access to a - * connection group. - */ - public static final String CONNECTION_GROUP_UPDATE = "UPDATE"; - - /** - * The string stored in the database to represent DELETE access to a - * connection group. - */ - public static final String CONNECTION_GROUP_DELETE = "DELETE"; - - /** - * The string stored in the database to represent ADMINISTER access to a - * connection group. - */ - public static final String CONNECTION_GROUP_ADMINISTER = "ADMINISTER"; - - /** - * The string stored in the database to represent a BALANCING - * connection group. - */ - public static final String CONNECTION_GROUP_BALANCING = "BALANCING"; - - /** - * The string stored in the database to represent an ORGANIZATIONAL - * connection group. - */ - public static final String CONNECTION_GROUP_ORGANIZATIONAL = - "ORGANIZATIONAL"; - - /** - * The identifier used to mark the root connection group. - */ - public static final String CONNECTION_GROUP_ROOT_IDENTIFIER = "ROOT"; - - /** - * The string stored in the database to represent permission to create - * users. - */ - public static final String SYSTEM_USER_CREATE = "CREATE_USER"; - - /** - * The string stored in the database to represent permission to create - * connections. - */ - public static final String SYSTEM_CONNECTION_CREATE = "CREATE_CONNECTION"; - - /** - * The string stored in the database to represent permission to create - * connection groups. - */ - public static final String SYSTEM_CONNECTION_GROUP_CREATE = "CREATE_CONNECTION_GROUP"; - - /** - * The string stored in the database to represent permission to administer - * the system as a whole. - */ - public static final String SYSTEM_ADMINISTER = "ADMINISTER"; - - /** - * Given the type of a permission affecting a user, returns the MySQL - * constant representing that permission type. - * - * @param type The type of permission to look up. - * @return The MySQL constant corresponding to the given permission type. - */ - public static String getUserConstant(ObjectPermission.Type type) { - - // Convert permission type to MySQL constant - switch (type) { - case READ: return USER_READ; - case UPDATE: return USER_UPDATE; - case ADMINISTER: return USER_ADMINISTER; - case DELETE: return USER_DELETE; - } - - // If we get here, permission support was not properly implemented - throw new UnsupportedOperationException( - "Unsupported permission type: " + type); - - } - - /** - * Given the type of a permission affecting a connection, returns the MySQL - * constant representing that permission type. - * - * @param type The type of permission to look up. - * @return The MySQL constant corresponding to the given permission type. - */ - public static String getConnectionConstant(ObjectPermission.Type type) { - - // Convert permission type to MySQL constant - switch (type) { - case READ: return CONNECTION_READ; - case UPDATE: return CONNECTION_UPDATE; - case ADMINISTER: return CONNECTION_ADMINISTER; - case DELETE: return CONNECTION_DELETE; - } - - // If we get here, permission support was not properly implemented - throw new UnsupportedOperationException( - "Unsupported permission type: " + type); - - } - - /** - * Given the type of a permission affecting a connection group, - * returns the MySQL constant representing that permission type. - * - * @param type The type of permission to look up. - * @return The MySQL constant corresponding to the given permission type. - */ - public static String getConnectionGroupConstant(ObjectPermission.Type type) { - - // Convert permission type to MySQL constant - switch (type) { - case READ: return CONNECTION_GROUP_READ; - case UPDATE: return CONNECTION_GROUP_UPDATE; - case ADMINISTER: return CONNECTION_GROUP_ADMINISTER; - case DELETE: return CONNECTION_GROUP_DELETE; - } - - // If we get here, permission support was not properly implemented - throw new UnsupportedOperationException( - "Unsupported permission type: " + type); - - } - - /** - * Given the type of a connection group, returns the MySQL constant - * representing that type. - * - * @param type The connection group type to look up. - * @return The MySQL constant corresponding to the given type. - */ - public static String getConnectionGroupTypeConstant(ConnectionGroup.Type type) { - - // Convert permission type to MySQL constant - switch (type) { - case ORGANIZATIONAL: return CONNECTION_GROUP_ORGANIZATIONAL; - case BALANCING: return CONNECTION_GROUP_BALANCING; - } - - // If we get here, permission support was not properly implemented - throw new UnsupportedOperationException( - "Unsupported connection group type: " + type); - - } - - /** - * Given the type of a permission affecting the system, returns the MySQL - * constant representing that permission type. - * - * @param type The type of permission to look up. - * @return The MySQL constant corresponding to the given permission type. - */ - public static String getSystemConstant(SystemPermission.Type type) { - - // Convert permission type to MySQL constant - switch (type) { - case CREATE_USER: return SYSTEM_USER_CREATE; - case CREATE_CONNECTION: return SYSTEM_CONNECTION_CREATE; - case CREATE_CONNECTION_GROUP: return SYSTEM_CONNECTION_GROUP_CREATE; - case ADMINISTER: return SYSTEM_ADMINISTER; - } - - // If we get here, permission support was not properly implemented - throw new UnsupportedOperationException( - "Unsupported permission type: " + type); - - } - -} 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 deleted file mode 100644 index 4200bfe5d..000000000 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLGuacamoleSocket.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2013 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.Inject; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.io.GuacamoleReader; -import org.glyptodon.guacamole.io.GuacamoleWriter; -import org.glyptodon.guacamole.net.GuacamoleSocket; - -/** - * A MySQL specific wrapper around a ConfiguredGuacamoleSocket. - * @author James Muehlner - */ -public class MySQLGuacamoleSocket implements GuacamoleSocket { - - /** - * Injected ActiveConnectionMap which will contain all active connections. - */ - @Inject - private ActiveConnectionMap activeConnectionMap; - - /** - * The wrapped socket. - */ - private GuacamoleSocket socket; - - /** - * 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 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 historyID, Integer connectionGroupID) { - this.socket = socket; - this.historyID = historyID; - this.connectionGroupID = connectionGroupID; - } - - @Override - public GuacamoleReader getReader() { - return socket.getReader(); - } - - @Override - public GuacamoleWriter getWriter() { - return socket.getWriter(); - } - - @Override - public void close() throws GuacamoleException { - - // Mark this connection as inactive - synchronized (activeConnectionMap) { - - if (isOpen()) - activeConnectionMap.closeConnection(historyID, connectionGroupID); - - // Close socket - socket.close(); - - } - - } - - @Override - public boolean isOpen() { - return socket.isOpen(); - } -} diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUser.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUser.java deleted file mode 100644 index 25b07f123..000000000 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUser.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2013 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 java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.auth.AbstractUser; -import org.glyptodon.guacamole.net.auth.User; -import org.glyptodon.guacamole.net.auth.permission.Permission; - -/** - * A MySQL based implementation of the User object. - * @author James Muehlner - */ -public class MySQLUser extends AbstractUser { - - /** - * The ID of this user in the database, if any. - */ - private Integer userID; - - /** - * The set of current permissions a user has. - */ - private Set permissions = new HashSet(); - - /** - * Any newly added permissions that have yet to be committed. - */ - private Set newPermissions = new HashSet(); - - /** - * Any newly deleted permissions that have yet to be deleted. - */ - private Set removedPermissions = new HashSet(); - - /** - * Creates a new, empty MySQLUser. - */ - public MySQLUser() { - } - - /** - * Initializes a new MySQLUser having the given username. - * - * @param name The name to assign to this MySQLUser. - */ - public void init(String name) { - init(null, name, null, Collections.EMPTY_SET); - } - - /** - * Initializes a new MySQLUser, copying all data from the given user - * object. - * - * @param user The user object to copy. - * @throws GuacamoleException If an error occurs while reading the user - * data in the given object. - */ - public void init(User user) throws GuacamoleException { - init(null, user.getUsername(), user.getPassword(), user.getPermissions()); - } - - /** - * Initializes a new MySQLUser initialized from the given data from the - * database. - * - * @param userID The ID of the user in the database, if any. - * @param username The username of this user. - * @param password The password to assign to this user. - * @param permissions The permissions to assign to this user, as - * retrieved from the database. - */ - public void init(Integer userID, String username, String password, - Set permissions) { - this.userID = userID; - setUsername(username); - setPassword(password); - this.permissions.addAll(permissions); - } - - /** - * Get the current set of permissions this user has. - * @return the current set of permissions. - */ - public Set getCurrentPermissions() { - return permissions; - } - - /** - * Get any new permissions that have yet to be inserted. - * @return the new set of permissions. - */ - public Set getNewPermissions() { - return newPermissions; - } - - /** - * Get any permissions that have not yet been deleted. - * @return the permissions that need to be deleted. - */ - public Set getRemovedPermissions() { - return removedPermissions; - } - - /** - * Reset the new and removed permission sets after they are - * no longer needed. - */ - public void resetPermissions() { - newPermissions.clear(); - removedPermissions.clear(); - } - - /** - * Returns the ID of this user in the database, if it exists. - * - * @return The ID of this user in the database, or null if this user - * was not retrieved from the database. - */ - public Integer getUserID() { - return userID; - } - - /** - * Sets the ID of this user to the given value. - * - * @param userID The ID to assign to this user. - */ - public void setUserID(Integer userID) { - this.userID = userID; - } - - @Override - public Set getPermissions() throws GuacamoleException { - return Collections.unmodifiableSet(permissions); - } - - @Override - public boolean hasPermission(Permission permission) throws GuacamoleException { - return permissions.contains(permission); - } - - @Override - public void addPermission(Permission permission) throws GuacamoleException { - permissions.add(permission); - newPermissions.add(permission); - removedPermissions.remove(permission); - } - - @Override - public void removePermission(Permission permission) throws GuacamoleException { - permissions.remove(permission); - newPermissions.remove(permission); - removedPermissions.add(permission); - } - -} diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/UserDirectory.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/UserDirectory.java deleted file mode 100644 index 66a1a16df..000000000 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/UserDirectory.java +++ /dev/null @@ -1,708 +0,0 @@ -/* - * Copyright (C) 2013 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.common.base.Preconditions; -import com.google.common.collect.Sets; -import com.google.inject.Inject; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.glyptodon.guacamole.GuacamoleClientException; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.GuacamoleSecurityException; -import org.glyptodon.guacamole.net.auth.Directory; -import org.glyptodon.guacamole.net.auth.User; -import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionGroupPermissionMapper; -import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionPermissionMapper; -import net.sourceforge.guacamole.net.auth.mysql.dao.SystemPermissionMapper; -import net.sourceforge.guacamole.net.auth.mysql.dao.UserPermissionMapper; -import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionGroupPermissionExample; -import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionGroupPermissionKey; -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.model.SystemPermissionExample; -import net.sourceforge.guacamole.net.auth.mysql.model.SystemPermissionKey; -import net.sourceforge.guacamole.net.auth.mysql.model.UserPermissionExample; -import net.sourceforge.guacamole.net.auth.mysql.model.UserPermissionKey; -import net.sourceforge.guacamole.net.auth.mysql.service.ConnectionGroupService; -import net.sourceforge.guacamole.net.auth.mysql.service.ConnectionService; -import net.sourceforge.guacamole.net.auth.mysql.service.PermissionCheckService; -import net.sourceforge.guacamole.net.auth.mysql.service.UserService; -import org.glyptodon.guacamole.GuacamoleUnsupportedException; -import org.glyptodon.guacamole.net.auth.permission.ConnectionGroupPermission; -import org.glyptodon.guacamole.net.auth.permission.ConnectionPermission; -import org.glyptodon.guacamole.net.auth.permission.Permission; -import org.glyptodon.guacamole.net.auth.permission.SystemPermission; -import org.glyptodon.guacamole.net.auth.permission.UserPermission; -import org.mybatis.guice.transactional.Transactional; - -/** - * A MySQL based implementation of the User Directory. - * @author James Muehlner - */ -public class UserDirectory implements Directory { - - /** - * The user this user directory belongs to. Access is based on his/her - * permission settings. - */ - private AuthenticatedUser currentUser; - - /** - * Service for accessing users. - */ - @Inject - private UserService userService; - - /** - * Service for accessing connections. - */ - @Inject - private ConnectionService connectionService; - - /** - * Service for accessing connection groups. - */ - @Inject - private ConnectionGroupService connectionGroupService; - - /** - * DAO for accessing user permissions, which will be injected. - */ - @Inject - private UserPermissionMapper userPermissionDAO; - - /** - * DAO for accessing connection permissions, which will be injected. - */ - @Inject - private ConnectionPermissionMapper connectionPermissionDAO; - - /** - * DAO for accessing connection group permissions, which will be injected. - */ - @Inject - private ConnectionGroupPermissionMapper connectionGroupPermissionDAO; - - /** - * DAO for accessing system permissions, which will be injected. - */ - @Inject - private SystemPermissionMapper systemPermissionDAO; - - /** - * Service for checking various permissions, which will be injected. - */ - @Inject - private PermissionCheckService permissionCheckService; - - /** - * Set the user for this directory. - * - * @param currentUser - * The user whose permissions define the visibility of other users in - * this directory. - */ - public void init(AuthenticatedUser currentUser) { - this.currentUser = currentUser; - } - - @Transactional - @Override - public org.glyptodon.guacamole.net.auth.User get(String identifier) - throws GuacamoleException { - - // Get user - MySQLUser user = userService.retrieveUser(identifier); - - if(user == null) - return null; - - // Verify access is granted - permissionCheckService.verifyUserAccess(currentUser, - user.getUserID(), - MySQLConstants.USER_READ); - - // Return user - return user; - - } - - @Transactional - @Override - public Set getIdentifiers() throws GuacamoleException { - return permissionCheckService.retrieveUsernames(currentUser, - MySQLConstants.USER_READ); - } - - @Override - @Transactional - public void add(org.glyptodon.guacamole.net.auth.User object) - throws GuacamoleException { - - String username = object.getUsername().trim(); - if(username.isEmpty()) - throw new GuacamoleClientException("The username cannot be blank."); - - // Verify current user has permission to create users - permissionCheckService.verifySystemAccess(currentUser, - MySQLConstants.SYSTEM_USER_CREATE); - Preconditions.checkNotNull(object); - - // Verify that no user already exists with this username. - MySQLUser previousUser = userService.retrieveUser(username); - if(previousUser != null) - throw new GuacamoleClientException("That username is already in use."); - - // Create new user - MySQLUser user = userService.createUser(username, object.getPassword()); - - // Create permissions of new user in database - createPermissions(user.getUserID(), object.getPermissions()); - - // Give the current user full access to the newly created user. - UserPermissionKey newUserPermission = new UserPermissionKey(); - newUserPermission.setUser_id(currentUser.getUserID()); - newUserPermission.setAffected_user_id(user.getUserID()); - - // READ permission on new user - newUserPermission.setPermission(MySQLConstants.USER_READ); - userPermissionDAO.insert(newUserPermission); - - // UPDATE permission on new user - newUserPermission.setPermission(MySQLConstants.USER_UPDATE); - userPermissionDAO.insert(newUserPermission); - - // DELETE permission on new user - newUserPermission.setPermission(MySQLConstants.USER_DELETE); - userPermissionDAO.insert(newUserPermission); - - // ADMINISTER permission on new user - newUserPermission.setPermission(MySQLConstants.USER_ADMINISTER); - userPermissionDAO.insert(newUserPermission); - - } - - /** - * Add the given permissions to the given user. - * - * @param user_id The ID of the user whose permissions should be updated. - * @param permissions The permissions to add. - * @throws GuacamoleException If an error occurs while updating the - * permissions of the given user. - */ - private void createPermissions(int user_id, Set permissions) throws GuacamoleException { - - // Partition given permissions by permission type - List newUserPermissions = new ArrayList(); - List newConnectionPermissions = new ArrayList(); - List newConnectionGroupPermissions = new ArrayList(); - List newSystemPermissions = new ArrayList(); - - for (Permission permission : permissions) { - - if (permission instanceof UserPermission) - newUserPermissions.add((UserPermission) permission); - - else if (permission instanceof ConnectionPermission) - newConnectionPermissions.add((ConnectionPermission) permission); - - else if (permission instanceof ConnectionGroupPermission) - newConnectionGroupPermissions.add((ConnectionGroupPermission) permission); - - else if (permission instanceof SystemPermission) - newSystemPermissions.add((SystemPermission) permission); - } - - // Create the new permissions - createUserPermissions(user_id, newUserPermissions); - createConnectionPermissions(user_id, newConnectionPermissions); - createConnectionGroupPermissions(user_id, newConnectionGroupPermissions); - createSystemPermissions(user_id, newSystemPermissions); - - } - - /** - * Remove the given permissions from the given user. - * - * @param user_id The ID of the user whose permissions should be updated. - * @param permissions The permissions to remove. - * @throws GuacamoleException If an error occurs while updating the - * permissions of the given user. - */ - private void removePermissions(int user_id, Set permissions) - throws GuacamoleException { - - // Partition given permissions by permission type - List removedUserPermissions = new ArrayList(); - List removedConnectionPermissions = new ArrayList(); - List removedConnectionGroupPermissions = new ArrayList(); - List removedSystemPermissions = new ArrayList(); - - for (Permission permission : permissions) { - - if (permission instanceof UserPermission) - removedUserPermissions.add((UserPermission) permission); - - else if (permission instanceof ConnectionPermission) - removedConnectionPermissions.add((ConnectionPermission) permission); - - else if (permission instanceof ConnectionGroupPermission) - removedConnectionGroupPermissions.add((ConnectionGroupPermission) permission); - - else if (permission instanceof SystemPermission) - removedSystemPermissions.add((SystemPermission) permission); - } - - // Delete the removed permissions. - deleteUserPermissions(user_id, removedUserPermissions); - deleteConnectionPermissions(user_id, removedConnectionPermissions); - deleteConnectionGroupPermissions(user_id, removedConnectionGroupPermissions); - deleteSystemPermissions(user_id, removedSystemPermissions); - - } - - /** - * Create the given user permissions for the given user. - * - * @param user_id The ID of the user to change the permissions of. - * @param permissions The new permissions the given user should have when - * this operation completes. - * @throws GuacamoleException If permission to alter the access permissions - * of affected objects is denied. - */ - private void createUserPermissions(int user_id, - Collection permissions) - throws GuacamoleException { - - // If no permissions given, stop now - if(permissions.isEmpty()) - return; - - // Get list of administerable user IDs - List administerableUserIDs = - permissionCheckService.retrieveUserIDs(currentUser, - MySQLConstants.USER_ADMINISTER); - - // Get set of usernames corresponding to administerable users - Map administerableUsers = - userService.translateUsernames(administerableUserIDs); - - // Insert all given permissions - for (UserPermission permission : permissions) { - - // Get original ID - Integer affected_id = - administerableUsers.get(permission.getObjectIdentifier()); - - // Verify that the user actually has permission to administrate - // every one of these users - if (affected_id == null) - throw new GuacamoleSecurityException( - "User #" + currentUser.getUserID() - + " does not have permission to administrate user " - + permission.getObjectIdentifier()); - - // Create new permission - UserPermissionKey newPermission = new UserPermissionKey(); - newPermission.setUser_id(currentUser.getUserID()); - newPermission.setPermission(MySQLConstants.getUserConstant(permission.getType())); - newPermission.setAffected_user_id(affected_id); - userPermissionDAO.insert(newPermission); - - } - - } - - /** - * Delete permissions having to do with users for a given user. - * - * @param user_id The ID of the user to change the permissions of. - * @param permissions The permissions the given user should no longer have - * when this operation completes. - * @throws GuacamoleException If permission to alter the access permissions - * of affected objects is denied. - */ - private void deleteUserPermissions(int user_id, - Collection permissions) - throws GuacamoleException { - - // If no permissions given, stop now - if(permissions.isEmpty()) - return; - - // Get list of administerable user IDs - List administerableUserIDs = - permissionCheckService.retrieveUserIDs(currentUser, - MySQLConstants.USER_ADMINISTER); - - // Get set of usernames corresponding to administerable users - Map administerableUsers = - userService.translateUsernames(administerableUserIDs); - - // Delete requested permissions - for (UserPermission permission : permissions) { - - // Get original ID - Integer affected_id = - administerableUsers.get(permission.getObjectIdentifier()); - - // Verify that the user actually has permission to administrate - // every one of these users - if (affected_id == null) - throw new GuacamoleSecurityException( - "User #" + currentUser.getUserID() - + " does not have permission to administrate user " - + permission.getObjectIdentifier()); - - // Delete requested permission - UserPermissionExample userPermissionExample = new UserPermissionExample(); - userPermissionExample.createCriteria() - .andUser_idEqualTo(user_id) - .andPermissionEqualTo(MySQLConstants.getUserConstant(permission.getType())) - .andAffected_user_idEqualTo(affected_id); - userPermissionDAO.deleteByExample(userPermissionExample); - - } - - } - - /** - * Create any new permissions having to do with connections for a given - * user. - * - * @param user_id The ID of the user to assign or remove permissions from. - * @param permissions The new permissions the user should have after this - * operation completes. - * @throws GuacamoleException If permission to alter the access permissions - * of affected objects is deniedD - */ - private void createConnectionPermissions(int user_id, - Collection permissions) - throws GuacamoleException { - - // If no permissions given, stop now - if(permissions.isEmpty()) - return; - - // Get list of administerable connection IDs - Set administerableConnectionIDs = Sets.newHashSet( - permissionCheckService.retrieveConnectionIDs(currentUser, - MySQLConstants.CONNECTION_ADMINISTER)); - - // Insert all given permissions - for (ConnectionPermission permission : permissions) { - - // Get original ID - Integer connection_id = Integer.valueOf(permission.getObjectIdentifier()); - - // Throw exception if permission to administer this connection - // is not granted - if (!administerableConnectionIDs.contains(connection_id)) - throw new GuacamoleSecurityException( - "User #" + currentUser.getUserID() - + " does not have permission to administrate connection " - + permission.getObjectIdentifier()); - - // Create new permission - ConnectionPermissionKey newPermission = new ConnectionPermissionKey(); - newPermission.setUser_id(user_id); - newPermission.setPermission(MySQLConstants.getConnectionConstant(permission.getType())); - newPermission.setConnection_id(connection_id); - connectionPermissionDAO.insert(newPermission); - - } - } - - /** - * Create any new permissions having to do with connection groups - * for a given user. - * - * @param user_id The ID of the user to assign or remove permissions from. - * @param permissions The new permissions the user should have after this - * operation completes. - * @throws GuacamoleException If permission to alter the access permissions - * of affected objects is deniedD - */ - private void createConnectionGroupPermissions(int user_id, - Collection permissions) - throws GuacamoleException { - - // If no permissions given, stop now - if(permissions.isEmpty()) - return; - - // Get list of administerable connection group IDs - Set administerableConnectionGroupIDs = Sets.newHashSet( - permissionCheckService.retrieveConnectionGroupIDs(currentUser, - MySQLConstants.CONNECTION_GROUP_ADMINISTER)); - - // Insert all given permissions - for (ConnectionGroupPermission permission : permissions) { - - // Get original ID - Integer connection_group_id = Integer.valueOf(permission.getObjectIdentifier()); - - // Throw exception if permission to administer this connection group - // is not granted - if (!administerableConnectionGroupIDs.contains(connection_group_id)) - throw new GuacamoleSecurityException( - "User #" + currentUser.getUserID() - + " does not have permission to administrate connection group" - + permission.getObjectIdentifier()); - - // Create new permission - ConnectionGroupPermissionKey newPermission = new ConnectionGroupPermissionKey(); - newPermission.setUser_id(user_id); - newPermission.setPermission(MySQLConstants.getConnectionGroupConstant(permission.getType())); - newPermission.setConnection_group_id(connection_group_id); - connectionGroupPermissionDAO.insert(newPermission); - - } - } - - /** - * Delete permissions having to do with connections for a given user. - * - * @param user_id The ID of the user to change the permissions of. - * @param permissions The permissions the given user should no longer have - * when this operation completes. - * @throws GuacamoleException If permission to alter the access permissions - * of affected objects is denied. - */ - private void deleteConnectionPermissions(int user_id, - Collection permissions) - throws GuacamoleException { - - // If no permissions given, stop now - if(permissions.isEmpty()) - return; - - // Get list of administerable connection IDs - Set administerableConnectionIDs = Sets.newHashSet( - permissionCheckService.retrieveConnectionIDs(currentUser, - MySQLConstants.CONNECTION_ADMINISTER)); - - // Delete requested permissions - for (ConnectionPermission permission : permissions) { - - // Get original ID - Integer connection_id = Integer.valueOf(permission.getObjectIdentifier()); - - // Verify that the user actually has permission to administrate - // every one of these connections - if (!administerableConnectionIDs.contains(connection_id)) - throw new GuacamoleSecurityException( - "User #" + currentUser.getUserID() - + " does not have permission to administrate connection " - + permission.getObjectIdentifier()); - - ConnectionPermissionExample connectionPermissionExample = new ConnectionPermissionExample(); - connectionPermissionExample.createCriteria() - .andUser_idEqualTo(user_id) - .andPermissionEqualTo(MySQLConstants.getConnectionConstant(permission.getType())) - .andConnection_idEqualTo(connection_id); - connectionPermissionDAO.deleteByExample(connectionPermissionExample); - - } - - } - - /** - * Delete permissions having to do with connection groups for a given user. - * - * @param user_id The ID of the user to change the permissions of. - * @param permissions The permissions the given user should no longer have - * when this operation completes. - * @throws GuacamoleException If permission to alter the access permissions - * of affected objects is denied. - */ - private void deleteConnectionGroupPermissions(int user_id, - Collection permissions) - throws GuacamoleException { - - // If no permissions given, stop now - if(permissions.isEmpty()) - return; - - // Get list of administerable connection group IDs - Set administerableConnectionGroupIDs = Sets.newHashSet( - permissionCheckService.retrieveConnectionGroupIDs(currentUser, - MySQLConstants.CONNECTION_GROUP_ADMINISTER)); - - // Delete requested permissions - for (ConnectionGroupPermission permission : permissions) { - - // Get original ID - Integer connection_group_id = Integer.valueOf(permission.getObjectIdentifier()); - - // Verify that the user actually has permission to administrate - // every one of these connection groups - if (!administerableConnectionGroupIDs.contains(connection_group_id)) - throw new GuacamoleSecurityException( - "User #" + currentUser.getUserID() - + " does not have permission to administrate connection group" - + permission.getObjectIdentifier()); - - ConnectionGroupPermissionExample connectionGroupPermissionExample = new ConnectionGroupPermissionExample(); - connectionGroupPermissionExample.createCriteria() - .andUser_idEqualTo(user_id) - .andPermissionEqualTo(MySQLConstants.getConnectionGroupConstant(permission.getType())) - .andConnection_group_idEqualTo(connection_group_id); - connectionGroupPermissionDAO.deleteByExample(connectionGroupPermissionExample); - - } - - } - - /** - * Create any new system permissions for a given user. All permissions in - * the given list will be inserted. - * - * @param user_id The ID of the user whose permissions should be updated. - * @param permissions The new system permissions that the given user should - * have when this operation completes. - * @throws GuacamoleException If permission to administer system permissions - * is denied. - */ - private void createSystemPermissions(int user_id, - Collection permissions) throws GuacamoleException { - - // If no permissions given, stop now - if(permissions.isEmpty()) - return; - - // Only a system administrator can add system permissions. - permissionCheckService.verifySystemAccess( - currentUser, SystemPermission.Type.ADMINISTER.name()); - - // Insert all requested permissions - for (SystemPermission permission : permissions) { - - // Insert permission - SystemPermissionKey newSystemPermission = new SystemPermissionKey(); - newSystemPermission.setUser_id(user_id); - newSystemPermission.setPermission(MySQLConstants.getSystemConstant(permission.getType())); - systemPermissionDAO.insert(newSystemPermission); - - } - - } - - /** - * Delete system permissions for a given user. All permissions in - * the given list will be removed from the user. - * - * @param user_id The ID of the user whose permissions should be updated. - * @param permissions The permissions the given user should no longer have - * when this operation completes. - * @throws GuacamoleException If the permissions specified could not be - * removed due to system restrictions. - */ - private void deleteSystemPermissions(int user_id, - Collection permissions) - throws GuacamoleException { - - // If no permissions given, stop now - if (permissions.isEmpty()) - return; - - // Prevent self-de-adminifying - if (user_id == currentUser.getUserID()) - throw new GuacamoleUnsupportedException("Removing your own administrative permissions is not allowed."); - - // Build list of requested system permissions - List systemPermissionTypes = new ArrayList(); - for (SystemPermission permission : permissions) - systemPermissionTypes.add(MySQLConstants.getSystemConstant(permission.getType())); - - // Delete the requested system permissions for this user - SystemPermissionExample systemPermissionExample = new SystemPermissionExample(); - systemPermissionExample.createCriteria().andUser_idEqualTo(user_id) - .andPermissionIn(systemPermissionTypes); - systemPermissionDAO.deleteByExample(systemPermissionExample); - - } - - @Override - @Transactional - public void update(org.glyptodon.guacamole.net.auth.User object) - throws GuacamoleException { - - // If user not actually from this auth provider, we can't handle updated - // permissions. - if (!(object instanceof MySQLUser)) - throw new GuacamoleUnsupportedException("User not from database."); - - MySQLUser mySQLUser = (MySQLUser) object; - - // Validate permission to update this user is granted - permissionCheckService.verifyUserAccess(currentUser, - mySQLUser.getUserID(), - MySQLConstants.USER_UPDATE); - - // Update the user in the database - userService.updateUser(mySQLUser); - - // Update permissions in database - createPermissions(mySQLUser.getUserID(), mySQLUser.getNewPermissions()); - removePermissions(mySQLUser.getUserID(), mySQLUser.getRemovedPermissions()); - - // The appropriate permissions have been inserted and deleted, so - // reset the new and removed permission sets. - mySQLUser.resetPermissions(); - - } - - @Override - @Transactional - public void remove(String identifier) throws GuacamoleException { - - // Get user pending deletion - MySQLUser user = userService.retrieveUser(identifier); - - // Prevent self-deletion - if (user.getUserID() == currentUser.getUserID()) - throw new GuacamoleUnsupportedException("Deleting your own user is not allowed."); - - // Validate current user has permission to remove the specified user - permissionCheckService.verifyUserAccess(currentUser, - user.getUserID(), - MySQLConstants.USER_DELETE); - - // Delete specified user - userService.deleteUser(user.getUserID()); - - } - - @Override - public void move(String identifier, Directory groupIdentifier) - throws GuacamoleException { - throw new GuacamoleSecurityException("Permission denied."); - } - -} diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionGroupService.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionGroupService.java deleted file mode 100644 index 4c21ee548..000000000 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionGroupService.java +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Copyright (C) 2013 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.service; - -import com.google.inject.Inject; -import com.google.inject.Provider; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.GuacamoleSocket; -import net.sourceforge.guacamole.net.auth.mysql.ActiveConnectionMap; -import net.sourceforge.guacamole.net.auth.mysql.AuthenticatedUser; -import net.sourceforge.guacamole.net.auth.mysql.MySQLConnection; -import net.sourceforge.guacamole.net.auth.mysql.MySQLConnectionGroup; -import net.sourceforge.guacamole.net.auth.mysql.MySQLConstants; -import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionGroupMapper; -import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionGroup; -import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionGroupExample; -import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionGroupExample.Criteria; -import net.sourceforge.guacamole.net.auth.mysql.properties.MySQLGuacamoleProperties; -import org.glyptodon.guacamole.GuacamoleClientTooManyException; -import org.glyptodon.guacamole.GuacamoleResourceNotFoundException; -import org.glyptodon.guacamole.GuacamoleServerBusyException; -import org.glyptodon.guacamole.properties.GuacamoleProperties; -import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; - -/** - * Service which provides convenience methods for creating, retrieving, and - * manipulating connection groups. - * - * @author James Muehlner - */ -public class ConnectionGroupService { - - /** - * Service for managing connections. - */ - @Inject - private ConnectionService connectionService; - - /** - * DAO for accessing connection groups. - */ - @Inject - private ConnectionGroupMapper connectionGroupDAO; - - /** - * Provider which creates MySQLConnectionGroups. - */ - @Inject - private Provider mysqlConnectionGroupProvider; - - /** - * The map of all active connections. - */ - @Inject - private ActiveConnectionMap activeConnectionMap; - - - /** - * Retrieves the connection group having the given - * name from the database. - * - * @param name - * The name of the connection to return. - * - * @param parentID - * The ID of the parent connection group. - * - * @param currentUser - * The user who queried this connection group. - * - * @return - * The connection having the given name, or null if no such connection - * group could be found. - */ - public MySQLConnectionGroup retrieveConnectionGroup(String name, Integer parentID, - AuthenticatedUser currentUser) { - - // Create criteria - ConnectionGroupExample example = new ConnectionGroupExample(); - Criteria criteria = example.createCriteria().andConnection_group_nameEqualTo(name); - if(parentID != null) - criteria.andParent_idEqualTo(parentID); - else - criteria.andParent_idIsNull(); - - // Query connection group by name and parentID - List connectionGroups = - connectionGroupDAO.selectByExample(example); - - // If no connection group found, return null - if(connectionGroups.isEmpty()) - return null; - - // Otherwise, return found connection - return toMySQLConnectionGroup(connectionGroups.get(0), currentUser); - - } - - /** - * Retrieves the connection group having the given unique identifier - * from the database. - * - * @param uniqueIdentifier - * The unique identifier of the connection group to retrieve. - * - * @param currentUser - * The user who queried this connection group. - * - * @return - * The connection group having the given unique identifier, or null if - * no such connection group was found. - * - * @throws GuacamoleException - * If an error occurs while retrieving the connection group. - */ - public MySQLConnectionGroup retrieveConnectionGroup(String uniqueIdentifier, - AuthenticatedUser currentUser) throws GuacamoleException { - - // The unique identifier for a MySQLConnectionGroup is the database ID - Integer connectionGroupID = null; - - // Try to parse the connectionID if it's not the root group - if(!MySQLConstants.CONNECTION_GROUP_ROOT_IDENTIFIER.equals(uniqueIdentifier)) { - try { - connectionGroupID = Integer.parseInt(uniqueIdentifier); - } catch(NumberFormatException e) { - throw new GuacamoleResourceNotFoundException("Invalid connection group ID."); - } - } - - return retrieveConnectionGroup(connectionGroupID, currentUser); - } - - /** - * Retrieves the connection group having the given ID from the database. - * - * @param id - * The ID of the connection group to retrieve. - * - * @param currentUser - * The user who queried this connection. - * - * @return - * The connection group having the given ID, or null if no such - * connection was found. - */ - public MySQLConnectionGroup retrieveConnectionGroup(Integer id, AuthenticatedUser currentUser) { - - // This is the root connection group, so just create it here - if(id == null) { - MySQLConnectionGroup connectionGroup = mysqlConnectionGroupProvider.get(); - connectionGroup.init(null, null, - MySQLConstants.CONNECTION_GROUP_ROOT_IDENTIFIER, - MySQLConstants.CONNECTION_GROUP_ROOT_IDENTIFIER, - org.glyptodon.guacamole.net.auth.ConnectionGroup.Type.ORGANIZATIONAL, - currentUser); - - return connectionGroup; - } - - // Query connection by ID - ConnectionGroup connectionGroup = connectionGroupDAO.selectByPrimaryKey(id); - - // If no connection found, return null - if(connectionGroup == null) - return null; - - // Otherwise, return found connection - return toMySQLConnectionGroup(connectionGroup, currentUser); - } - - /** - * Connect to the connection within the given group with the lowest number - * of currently active users. - * - * @param group - * The group to load balance across. - * - * @param info - * The information to use when performing the connection handshake. - * - * @param currentUser - * The user who is connecting to the socket. - * - * @return - * The connected socket. - * - * @throws GuacamoleException - * If an error occurs while connecting the socket. - */ - public GuacamoleSocket connect(MySQLConnectionGroup group, - GuacamoleClientInformation info, AuthenticatedUser currentUser) - throws GuacamoleException { - - // Get all connections in the group. - List connectionIDs = connectionService.getAllConnectionIDs - (group.getConnectionGroupID()); - - synchronized (activeConnectionMap) { - - // Get the least used connection. - Integer leastUsedConnectionID = - activeConnectionMap.getLeastUsedConnection(connectionIDs); - - if(leastUsedConnectionID == null) - throw new GuacamoleResourceNotFoundException("No connections found in group."); - - if(GuacamoleProperties.getProperty( - MySQLGuacamoleProperties.MYSQL_DISALLOW_SIMULTANEOUS_CONNECTIONS, false) - && activeConnectionMap.isActive(leastUsedConnectionID)) - throw new GuacamoleServerBusyException - ("Cannot connect. All connections are in use."); - - if(GuacamoleProperties.getProperty( - MySQLGuacamoleProperties.MYSQL_DISALLOW_DUPLICATE_CONNECTIONS, true) - && activeConnectionMap.isConnectionGroupUserActive(group.getConnectionGroupID(), currentUser.getUserID())) - throw new GuacamoleClientTooManyException - ("Cannot connect. Connection group already in use by this user."); - - // Get the connection - MySQLConnection connection = connectionService - .retrieveConnection(leastUsedConnectionID, currentUser); - - // Connect to the connection - return connectionService.connect(connection, info, currentUser, group.getConnectionGroupID()); - - } - - } - - /** - * Returns a list of the IDs of all connection groups with a given parent ID. - * @param parentID The ID of the parent for all the queried connection groups. - * @return a list of the IDs of all connection groups with a given parent ID. - */ - public List getAllConnectionGroupIDs(Integer parentID) { - - // Create criteria - ConnectionGroupExample example = new ConnectionGroupExample(); - Criteria criteria = example.createCriteria(); - - if(parentID != null) - criteria.andParent_idEqualTo(parentID); - else - criteria.andParent_idIsNull(); - - // Query the connections - List connectionGroups = connectionGroupDAO.selectByExample(example); - - // List of IDs of connections with the given parent - List connectionGroupIDs = new ArrayList(); - - for(ConnectionGroup connectionGroup : connectionGroups) { - connectionGroupIDs.add(connectionGroup.getConnection_group_id()); - } - - return connectionGroupIDs; - } - - /** - * Get the identifiers of all the connection groups defined in the system - * with a certain parentID. - * - * @return A Set of identifiers of all the connection groups defined - * in the system with the given parentID. - */ - public Set getAllConnectionGroupIdentifiers(Integer parentID) { - - // Set of all present connection identifiers - Set identifiers = new HashSet(); - - // Set up Criteria - ConnectionGroupExample example = new ConnectionGroupExample(); - Criteria criteria = example.createCriteria(); - if(parentID != null) - criteria.andParent_idEqualTo(parentID); - else - criteria.andParent_idIsNull(); - - // Query connection identifiers - List connectionGroups = - connectionGroupDAO.selectByExample(example); - for (ConnectionGroup connectionGroup : connectionGroups) - identifiers.add(String.valueOf(connectionGroup.getConnection_group_id())); - - return identifiers; - - } - - /** - * Convert the given database-retrieved Connection into a MySQLConnection. - * The parameters of the given connection will be read and added to the - * MySQLConnection in the process. - * - * @param connection - * The connection to convert. - * - * @param currentUser - * The user who queried this connection. - * - * @return - * A new MySQLConnection containing all data associated with the - * specified connection. - */ - private MySQLConnectionGroup toMySQLConnectionGroup(ConnectionGroup connectionGroup, - AuthenticatedUser currentUser) { - - // Create new MySQLConnection from retrieved data - MySQLConnectionGroup mySQLConnectionGroup = mysqlConnectionGroupProvider.get(); - - String mySqlType = connectionGroup.getType(); - org.glyptodon.guacamole.net.auth.ConnectionGroup.Type authType; - - if(mySqlType.equals(MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL)) - authType = org.glyptodon.guacamole.net.auth.ConnectionGroup.Type.ORGANIZATIONAL; - else - authType = org.glyptodon.guacamole.net.auth.ConnectionGroup.Type.BALANCING; - - mySQLConnectionGroup.init( - connectionGroup.getConnection_group_id(), - connectionGroup.getParent_id(), - connectionGroup.getConnection_group_name(), - Integer.toString(connectionGroup.getConnection_group_id()), - authType, - currentUser - ); - - return mySQLConnectionGroup; - - } - - /** - * Get the connection group IDs of all the connection groups defined in the system. - * - * @return A list of connection group IDs of all the connection groups defined in the system. - */ - public List getAllConnectionGroupIDs() { - - // Set of all present connection group IDs - List connectionGroupIDs = new ArrayList(); - - // Query all connection IDs - List connections = - connectionGroupDAO.selectByExample(new ConnectionGroupExample()); - for (ConnectionGroup connection : connections) - connectionGroupIDs.add(connection.getConnection_group_id()); - - return connectionGroupIDs; - - } - - /** - * Creates a new connection group having the given name and type. - * - * @param name - * The name to assign to the new connection group. - * - * @param currentUser - * The user who created this connection group. - * - * @param parentID - * The ID of the parent of the new connection group, if any. - * - * @param type - * The type of the new connection group. - * - * @return A new MySQLConnectionGroup containing the data of the newly created - * connection group. - */ - public MySQLConnectionGroup createConnectionGroup(String name, AuthenticatedUser currentUser, - Integer parentID, String type) { - - // Initialize database connection - ConnectionGroup connectionGroup = new ConnectionGroup(); - connectionGroup.setConnection_group_name(name); - connectionGroup.setParent_id(parentID); - connectionGroup.setType(type); - - // Create connection - connectionGroupDAO.insert(connectionGroup); - return toMySQLConnectionGroup(connectionGroup, currentUser); - - } - - /** - * Updates the connection group in the database corresponding to the given - * MySQLConnectionGroup. - * - * @param mySQLConnectionGroup The MySQLConnectionGroup to update (save) - * to the database. - * This connection must already exist. - */ - public void updateConnectionGroup(MySQLConnectionGroup mySQLConnectionGroup) { - - // Populate connection - ConnectionGroup connectionGroup = new ConnectionGroup(); - connectionGroup.setConnection_group_id(mySQLConnectionGroup.getConnectionGroupID()); - connectionGroup.setParent_id(mySQLConnectionGroup.getParentID()); - connectionGroup.setConnection_group_name(mySQLConnectionGroup.getName()); - - switch(mySQLConnectionGroup.getType()) { - case BALANCING : - connectionGroup.setType(MySQLConstants.CONNECTION_GROUP_BALANCING); - break; - case ORGANIZATIONAL: - connectionGroup.setType(MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL); - break; - } - - // Update the connection group in the database - connectionGroupDAO.updateByPrimaryKey(connectionGroup); - - } - - /** - * Deletes the connection group having the given ID from the database. - * @param id The ID of the connection group to delete. - */ - public void deleteConnectionGroup(int id) { - connectionGroupDAO.deleteByPrimaryKey(id); - } -} 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 deleted file mode 100644 index 63126eb76..000000000 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionService.java +++ /dev/null @@ -1,548 +0,0 @@ -/* - * Copyright (C) 2013 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.service; - -import com.google.inject.Inject; -import com.google.inject.Provider; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.GuacamoleSocket; -import org.glyptodon.guacamole.net.InetGuacamoleSocket; -import org.glyptodon.guacamole.net.SSLGuacamoleSocket; -import net.sourceforge.guacamole.net.auth.mysql.ActiveConnectionMap; -import net.sourceforge.guacamole.net.auth.mysql.AuthenticatedUser; -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.dao.ConnectionHistoryMapper; -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.Connection; -import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionExample; -import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionExample.Criteria; -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.ConnectionParameter; -import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionParameterExample; -import net.sourceforge.guacamole.net.auth.mysql.properties.MySQLGuacamoleProperties; -import org.glyptodon.guacamole.properties.GuacamoleProperties; -import org.glyptodon.guacamole.protocol.ConfiguredGuacamoleSocket; -import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; -import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; -import org.apache.ibatis.session.RowBounds; -import org.glyptodon.guacamole.GuacamoleClientTooManyException; -import org.glyptodon.guacamole.GuacamoleResourceConflictException; -import org.glyptodon.guacamole.token.StandardTokens; -import org.glyptodon.guacamole.token.TokenFilter; - -/** - * Service which provides convenience methods for creating, retrieving, and - * manipulating connections. - * - * @author Michael Jumper, James Muehlner - */ -public class ConnectionService { - - /** - * DAO for accessing connections. - */ - @Inject - private ConnectionMapper connectionDAO; - - /** - * DAO for accessing connection parameters. - */ - @Inject - private ConnectionParameterMapper connectionParameterDAO; - - /** - * DAO for accessing connection history. - */ - @Inject - private ConnectionHistoryMapper connectionHistoryDAO; - - /** - * Provider which creates MySQLConnections. - */ - @Inject - private Provider mySQLConnectionProvider; - - /** - * Provider which creates MySQLGuacamoleSockets. - */ - @Inject - private Provider mySQLGuacamoleSocketProvider; - - /** - * Map of all currently active connections. - */ - @Inject - private ActiveConnectionMap activeConnectionMap; - - /** - * 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 parentID - * The ID of the parent connection group. - * - * @param currentUser - * 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, Integer parentID, - AuthenticatedUser currentUser) { - - // Create criteria - ConnectionExample example = new ConnectionExample(); - Criteria criteria = example.createCriteria().andConnection_nameEqualTo(name); - if(parentID != null) - criteria.andParent_idEqualTo(parentID); - else - criteria.andParent_idIsNull(); - - // Query connection by name and parentID - List connections = - connectionDAO.selectByExample(example); - - // If no connection found, return null - if(connections.isEmpty()) - return null; - - // Otherwise, return found connection - return toMySQLConnection(connections.get(0), currentUser); - - } - - /** - * Retrieves the connection having the given unique identifier - * from the database. - * - * @param uniqueIdentifier - * The unique identifier of the connection to retrieve. - * - * @param currentUser - * The user who queried this connection. - * - * @return - * The connection having the given unique identifier, or null if no - * such connection was found. - */ - public MySQLConnection retrieveConnection(String uniqueIdentifier, AuthenticatedUser currentUser) { - - // The unique identifier for a MySQLConnection is the database ID - int connectionID; - try { - connectionID = Integer.parseInt(uniqueIdentifier); - } catch(NumberFormatException e) { - // Invalid number means it can't be a DB record; not found - return null; - } - - return retrieveConnection(connectionID, currentUser); - } - - /** - * Retrieves the connection having the given ID from the database. - * - * @param id - * The ID of the connection to retrieve. - * - * @param currentUser - * 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, AuthenticatedUser currentUser) { - - // Query connection by ID - Connection connection = connectionDAO.selectByPrimaryKey(id); - - // If no connection found, return null - if(connection == null) - return null; - - // Otherwise, return found connection - return toMySQLConnection(connection, currentUser); - } - - /** - * Returns a list of the IDs of all connections with a given parent ID. - * @param parentID The ID of the parent for all the queried connections. - * @return a list of the IDs of all connections with a given parent ID. - */ - public List getAllConnectionIDs(Integer parentID) { - - // Create criteria - ConnectionExample example = new ConnectionExample(); - Criteria criteria = example.createCriteria(); - - if(parentID != null) - criteria.andParent_idEqualTo(parentID); - else - criteria.andParent_idIsNull(); - - // Query the connections - List connections = connectionDAO.selectByExample(example); - - // List of IDs of connections with the given parent - List connectionIDs = new ArrayList(); - - for(Connection connection : connections) { - connectionIDs.add(connection.getConnection_id()); - } - - return connectionIDs; - } - - /** - * Convert the given database-retrieved Connection into a MySQLConnection. - * The parameters of the given connection will be read and added to the - * MySQLConnection in the process. - * - * @param connection - * The connection to convert. - * - * @param currentUser - * The user who queried this connection. - * - * @return A new MySQLConnection containing all data associated with the - * specified connection. - */ - private MySQLConnection toMySQLConnection(Connection connection, AuthenticatedUser currentUser) { - - // Build configuration - GuacamoleConfiguration config = new GuacamoleConfiguration(); - - // Query parameters for configuration - ConnectionParameterExample connectionParameterExample = new ConnectionParameterExample(); - connectionParameterExample.createCriteria().andConnection_idEqualTo(connection.getConnection_id()); - List connectionParameters = - connectionParameterDAO.selectByExample(connectionParameterExample); - - // Set protocol - config.setProtocol(connection.getProtocol()); - - // Set all values for all parameters - for (ConnectionParameter parameter : connectionParameters) - config.setParameter(parameter.getParameter_name(), - parameter.getParameter_value()); - - // Create new MySQLConnection from retrieved data - MySQLConnection mySQLConnection = mySQLConnectionProvider.get(); - mySQLConnection.init( - connection.getConnection_id(), - connection.getParent_id(), - connection.getConnection_name(), - Integer.toString(connection.getConnection_id()), - config, - retrieveHistory(connection.getConnection_id()), - currentUser - ); - - return mySQLConnection; - - } - - /** - * Retrieves the history of the connection having the given ID. - * - * @param connectionID The ID of the connection to retrieve the history of. - * @return A list of MySQLConnectionRecord documenting the history of this - * connection. - */ - public List retrieveHistory(int connectionID) { - - // Retrieve history records relating to given connection ID - ConnectionHistoryExample example = new ConnectionHistoryExample(); - example.createCriteria().andConnection_idEqualTo(connectionID); - - // We want to return the newest records first - example.setOrderByClause("start_date DESC"); - - // Set the maximum number of history records returned to 100 - RowBounds rowBounds = new RowBounds(0, 100); - - // Retrieve all connection history entries - List connectionHistories = - connectionHistoryDAO.selectByExampleWithRowbounds(example, rowBounds); - - // Convert history entries to connection records - List connectionRecords = new ArrayList(); - Set userIDSet = new HashSet(); - for(ConnectionHistory history : connectionHistories) { - userIDSet.add(history.getUser_id()); - } - - // Determine whether connection is currently active - int user_count = activeConnectionMap.getCurrentUserCount(connectionID); - - // 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()); - - // If there are active users, list the top N not-ended connections - // as active (best guess) - MySQLConnectionRecord connectionRecord; - if (user_count > 0 && endDate == null) { - connectionRecord = new MySQLConnectionRecord(startDate, endDate, username, true); - user_count--; - } - - // If no active users, or end date is recorded, connection is not - // active. - else - connectionRecord = new MySQLConnectionRecord(startDate, endDate, username, false); - - connectionRecords.add(connectionRecord); - - } - - return connectionRecords; - } - - - - /** - * Create a MySQLGuacamoleSocket using the provided connection. - * - * @param connection - * The connection to use when connecting the socket. - * - * @param info - * The information to use when performing the connection handshake. - * - * @param currentUser - * 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, AuthenticatedUser currentUser, - Integer connectionGroupID) - throws GuacamoleException { - - synchronized (activeConnectionMap) { - - // If the given connection is active, and multiple simultaneous - // connections are not allowed, disallow connection - if(GuacamoleProperties.getProperty( - MySQLGuacamoleProperties.MYSQL_DISALLOW_SIMULTANEOUS_CONNECTIONS, false) - && activeConnectionMap.isActive(connection.getConnectionID())) - throw new GuacamoleResourceConflictException("Cannot connect. This connection is in use."); - - if(GuacamoleProperties.getProperty( - MySQLGuacamoleProperties.MYSQL_DISALLOW_DUPLICATE_CONNECTIONS, true) - && activeConnectionMap.isConnectionUserActive(connection.getConnectionID(), currentUser.getUserID())) - throw new GuacamoleClientTooManyException - ("Cannot connect. Connection already in use by this user."); - - // Get guacd connection information - String host = GuacamoleProperties.getRequiredProperty(GuacamoleProperties.GUACD_HOSTNAME); - int port = GuacamoleProperties.getRequiredProperty(GuacamoleProperties.GUACD_PORT); - - // Build token filter containing credential tokens - TokenFilter tokenFilter = new TokenFilter(); - StandardTokens.addStandardTokens(tokenFilter, currentUser.getCredentials()); - - // Filter the configuration - GuacamoleConfiguration config = new GuacamoleConfiguration(connection.getConfiguration()); - tokenFilter.filterValues(config.getParameters()); - - // Get socket - GuacamoleSocket socket; - if (GuacamoleProperties.getProperty(GuacamoleProperties.GUACD_SSL, false)) - socket = new ConfiguredGuacamoleSocket( - new SSLGuacamoleSocket(host, port), - config, info - ); - else - socket = new ConfiguredGuacamoleSocket( - new InetGuacamoleSocket(host, port), - config, info - ); - - // Mark this connection as active - int historyID = activeConnectionMap.openConnection(connection.getConnectionID(), - currentUser.getUserID(), connectionGroupID); - - // Return new MySQLGuacamoleSocket - MySQLGuacamoleSocket mySQLGuacamoleSocket = mySQLGuacamoleSocketProvider.get(); - mySQLGuacamoleSocket.init(socket, historyID, connectionGroupID); - - return mySQLGuacamoleSocket; - - } - - } - - /** - * Creates a new connection having the given name and protocol. - * - * @param name - * The name to assign to the new connection. - * - * @param protocol - * The protocol to assign to the new connection. - * - * @param currentUser - * The user who created this connection. - * - * @param parentID - * The ID of the parent connection group. - * - * @return - * A new MySQLConnection containing the data of the newly created - * connection. - */ - public MySQLConnection createConnection(String name, String protocol, - AuthenticatedUser currentUser, Integer parentID) { - - // Initialize database connection - Connection connection = new Connection(); - connection.setConnection_name(name); - connection.setProtocol(protocol); - connection.setParent_id(parentID); - - // Create connection - connectionDAO.insert(connection); - return toMySQLConnection(connection, currentUser); - - } - - /** - * Deletes the connection having the given ID from the database. - * @param id The ID of the connection to delete. - */ - public void deleteConnection(int id) { - connectionDAO.deleteByPrimaryKey(id); - } - - /** - * Updates the connection in the database corresponding to the given - * MySQLConnection. - * - * @param mySQLConnection The MySQLConnection to update (save) to the - * database. This connection must already exist. - */ - public void updateConnection(MySQLConnection mySQLConnection) { - - // Populate connection - Connection connection = new Connection(); - connection.setConnection_id(mySQLConnection.getConnectionID()); - connection.setParent_id(mySQLConnection.getParentID()); - connection.setConnection_name(mySQLConnection.getName()); - connection.setProtocol(mySQLConnection.getConfiguration().getProtocol()); - - // Update the connection in the database - connectionDAO.updateByPrimaryKey(connection); - - } - - /** - * Get the identifiers of all the connections defined in the system - * with a certain parentID. - * - * @return A Set of identifiers of all the connections defined in the system - * with the given parentID. - */ - public Set getAllConnectionIdentifiers(Integer parentID) { - - // Set of all present connection identifiers - Set identifiers = new HashSet(); - - // Set up Criteria - ConnectionExample example = new ConnectionExample(); - Criteria criteria = example.createCriteria(); - if(parentID != null) - criteria.andParent_idEqualTo(parentID); - else - criteria.andParent_idIsNull(); - - // Query connection identifiers - List connections = - connectionDAO.selectByExample(example); - for (Connection connection : connections) - identifiers.add(String.valueOf(connection.getConnection_id())); - - return identifiers; - - } - - /** - * Get the connection IDs of all the connections defined in the system - * with a certain parent connection group. - * - * @return A list of connection IDs of all the connections defined in the system. - */ - public List getAllConnectionIDs() { - - // Set of all present connection IDs - List connectionIDs = new ArrayList(); - - // Create the criteria - ConnectionExample example = new ConnectionExample(); - - // Query the connections - List connections = - connectionDAO.selectByExample(example); - for (Connection connection : connections) - connectionIDs.add(connection.getConnection_id()); - - return connectionIDs; - - } - -} diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/PermissionCheckService.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/PermissionCheckService.java deleted file mode 100644 index a6a8d2326..000000000 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/PermissionCheckService.java +++ /dev/null @@ -1,968 +0,0 @@ -/* - * Copyright (C) 2013 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.service; - -import com.google.inject.Inject; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import net.sourceforge.guacamole.net.auth.mysql.AuthenticatedUser; -import org.glyptodon.guacamole.GuacamoleSecurityException; -import net.sourceforge.guacamole.net.auth.mysql.MySQLConnectionGroup; -import net.sourceforge.guacamole.net.auth.mysql.MySQLConstants; -import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionGroupPermissionMapper; -import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionPermissionMapper; -import net.sourceforge.guacamole.net.auth.mysql.dao.SystemPermissionMapper; -import net.sourceforge.guacamole.net.auth.mysql.dao.UserPermissionMapper; -import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionGroupPermissionExample; -import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionGroupPermissionKey; -import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionPermissionExample; -import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionPermissionExample.Criteria; -import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionPermissionKey; -import net.sourceforge.guacamole.net.auth.mysql.model.SystemPermissionExample; -import net.sourceforge.guacamole.net.auth.mysql.model.SystemPermissionKey; -import net.sourceforge.guacamole.net.auth.mysql.model.UserPermissionExample; -import net.sourceforge.guacamole.net.auth.mysql.model.UserPermissionKey; -import org.glyptodon.guacamole.net.auth.permission.ConnectionGroupPermission; -import org.glyptodon.guacamole.net.auth.permission.ConnectionPermission; -import org.glyptodon.guacamole.net.auth.permission.Permission; -import org.glyptodon.guacamole.net.auth.permission.SystemPermission; -import org.glyptodon.guacamole.net.auth.permission.UserPermission; - -/** - * A service to retrieve information about what objects a user has permission to. - * @author James Muehlner - */ -public class PermissionCheckService { - - /** - * Service for accessing users. - */ - @Inject - private UserService userService; - - /** - * Service for accessing connections. - */ - @Inject - private ConnectionService connectionService; - - /** - * Service for accessing connection groups. - */ - @Inject - private ConnectionGroupService connectionGroupService; - - /** - * DAO for accessing permissions related to users. - */ - @Inject - private UserPermissionMapper userPermissionDAO; - - /** - * DAO for accessing permissions related to connections. - */ - @Inject - private ConnectionPermissionMapper connectionPermissionDAO; - - /** - * DAO for accessing permissions related to connection groups. - */ - @Inject - private ConnectionGroupPermissionMapper connectionGroupPermissionDAO; - - /** - * DAO for accessing permissions related to the system as a whole. - */ - @Inject - private SystemPermissionMapper systemPermissionDAO; - - /** - * Verifies that the user has the specified access to the given other - * user. If permission is denied, a GuacamoleSecurityException is thrown. - * - * @param currentUser - * The user to check. - * - * @param affectedUserID - * The user that would be affected by the operation if permission is - * granted. - * - * @param permissionType - * The type of permission to check for. - * - * @throws GuacamoleSecurityException - * If the specified permission is not granted. - */ - public void verifyUserAccess(AuthenticatedUser currentUser, int affectedUserID, - String permissionType) throws GuacamoleSecurityException { - - // If permission does not exist, throw exception - if(!checkUserAccess(currentUser, affectedUserID, permissionType)) - throw new GuacamoleSecurityException("Permission denied."); - - } - - /** - * Verifies that the user has the specified access to the given connection. - * If permission is denied, a GuacamoleSecurityException is thrown. - * - * @param currentUser - * The user to check. - * - * @param affectedConnectionID - * The connection that would be affected by the operation if permission - * is granted. - * - * @param permissionType - * The type of permission to check for. - * - * @throws GuacamoleSecurityException - * If the specified permission is not granted. - */ - public void verifyConnectionAccess(AuthenticatedUser currentUser, - int affectedConnectionID, String permissionType) throws GuacamoleSecurityException { - - // If permission does not exist, throw exception - if(!checkConnectionAccess(currentUser, affectedConnectionID, permissionType)) - throw new GuacamoleSecurityException("Permission denied."); - - } - - /** - * Verifies that the user has the specified access to the given connection group. - * If permission is denied, a GuacamoleSecurityException is thrown. - * - * @param currentUser - * The user to check. - * - * @param affectedConnectionGroupID - * The connection group that would be affected by the operation if - * permission is granted. - * - * @param permissionType - * The type of permission to check for. - * - * @throws GuacamoleSecurityException - * If the specified permission is not granted. - */ - public void verifyConnectionGroupAccess(AuthenticatedUser currentUser, - Integer affectedConnectionGroupID, String permissionType) throws GuacamoleSecurityException { - - // If permission does not exist, throw exception - if(!checkConnectionGroupAccess(currentUser, affectedConnectionGroupID, permissionType)) - throw new GuacamoleSecurityException("Permission denied."); - - } - - /** - * Verifies that the user has the specified access to the system. If - * permission is denied, a GuacamoleSecurityException is thrown. - * - * @param currentUser - * The user to check. - * - * @param systemPermissionType - * The type of permission to check for. - * - * @throws GuacamoleSecurityException - * If the specified permission is not granted. - */ - public void verifySystemAccess(AuthenticatedUser currentUser, String systemPermissionType) - throws GuacamoleSecurityException { - - // If permission does not exist, throw exception - if(!checkSystemAccess(currentUser, systemPermissionType)) - throw new GuacamoleSecurityException("Permission denied."); - - } - - /** - * Checks whether a user has the specified type of access to the affected - * user. - * - * @param currentUser - * The user to check. - * - * @param affectedUserID - * The user that would be affected by the operation if permission is - * granted. - * - * @param permissionType - * The type of permission to check for. - * - * @return - * true if the specified permission is granted, false otherwise. - */ - public boolean checkUserAccess(AuthenticatedUser currentUser, - Integer affectedUserID, String permissionType) { - - // A system administrator has full access to everything. - if(checkSystemAdministratorAccess(currentUser)) - return true; - - // Check existence of requested permission - UserPermissionExample example = new UserPermissionExample(); - example.createCriteria().andUser_idEqualTo(currentUser.getUserID()).andAffected_user_idEqualTo(affectedUserID).andPermissionEqualTo(permissionType); - return userPermissionDAO.countByExample(example) > 0; - - } - - /** - * Checks whether a user has the specified type of access to the affected - * connection. - * - * @param currentUser - * The user to check. - * - * @param affectedConnectionID - * The connection that would be affected by the operation if permission - * is granted. - * - * @param permissionType - * The type of permission to check for. - * - * @return - * true if the specified permission is granted, false otherwise. - */ - public boolean checkConnectionAccess(AuthenticatedUser currentUser, - Integer affectedConnectionID, String permissionType) { - - // A system administrator has full access to everything. - if(checkSystemAdministratorAccess(currentUser)) - return true; - - // Check existence of requested permission - ConnectionPermissionExample example = new ConnectionPermissionExample(); - example.createCriteria().andUser_idEqualTo(currentUser.getUserID()).andConnection_idEqualTo(affectedConnectionID).andPermissionEqualTo(permissionType); - return connectionPermissionDAO.countByExample(example) > 0; - - } - - /** - * Checks whether a user has the specified type of access to the affected - * connection group. - * - * @param currentUser - * The user to check. - * - * @param affectedConnectionGroupID - * The connection group that would be affected by the operation if - * permission is granted. - * - * @param permissionType - * The type of permission to check for. - * - * @return - * true if the specified permission is granted, false otherwise. - */ - public boolean checkConnectionGroupAccess(AuthenticatedUser currentUser, - Integer affectedConnectionGroupID, String permissionType) { - - // All users have implicit permission to read and update the root connection group - if(affectedConnectionGroupID == null && - MySQLConstants.CONNECTION_GROUP_READ.equals(permissionType) || - MySQLConstants.CONNECTION_GROUP_UPDATE.equals(permissionType)) - return true; - - // A system administrator has full access to everything. - if(checkSystemAdministratorAccess(currentUser)) - return true; - - // Check existence of requested permission - ConnectionGroupPermissionExample example = new ConnectionGroupPermissionExample(); - example.createCriteria().andUser_idEqualTo(currentUser.getUserID()).andConnection_group_idEqualTo(affectedConnectionGroupID).andPermissionEqualTo(permissionType); - return connectionGroupPermissionDAO.countByExample(example) > 0; - - } - - /** - * Checks whether a user has the specified type of access to the system. - * - * @param currentUser - * The user to check. - * - * @param systemPermissionType - * The type of permission to check for. - * - * @return - * true if the specified permission is granted, false otherwise. - */ - private boolean checkSystemAccess(AuthenticatedUser currentUser, String systemPermissionType) { - - // A system administrator has full access to everything. - if(checkSystemAdministratorAccess(currentUser)) - return true; - - // Check existence of requested permission - SystemPermissionExample example = new SystemPermissionExample(); - example.createCriteria().andUser_idEqualTo(currentUser.getUserID()).andPermissionEqualTo(systemPermissionType); - return systemPermissionDAO.countByExample(example) > 0; - - } - - /** - * Checks whether a user has system administrator access to the system. - * - * @param currentUser - * The user to check. - * - * @return - * true if the system administrator access exists, false otherwise. - */ - private boolean checkSystemAdministratorAccess(AuthenticatedUser currentUser) { - - // Check existence of system administrator permission - SystemPermissionExample example = new SystemPermissionExample(); - example.createCriteria().andUser_idEqualTo(currentUser.getUserID()) - .andPermissionEqualTo(MySQLConstants.SYSTEM_ADMINISTER); - return systemPermissionDAO.countByExample(example) > 0; - } - - /** - * Verifies that the specified group can be used for organization - * by the given user. - * - * @param connectionGroupID - * The ID of the affected ConnectionGroup. - * - * @param currentUser - * The user to check. - * - * @param type - * The desired usage. - * - * @throws GuacamoleSecurityException - * If the connection group cannot be used for organization. - */ - public void verifyConnectionGroupUsageAccess(Integer connectionGroupID, - AuthenticatedUser currentUser, String type) throws GuacamoleSecurityException { - - // If permission does not exist, throw exception - if(!checkConnectionGroupUsageAccess(connectionGroupID, currentUser, type)) - throw new GuacamoleSecurityException("Permission denied."); - - } - - /** - * Check whether a user can use connectionGroup for the given usage. - * - * @param connectionGroupID - * The ID of the affected connection group. - * - * @param currentUser - * The user to check. - * - * @param usage - * The desired usage. - * - * @return - * true if the user can use the connection group for the given usage. - */ - private boolean checkConnectionGroupUsageAccess( - Integer connectionGroupID, AuthenticatedUser currentUser, String usage) { - - // The root level connection group can only be used for organization - if(connectionGroupID == null) - return MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL.equals(usage); - - // A system administrator has full access to everything. - if(checkSystemAdministratorAccess(currentUser)) - return true; - - // A connection group administrator can use the group either way. - if(checkConnectionGroupAccess(currentUser, connectionGroupID, - MySQLConstants.CONNECTION_GROUP_ADMINISTER)) - return true; - - // Query the connection group - MySQLConnectionGroup connectionGroup = connectionGroupService. - retrieveConnectionGroup(connectionGroupID, currentUser); - - // If the connection group is not found, it cannot be used. - if(connectionGroup == null) - return false; - - // Verify that the desired usage matches the type. - return MySQLConstants.getConnectionGroupTypeConstant( - connectionGroup.getType()).equals(usage); - - } - - /** - * Find the list of the IDs of all users a user has permission to. - * The access type is defined by permissionType. - * - * @param currentUser - * The user to check. - * - * @param permissionType - * The type of permission to check for. - * - * @return - * A list of all user IDs this user has the specified access to. - */ - public List retrieveUserIDs(AuthenticatedUser currentUser, String permissionType) { - - // A system administrator has access to all users. - if(checkSystemAdministratorAccess(currentUser)) - return userService.getAllUserIDs(); - - // Query all user permissions for the given user and permission type - UserPermissionExample example = new UserPermissionExample(); - example.createCriteria().andUser_idEqualTo(currentUser.getUserID()).andPermissionEqualTo(permissionType); - example.setDistinct(true); - List userPermissions = - userPermissionDAO.selectByExample(example); - - // Convert result into list of IDs - List currentUsers = new ArrayList(userPermissions.size()); - for(UserPermissionKey permission : userPermissions) - currentUsers.add(permission.getAffected_user_id()); - - return currentUsers; - - } - - /** - * Find the list of the IDs of all connections a user has permission to. - * The access type is defined by permissionType. - * - * @param currentUser - * The user to check. - * - * @param permissionType - * The type of permission to check for. - * - * @return - * A list of all connection IDs this user has the specified access to. - */ - public List retrieveConnectionIDs(AuthenticatedUser currentUser, - String permissionType) { - - return retrieveConnectionIDs(currentUser, null, permissionType, false); - - } - - /** - * Find the list of the IDs of all connections a user has permission to. - * The access type is defined by permissionType. - * - * @param currentUser - * The user to check. - * - * @param parentID - * The parent connection group. - * - * @param permissionType - * The type of permission to check for. - * - * @return - * A list of all connection IDs this user has the specified access to. - */ - public List retrieveConnectionIDs(AuthenticatedUser currentUser, Integer parentID, - String permissionType) { - - return retrieveConnectionIDs(currentUser, parentID, permissionType, true); - - } - - /** - * Find the list of the IDs of all connections a user has permission to. - * The access type is defined by permissionType. - * - * @param currentUser - * The user to check. - * - * @param parentID - * The parent connection group. - * - * @param permissionType - * The type of permission to check for. - * - * @param checkParentID - * Whether the parentID should be checked or not. - * - * @return - * A list of all connection IDs this user has the specified access to. - */ - private List retrieveConnectionIDs(AuthenticatedUser currentUser, Integer parentID, - String permissionType, boolean checkParentID) { - - // A system administrator has access to all connections. - if(checkSystemAdministratorAccess(currentUser)) { - if(checkParentID) - return connectionService.getAllConnectionIDs(parentID); - else - return connectionService.getAllConnectionIDs(); - } - - // Query all connection permissions for the given user and permission type - ConnectionPermissionExample example = new ConnectionPermissionExample(); - Criteria criteria = example.createCriteria().andUser_idEqualTo(currentUser.getUserID()) - .andPermissionEqualTo(permissionType); - - // Ensure that the connections are all under the parent ID, if needed - if(checkParentID) { - // Get the IDs of all connections in the connection group - List allConnectionIDs = connectionService.getAllConnectionIDs(parentID); - - if(allConnectionIDs.isEmpty()) - return Collections.EMPTY_LIST; - - criteria.andConnection_idIn(allConnectionIDs); - } - - example.setDistinct(true); - List connectionPermissions = - connectionPermissionDAO.selectByExample(example); - - // Convert result into list of IDs - List connectionIDs = new ArrayList(connectionPermissions.size()); - for(ConnectionPermissionKey permission : connectionPermissions) - connectionIDs.add(permission.getConnection_id()); - - return connectionIDs; - - } - - /** - * Find the list of the IDs of all connection groups a user has permission to. - * The access type is defined by permissionType. - * - * @param currentUser - * The user to check. - * - * @param permissionType - * The type of permission to check for. - * - * @return - * A list of all connection group IDs this user has the specified - * access to. - */ - public List retrieveConnectionGroupIDs(AuthenticatedUser currentUser, - String permissionType) { - - return retrieveConnectionGroupIDs(currentUser, null, permissionType, false); - - } - - /** - * Find the list of the IDs of all connection groups a user has permission to. - * The access type is defined by permissionType. - * - * @param currentUser - * The user to check. - * - * @param parentID - * The parent connection group. - * - * @param permissionType - * The type of permission to check for. - * - * @return - * A list of all connection group IDs this user has the specified - * access to. - */ - public List retrieveConnectionGroupIDs(AuthenticatedUser currentUser, Integer parentID, - String permissionType) { - - return retrieveConnectionGroupIDs(currentUser, parentID, permissionType, true); - - } - - /** - * Find the list of the IDs of all connection groups a user has permission to. - * The access type is defined by permissionType. - * - * @param currentUser - * The user to check. - * - * @param parentID - * The parent connection group. - * - * @param permissionType - * The type of permission to check for. - * - * @param checkParentID - * Whether the parentID should be checked or not. - * - * @return - * A list of all connection group IDs this user has the specified - * access to. - */ - private List retrieveConnectionGroupIDs(AuthenticatedUser currentUser, Integer parentID, - String permissionType, boolean checkParentID) { - - // A system administrator has access to all connectionGroups . - if(checkSystemAdministratorAccess(currentUser)) { - if(checkParentID) - return connectionGroupService.getAllConnectionGroupIDs(parentID); - else - return connectionGroupService.getAllConnectionGroupIDs(); - } - - // Query all connection permissions for the given user and permission type - ConnectionGroupPermissionExample example = new ConnectionGroupPermissionExample(); - ConnectionGroupPermissionExample.Criteria criteria = - example.createCriteria().andUser_idEqualTo(currentUser.getUserID()) - .andPermissionEqualTo(permissionType); - - // Ensure that the connection groups are all under the parent ID, if needed - if(checkParentID) { - // Get the IDs of all connection groups in the connection group - List allConnectionGroupIDs = connectionGroupService - .getAllConnectionGroupIDs(parentID); - - if(allConnectionGroupIDs.isEmpty()) - return Collections.EMPTY_LIST; - - criteria.andConnection_group_idIn(allConnectionGroupIDs); - } - - example.setDistinct(true); - List connectionGroupPermissions = - connectionGroupPermissionDAO.selectByExample(example); - - // Convert result into list of IDs - List connectionGroupIDs = new ArrayList(connectionGroupPermissions.size()); - for(ConnectionGroupPermissionKey permission : connectionGroupPermissions) - connectionGroupIDs.add(permission.getConnection_group_id()); - - // All users have implicit access to read and update the root group - if(MySQLConstants.CONNECTION_GROUP_READ.equals(permissionType) - && MySQLConstants.CONNECTION_GROUP_UPDATE.equals(permissionType) - && !checkParentID) - connectionGroupIDs.add(null); - - return connectionGroupIDs; - - } - - /** - * Retrieve all existing usernames that the given user has permission to - * perform the given operation upon. - * - * @param currentUser - * The user whose permissions should be checked. - * - * @param permissionType - * The permission to check. - * - * @return - * A set of all usernames for which the given user has the given - * permission. - */ - public Set retrieveUsernames(AuthenticatedUser currentUser, String permissionType) { - - // A system administrator has access to all users. - if(checkSystemAdministratorAccess(currentUser)) - return userService.getAllUsernames(); - - // List of all user IDs for which this user has read access - List currentUsers = - retrieveUserIDs(currentUser, MySQLConstants.USER_READ); - - // Query all associated users - return userService.translateUsernames(currentUsers).keySet(); - - } - - /** - * Retrieve all existing connection identifiers that the given user has - * permission to perform the given operation upon. - * - * @param currentUser - * The user whose permissions should be checked. - * - * @param permissionType - * The permission to check. - * - * @param parentID - * The parent connection group. - * - * @return - * A set of all connection identifiers for which the given user has the - * given permission. - */ - public Set retrieveConnectionIdentifiers(AuthenticatedUser currentUser, Integer parentID, - String permissionType) { - - // A system administrator has access to all connections. - if(checkSystemAdministratorAccess(currentUser)) - return connectionService.getAllConnectionIdentifiers(parentID); - - // List of all connection IDs for which this user has access - List connectionIDs = - retrieveConnectionIDs(currentUser, parentID, permissionType); - - // Unique Identifiers for MySQLConnections are the database IDs - Set connectionIdentifiers = new HashSet(); - - for(Integer connectionID : connectionIDs) - connectionIdentifiers.add(Integer.toString(connectionID)); - - return connectionIdentifiers; - } - - /** - * Retrieve all existing connection group identifiers that the given user - * has permission to perform the given operation upon. - * - * @param currentUser - * The user whose permissions should be checked. - * - * @param permissionType - * The permission to check. - * - * @param parentID - * The parent connection group. - * - * @return - * A set of all connection group identifiers for which the given user - * has the given permission. - */ - public Set retrieveConnectionGroupIdentifiers(AuthenticatedUser currentUser, Integer parentID, - String permissionType) { - - // A system administrator has access to all connections. - if(checkSystemAdministratorAccess(currentUser)) - return connectionGroupService.getAllConnectionGroupIdentifiers(parentID); - - // List of all connection group IDs for which this user has access - List connectionGroupIDs = - retrieveConnectionGroupIDs(currentUser, parentID, permissionType); - - // Unique Identifiers for MySQLConnectionGroups are the database IDs - Set connectionGroupIdentifiers = new HashSet(); - - for(Integer connectionGroupID : connectionGroupIDs) - connectionGroupIdentifiers.add(Integer.toString(connectionGroupID)); - - return connectionGroupIdentifiers; - } - - /** - * Retrieves all user permissions granted to the user having the given ID. - * - * @param userID The ID of the user to retrieve permissions of. - * @return A set of all user permissions granted to the user having the - * given ID. - */ - public Set retrieveUserPermissions(int userID) { - - // Set of all permissions - Set permissions = new HashSet(); - - // Query all user permissions - UserPermissionExample userPermissionExample = new UserPermissionExample(); - userPermissionExample.createCriteria().andUser_idEqualTo(userID); - List userPermissions = - userPermissionDAO.selectByExample(userPermissionExample); - - // Get list of affected user IDs - List affectedUserIDs = new ArrayList(); - for(UserPermissionKey userPermission : userPermissions) - affectedUserIDs.add(userPermission.getAffected_user_id()); - - // Get corresponding usernames - Map affectedUsers = - userService.retrieveUsernames(affectedUserIDs); - - // Add user permissions - for(UserPermissionKey userPermission : userPermissions) { - - // Construct permission from data - UserPermission permission = new UserPermission( - UserPermission.Type.valueOf(userPermission.getPermission()), - affectedUsers.get(userPermission.getAffected_user_id()) - ); - - // Add to set - permissions.add(permission); - - } - - return permissions; - - } - - /** - * Retrieves all connection permissions granted to the user having the - * given ID. - * - * @param userID The ID of the user to retrieve permissions of. - * @return A set of all connection permissions granted to the user having - * the given ID. - */ - public Set retrieveConnectionPermissions(int userID) { - - // Set of all permissions - Set permissions = new HashSet(); - - // Query all connection permissions - ConnectionPermissionExample connectionPermissionExample = new ConnectionPermissionExample(); - connectionPermissionExample.createCriteria().andUser_idEqualTo(userID); - List connectionPermissions = - connectionPermissionDAO.selectByExample(connectionPermissionExample); - - // Add connection permissions - for(ConnectionPermissionKey connectionPermission : connectionPermissions) { - - // Construct permission from data - ConnectionPermission permission = new ConnectionPermission( - ConnectionPermission.Type.valueOf(connectionPermission.getPermission()), - String.valueOf(connectionPermission.getConnection_id()) - ); - - // Add to set - permissions.add(permission); - - } - - return permissions; - - } - - /** - * Retrieves all connection group permissions granted to the user having the - * given ID. - * - * @param userID The ID of the user to retrieve permissions of. - * @return A set of all connection group permissions granted to the user having - * the given ID. - */ - public Set retrieveConnectionGroupPermissions(int userID) { - - // Set of all permissions - Set permissions = new HashSet(); - - // Query all connection permissions - ConnectionGroupPermissionExample connectionGroupPermissionExample = new ConnectionGroupPermissionExample(); - connectionGroupPermissionExample.createCriteria().andUser_idEqualTo(userID); - List connectionGroupPermissions = - connectionGroupPermissionDAO.selectByExample(connectionGroupPermissionExample); - - // Add connection permissions - for(ConnectionGroupPermissionKey connectionGroupPermission : connectionGroupPermissions) { - - // Construct permission from data - ConnectionGroupPermission permission = new ConnectionGroupPermission( - ConnectionGroupPermission.Type.valueOf(connectionGroupPermission.getPermission()), - String.valueOf(connectionGroupPermission.getConnection_group_id()) - ); - - // Add to set - permissions.add(permission); - - } - - // All users have implict access to read the root connection group - permissions.add(new ConnectionGroupPermission( - ConnectionGroupPermission.Type.READ, - MySQLConstants.CONNECTION_GROUP_ROOT_IDENTIFIER - )); - - // All users have implict access to update the root connection group - permissions.add(new ConnectionGroupPermission( - ConnectionGroupPermission.Type.UPDATE, - MySQLConstants.CONNECTION_GROUP_ROOT_IDENTIFIER - )); - - return permissions; - - } - - /** - * Retrieves all system permissions granted to the user having the - * given ID. - * - * @param userID The ID of the user to retrieve permissions of. - * @return A set of all system permissions granted to the user having the - * given ID. - */ - public Set retrieveSystemPermissions(int userID) { - - // Set of all permissions - Set permissions = new HashSet(); - - // And finally, system permissions - SystemPermissionExample systemPermissionExample = new SystemPermissionExample(); - systemPermissionExample.createCriteria().andUser_idEqualTo(userID); - List systemPermissions = - systemPermissionDAO.selectByExample(systemPermissionExample); - for(SystemPermissionKey systemPermission : systemPermissions) { - - // User creation permission - if(systemPermission.getPermission().equals(MySQLConstants.SYSTEM_USER_CREATE)) - permissions.add(new SystemPermission(SystemPermission.Type.CREATE_USER)); - - // System creation permission - else if(systemPermission.getPermission().equals(MySQLConstants.SYSTEM_CONNECTION_CREATE)) - permissions.add(new SystemPermission(SystemPermission.Type.CREATE_CONNECTION)); - - // System creation permission - else if(systemPermission.getPermission().equals(MySQLConstants.SYSTEM_CONNECTION_GROUP_CREATE)) - permissions.add(new SystemPermission(SystemPermission.Type.CREATE_CONNECTION_GROUP)); - - // System administration permission - else if(systemPermission.getPermission().equals(MySQLConstants.SYSTEM_ADMINISTER)) - permissions.add(new SystemPermission(SystemPermission.Type.ADMINISTER)); - - } - - return permissions; - - } - - /** - * Retrieves all permissions granted to the user having the given ID. - * - * @param userID The ID of the user to retrieve permissions of. - * @return A set of all permissions granted to the user having the given - * ID. - */ - public Set retrieveAllPermissions(int userID) { - - // Set which will contain all permissions - Set allPermissions = new HashSet(); - - // Add user permissions - allPermissions.addAll(retrieveUserPermissions(userID)); - - // Add connection permissions - allPermissions.addAll(retrieveConnectionPermissions(userID)); - - // add connection group permissions - allPermissions.addAll(retrieveConnectionGroupPermissions(userID)); - - // Add system permissions - allPermissions.addAll(retrieveSystemPermissions(userID)); - - return allPermissions; - } - -} 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 deleted file mode 100644 index 9338fbdcb..000000000 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/UserService.java +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright (C) 2013 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.service; - -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.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.auth.Credentials; -import net.sourceforge.guacamole.net.auth.mysql.MySQLUser; -import net.sourceforge.guacamole.net.auth.mysql.dao.UserMapper; -import net.sourceforge.guacamole.net.auth.mysql.model.User; -import net.sourceforge.guacamole.net.auth.mysql.model.UserExample; -import net.sourceforge.guacamole.net.auth.mysql.model.UserWithBLOBs; - -/** - * Service which provides convenience methods for creating, retrieving, and - * manipulating users. - * - * @author Michael Jumper, James Muehlner - */ -public class UserService { - - /** - * DAO for accessing users. - */ - @Inject - private UserMapper userDAO; - - /** - * Provider for creating users. - */ - @Inject - private Provider mySQLUserProvider; - - /** - * Service for checking permissions. - */ - @Inject - private PermissionCheckService permissionCheckService; - - /** - * Service for encrypting passwords. - */ - @Inject - private PasswordEncryptionService passwordService; - - /** - * Service for generating random salts. - */ - @Inject - private SaltService saltService; - - /** - * Create a new MySQLUser based on the provided User. - * - * @param user The User to use when populating the data of the given - * MySQLUser. - * @return A new MySQLUser object, populated with the data of the given - * user. - * - * @throws GuacamoleException If an error occurs while reading the data - * of the provided User. - */ - public MySQLUser toMySQLUser(org.glyptodon.guacamole.net.auth.User user) throws GuacamoleException { - MySQLUser mySQLUser = mySQLUserProvider.get(); - mySQLUser.init(user); - return mySQLUser; - } - - /** - * Create a new MySQLUser based on the provided database record. - * - * @param user The database record describing the user. - * @return A new MySQLUser object, populated with the data of the given - * database record. - */ - private MySQLUser toMySQLUser(UserWithBLOBs user) { - - // Retrieve user from provider - MySQLUser mySQLUser = mySQLUserProvider.get(); - - // Init with data from given database user - mySQLUser.init( - user.getUser_id(), - user.getUsername(), - null, - permissionCheckService.retrieveAllPermissions(user.getUser_id()) - ); - - // Return new user - return mySQLUser; - - } - - /** - * Retrieves the user having the given ID from the database. - * - * @param id The ID of the user to retrieve. - * @return The existing MySQLUser object if found, null otherwise. - */ - public MySQLUser retrieveUser(int id) { - - // Query user by ID - UserWithBLOBs user = userDAO.selectByPrimaryKey(id); - - // If no user found, return null - if(user == null) - return null; - - // Otherwise, return found user - return toMySQLUser(user); - - } - - /** - * Retrieves the user having the given username from the database. - * - * @param name The username of the user to retrieve. - * @return The existing MySQLUser object if found, null otherwise. - */ - public MySQLUser retrieveUser(String name) { - - // Query user by ID - UserExample example = new UserExample(); - example.createCriteria().andUsernameEqualTo(name); - List users = userDAO.selectByExampleWithBLOBs(example); - - // If no user found, return null - if(users.isEmpty()) - return null; - - // Otherwise, return found user - return toMySQLUser(users.get(0)); - - } - - /** - * Retrieves the user corresponding to the given credentials from the - * database. - * - * @param credentials The credentials to use when locating the user. - * @return The existing MySQLUser object if the credentials given are - * valid, null otherwise. - */ - public MySQLUser retrieveUser(Credentials credentials) { - - // No null users in database - if (credentials.getUsername() == null) - return null; - - // Query user - UserExample userExample = new UserExample(); - userExample.createCriteria().andUsernameEqualTo(credentials.getUsername()); - List users = userDAO.selectByExampleWithBLOBs(userExample); - - // Check that a user was found - if (users.isEmpty()) - return null; - - // Assert only one user found - assert users.size() == 1 : "Multiple users with same username."; - - // Get first (and only) user - UserWithBLOBs user = users.get(0); - - // Check password, if invalid return null - if (!passwordService.checkPassword(credentials.getPassword(), - user.getPassword_hash(), user.getPassword_salt())) - return null; - - // Return found user - return toMySQLUser(user); - - } - - /** - * Retrieves a translation map of usernames to their corresponding IDs. - * - * @param ids The IDs of the users to retrieve the usernames of. - * @return A map containing the names of all users and their corresponding - * IDs. - */ - public Map translateUsernames(List ids) { - - // If no IDs given, just return empty map - if (ids.isEmpty()) - return Collections.EMPTY_MAP; - - // Map of all names onto their corresponding IDs - Map names = new HashMap(); - - // Get all users having the given IDs - UserExample example = new UserExample(); - example.createCriteria().andUser_idIn(ids); - List users = - userDAO.selectByExample(example); - - // Produce set of names - for (User user : users) - names.put(user.getUsername(), user.getUser_id()); - - return names; - - } - - /** - * Retrieves a map of all usernames for the given IDs. - * - * @param ids The IDs of the users to retrieve the usernames of. - * @return A map containing the names of all users and their corresponding - * IDs. - */ - public Map retrieveUsernames(Collection ids) { - - // If no IDs given, just return empty map - if (ids.isEmpty()) - return Collections.EMPTY_MAP; - - // Map of all names onto their corresponding IDs - Map names = new HashMap(); - - // Get all users having the given IDs - UserExample example = new UserExample(); - example.createCriteria().andUser_idIn(Lists.newArrayList(ids)); - List users = - userDAO.selectByExample(example); - - // Produce set of names - for (User user : users) - names.put(user.getUser_id(), user.getUsername()); - - return names; - - } - - /** - * Creates a new user having the given username and password. - * - * @param username The username to assign to the new user. - * @param password The password to assign to the new user. - * @return A new MySQLUser containing the data of the newly created - * user. - */ - public MySQLUser createUser(String username, String password) { - - // Initialize database user - UserWithBLOBs user = new UserWithBLOBs(); - user.setUsername(username); - - // Set password if specified - if (password != null) { - byte[] salt = saltService.generateSalt(); - user.setPassword_salt(salt); - user.setPassword_hash( - passwordService.createPasswordHash(password, salt)); - } - - // Create user - userDAO.insert(user); - return toMySQLUser(user); - - } - - /** - * Deletes the user having the given ID from the database. - * @param user_id The ID of the user to delete. - */ - public void deleteUser(int user_id) { - userDAO.deleteByPrimaryKey(user_id); - } - - /** - * Updates the user in the database corresponding to the given MySQLUser. - * - * @param mySQLUser The MySQLUser to update (save) to the database. This - * user must already exist. - */ - public void updateUser(MySQLUser mySQLUser) { - - UserWithBLOBs user = new UserWithBLOBs(); - user.setUser_id(mySQLUser.getUserID()); - user.setUsername(mySQLUser.getUsername()); - - // Set password if specified - if (mySQLUser.getPassword() != null) { - byte[] salt = saltService.generateSalt(); - user.setPassword_salt(salt); - user.setPassword_hash( - passwordService.createPasswordHash(mySQLUser.getPassword(), salt)); - } - - // Update the user in the database - userDAO.updateByPrimaryKeySelective(user); - - } - - /** - * Get the usernames of all the users defined in the system. - * - * @return A Set of usernames of all the users defined in the system. - */ - public Set getAllUsernames() { - - // Set of all present usernames - Set usernames = new HashSet(); - - // Query all usernames - List users = - userDAO.selectByExample(new UserExample()); - for (User user : users) - usernames.add(user.getUsername()); - - return usernames; - - } - - /** - * Get the user IDs of all the users defined in the system. - * - * @return A list of user IDs of all the users defined in the system. - */ - public List getAllUserIDs() { - - // Set of all present user IDs - List userIDs = new ArrayList(); - - // Query all user IDs - List users = - userDAO.selectByExample(new UserExample()); - for (User user : users) - userIDs.add(user.getUser_id()); - - return userIDs; - - } - -} diff --git a/extensions/guacamole-auth-mysql/src/main/resources/generatorConfig.xml b/extensions/guacamole-auth-mysql/src/main/resources/generatorConfig.xml deleted file mode 100644 index fca4e00d6..000000000 --- a/extensions/guacamole-auth-mysql/src/main/resources/generatorConfig.xml +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - - -
- - - - -
- - - - - -
- -
-
- diff --git a/guacamole/src/main/webapp/app/home/controllers/homeController.js b/guacamole/src/main/webapp/app/home/controllers/homeController.js index 728b29923..70051971c 100644 --- a/guacamole/src/main/webapp/app/home/controllers/homeController.js +++ b/guacamole/src/main/webapp/app/home/controllers/homeController.js @@ -84,6 +84,7 @@ angular.module('home').controller('homeController', ['$scope', '$injector', // System permissions PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) + || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_USER) || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION) || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION_GROUP) diff --git a/pom.xml b/pom.xml index 7c3e2323b..e3af9a7df 100644 --- a/pom.xml +++ b/pom.xml @@ -27,6 +27,11 @@ guacamole-common-js + + extensions/guacamole-auth-jdbc + extensions/guacamole-auth-ldap + extensions/guacamole-auth-noauth +