GUAC-1101: Implement connection groups.

This commit is contained in:
Michael Jumper
2015-02-27 14:00:45 -08:00
parent 111581e5cb
commit ac14cf0ff3
12 changed files with 915 additions and 16 deletions

View File

@@ -0,0 +1,104 @@
/*
* 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.Collections;
import java.util.Set;
import net.sourceforge.guacamole.net.auth.mysql.service.ConnectionGroupService;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.ConnectionGroup;
import org.glyptodon.guacamole.net.auth.Directory;
import org.mybatis.guice.transactional.Transactional;
/**
* A MySQL based implementation of the ConnectionGroup Directory.
*
* @author James Muehlner
* @author Michael Jumper
*/
public class ConnectionGroupDirectory implements Directory<ConnectionGroup> {
/**
* The user this connection group directory belongs to. Access is based on
* his/her permission settings.
*/
private AuthenticatedUser currentUser;
/**
* Service for managing connection group objects.
*/
@Inject
private ConnectionGroupService connectionGroupService;
/**
* Set the user for this directory.
*
* @param currentUser
* The user whose permissions define the visibility of connection
* groups in this directory.
*/
public void init(AuthenticatedUser currentUser) {
this.currentUser = currentUser;
}
@Override
public ConnectionGroup get(String identifier) throws GuacamoleException {
return connectionGroupService.retrieveObject(currentUser, identifier);
}
@Override
@Transactional
public Collection<ConnectionGroup> getAll(Collection<String> identifiers) throws GuacamoleException {
Collection<MySQLConnectionGroup> objects = connectionGroupService.retrieveObjects(currentUser, identifiers);
return Collections.<ConnectionGroup>unmodifiableCollection(objects);
}
@Override
@Transactional
public Set<String> getIdentifiers() throws GuacamoleException {
return connectionGroupService.getIdentifiers(currentUser);
}
@Override
@Transactional
public void add(ConnectionGroup object) throws GuacamoleException {
connectionGroupService.createObject(currentUser, object);
}
@Override
@Transactional
public void update(ConnectionGroup object) throws GuacamoleException {
MySQLConnectionGroup connectionGroup = (MySQLConnectionGroup) object;
connectionGroupService.updateObject(currentUser, connectionGroup);
}
@Override
@Transactional
public void remove(String identifier) throws GuacamoleException {
connectionGroupService.deleteObject(currentUser, identifier);
}
}

View File

@@ -29,6 +29,7 @@ import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.name.Names;
import java.util.Properties;
import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionGroupMapper;
import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionMapper;
import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionRecordMapper;
import net.sourceforge.guacamole.net.auth.mysql.dao.ParameterMapper;
@@ -39,6 +40,7 @@ import org.glyptodon.guacamole.net.auth.Credentials;
import org.glyptodon.guacamole.net.auth.UserContext;
import net.sourceforge.guacamole.net.auth.mysql.dao.UserMapper;
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.GuacamoleSocketService;
import net.sourceforge.guacamole.net.auth.mysql.service.PasswordEncryptionService;
@@ -145,6 +147,7 @@ public class MySQLAuthenticationProvider implements AuthenticationProvider {
// Add MyBatis mappers
addMapperClass(ConnectionMapper.class);
addMapperClass(ConnectionGroupMapper.class);
addMapperClass(ConnectionRecordMapper.class);
addMapperClass(ParameterMapper.class);
addMapperClass(SystemPermissionMapper.class);
@@ -153,7 +156,9 @@ public class MySQLAuthenticationProvider implements AuthenticationProvider {
// Bind core implementations of guacamole-ext classes
bind(Environment.class).toInstance(environment);
bind(ConnectionDirectory.class);
bind(ConnectionGroupDirectory.class);
bind(MySQLConnection.class);
bind(MySQLConnectionGroup.class);
bind(MySQLGuacamoleConfiguration.class);
bind(MySQLUser.class);
bind(MySQLUserContext.class);
@@ -163,6 +168,7 @@ public class MySQLAuthenticationProvider implements AuthenticationProvider {
// Bind services
bind(ConnectionService.class);
bind(ConnectionGroupService.class);
bind(PasswordEncryptionService.class).to(SHA256PasswordEncryptionService.class);
bind(SaltService.class).to(SecureRandomSaltService.class);
bind(SystemPermissionService.class);

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 net.sourceforge.guacamole.net.auth.mysql;
import com.google.inject.Inject;
import java.util.Set;
import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionGroupModel;
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.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;
/**
* A MySQL based implementation of the ConnectionGroup object.
*
* @author James Muehlner
*/
public class MySQLConnectionGroup 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 MySQLConnection.
*/
public MySQLConnectionGroup() {
}
@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 MySQLRootConnectionGroup.IDENTIFIER;
return parentIdentifier;
}
@Override
public void setParentIdentifier(String parentIdentifier) {
// Translate root identifier back into null
if (parentIdentifier != null
&& parentIdentifier.equals(MySQLRootConnectionGroup.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

@@ -23,8 +23,8 @@
package net.sourceforge.guacamole.net.auth.mysql;
import com.google.inject.Inject;
import java.util.Collections;
import java.util.Set;
import net.sourceforge.guacamole.net.auth.mysql.service.ConnectionGroupService;
import net.sourceforge.guacamole.net.auth.mysql.service.ConnectionService;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.GuacamoleSecurityException;
@@ -66,6 +66,12 @@ public class MySQLRootConnectionGroup implements ConnectionGroup {
@Inject
private ConnectionService connectionService;
/**
* Service for managing connection group objects.
*/
@Inject
private ConnectionGroupService connectionGroupService;
/**
* Creates a new, empty MySQLRootConnectionGroup.
*/
@@ -115,14 +121,13 @@ public class MySQLRootConnectionGroup implements ConnectionGroup {
@Override
public Set<String> getConnectionIdentifiers() throws GuacamoleException {
return connectionService.getRootIdentifiers(currentUser);
return connectionService.getIdentifiersWithin(currentUser, null);
}
@Override
public Set<String> getConnectionGroupIdentifiers()
throws GuacamoleException {
/* STUB */
return Collections.EMPTY_SET;
return connectionGroupService.getIdentifiersWithin(currentUser, null);
}
@Override

View File

@@ -25,15 +25,12 @@ package net.sourceforge.guacamole.net.auth.mysql;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.Collections;
import org.glyptodon.guacamole.GuacamoleException;
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 org.glyptodon.guacamole.net.auth.simple.SimpleConnectionGroup;
import org.glyptodon.guacamole.net.auth.simple.SimpleConnectionGroupDirectory;
/**
* The MySQL representation of a UserContext.
@@ -60,6 +57,13 @@ public class MySQLUserContext implements UserContext {
@Inject
private ConnectionDirectory connectionDirectory;
/**
* Connection group directory restricted by the permissions of the user
* associated with this context.
*/
@Inject
private ConnectionGroupDirectory connectionGroupDirectory;
/**
* Provider for creating the root group.
*/
@@ -79,6 +83,7 @@ public class MySQLUserContext implements UserContext {
// Init directories
userDirectory.init(currentUser);
connectionDirectory.init(currentUser);
connectionGroupDirectory.init(currentUser);
}
@@ -99,8 +104,7 @@ public class MySQLUserContext implements UserContext {
@Override
public Directory<ConnectionGroup> getConnectionGroupDirectory() throws GuacamoleException {
/* STUB */
return new SimpleConnectionGroupDirectory(Collections.singleton(getRootConnectionGroup()));
return connectionGroupDirectory;
}
@Override

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 net.sourceforge.guacamole.net.auth.mysql.dao;
import java.util.Set;
import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionGroupModel;
import net.sourceforge.guacamole.net.auth.mysql.model.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);
}

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 net.sourceforge.guacamole.net.auth.mysql.model;
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

@@ -32,6 +32,7 @@ import java.util.List;
import java.util.Map;
import net.sourceforge.guacamole.net.auth.mysql.AuthenticatedUser;
import net.sourceforge.guacamole.net.auth.mysql.MySQLConnection;
import net.sourceforge.guacamole.net.auth.mysql.MySQLConnectionGroup;
import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionRecordMapper;
import net.sourceforge.guacamole.net.auth.mysql.dao.ParameterMapper;
import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionModel;
@@ -43,6 +44,7 @@ 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;
@@ -269,4 +271,18 @@ public abstract class AbstractGuacamoleSocketService implements GuacamoleSocketS
}
}
@Override
public GuacamoleSocket getGuacamoleSocket(AuthenticatedUser user,
MySQLConnectionGroup 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,215 @@
/*
* Copyright (C) 2013 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package net.sourceforge.guacamole.net.auth.mysql.service;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.Set;
import net.sourceforge.guacamole.net.auth.mysql.AuthenticatedUser;
import net.sourceforge.guacamole.net.auth.mysql.MySQLConnectionGroup;
import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionGroupMapper;
import net.sourceforge.guacamole.net.auth.mysql.dao.DirectoryObjectMapper;
import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionGroupModel;
import org.glyptodon.guacamole.GuacamoleClientException;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.GuacamoleSecurityException;
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<MySQLConnectionGroup,
ConnectionGroup, ConnectionGroupModel> {
/**
* Mapper for accessing connection groups.
*/
@Inject
private ConnectionGroupMapper connectionGroupMapper;
/**
* Provider for creating connection groups.
*/
@Inject
private Provider<MySQLConnectionGroup> connectionGroupProvider;
/**
* Service for creating and tracking sockets.
*/
@Inject
private GuacamoleSocketService socketService;
@Override
protected DirectoryObjectMapper<ConnectionGroupModel> getObjectMapper() {
return connectionGroupMapper;
}
@Override
protected MySQLConnectionGroup getObjectInstance(AuthenticatedUser currentUser,
ConnectionGroupModel model) {
MySQLConnectionGroup connectionGroup = connectionGroupProvider.get();
connectionGroup.init(currentUser, model);
return connectionGroup;
}
@Override
protected ConnectionGroupModel getModelInstance(AuthenticatedUser currentUser,
final ConnectionGroup object) {
// Create new MySQLConnectionGroup backed by blank model
ConnectionGroupModel model = new ConnectionGroupModel();
MySQLConnectionGroup connectionGroup = getObjectInstance(currentUser, model);
// Set model contents through MySQLConnection, 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 validateNewObject(AuthenticatedUser user, ConnectionGroup object)
throws GuacamoleException {
// Name must not be blank
if (object.getName().trim().isEmpty())
throw new GuacamoleClientException("Connection group names must not be blank.");
// FIXME: Do not attempt to create duplicate connection groups
}
@Override
protected void validateExistingObject(AuthenticatedUser user,
MySQLConnectionGroup object) throws GuacamoleException {
// Name must not be blank
if (object.getName().trim().isEmpty())
throw new GuacamoleClientException("Connection group names must not be blank.");
// FIXME: Check whether such a connection group is already present
}
/**
* 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,
MySQLConnectionGroup 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

@@ -239,28 +239,38 @@ public class ConnectionService extends DirectoryObjectService<MySQLConnection, C
}
/**
* Returns the set of all identifiers for all connections within the root
* connection group that the user has read access to.
* 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 root
* connection group that the user has read access to.
* 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> getRootIdentifiers(AuthenticatedUser user) throws GuacamoleException {
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(null);
return connectionMapper.selectIdentifiersWithin(identifier);
// Otherwise only return explicitly readable identifiers
else
return connectionMapper.selectReadableIdentifiersWithin(user.getUser().getModel(), null);
return connectionMapper.selectReadableIdentifiersWithin(user.getUser().getModel(), identifier);
}

View File

@@ -25,9 +25,11 @@ package net.sourceforge.guacamole.net.auth.mysql.service;
import java.util.List;
import net.sourceforge.guacamole.net.auth.mysql.AuthenticatedUser;
import net.sourceforge.guacamole.net.auth.mysql.MySQLConnection;
import net.sourceforge.guacamole.net.auth.mysql.MySQLConnectionGroup;
import 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;
@@ -83,4 +85,48 @@ public interface GuacamoleSocketService {
*/
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,
MySQLConnectionGroup 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,143 @@
<?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="net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionGroupMapper" >
<!-- Result mapper for connection objects -->
<resultMap id="ConnectionGroupResultMap" type="net.sourceforge.guacamole.net.auth.mysql.model.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,
protocol
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>
<!-- 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="net.sourceforge.guacamole.net.auth.mysql.model.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="net.sourceforge.guacamole.net.auth.mysql.model.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>