Merge pull request #98 from glyptodon/mysql-speed

GUAC-1101: Rewrite guacamole-auth-mysql with speed in mind.
This commit is contained in:
James Muehlner
2015-03-01 13:16:50 -08:00
114 changed files with 8300 additions and 5964 deletions

View File

@@ -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/

View File

@@ -0,0 +1,82 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.glyptodon.guacamole</groupId>
<artifactId>guacamole-auth-jdbc-base</artifactId>
<packaging>jar</packaging>
<name>guacamole-auth-jdbc-base</name>
<url>http://guac-dev.org/</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.glyptodon.guacamole</groupId>
<artifactId>guacamole-auth-jdbc</artifactId>
<version>0.9.5</version>
<relativePath>../../</relativePath>
</parent>
<build>
<plugins>
<!-- Written for 1.6 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Guacamole Extension API -->
<dependency>
<groupId>org.glyptodon.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
<scope>provided</scope>
</dependency>
<!-- SLF4J - logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<!-- MyBatis Guice -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-guice</artifactId>
<version>3.6</version>
</dependency>
<!-- Guice -->
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-multibindings</artifactId>
<version>3.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -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);
}
}

View File

@@ -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 <ModelType>
* The type of model object that corresponds to this object.
*/
public abstract class DirectoryObject<ModelType extends ObjectModel>
extends ModeledObject<ModelType> implements Identifiable {
@Override
public String getIdentifier() {
return getModel().getIdentifier();
}
@Override
public void setIdentifier(String identifier) {
getModel().setIdentifier(identifier);
}
}

View File

@@ -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 <ModelType>
* The type of object contained within the directory whose objects are
* mapped by this mapper.
*/
public interface DirectoryObjectMapper<ModelType> {
/**
* 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<String> 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<String> 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<ModelType> select(@Param("identifiers") Collection<String> 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<ModelType> selectReadable(@Param("user") UserModel user,
@Param("identifiers") Collection<String> 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);
}

View File

@@ -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 <InternalType>
* The specific internal implementation of the type of object this service
* provides access to.
*
* @param <ExternalType>
* The external interface or implementation of the type of object this
* service provides access to, as defined by the guacamole-ext API.
*
* @param <ModelType>
* The underlying model object used to represent InternalType in the
* database.
*/
public abstract class DirectoryObjectService<InternalType extends DirectoryObject<ModelType>,
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<ModelType> 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<InternalType> getObjectInstances(AuthenticatedUser currentUser,
Collection<ModelType> models) {
// Create new collection of objects by manually converting each model
Collection<InternalType> objects = new ArrayList<InternalType>(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<InternalType> 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<InternalType> retrieveObjects(AuthenticatedUser user,
Collection<String> identifiers) throws GuacamoleException {
// Do not query if no identifiers given
if (identifiers.isEmpty())
return Collections.EMPTY_LIST;
Collection<ModelType> 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<ObjectPermissionModel> implicitPermissions =
new ArrayList<ObjectPermissionModel>(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<String> 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());
}
}

View File

@@ -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 <ModelType>
* The type of model object which corresponds to this object.
*/
public abstract class ModeledObject<ModelType> 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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<Connection> {
/**
* 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<Connection> getAll(Collection<String> identifiers) throws GuacamoleException {
Collection<ModeledConnection> objects = connectionService.retrieveObjects(getCurrentUser(), identifiers);
return Collections.<Connection>unmodifiableCollection(objects);
}
@Override
@Transactional
public Set<String> 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);
}
}

View File

@@ -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<ConnectionModel> {
/**
* 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<String> 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<String> 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);
}

View File

@@ -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.");
}
}

View File

@@ -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<ConnectionRecordModel> 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);
}

View File

@@ -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;
}
}

View File

@@ -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<ModeledConnection, Connection, ConnectionModel> {
/**
* 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<ModeledConnection> connectionProvider;
/**
* Service for creating and tracking sockets.
*/
@Inject
private GuacamoleSocketService socketService;
@Override
protected DirectoryObjectMapper<ConnectionModel> 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<ParameterModel> getParameterModels(ModeledConnection connection) {
Map<String, String> parameters = connection.getConfiguration().getParameters();
// Convert parameters to model objects
Collection<ParameterModel> parameterModels = new ArrayList(parameters.size());
for (Map.Entry<String, String> 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<ParameterModel> 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<ParameterModel> 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<String> 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<String, String> retrieveParameters(AuthenticatedUser user,
String identifier) {
Map<String, String> parameterMap = new HashMap<String, String>();
// 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<ConnectionRecord> 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<ConnectionRecordModel> models = connectionRecordMapper.select(identifier);
// Get currently-active connections
List<ConnectionRecord> records = new ArrayList<ConnectionRecord>(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.");
}
}

View File

@@ -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<ConnectionModel>
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<ModeledGuacamoleConfiguration> 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<? extends ConnectionRecord> 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();
}
}

View File

@@ -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;
}
}

View File

@@ -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<String, String> 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<String, String> parameters) {
this.parameters = parameters;
super.setParameters(parameters);
}
@Override
public Map<String, String> getParameters() {
// Retrieve visible parameters, if not overridden by setParameters()
if (parameters == null) {
// Retrieve all visible parameters
Map<String, String> visibleParameters =
connectionService.retrieveParameters(currentUser, connectionModel.getIdentifier());
// Use retrieved parameters to back future operations
super.setParameters(visibleParameters);
}
return super.getParameters();
}
}

View File

@@ -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<ParameterModel> 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<ParameterModel> 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);
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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<ConnectionGroup> {
/**
* 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<ConnectionGroup> getAll(Collection<String> identifiers) throws GuacamoleException {
Collection<ModeledConnectionGroup> objects = connectionGroupService.retrieveObjects(getCurrentUser(), identifiers);
return Collections.<ConnectionGroup>unmodifiableCollection(objects);
}
@Override
@Transactional
public Set<String> 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);
}
}

View File

@@ -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<ConnectionGroupModel> {
/**
* 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<String> 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<String> 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);
}

View File

@@ -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.");
}
}

View File

@@ -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<ModeledConnectionGroup,
ConnectionGroup, ConnectionGroupModel> {
/**
* 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<ModeledConnectionGroup> connectionGroupProvider;
/**
* Service for creating and tracking sockets.
*/
@Inject
private GuacamoleSocketService socketService;
@Override
protected DirectoryObjectMapper<ConnectionGroupModel> 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<String> 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.");
}
}

View File

@@ -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<ConnectionGroupModel>
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<String> getConnectionIdentifiers()
throws GuacamoleException {
return connectionService.getIdentifiersWithin(getCurrentUser(), getIdentifier());
}
@Override
public Set<String> getConnectionGroupIdentifiers()
throws GuacamoleException {
return connectionGroupService.getIdentifiersWithin(getCurrentUser(), getIdentifier());
}
}

View File

@@ -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<String> getConnectionIdentifiers() throws GuacamoleException {
return connectionService.getIdentifiersWithin(getCurrentUser(), null);
}
@Override
public Set<String> 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;
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 {}

View File

@@ -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<ConnectionGroupPermissionSet> 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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 {}

View File

@@ -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<ConnectionPermissionSet> 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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<ObjectPermissionModel> {
/**
* 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<String> selectAccessibleIdentifiers(@Param("user") UserModel user,
@Param("permissions") Collection<ObjectPermission.Type> permissions,
@Param("identifiers") Collection<String> identifiers);
}

View File

@@ -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<ObjectPermission.Type> {
/**
* 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;
}
}

View File

@@ -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<ObjectPermissionSet, ObjectPermission, ObjectPermissionModel> {
@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<ObjectPermission> 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<String> 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<String> 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<ObjectPermission> permissions)
throws GuacamoleException {
// Create permissions only if user has permission to do so
if (canAlterPermissions(user, targetUser, permissions)) {
Collection<ObjectPermissionModel> 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<ObjectPermission> permissions)
throws GuacamoleException {
// Delete permissions only if user has permission to do so
if (canAlterPermissions(user, targetUser, permissions)) {
Collection<ObjectPermissionModel> 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<String> retrieveAccessibleIdentifiers(AuthenticatedUser user,
ModeledUser targetUser, Collection<ObjectPermission.Type> permissions,
Collection<String> 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.");
}
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.glyptodon.guacamole.auth.jdbc.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<ObjectPermission> 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<String> getAccessibleObjects(Collection<ObjectPermission.Type> permissions,
Collection<String> identifiers) throws GuacamoleException {
return getObjectPermissionService().retrieveAccessibleIdentifiers(getCurrentUser(), user, permissions, identifiers);
}
@Override
public void addPermissions(Set<ObjectPermission> permissions)
throws GuacamoleException {
getObjectPermissionService().createPermissions(getCurrentUser(), user, permissions);
}
@Override
public void removePermissions(Set<ObjectPermission> permissions)
throws GuacamoleException {
getObjectPermissionService().deletePermissions(getCurrentUser(), user, permissions);
}
}

View File

@@ -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 <PermissionType>
* The type of permission model object handled by this mapper.
*/
public interface PermissionMapper<PermissionType> {
/**
* 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<PermissionType> 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<PermissionType> 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<PermissionType> permissions);
}

View File

@@ -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 <PermissionType>
* The type of permissions allowed within this model.
*/
public abstract class PermissionModel<PermissionType> {
/**
* 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;
}
}

View File

@@ -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 <PermissionSetType>
* The type of permission sets this service provides access to.
*
* @param <PermissionType>
* The type of permission this service provides access to.
*
* @param <ModelType>
* The underlying model object used to represent PermissionType in the
* database.
*/
public abstract class PermissionService<PermissionSetType extends PermissionSet<PermissionType>,
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<ModelType> 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<PermissionType> getPermissionInstances(Collection<ModelType> models) {
// Create new collection of permissions by manually converting each model
Set<PermissionType> permissions = new HashSet<PermissionType>(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<ModelType> getModelInstances(ModeledUser targetUser,
Collection<PermissionType> permissions) {
// Create new collection of models by manually converting each permission
Collection<ModelType> models = new ArrayList<ModelType>(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<PermissionType> 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<PermissionType> 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<PermissionType> permissions) throws GuacamoleException;
}

View File

@@ -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<SystemPermissionModel> {
/**
* 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);
}

View File

@@ -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<SystemPermission.Type> {
/**
* Creates a new, empty System permission.
*/
public SystemPermissionModel() {
}
}

View File

@@ -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<SystemPermissionSet, SystemPermission, SystemPermissionModel> {
/**
* Mapper for system-level permissions.
*/
@Inject
private SystemPermissionMapper systemPermissionMapper;
/**
* Provider for creating system permission sets.
*/
@Inject
private Provider<SystemPermissionSet> 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<SystemPermission> permissions) throws GuacamoleException {
// Only an admin can create system permissions
if (user.getUser().isAdministrator()) {
Collection<SystemPermissionModel> 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<SystemPermission> permissions) throws GuacamoleException {
// Only an admin can delete system permissions
if (user.getUser().isAdministrator()) {
Collection<SystemPermissionModel> 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.");
}
}

View File

@@ -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<SystemPermission> 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<SystemPermission> permissions)
throws GuacamoleException {
systemPermissionService.createPermissions(getCurrentUser(), user, permissions);
}
@Override
public void removePermissions(Set<SystemPermission> permissions)
throws GuacamoleException {
systemPermissionService.deletePermissions(getCurrentUser(), user, permissions);
}
}

View File

@@ -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 {}

View File

@@ -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<UserPermissionSet> 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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
}
}
}

View File

@@ -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.

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<String, LinkedList<ConnectionRecord>> activeConnections =
new HashMap<String, LinkedList<ConnectionRecord>>();
/**
* 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<ConnectionRecord> connections = activeConnections.get(identifier);
if (connections == null) {
connections = new LinkedList<ConnectionRecord>();
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<ConnectionRecord> 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<ParameterModel> 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<ConnectionRecord> getActiveConnections(Connection connection) {
synchronized (activeConnections) {
String identifier = connection.getIdentifier();
// Get set of active connection records
LinkedList<ConnectionRecord> 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<ConnectionRecord> getActiveConnections(ConnectionGroup connectionGroup) {
// STUB
return Collections.EMPTY_LIST;
}
}

View File

@@ -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;
}
}

View File

@@ -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<ConnectionRecord> 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<ConnectionRecord> getActiveConnections(ConnectionGroup connectionGroup);
}

View File

@@ -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
}
}

View File

@@ -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;

View File

@@ -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;
}
/**

View File

@@ -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<UserModel> 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);
}
}

View File

@@ -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
@@ -52,45 +53,65 @@ public class MySQLUserContext implements UserContext {
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<RootConnectionGroup> 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<String, User> getUserDirectory() throws GuacamoleException {
public Directory<User> getUserDirectory() throws GuacamoleException {
return userDirectory;
}
@Override
public Directory<Connection> getConnectionDirectory() throws GuacamoleException {
return connectionDirectory;
}
@Override
public Directory<ConnectionGroup> 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;
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.glyptodon.guacamole.auth.jdbc.user;
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.Credentials;
/**
* 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<UserContext> 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;
}
}

View File

@@ -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<User> {
/**
* 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<User> getAll(Collection<String> identifiers) throws GuacamoleException {
Collection<ModeledUser> objects = userService.retrieveObjects(getCurrentUser(), identifiers);
return Collections.<User>unmodifiableCollection(objects);
}
@Override
@Transactional
public Set<String> 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);
}
}

View File

@@ -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<UserModel> {
/**
* 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);
}

View File

@@ -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;
}
}

View File

@@ -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<ModeledUser, User, UserModel> {
/**
* Mapper for accessing users.
*/
@Inject
private UserMapper userMapper;
/**
* Mapper for manipulating user permissions.
*/
@Inject
private UserPermissionMapper userPermissionMapper;
/**
* Provider for creating users.
*/
@Inject
private Provider<ModeledUser> userProvider;
@Override
protected DirectoryObjectMapper<UserModel> 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<UserModel> 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;
}
}

View File

@@ -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;

View File

@@ -0,0 +1,2 @@
target/
*~

View File

@@ -0,0 +1,78 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.glyptodon.guacamole</groupId>
<artifactId>guacamole-auth-jdbc-mysql</artifactId>
<packaging>jar</packaging>
<name>guacamole-auth-jdbc-mysql</name>
<url>http://guac-dev.org/</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.glyptodon.guacamole</groupId>
<artifactId>guacamole-auth-jdbc</artifactId>
<version>0.9.5</version>
<relativePath>../../</relativePath>
</parent>
<build>
<plugins>
<!-- Written for 1.6 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<!-- Assembly plugin - for easy distribution -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
<executions>
<execution>
<id>jar-with-dependencies</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<finalName>extension/${project.artifactId}-${project.version}</finalName>
<appendAssemblyId>false</appendAssemblyId>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Guacamole Extension API -->
<dependency>
<groupId>org.glyptodon.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
<scope>provided</scope>
</dependency>
<!-- Guacamole JDBC Authentication -->
<dependency>
<groupId>org.glyptodon.guacamole</groupId>
<artifactId>guacamole-auth-jdbc-base</artifactId>
<version>0.9.5</version>
</dependency>
</dependencies>
</project>

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<!--
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.
-->
<mapper namespace="org.glyptodon.guacamole.auth.jdbc.connection.ConnectionMapper" >
<!-- Result mapper for connection objects -->
<resultMap id="ConnectionResultMap" type="org.glyptodon.guacamole.auth.jdbc.connection.ConnectionModel" >
<id column="connection_id" property="objectID" jdbcType="INTEGER"/>
<result column="connection_name" property="name" jdbcType="VARCHAR"/>
<result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/>
<result column="protocol" property="protocol" jdbcType="VARCHAR"/>
</resultMap>
<!-- Select all connection identifiers -->
<select id="selectIdentifiers" resultType="string">
SELECT connection_id
FROM guacamole_connection
</select>
<!-- Select identifiers of all readable connections -->
<select id="selectReadableIdentifiers" resultType="string">
SELECT connection_id
FROM guacamole_connection_permission
WHERE
user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = 'READ'
</select>
<!-- Select all connection identifiers within a particular connection group -->
<select id="selectIdentifiersWithin" resultType="string">
SELECT connection_id
FROM guacamole_connection
WHERE
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=VARCHAR}</if>
<if test="parentIdentifier == null">parent_id IS NULL</if>
</select>
<!-- Select identifiers of all readable connections within a particular connection group -->
<select id="selectReadableIdentifiersWithin" resultType="string">
SELECT guacamole_connection.connection_id
FROM guacamole_connection
JOIN guacamole_connection_permission ON guacamole_connection_permission.connection_id = guacamole_connection.connection_id
WHERE
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=VARCHAR}</if>
<if test="parentIdentifier == null">parent_id IS NULL</if>
AND user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = 'READ'
</select>
<!-- Select multiple connections by identifier -->
<select id="select" resultMap="ConnectionResultMap">
SELECT
connection_id,
connection_name,
parent_id,
protocol
FROM guacamole_connection
WHERE connection_id IN
<foreach collection="identifiers" item="identifier"
open="(" separator="," close=")">
#{identifier,jdbcType=VARCHAR}
</foreach>
</select>
<!-- Select multiple connections by identifier only if readable -->
<select id="selectReadable" resultMap="ConnectionResultMap">
SELECT
guacamole_connection.connection_id,
connection_name,
parent_id,
protocol
FROM guacamole_connection
JOIN guacamole_connection_permission ON guacamole_connection_permission.connection_id = guacamole_connection.connection_id
WHERE guacamole_connection.connection_id IN
<foreach collection="identifiers" item="identifier"
open="(" separator="," close=")">
#{identifier,jdbcType=VARCHAR}
</foreach>
AND user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = 'READ'
</select>
<!-- Select single connection by name -->
<select id="selectOneByName" resultMap="ConnectionResultMap">
SELECT
connection_id,
connection_name,
parent_id,
protocol
FROM guacamole_connection
WHERE
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=VARCHAR}</if>
<if test="parentIdentifier == null">parent_id IS NULL</if>
AND connection_name = #{name,jdbcType=VARCHAR}
</select>
<!-- Delete single connection by identifier -->
<delete id="delete">
DELETE FROM guacamole_connection
WHERE connection_id = #{identifier,jdbcType=VARCHAR}
</delete>
<!-- Insert single connection -->
<insert id="insert" useGeneratedKeys="true" keyProperty="object.objectID"
parameterType="org.glyptodon.guacamole.auth.jdbc.connection.ConnectionModel">
INSERT INTO guacamole_connection (
connection_name,
parent_id,
protocol
)
VALUES (
#{object.name,jdbcType=VARCHAR},
#{object.parentIdentifier,jdbcType=VARCHAR},
#{object.protocol,jdbcType=VARCHAR}
)
</insert>
<!-- Update single connection -->
<update id="update" parameterType="org.glyptodon.guacamole.auth.jdbc.connection.ConnectionModel">
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}
</update>
</mapper>

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<!--
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.
-->
<mapper namespace="org.glyptodon.guacamole.auth.jdbc.connection.ConnectionRecordMapper" >
<!-- Result mapper for system permissions -->
<resultMap id="ConnectionRecordResultMap" type="org.glyptodon.guacamole.auth.jdbc.connection.ConnectionRecordModel">
<result column="connection_id" property="connectionIdentifier" jdbcType="INTEGER"/>
<result column="user_id" property="userID" jdbcType="INTEGER"/>
<result column="username" property="username" jdbcType="VARCHAR"/>
<result column="start_date" property="startDate" jdbcType="TIMESTAMP"/>
<result column="end_date" property="endDate" jdbcType="TIMESTAMP"/>
</resultMap>
<!-- Select all connection records from a given connection -->
<select id="select" resultMap="ConnectionRecordResultMap">
SELECT
connection_id,
guacamole_connection_history.user_id,
username,
start_date,
end_date
FROM guacamole_connection_history
JOIN guacamole_user ON guacamole_connection_history.user_id = guacamole_user.user_id
WHERE
connection_id = #{identifier,jdbcType=VARCHAR}
ORDER BY
start_date DESC,
end_date DESC
</select>
<!-- Insert the given connection record -->
<insert id="insert" parameterType="org.glyptodon.guacamole.auth.jdbc.connection.ConnectionRecordModel">
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}
)
</insert>
</mapper>

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<!--
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.
-->
<mapper namespace="org.glyptodon.guacamole.auth.jdbc.connection.ParameterMapper">
<!-- Result mapper for connection parameters -->
<resultMap id="ParameterResultMap" type="org.glyptodon.guacamole.auth.jdbc.connection.ParameterModel">
<result column="connection_id" property="connectionIdentifier" jdbcType="INTEGER"/>
<result column="parameter_name" property="name" jdbcType="VARCHAR"/>
<result column="parameter_value" property="value" jdbcType="VARCHAR"/>
</resultMap>
<!-- Select all parameters of a given connection -->
<select id="select" resultMap="ParameterResultMap">
SELECT
connection_id,
parameter_name,
parameter_value
FROM guacamole_connection_parameter
WHERE
connection_id = #{identifier,jdbcType=VARCHAR}
</select>
<!-- Delete all parameters of a given connection -->
<delete id="delete">
DELETE FROM guacamole_connection_parameter
WHERE connection_id = #{identifier,jdbcType=VARCHAR}
</delete>
<!-- Insert all given parameters -->
<insert id="insert" parameterType="org.glyptodon.guacamole.auth.jdbc.connection.ParameterModel">
INSERT INTO guacamole_connection_parameter (
connection_id,
parameter_name,
parameter_value
)
VALUES
<foreach collection="parameters" item="parameter" separator=",">
(#{parameter.connectionIdentifier,jdbcType=VARCHAR},
#{parameter.name,jdbcType=VARCHAR},
#{parameter.value,jdbcType=VARCHAR})
</foreach>
</insert>
</mapper>

View File

@@ -0,0 +1,159 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<!--
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.
-->
<mapper namespace="org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupMapper" >
<!-- Result mapper for connection objects -->
<resultMap id="ConnectionGroupResultMap" type="org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupModel" >
<id column="connection_group_id" property="objectID" jdbcType="INTEGER"/>
<result column="connection_group_name" property="name" jdbcType="VARCHAR"/>
<result column="parent_id" property="parentIdentifier" jdbcType="INTEGER"/>
<result column="type" property="type" jdbcType="VARCHAR"
javaType="org.glyptodon.guacamole.net.auth.ConnectionGroup$Type"/>
</resultMap>
<!-- Select all connection group identifiers -->
<select id="selectIdentifiers" resultType="string">
SELECT connection_group_id
FROM guacamole_connection_group
</select>
<!-- Select identifiers of all readable connection groups -->
<select id="selectReadableIdentifiers" resultType="string">
SELECT connection_group_id
FROM guacamole_connection_group_permission
WHERE
user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = 'READ'
</select>
<!-- Select all connection identifiers within a particular connection group -->
<select id="selectIdentifiersWithin" resultType="string">
SELECT connection_group_id
FROM guacamole_connection_group
WHERE
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=VARCHAR}</if>
<if test="parentIdentifier == null">parent_id IS NULL</if>
</select>
<!-- Select identifiers of all readable connection groups within a particular connection group -->
<select id="selectReadableIdentifiersWithin" resultType="string">
SELECT guacamole_connection_group.connection_group_id
FROM guacamole_connection_group
JOIN guacamole_connection_group_permission ON guacamole_connection_group_permission.connection_group_id = guacamole_connection_group.connection_group_id
WHERE
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=VARCHAR}</if>
<if test="parentIdentifier == null">parent_id IS NULL</if>
AND user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = 'READ'
</select>
<!-- Select multiple connection groups by identifier -->
<select id="select" resultMap="ConnectionGroupResultMap">
SELECT
connection_group_id,
connection_group_name,
parent_id,
type
FROM guacamole_connection_group
WHERE connection_group_id IN
<foreach collection="identifiers" item="identifier"
open="(" separator="," close=")">
#{identifier,jdbcType=VARCHAR}
</foreach>
</select>
<!-- Select multiple connection groups by identifier only if readable -->
<select id="selectReadable" resultMap="ConnectionGroupResultMap">
SELECT
guacamole_connection_group.connection_group_id,
connection_group_name,
parent_id,
type
FROM guacamole_connection_group
JOIN guacamole_connection_group_permission ON guacamole_connection_group_permission.connection_group_id = guacamole_connection_group.connection_group_id
WHERE guacamole_connection_group.connection_group_id IN
<foreach collection="identifiers" item="identifier"
open="(" separator="," close=")">
#{identifier,jdbcType=VARCHAR}
</foreach>
AND user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = 'READ'
</select>
<!-- Select single connection group by name -->
<select id="selectOneByName" resultMap="ConnectionGroupResultMap">
SELECT
connection_group_id,
connection_group_name,
parent_id,
type
FROM guacamole_connection_group
WHERE
<if test="parentIdentifier != null">parent_id = #{parentIdentifier,jdbcType=VARCHAR}</if>
<if test="parentIdentifier == null">parent_id IS NULL</if>
AND connection_group_name = #{name,jdbcType=VARCHAR}
</select>
<!-- Delete single connection group by identifier -->
<delete id="delete">
DELETE FROM guacamole_connection_group
WHERE connection_group_id = #{identifier,jdbcType=VARCHAR}
</delete>
<!-- Insert single connection -->
<insert id="insert" useGeneratedKeys="true" keyProperty="object.objectID"
parameterType="org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupModel">
INSERT INTO guacamole_connection_group (
connection_group_name,
parent_id,
type
)
VALUES (
#{object.name,jdbcType=VARCHAR},
#{object.parentIdentifier,jdbcType=VARCHAR},
#{object.type,jdbcType=VARCHAR}
)
</insert>
<!-- Update single connection group -->
<update id="update" parameterType="org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupModel">
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}
</update>
</mapper>

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<!--
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.
-->
<mapper namespace="org.glyptodon.guacamole.auth.jdbc.permission.ConnectionGroupPermissionMapper" >
<!-- Result mapper for connection permissions -->
<resultMap id="ConnectionGroupPermissionResultMap" type="org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionModel">
<result column="user_id" property="userID" jdbcType="INTEGER"/>
<result column="username" property="username" jdbcType="VARCHAR"/>
<result column="permission" property="type" jdbcType="VARCHAR"
javaType="org.glyptodon.guacamole.net.auth.permission.ObjectPermission$Type"/>
<result column="connection_group_id" property="objectIdentifier" jdbcType="INTEGER"/>
</resultMap>
<!-- Select all permissions for a given user -->
<select id="select" resultMap="ConnectionGroupPermissionResultMap">
SELECT
guacamole_connection_group_permission.user_id,
username,
permission,
connection_group_id
FROM guacamole_connection_group_permission
JOIN guacamole_user ON guacamole_connection_group_permission.user_id = guacamole_user.user_id
WHERE guacamole_connection_group_permission.user_id = #{user.objectID,jdbcType=INTEGER}
</select>
<!-- Select the single permission matching the given criteria -->
<select id="selectOne" resultMap="ConnectionGroupPermissionResultMap">
SELECT
guacamole_connection_group_permission.user_id,
username,
permission,
connection_group_id
FROM guacamole_connection_group_permission
JOIN guacamole_user ON guacamole_connection_group_permission.user_id = guacamole_user.user_id
WHERE
guacamole_connection_group_permission.user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = #{type,jdbcType=VARCHAR}
AND connection_group_id = #{identifier,jdbcType=VARCHAR}
</select>
<!-- Select identifiers accessible by the given user for the given permissions -->
<select id="selectAccessibleIdentifiers" resultType="string">
SELECT DISTINCT connection_group_id
FROM guacamole_connection_group_permission
WHERE
user_id = #{user.objectID,jdbcType=INTEGER}
AND connection_group_id IN
<foreach collection="identifiers" item="identifier"
open="(" separator="," close=")">
#{identifier,jdbcType=VARCHAR}
</foreach>
AND permission IN
<foreach collection="permissions" item="permission"
open="(" separator="," close=")">
#{permission,jdbcType=VARCHAR}
</foreach>
</select>
<!-- Delete all given permissions -->
<delete id="delete" parameterType="org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionModel">
DELETE FROM guacamole_connection_group_permission
WHERE (user_id, permission, connection_group_id) IN
<foreach collection="permissions" item="permission"
open="(" separator="," close=")">
(#{permission.userID,jdbcType=INTEGER},
#{permission.type,jdbcType=VARCHAR},
#{permission.objectIdentifier,jdbcType=VARCHAR})
</foreach>
</delete>
<!-- Insert all given permissions -->
<insert id="insert" parameterType="org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionModel">
INSERT IGNORE INTO guacamole_connection_group_permission (
user_id,
permission,
connection_group_id
)
VALUES
<foreach collection="permissions" item="permission" separator=",">
(#{permission.userID,jdbcType=INTEGER},
#{permission.type,jdbcType=VARCHAR},
#{permission.objectIdentifier,jdbcType=VARCHAR})
</foreach>
</insert>
</mapper>

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<!--
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.
-->
<mapper namespace="org.glyptodon.guacamole.auth.jdbc.permission.ConnectionPermissionMapper" >
<!-- Result mapper for connection permissions -->
<resultMap id="ConnectionPermissionResultMap" type="org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionModel">
<result column="user_id" property="userID" jdbcType="INTEGER"/>
<result column="username" property="username" jdbcType="VARCHAR"/>
<result column="permission" property="type" jdbcType="VARCHAR"
javaType="org.glyptodon.guacamole.net.auth.permission.ObjectPermission$Type"/>
<result column="connection_id" property="objectIdentifier" jdbcType="INTEGER"/>
</resultMap>
<!-- Select all permissions for a given user -->
<select id="select" resultMap="ConnectionPermissionResultMap">
SELECT
guacamole_connection_permission.user_id,
username,
permission,
connection_id
FROM guacamole_connection_permission
JOIN guacamole_user ON guacamole_connection_permission.user_id = guacamole_user.user_id
WHERE guacamole_connection_permission.user_id = #{user.objectID,jdbcType=INTEGER}
</select>
<!-- Select the single permission matching the given criteria -->
<select id="selectOne" resultMap="ConnectionPermissionResultMap">
SELECT
guacamole_connection_permission.user_id,
username,
permission,
connection_id
FROM guacamole_connection_permission
JOIN guacamole_user ON guacamole_connection_permission.user_id = guacamole_user.user_id
WHERE
guacamole_connection_permission.user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = #{type,jdbcType=VARCHAR}
AND connection_id = #{identifier,jdbcType=VARCHAR}
</select>
<!-- Select identifiers accessible by the given user for the given permissions -->
<select id="selectAccessibleIdentifiers" resultType="string">
SELECT DISTINCT connection_id
FROM guacamole_connection_permission
WHERE
user_id = #{user.objectID,jdbcType=INTEGER}
AND connection_id IN
<foreach collection="identifiers" item="identifier"
open="(" separator="," close=")">
#{identifier,jdbcType=VARCHAR}
</foreach>
AND permission IN
<foreach collection="permissions" item="permission"
open="(" separator="," close=")">
#{permission,jdbcType=VARCHAR}
</foreach>
</select>
<!-- Delete all given permissions -->
<delete id="delete" parameterType="org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionModel">
DELETE FROM guacamole_connection_permission
WHERE (user_id, permission, connection_id) IN
<foreach collection="permissions" item="permission"
open="(" separator="," close=")">
(#{permission.userID,jdbcType=INTEGER},
#{permission.type,jdbcType=VARCHAR},
#{permission.objectIdentifier,jdbcType=VARCHAR})
</foreach>
</delete>
<!-- Insert all given permissions -->
<insert id="insert" parameterType="org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionModel">
INSERT IGNORE INTO guacamole_connection_permission (
user_id,
permission,
connection_id
)
VALUES
<foreach collection="permissions" item="permission" separator=",">
(#{permission.userID,jdbcType=INTEGER},
#{permission.type,jdbcType=VARCHAR},
#{permission.objectIdentifier,jdbcType=VARCHAR})
</foreach>
</insert>
</mapper>

View File

@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<!--
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.
-->
<mapper namespace="org.glyptodon.guacamole.auth.jdbc.permission.SystemPermissionMapper" >
<!-- Result mapper for system permissions -->
<resultMap id="SystemPermissionResultMap" type="org.glyptodon.guacamole.auth.jdbc.permission.SystemPermissionModel">
<result column="user_id" property="userID" jdbcType="INTEGER"/>
<result column="username" property="username" jdbcType="VARCHAR"/>
<result column="permission" property="type" jdbcType="VARCHAR"
javaType="org.glyptodon.guacamole.net.auth.permission.SystemPermission$Type"/>
</resultMap>
<!-- Select all permissions for a given user -->
<select id="select" resultMap="SystemPermissionResultMap">
SELECT
guacamole_system_permission.user_id,
username,
permission
FROM guacamole_system_permission
JOIN guacamole_user ON guacamole_system_permission.user_id = guacamole_user.user_id
WHERE guacamole_system_permission.user_id = #{user.objectID,jdbcType=INTEGER}
</select>
<!-- Select the single permission matching the given criteria -->
<select id="selectOne" resultMap="SystemPermissionResultMap">
SELECT
guacamole_system_permission.user_id,
username,
permission
FROM guacamole_system_permission
JOIN guacamole_user ON guacamole_system_permission.user_id = guacamole_user.user_id
WHERE
guacamole_system_permission.user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = #{type,jdbcType=VARCHAR}
</select>
<!-- Delete all given permissions -->
<delete id="delete" parameterType="org.glyptodon.guacamole.auth.jdbc.permission.SystemPermissionModel">
DELETE FROM guacamole_system_permission
WHERE (user_id, permission) IN
<foreach collection="permissions" item="permission"
open="(" separator="," close=")">
(#{permission.userID,jdbcType=INTEGER},
#{permission.type,jdbcType=VARCHAR})
</foreach>
</delete>
<!-- Insert all given permissions -->
<insert id="insert" parameterType="org.glyptodon.guacamole.auth.jdbc.permission.SystemPermissionModel">
INSERT IGNORE INTO guacamole_system_permission (
user_id,
permission
)
VALUES
<foreach collection="permissions" item="permission" separator=",">
(#{permission.userID,jdbcType=INTEGER},
#{permission.type,jdbcType=VARCHAR})
</foreach>
</insert>
</mapper>

View File

@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<!--
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.
-->
<mapper namespace="org.glyptodon.guacamole.auth.jdbc.permission.UserPermissionMapper" >
<!-- Result mapper for user permissions -->
<resultMap id="UserPermissionResultMap" type="org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionModel">
<result column="user_id" property="userID" jdbcType="INTEGER"/>
<result column="username" property="username" jdbcType="VARCHAR"/>
<result column="permission" property="type" jdbcType="VARCHAR"
javaType="org.glyptodon.guacamole.net.auth.permission.ObjectPermission$Type"/>
<result column="affected_username" property="objectIdentifier" jdbcType="INTEGER"/>
</resultMap>
<!-- Select all permissions for a given user -->
<select id="select" resultMap="UserPermissionResultMap">
SELECT
guacamole_user_permission.user_id,
guacamole_user.username,
permission,
affected.username AS affected_username
FROM guacamole_user_permission
JOIN guacamole_user ON guacamole_user_permission.user_id = guacamole_user.user_id
JOIN guacamole_user affected ON guacamole_user_permission.affected_user_id = affected.user_id
WHERE guacamole_user_permission.user_id = #{user.objectID,jdbcType=INTEGER}
</select>
<!-- Select the single permission matching the given criteria -->
<select id="selectOne" resultMap="UserPermissionResultMap">
SELECT
guacamole_user_permission.user_id,
guacamole_user.username,
permission,
affected.username AS affected_username
FROM guacamole_user_permission
JOIN guacamole_user ON guacamole_user_permission.user_id = guacamole_user.user_id
JOIN guacamole_user affected ON guacamole_user_permission.affected_user_id = affected.user_id
WHERE
guacamole_user_permission.user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = #{type,jdbcType=VARCHAR}
AND affected.username = #{identifier,jdbcType=VARCHAR}
</select>
<!-- Select identifiers accessible by the given user for the given permissions -->
<select id="selectAccessibleIdentifiers" resultType="string">
SELECT DISTINCT username
FROM guacamole_user_permission
JOIN guacamole_user ON guacamole_user_permission.affected_user_id = guacamole_user.user_id
WHERE
guacamole_user_permission.user_id = #{user.objectID,jdbcType=INTEGER}
AND username IN
<foreach collection="identifiers" item="identifier"
open="(" separator="," close=")">
#{identifier,jdbcType=VARCHAR}
</foreach>
AND permission IN
<foreach collection="permissions" item="permission"
open="(" separator="," close=")">
#{permission,jdbcType=VARCHAR}
</foreach>
</select>
<!-- Delete all given permissions -->
<delete id="delete" parameterType="org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionModel">
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
<foreach collection="permissions" item="permission"
open="(" separator="," close=")">
(#{permission.userID,jdbcType=INTEGER},
#{permission.type,jdbcType=VARCHAR},
#{permission.objectIdentifier,jdbcType=VARCHAR})
</foreach>
</delete>
<!-- Insert all given permissions -->
<insert id="insert" parameterType="org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionModel">
INSERT IGNORE INTO guacamole_user_permission (
user_id,
permission,
affected_user_id
)
SELECT permissions.user_id, permissions.permission, guacamole_user.user_id FROM
<foreach collection="permissions" item="permission"
open="(" separator="UNION ALL" close=")">
SELECT #{permission.userID,jdbcType=INTEGER} AS user_id,
#{permission.type,jdbcType=VARCHAR} AS permission,
#{permission.objectIdentifier,jdbcType=VARCHAR} AS username
</foreach>
AS permissions
JOIN guacamole_user ON guacamole_user.username = permissions.username;
</insert>
</mapper>

View File

@@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<!--
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.
-->
<mapper namespace="org.glyptodon.guacamole.auth.jdbc.user.UserMapper" >
<!-- Result mapper for user objects -->
<resultMap id="UserResultMap" type="org.glyptodon.guacamole.auth.jdbc.user.UserModel" >
<id column="user_id" property="objectID" jdbcType="INTEGER"/>
<result column="username" property="identifier" jdbcType="VARCHAR"/>
<result column="password_hash" property="passwordHash" jdbcType="BINARY"/>
<result column="password_salt" property="passwordSalt" jdbcType="BINARY"/>
</resultMap>
<!-- Select all usernames -->
<select id="selectIdentifiers" resultType="string">
SELECT username
FROM guacamole_user
</select>
<!-- Select usernames of all readable users -->
<select id="selectReadableIdentifiers" resultType="string">
SELECT username
FROM guacamole_user
JOIN guacamole_user_permission ON affected_user_id = guacamole_user.user_id
WHERE
guacamole_user_permission.user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = 'read'
</select>
<!-- Select multiple users by username -->
<select id="select" resultMap="UserResultMap">
SELECT
user_id,
username,
password_hash,
password_salt
FROM guacamole_user
WHERE username IN
<foreach collection="identifiers" item="identifier"
open="(" separator="," close=")">
#{identifier,jdbcType=VARCHAR}
</foreach>
</select>
<!-- Select multiple users by username only if readable -->
<select id="selectReadable" resultMap="UserResultMap">
SELECT
guacamole_user.user_id,
username,
password_hash,
password_salt
FROM guacamole_user
JOIN guacamole_user_permission ON affected_user_id = guacamole_user.user_id
WHERE username IN
<foreach collection="identifiers" item="identifier"
open="(" separator="," close=")">
#{identifier,jdbcType=VARCHAR}
</foreach>
AND guacamole_user_permission.user_id = #{user.objectID,jdbcType=INTEGER}
AND permission = 'read'
</select>
<!-- Select single user by credentials -->
<select id="selectOneByCredentials" resultMap="UserResultMap">
SELECT
user_id,
username,
password_hash,
password_salt
FROM guacamole_user
WHERE
username = #{username,jdbcType=VARCHAR}
AND password_hash = UNHEX(SHA2(CONCAT(#{password,jdbcType=VARCHAR}, HEX(password_salt)), 256))
</select>
<!-- Select single user by username -->
<select id="selectOne" resultMap="UserResultMap">
SELECT
user_id,
username,
password_hash,
password_salt
FROM guacamole_user
WHERE
username = #{identifier,jdbcType=VARCHAR}
</select>
<!-- Delete single user by username -->
<delete id="delete">
DELETE FROM guacamole_user
WHERE username = #{identifier,jdbcType=VARCHAR}
</delete>
<!-- Insert single user -->
<insert id="insert" useGeneratedKeys="true" keyProperty="object.objectID"
parameterType="org.glyptodon.guacamole.auth.jdbc.user.UserModel">
INSERT INTO guacamole_user (
username,
password_hash,
password_salt
)
VALUES (
#{object.identifier,jdbcType=VARCHAR},
#{object.passwordHash,jdbcType=BINARY},
#{object.passwordSalt,jdbcType=BINARY}
)
</insert>
<!-- Update single user -->
<update id="update" parameterType="org.glyptodon.guacamole.auth.jdbc.user.UserModel">
UPDATE guacamole_user
SET password_hash = #{object.passwordHash,jdbcType=BINARY},
password_salt = #{object.passwordSalt,jdbcType=BINARY}
WHERE user_id = #{object.objectID,jdbcType=VARCHAR}
</update>
</mapper>

View File

@@ -0,0 +1,71 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.glyptodon.guacamole</groupId>
<artifactId>guacamole-auth-jdbc</artifactId>
<packaging>pom</packaging>
<version>0.9.5</version>
<name>guacamole-auth-jdbc</name>
<url>http://guac-dev.org/</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<modules>
<!-- Base JDBC classes -->
<module>modules/guacamole-auth-jdbc-base</module>
<!-- MySQL authentication -->
<module>modules/guacamole-auth-jdbc-mysql</module>
</modules>
<build>
<plugins>
<!-- Assembly plugin - for easy distribution -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
<inherited>false</inherited>
<executions>
<execution>
<id>make-dist-archive</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<finalName>${project.artifactId}-${project.version}</finalName>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>src/main/assembly/dist.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<!-- Guacamole Extension API -->
<dependency>
<groupId>org.glyptodon.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
<version>0.9.5</version>
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -0,0 +1,32 @@
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>dist</id>
<baseDirectory>${project.artifactId}-${project.version}</baseDirectory>
<!-- Output tar.gz -->
<formats>
<format>tar.gz</format>
</formats>
<!-- Include all implementations -->
<fileSets>
<!-- MySQL implementation -->
<fileSet>
<outputDirectory>/mysql/schema</outputDirectory>
<directory>modules/guacamole-auth-jdbc-mysql/schema</directory>
</fileSet>
<fileSet>
<directory>modules/guacamole-auth-jdbc-mysql/target/extension</directory>
<outputDirectory>/mysql</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
</fileSets>
</assembly>

View File

@@ -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:
<settings>
<profiles>
...profiles...
</profiles>
</settings>
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:
<profile>
<id>guacamole-mybatis</id>
<properties>
<guacamole.database.catalog>DATABASE</guacamole.database.catalog>
<guacamole.database.user>USERNAME</guacamole.database.user>
<guacamole.database.password>PASSWORD</guacamole.database.password>
</properties>
</profile>
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:
<activeProfiles>
<activeProfile>guacamole-mybatis</activeProfile>
</activeProfiles>
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/

View File

@@ -1,21 +0,0 @@
<settings>
<!-- Profile defining the properties required for a MyBatis build -->
<profiles>
<profile>
<id>guacamole-mybatis</id>
<properties>
<guacamole.database.catalog>SCHEMA</guacamole.database.catalog>
<guacamole.database.schema>DATABASE</guacamole.database.schema>
<guacamole.database.user>USER</guacamole.database.user>
<guacamole.database.password>PASS</guacamole.database.password>
</properties>
</profile>
</profiles>
<!-- Activate by default -->
<activeProfiles>
<activeProfile>guacamole-mybatis</activeProfile>
</activeProfiles>
</settings>

View File

@@ -1,125 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.glyptodon.guacamole</groupId>
<artifactId>guacamole-auth-mysql</artifactId>
<packaging>jar</packaging>
<version>0.9.5</version>
<name>guacamole-auth-mysql</name>
<url>http://guac-dev.org/</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<!-- Written for 1.6 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<!-- Assembly plugin - for easy distribution -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
<configuration>
<finalName>${project.artifactId}-${project.version}</finalName>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>src/main/assembly/dist.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-dist-archive</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- MyBatis Generator plugin -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<executions>
<execution>
<id>Generate MyBatis Artifacts</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<!-- MySQL Connector -->
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.23</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Guacamole Java API -->
<dependency>
<groupId>org.glyptodon.guacamole</groupId>
<artifactId>guacamole-common</artifactId>
<version>0.9.4</version>
</dependency>
<!-- Guacamole Extension API -->
<dependency>
<groupId>org.glyptodon.guacamole</groupId>
<artifactId>guacamole-ext</artifactId>
<version>0.9.5</version>
</dependency>
<!-- SLF4J - logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.1.1</version>
</dependency>
<!-- MyBatis Guice -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-guice</artifactId>
<version>3.2</version>
</dependency>
<!-- Google Collections -->
<dependency>
<groupId>com.google.collections</groupId>
<artifactId>google-collections</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,54 +0,0 @@
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>dist</id>
<baseDirectory>${project.artifactId}-${project.version}</baseDirectory>
<!-- Output tar.gz -->
<formats>
<format>tar.gz</format>
</formats>
<!-- Include docs and schema -->
<fileSets>
<!-- Include docs -->
<fileSet>
<outputDirectory>/</outputDirectory>
<directory>doc</directory>
</fileSet>
<!-- Include schema -->
<fileSet>
<outputDirectory>/schema</outputDirectory>
<directory>schema</directory>
</fileSet>
</fileSets>
<!-- Include self and all dependencies except guacamole-common
and guacamole-ext -->
<dependencySets>
<dependencySet>
<outputDirectory>/lib</outputDirectory>
<scope>runtime</scope>
<unpack>false</unpack>
<useProjectArtifact>true</useProjectArtifact>
<useTransitiveFiltering>true</useTransitiveFiltering>
<excludes>
<!-- Do not include guacamole-common -->
<exclude>org.glyptodon.guacamole:guacamole-common</exclude>
<!-- Do not include guacamole-ext -->
<exclude>org.glyptodon.guacamole:guacamole-ext</exclude>
</excludes>
</dependencySet>
</dependencySets>
</assembly>

View File

@@ -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<Integer, Connection> activeConnectionMap =
new HashMap<Integer, Connection>();
/**
* Map of all the connection group users to the count of current usages.
*/
private Map<ConnectionUser, Integer> activeConnectionGroupUserMap =
new HashMap<ConnectionUser, Integer>();
/**
* Map of all the connection users to the count of current usages.
*/
private Map<ConnectionUser, Integer> activeConnectionUserMap =
new HashMap<ConnectionUser, Integer>();
/**
* 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<Integer> 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);
}
}

View File

@@ -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<String, Connection>{
/**
* 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<String> 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<String, Connection> 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);
}
}

View File

@@ -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<String, ConnectionGroup>{
/**
* 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<String> 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<String, ConnectionGroup> 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);
}
}

View File

@@ -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;
}
}

Some files were not shown because too many files have changed in this diff Show More