diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionDirectory.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionDirectory.java index 11398ba12..d66f72fc8 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionDirectory.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionDirectory.java @@ -66,6 +66,11 @@ public class ConnectionDirectory implements Directory{ */ private int user_id; + /** + * The ID of the parent connection group. + */ + private Integer parentID; + /** * Service for checking permissions. */ @@ -91,21 +96,23 @@ public class ConnectionDirectory implements Directory{ private ConnectionParameterMapper connectionParameterDAO; /** - * Set the user for this directory. + * Set the user and parentID for this directory. * * @param user_id The ID of the user owning this connection directory. + * @param parentID The ID of the parent connection group. */ - public void init(int user_id) { + public void init(int user_id, Integer parentID) { + this.parentID = parentID; this.user_id = user_id; } @Transactional @Override - public Connection get(String identifier) throws GuacamoleException { + public Connection get(String name) throws GuacamoleException { // Get connection MySQLConnection connection = - connectionService.retrieveConnection(identifier, user_id); + connectionService.retrieveConnection(name, parentID, user_id); // Verify access is granted permissionCheckService.verifyConnectionAccess( @@ -121,8 +128,13 @@ public class ConnectionDirectory implements Directory{ @Transactional @Override public Set getIdentifiers() throws GuacamoleException { - return permissionCheckService.retrieveConnectionNames(user_id, - MySQLConstants.CONNECTION_READ); + + // Verify permission to use the connection group for organizational purposes + permissionCheckService.verifyConnectionGroupUsageAccess + (parentID, user_id, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL); + + return permissionCheckService.retrieveConnectionNames(user_id, + parentID, MySQLConstants.CONNECTION_READ); } @Transactional @@ -132,14 +144,22 @@ public class ConnectionDirectory implements Directory{ String identifier = object.getIdentifier().trim(); if(identifier.isEmpty()) throw new GuacamoleClientException("The connection identifier cannot be blank."); - + // Verify permission to create permissionCheckService.verifySystemAccess(this.user_id, MySQLConstants.SYSTEM_CONNECTION_CREATE); + + // Verify permission to edit the connection group + permissionCheckService.verifyConnectionGroupAccess(this.user_id, + this.parentID, MySQLConstants.CONNECTION_GROUP_UPDATE); + + // Verify permission to use the connection group for organizational purposes + permissionCheckService.verifyConnectionGroupUsageAccess + (parentID, user_id, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL); // Verify that no connection already exists with this identifier. MySQLConnection previousConnection = - connectionService.retrieveConnection(identifier, user_id); + connectionService.retrieveConnection(identifier, user_id, parentID); if(previousConnection != null) throw new GuacamoleClientException("That connection identifier is already in use."); @@ -198,7 +218,6 @@ public class ConnectionDirectory implements Directory{ // Insert connection parameter connectionParameterDAO.insert(parameter); - } } @@ -239,7 +258,7 @@ public class ConnectionDirectory implements Directory{ // Get connection MySQLConnection mySQLConnection = - connectionService.retrieveConnection(identifier, user_id); + connectionService.retrieveConnection(identifier, parentID, user_id); // Verify permission to delete permissionCheckService.verifyConnectionAccess(this.user_id, diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionGroupDirectory.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionGroupDirectory.java new file mode 100644 index 000000000..a9cd27a46 --- /dev/null +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionGroupDirectory.java @@ -0,0 +1,146 @@ + +package net.sourceforge.guacamole.net.auth.mysql; + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is guacamole-auth-mysql. + * + * The Initial Developer of the Original Code is + * James Muehlner. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +import com.google.inject.Inject; +import java.util.Set; +import net.sourceforge.guacamole.GuacamoleClientException; +import net.sourceforge.guacamole.GuacamoleException; +import net.sourceforge.guacamole.net.auth.Connection; +import net.sourceforge.guacamole.net.auth.ConnectionGroup; +import net.sourceforge.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.PermissionCheckService; +import net.sourceforge.guacamole.protocol.GuacamoleConfiguration; +import org.mybatis.guice.transactional.Transactional; + +/** + * A MySQL-based implementation of the connection group directory. + * + * @author James Muehlner + */ +public class ConnectionGroupDirectory implements Directory{ + + /** + * The ID of the user who this connection directory belongs to. + * Access is based on his/her permission settings. + */ + private int user_id; + + /** + * The ID of the parent connection for this connection. + */ + private Integer parentID; + + /** + * Service for checking permissions. + */ + @Inject + private PermissionCheckService permissionCheckService; + + /** + * 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; + + @Transactional + @Override + public ConnectionGroup get(String name) throws GuacamoleException { + + // Get connection + MySQLConnectionGroup connectionGroup = + connectionGroupService.retrieveConnectionGroup(name, parentID, user_id); + + // Verify access is granted + permissionCheckService.verifyConnectionGroupAccess( + this.user_id, + connectionGroup.getConnectionGroupID(), + MySQLConstants.CONNECTION_GROUP_READ); + + // Return connection group + return connectionGroup; + + } + + @Transactional + @Override + public Set getIdentifiers() throws GuacamoleException { + + // Verify permission to use the connection group for organizational purposes + permissionCheckService.verifyConnectionGroupUsageAccess + (parentID, user_id, MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL); + + return permissionCheckService.retrieveConnectionGroupNames(user_id, + parentID, MySQLConstants.CONNECTION_GROUP_READ); + } + + @Override + public void add(ConnectionGroup object) throws GuacamoleException { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void update(ConnectionGroup object) throws GuacamoleException { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void remove(String identifier) throws GuacamoleException { + throw new UnsupportedOperationException("Not supported yet."); + } + + +} diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnection.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnection.java index b55e906b1..675d1a767 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnection.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnection.java @@ -61,7 +61,7 @@ public class MySQLConnection extends AbstractConnection { private Integer connectionID; /** - * The ID of the parent connection for this connection group. + * The ID of the parent connection for this connection. */ private Integer parentID; @@ -129,12 +129,13 @@ public class MySQLConnection extends AbstractConnection { * @param history All ConnectionRecords associated with this connection. * @param userID The IID of the user who queried this connection. */ - public void init(Integer connectionID, Integer parentID, String identifier, - GuacamoleConfiguration config, + public void init(Integer connectionID, Integer parentID, String name, + String identifier, GuacamoleConfiguration config, List history, int userID) { this.connectionID = connectionID; this.parentID = parentID; + setName(name); setIdentifier(identifier); setConfiguration(config); this.history.addAll(history); diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnectionGroup.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnectionGroup.java index 42629bb4b..d7952e3cb 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnectionGroup.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnectionGroup.java @@ -38,6 +38,7 @@ package net.sourceforge.guacamole.net.auth.mysql; * ***** END LICENSE BLOCK ***** */ import com.google.inject.Inject; +import com.google.inject.Provider; import net.sourceforge.guacamole.GuacamoleException; import net.sourceforge.guacamole.net.GuacamoleSocket; import net.sourceforge.guacamole.net.auth.AbstractConnectionGroup; @@ -64,6 +65,11 @@ public class MySQLConnectionGroup extends AbstractConnectionGroup { * The ID of the parent connection group for this connection group. */ private Integer parentID; + + /** + * The type of this connection group. + */ + private String type; /** * The ID of the user who queried or created this connection group. @@ -73,12 +79,12 @@ public class MySQLConnectionGroup extends AbstractConnectionGroup { /** * A Directory of connections that have this connection group as a parent. */ - private Directory connectionDirectory = null; + private ConnectionDirectory connectionDirectory = null; /** * A Directory of connection groups that have this connection group as a parent. */ - private Directory connectionGroupDirectory = null; + private ConnectionGroupDirectory connectionGroupDirectory = null; /** * Service managing connections. @@ -97,6 +103,16 @@ public class MySQLConnectionGroup extends AbstractConnectionGroup { */ @Inject private PermissionCheckService permissionCheckService; + + /** + * Service for creating new ConnectionDirectory objects. + */ + @Inject Provider connectionDirectoryProvider; + + /** + * Service for creating new ConnectionGroupDirectory objects. + */ + @Inject Provider connectionGroupDirectoryProvider; /** * Create a default, empty connection group. @@ -140,42 +156,74 @@ public class MySQLConnectionGroup extends AbstractConnectionGroup { * Initialize from explicit values. * * @param connectionGroupID The ID of the associated database record, if any. - * @param parentID The D of the parent connection group for this connection group, if any. + * @param parentID The ID of the parent connection group for this connection group, if any. * @param identifier The unique identifier associated with this connection group. + * @param type The type of this connection group. * @param userID The IID of the user who queried this connection. */ - public void init(Integer connectionGroupID, Integer parentID, String identifier, int userID) { + public void init(Integer connectionGroupID, Integer parentID, String name, + String identifier, String type, int userID) { this.connectionGroupID = connectionGroupID; this.parentID = parentID; + setName(name); setIdentifier(identifier); + this.type = type; this.userID = userID; + + connectionDirectory = connectionDirectoryProvider.get(); + connectionDirectory.init(userID, parentID); + + connectionGroupDirectory = connectionGroupDirectoryProvider.get(); + //connectionGroupDirectory.init(userID, parentID); } @Override public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException { - return connectionGroupService.connect(this, info, userID); - } - - private void loadConnectionDirectory() { + // Verify permission to use the connection group for balancing purposes + permissionCheckService.verifyConnectionGroupUsageAccess + (this.connectionGroupID, this.userID, MySQLConstants.CONNECTION_GROUP_BALANCING); + + return connectionGroupService.connect(this, info, userID); } @Override public Directory getConnectionDirectory() throws GuacamoleException { - if(connectionDirectory == null) - loadConnectionDirectory(); return connectionDirectory; } - - private void loadConnectionGroupDirectory() { - - } @Override public Directory getConnectionGroupDirectory() throws GuacamoleException { - if(connectionGroupDirectory == null) - loadConnectionGroupDirectory(); return connectionGroupDirectory; } + + /** + * Returns the connection group type. + * @return the connection group type. + */ + public String getType() { + return type; + } + + /** + * Sets the connection group type. + * @param type the connection group type. + */ + public void setType(String type) { + this.type = type; + } + + @Override + public void setBalancing(boolean balancing) { + if(balancing) + this.type = MySQLConstants.CONNECTION_GROUP_BALANCING; + else + this.type = MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL; + } + + @Override + public boolean isBalancing() { + return MySQLConstants.CONNECTION_GROUP_BALANCING.equals(this.type); + } } diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConstants.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConstants.java index 9b72c3f57..e08a7a996 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConstants.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConstants.java @@ -96,6 +96,43 @@ public final class MySQLConstants { */ public static final String CONNECTION_ADMINISTER = "ADMINISTER"; + /** + * The string stored in the database to represent READ access to a + * connection. + */ + public static final String CONNECTION_GROUP_READ = "READ"; + + /** + * The string stored in the database to represent UPDATE access to a + * connection group. + */ + public static final String CONNECTION_GROUP_UPDATE = "UPDATE"; + + /** + * The string stored in the database to represent DELETE access to a + * connection group. + */ + public static final String CONNECTION_GROUP_DELETE = "DELETE"; + + /** + * The string stored in the database to represent ADMINISTER access to a + * connection group. + */ + public static final String CONNECTION_GROUP_ADMINISTER = "ADMINISTER"; + + /** + * The string stored in the database to represent a BALANCING + * connection group. + */ + public static final String CONNECTION_GROUP_BALANCING = "BALANCING"; + + /** + * The string stored in the database to represent an ORGANIZATIONAL + * connection group. + */ + public static final String CONNECTION_GROUP_ORGANIZATIONAL = + "ORGANIZATIONAL"; + /** * The string stored in the database to represent permission to create * users. @@ -160,6 +197,28 @@ public final class MySQLConstants { } + /** + * Given the type of a permission affecting a connection group, + * returns the MySQL constant representing that permission type. + * + * @param type The type of permission to look up. + * @return The MySQL constant corresponding to the given permission type. + */ + public static String getConnectionGroupConstant(ObjectPermission.Type type) { + + // Convert permission type to MySQL constant + switch (type) { + case READ: return CONNECTION_GROUP_READ; + case UPDATE: return CONNECTION_GROUP_UPDATE; + case ADMINISTER: return CONNECTION_GROUP_ADMINISTER; + case DELETE: return CONNECTION_GROUP_DELETE; + } + + // If we get here, permission support was not properly implemented + throw new UnsupportedOperationException( + "Unsupported permission type: " + type); + + } /** * Given the type of a permission affecting the system, returns the MySQL diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionGroupService.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionGroupService.java index 5ac9e2ff3..173283692 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionGroupService.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionGroupService.java @@ -49,10 +49,13 @@ import java.util.List; import java.util.Map; import java.util.Set; import net.sourceforge.guacamole.net.GuacamoleSocket; +import net.sourceforge.guacamole.net.auth.mysql.MySQLConnection; import net.sourceforge.guacamole.net.auth.mysql.MySQLConnectionGroup; import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionGroupMapper; +import net.sourceforge.guacamole.net.auth.mysql.model.Connection; import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionGroup; import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionGroupExample; +import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionGroupExample.Criteria; import net.sourceforge.guacamole.protocol.GuacamoleClientInformation; /** @@ -76,32 +79,60 @@ public class ConnectionGroupService { private Provider mysqlConnectionGroupProvider; + /** - * Retrieves all connection groups with a given parent connection group ID. - * - * @param parentID The parent ID of the connection groups. - * @param userID The ID of the user who queried these connection groups. - * @return All connection groups with a given parent connection group ID. + * Retrieves the connection group having the given + * name from the database. + * + * @param name The name of the connection to return. + * @param parentID The ID of the parent connection group. + * @param userID The ID of the user who queried this connection group. + * @return The connection having the given name, or null if no such + * connection group could be found. */ - public Collection getConnectionGroupsByParentConnectionGroup(Integer parentID, int userID) { - - // A null parentID indicates the root-level group + public MySQLConnectionGroup retrieveConnectionGroup(String name, Integer parentID, + int userID) { + + // Create criteria ConnectionGroupExample example = new ConnectionGroupExample(); + Criteria criteria = example.createCriteria().andConnection_group_nameEqualTo(name); if(parentID != null) - example.createCriteria().andParent_group_idEqualTo(parentID); + criteria.andParent_idEqualTo(parentID); else - example.createCriteria().andParent_group_idIsNull(); + criteria.andParent_idIsNull(); - // Get all connections with the given parent ID - List connectionGroups = connectionGroupDAO.selectByExample(example); - - List mySQLConnectionGroups = new ArrayList(); - - for(ConnectionGroup connectionGroup : connectionGroups) { - mySQLConnectionGroups.add(toMySQLConnectionGroup(connectionGroup, userID)); - } - - return mySQLConnectionGroups; + // Query connection group by name and parentID + List connectionGroups = + connectionGroupDAO.selectByExample(example); + + // If no connection group found, return null + if(connectionGroups.isEmpty()) + return null; + + // Otherwise, return found connection + return toMySQLConnectionGroup(connectionGroups.get(0), userID); + + } + + /** + * Retrieves the connection group having the given ID from the database. + * + * @param id The ID of the connection group to retrieve. + * @param userID The ID of the user who queried this connection. + * @return The connection group having the given ID, or null if no such + * connection was found. + */ + public MySQLConnectionGroup retrieveConnectionGroup(int id, int userID) { + + // Query connection by ID + ConnectionGroup connectionGroup = connectionGroupDAO.selectByPrimaryKey(id); + + // If no connection found, return null + if(connectionGroup == null) + return null; + + // Otherwise, return found connection + return toMySQLConnectionGroup(connectionGroup, userID); } public GuacamoleSocket connect(MySQLConnectionGroup group, @@ -159,6 +190,66 @@ public class ConnectionGroupService { } + /** + * Retrieves a translation map of connection group names to their + * corresponding IDs. + * + * @param ids The IDs of the connection groups to retrieve the names of. + * @return A map containing the names of all connection groups and their + * corresponding IDs. + */ + public Map translateNames(List ids) { + + // If no IDs given, just return empty map + if (ids.isEmpty()) + return Collections.EMPTY_MAP; + + // Map of all names onto their corresponding IDs. + Map names = new HashMap(); + + // Get all connections having the given IDs + ConnectionGroupExample example = new ConnectionGroupExample(); + example.createCriteria().andConnection_group_idIn(ids); + List connectionGroups = connectionGroupDAO.selectByExample(example); + + // Produce set of names + for (ConnectionGroup connectionGroup : connectionGroups) + names.put(connectionGroup.getConnection_group_name(), + connectionGroup.getConnection_group_id()); + + return names; + + } + + /** + * Returns a list of the IDs of all connection groups with a given parent ID. + * @param parentID The ID of the parent for all the queried connection groups. + * @return a list of the IDs of all connection groups with a given parent ID. + */ + public List getAllConnectionGroupIDs(Integer parentID) { + + // Create criteria + ConnectionGroupExample example = new ConnectionGroupExample(); + Criteria criteria = example.createCriteria(); + + if(parentID != null) + criteria.andConnection_group_idEqualTo(parentID); + else + criteria.andConnection_group_idIsNull(); + + // Query the connections + List connectionGroups = connectionGroupDAO.selectByExample(example); + + // List of IDs of connections with the given parent + List connectionGroupIDs = new ArrayList(); + + for(ConnectionGroup connectionGroup : connectionGroups) { + connectionGroupIDs.add(connectionGroup.getConnection_group_id()); + } + + return connectionGroupIDs; + } + /** * Convert the given database-retrieved Connection into a MySQLConnection. * The parameters of the given connection will be read and added to the @@ -175,8 +266,10 @@ public class ConnectionGroupService { MySQLConnectionGroup mySQLConnectionGroup = mysqlConnectionGroupProvider.get(); mySQLConnectionGroup.init( connectionGroup.getConnection_group_id(), - connectionGroup.getParent_group_id(), + connectionGroup.getParent_id(), connectionGroup.getConnection_group_name(), + Integer.toString(connectionGroup.getConnection_group_id()), + connectionGroup.getType(), userID ); diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionService.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionService.java index 5af33c28e..70f59feb2 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionService.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionService.java @@ -63,6 +63,7 @@ import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionMapper; import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionParameterMapper; import net.sourceforge.guacamole.net.auth.mysql.model.Connection; import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionExample; +import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionExample.Criteria; import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionHistory; import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionHistoryExample; import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionParameter; @@ -128,15 +129,23 @@ public class ConnectionService { * Retrieves the connection having the given name from the database. * * @param name The name of the connection to return. + * @param parentID The ID of the parent connection group. * @param userID The ID of the user who queried this connection. * @return The connection having the given name, or null if no such * connection could be found. */ - public MySQLConnection retrieveConnection(String name, int userID) { + public MySQLConnection retrieveConnection(String name, Integer parentID, + int userID) { - // Query connection by connection identifier (name) + // Create criteria ConnectionExample example = new ConnectionExample(); - example.createCriteria().andConnection_nameEqualTo(name); + Criteria criteria = example.createCriteria().andConnection_nameEqualTo(name); + if(parentID != null) + criteria.andParent_idEqualTo(parentID); + else + criteria.andParent_idIsNull(); + + // Query connection by name and parentID List connections = connectionDAO.selectByExample(example); @@ -144,9 +153,6 @@ public class ConnectionService { if(connections.isEmpty()) return null; - // Assert only one connection found - assert connections.size() == 1 : "Multiple connections with same name."; - // Otherwise, return found connection return toMySQLConnection(connections.get(0), userID); @@ -172,34 +178,6 @@ public class ConnectionService { // Otherwise, return found connection return toMySQLConnection(connection, userID); } - - /** - * Retrieves all connections with a given parent connection group ID. - * - * @param parentID The parent ID of the connections. - * @param userID The ID of the user who queried these connections. - * @return All connections with a given parent connection group ID. - */ - public Collection getConnectionsByParentConnectionGroup(Integer parentID, int userID) { - - // A null parentID indicates the root-level group - ConnectionExample example = new ConnectionExample(); - if(parentID != null) - example.createCriteria().andConnection_group_idEqualTo(parentID); - else - example.createCriteria().andConnection_group_idIsNull(); - - // Get all connections with the given parent ID - List connections = connectionDAO.selectByExample(example); - - List mySQLConnections = new ArrayList(); - - for(Connection connection : connections) { - mySQLConnections.add(toMySQLConnection(connection, userID)); - } - - return mySQLConnections; - } /** * Retrieves a translation map of connection names to their corresponding @@ -261,6 +239,35 @@ public class ConnectionService { return names; } + + /** + * Returns a list of the IDs of all connections with a given parent ID. + * @param parentID The ID of the parent for all the queried connections. + * @return a list of the IDs of all connections with a given parent ID. + */ + public List getAllConnectionIDs(Integer parentID) { + + // Create criteria + ConnectionExample example = new ConnectionExample(); + Criteria criteria = example.createCriteria(); + + if(parentID != null) + criteria.andConnection_idEqualTo(parentID); + else + criteria.andConnection_idIsNull(); + + // Query the connections + List connections = connectionDAO.selectByExample(example); + + // List of IDs of connections with the given parent + List connectionIDs = new ArrayList(); + + for(Connection connection : connections) { + connectionIDs.add(connection.getConnection_id()); + } + + return connectionIDs; + } /** * Convert the given database-retrieved Connection into a MySQLConnection. @@ -295,8 +302,9 @@ public class ConnectionService { MySQLConnection mySQLConnection = mySQLConnectionProvider.get(); mySQLConnection.init( connection.getConnection_id(), - connection.getConnection_group_id(), + connection.getParent_id(), connection.getConnection_name(), + Integer.toString(connection.getConnection_id()), config, retrieveHistory(connection.getConnection_id()), userID @@ -451,18 +459,28 @@ public class ConnectionService { } /** - * Get the names of all the connections defined in the system. + * Get the names of all the connections defined in the system + * with a certain parentID. * - * @return A Set of names of all the connections defined in the system. + * @return A Set of names of all the connections defined in the system + * with the given parentID. */ - public Set getAllConnectionNames() { + public Set getAllConnectionNames(Integer parentID) { // Set of all present connection names Set names = new HashSet(); + + // Set up Criteria + ConnectionExample example = new ConnectionExample(); + Criteria criteria = example.createCriteria(); + if(parentID != null) + criteria.andParent_idEqualTo(parentID); + else + criteria.andParent_idIsNull(); - // Query all connection names + // Query connection names List connections = - connectionDAO.selectByExample(new ConnectionExample()); + connectionDAO.selectByExample(example); for (Connection connection : connections) names.add(connection.getConnection_name()); @@ -471,7 +489,10 @@ public class ConnectionService { } /** - * Get the connection IDs of all the connections defined in the system. + * Get the connection IDs of all the connections defined in the system + * with a certain parent connection group. + * + * @param parentID The parent connection group ID. * * @return A list of connection IDs of all the connections defined in the system. */ @@ -480,9 +501,17 @@ public class ConnectionService { // Set of all present connection IDs List connectionIDs = new ArrayList(); - // Query all connection IDs + // Create the criteria + ConnectionExample example = new ConnectionExample(); + /*Criteria criteria = example.createCriteria(); + if(parentID != null) + criteria.andParent_idEqualTo(parentID); + else + criteria.andParent_idIsNull();*/ + + // Query the connections List connections = - connectionDAO.selectByExample(new ConnectionExample()); + connectionDAO.selectByExample(example); for (Connection connection : connections) connectionIDs.add(connection.getConnection_id()); diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/PermissionCheckService.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/PermissionCheckService.java index d2a1f023c..16a46f6b5 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/PermissionCheckService.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/PermissionCheckService.java @@ -42,6 +42,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import net.sourceforge.guacamole.GuacamoleSecurityException; +import net.sourceforge.guacamole.net.auth.mysql.MySQLConnectionGroup; import net.sourceforge.guacamole.net.auth.mysql.MySQLConstants; import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionGroupPermissionMapper; import net.sourceforge.guacamole.net.auth.mysql.dao.ConnectionPermissionMapper; @@ -50,6 +51,7 @@ import net.sourceforge.guacamole.net.auth.mysql.dao.UserPermissionMapper; import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionGroupPermissionExample; import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionGroupPermissionKey; import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionPermissionExample; +import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionPermissionExample.Criteria; import net.sourceforge.guacamole.net.auth.mysql.model.ConnectionPermissionKey; import net.sourceforge.guacamole.net.auth.mysql.model.SystemPermissionExample; import net.sourceforge.guacamole.net.auth.mysql.model.SystemPermissionKey; @@ -289,6 +291,69 @@ public class PermissionCheckService { andPermissionEqualTo(MySQLConstants.SYSTEM_ADMINISTER); return systemPermissionDAO.countByExample(example) > 0; } + + /** + * Verifies that the specified group can be used for organization + * by the given user. + * + * @param connectionGroupID The ID of the affected ConnectionGroup. + * @param userID The ID of the user to check. + * @throws GuacamoleSecurityException If the connection group + * cannot be used for organization. + */ + public void verifyConnectionGroupUsageAccess(Integer connectionGroupID, + int userID, String type) throws GuacamoleSecurityException { + + // If permission does not exist, throw exception + if(!checkConnectionGroupUsageAccess(connectionGroupID, userID, type)) + throw new GuacamoleSecurityException("Permission denied."); + + } + + /** + * Check whether a user can use connectionGroup for the given usage. + * @param connectionGroupID the ID of the affected connection group. + * @param userID The ID of the user to check. + * @param usage The desired usage. + * @return true if the user can use the connection group for the given usage. + */ + private boolean checkConnectionGroupUsageAccess( + Integer connectionGroupID, int userID, String usage) { + + // The root level connection group can only be used for organization + if(connectionGroupID == null) + return MySQLConstants.CONNECTION_GROUP_ORGANIZATIONAL.equals(usage); + + // A system administrator has full access to everything. + if(checkSystemAdministratorAccess(userID)) + return true; + + // Query the connection group + MySQLConnectionGroup connectionGroup = connectionGroupService. + retrieveConnectionGroup(connectionGroupID, userID); + + // If the connection group is not found, it cannot be used. + if(connectionGroup == null) + return false; + + // Verify that the desired usage matches the type. + return connectionGroup.getType().equals(usage); + + } + + /** + * + * @param userID + * @throws GuacamoleSecurityException + */ + private void verifySystemAdministratorAccess(int userID) + throws GuacamoleSecurityException { + + // If permission does not exist, throw exception + if(!checkSystemAdministratorAccess(userID)) + throw new GuacamoleSecurityException("Permission denied."); + } + /** * Find the list of the IDs of all users a user has permission to. @@ -332,13 +397,61 @@ public class PermissionCheckService { public List retrieveConnectionIDs(int userID, String permissionType) { + return retrieveConnectionIDs(userID, null, permissionType, false); + + } + + /** + * Find the list of the IDs of all connections a user has permission to. + * The access type is defined by permissionType. + * + * @param userID The ID of the user to check. + * @param parentID the parent connection group. + * @param permissionType The type of permission to check for. + * @return A list of all connection IDs this user has the specified access + * to. + */ + public List retrieveConnectionIDs(int userID, Integer parentID, + String permissionType) { + + return retrieveConnectionIDs(userID, parentID, permissionType, true); + + } + + /** + * Find the list of the IDs of all connections a user has permission to. + * The access type is defined by permissionType. + * + * @param userID The ID of the user to check. + * @param parentID the parent connection group. + * @param permissionType The type of permission to check for. + * @param checkParentID Whether the parentID should be checked or not. + * @return A list of all connection IDs this user has the specified access + * to. + */ + private List retrieveConnectionIDs(int userID, Integer parentID, + String permissionType, boolean checkParentID) { + // A system administrator has access to all connections. - if(checkSystemAdministratorAccess(userID)) - return connectionService.getAllConnectionIDs(); + if(checkSystemAdministratorAccess(userID)) { + if(checkParentID) + return connectionService.getAllConnectionIDs(parentID); + else + return connectionService.getAllConnectionIDs(); + } // Query all connection permissions for the given user and permission type ConnectionPermissionExample example = new ConnectionPermissionExample(); - example.createCriteria().andUser_idEqualTo(userID).andPermissionEqualTo(permissionType); + Criteria criteria = example.createCriteria().andUser_idEqualTo(userID) + .andPermissionEqualTo(permissionType); + + // Ensure that the connections are all under the parent ID, if needed + if(checkParentID) { + // Get the IDs of all connections in the connection group + List allConnectionIDs = connectionService.getAllConnectionIDs(parentID); + criteria.andConnection_idIn(allConnectionIDs); + } + example.setDistinct(true); List connectionPermissions = connectionPermissionDAO.selectByExample(example); @@ -364,13 +477,63 @@ public class PermissionCheckService { public List retrieveConnectionGroupIDs(int userID, String permissionType) { - // A system administrator has access to all connections. - if(checkSystemAdministratorAccess(userID)) - return connectionGroupService.getAllConnectionGroupIDs(); + return retrieveConnectionGroupIDs(userID, null, permissionType, false); + + } + + /** + * Find the list of the IDs of all connection groups a user has permission to. + * The access type is defined by permissionType. + * + * @param userID The ID of the user to check. + * @param parentID the parent connection group. + * @param permissionType The type of permission to check for. + * @return A list of all connection group IDs this user has the specified access + * to. + */ + public List retrieveConnectionGroupIDs(int userID, Integer parentID, + String permissionType) { + + return retrieveConnectionGroupIDs(userID, parentID, permissionType, true); + + } + + /** + * Find the list of the IDs of all connection groups a user has permission to. + * The access type is defined by permissionType. + * + * @param userID The ID of the user to check. + * @param parentID the parent connection group. + * @param permissionType The type of permission to check for. + * @param checkParentID Whether the parentID should be checked or not. + * @return A list of all connection group IDs this user has the specified access + * to. + */ + private List retrieveConnectionGroupIDs(int userID, Integer parentID, + String permissionType, boolean checkParentID) { + + // A system administrator has access to all connectionGroups . + if(checkSystemAdministratorAccess(userID)) { + if(checkParentID) + return connectionGroupService.getAllConnectionGroupIDs(parentID); + else + return connectionGroupService.getAllConnectionGroupIDs(); + } // Query all connection permissions for the given user and permission type ConnectionGroupPermissionExample example = new ConnectionGroupPermissionExample(); - example.createCriteria().andUser_idEqualTo(userID).andPermissionEqualTo(permissionType); + ConnectionGroupPermissionExample.Criteria criteria = + example.createCriteria().andUser_idEqualTo(userID) + .andPermissionEqualTo(permissionType); + + // Ensure that the connection groups are all under the parent ID, if needed + if(checkParentID) { + // Get the IDs of all connection groups in the connection group + List allConnectionGroupIDs = connectionGroupService + .getAllConnectionGroupIDs(parentID); + criteria.andConnection_group_idIn(allConnectionGroupIDs); + } + example.setDistinct(true); List connectionGroupPermissions = connectionGroupPermissionDAO.selectByExample(example); @@ -414,24 +577,52 @@ public class PermissionCheckService { * * @param userID The user whose permissions should be checked. * @param permissionType The permission to check. + * @param parentID The parent connection group. * @return A set of all connection names for which the given user has the * given permission. */ - public Set retrieveConnectionNames(int userID, String permissionType) { + public Set retrieveConnectionNames(int userID, Integer parentID, + String permissionType) { // A system administrator has access to all connections. if(checkSystemAdministratorAccess(userID)) - return connectionService.getAllConnectionNames(); + return connectionService.getAllConnectionNames(parentID); - // List of all connection IDs for which this connection has read access + // List of all connection IDs for which this user has access List connectionIDs = - retrieveConnectionIDs(userID, MySQLConstants.CONNECTION_READ); + retrieveConnectionIDs(userID, parentID, permissionType); // Query all associated connections return connectionService.translateNames(connectionIDs).keySet(); } + /** + * Retrieve all existing connection names that the given user has permission + * to perform the given operation upon. + * + * @param userID The user whose permissions should be checked. + * @param permissionType The permission to check. + * @param parentID The parent connection group. + * @return A set of all connection names for which the given user has the + * given permission. + */ + public Set retrieveConnectionGroupNames(int userID, Integer parentID, + String permissionType) { + + // A system administrator has access to all connections. + if(checkSystemAdministratorAccess(userID)) + return connectionService.getAllConnectionNames(parentID); + + // List of all connection group IDs for which this user has access + List connectionGroupIDs = + retrieveConnectionGroupIDs(userID, parentID, permissionType); + + // Query all associated connections + return connectionGroupService.translateNames(connectionGroupIDs).keySet(); + + } + /** * Retrieves all user permissions granted to the user having the given ID. * diff --git a/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/AbstractConnection.java b/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/AbstractConnection.java index 27bb7d263..4dfdfa77e 100644 --- a/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/AbstractConnection.java +++ b/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/AbstractConnection.java @@ -47,6 +47,11 @@ import net.sourceforge.guacamole.protocol.GuacamoleConfiguration; */ public abstract class AbstractConnection implements Connection { + /** + * The name associated with this connection. + */ + private String name; + /** * The unique identifier associated with this connection. */ @@ -57,6 +62,16 @@ public abstract class AbstractConnection implements Connection { */ private GuacamoleConfiguration configuration; + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + @Override public String getIdentifier() { return identifier; diff --git a/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/AbstractConnectionGroup.java b/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/AbstractConnectionGroup.java index 485979fd6..7150aa305 100644 --- a/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/AbstractConnectionGroup.java +++ b/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/AbstractConnectionGroup.java @@ -45,10 +45,25 @@ package net.sourceforge.guacamole.net.auth; */ public abstract class AbstractConnectionGroup implements ConnectionGroup { + /** + * The name associated with this connection group. + */ + private String name; + /** * The unique identifier associated with this connection group. */ private String identifier; + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } @Override public String getIdentifier() { diff --git a/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/Connection.java b/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/Connection.java index fdfea4577..d2acb5e1f 100644 --- a/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/Connection.java +++ b/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/Connection.java @@ -54,6 +54,19 @@ import net.sourceforge.guacamole.protocol.GuacamoleConfiguration; */ public interface Connection { + /** + * Returns the name assigned to this Connection. + * @return The name assigned to this Connection. + */ + public String getName(); + + /** + * Sets the name assigned to this Connection. + * + * @param identifier The name to assign. + */ + public void setName(String name); + /** * Returns the unique identifier assigned to this Connection. * @return The unique identifier assigned to this Connection. diff --git a/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/ConnectionGroup.java b/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/ConnectionGroup.java index 5be500394..ac8eddd0f 100644 --- a/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/ConnectionGroup.java +++ b/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/ConnectionGroup.java @@ -50,6 +50,19 @@ import net.sourceforge.guacamole.protocol.GuacamoleClientInformation; */ public interface ConnectionGroup { + /** + * Returns the name assigned to this ConnectionGroup. + * @return The name assigned to this ConnectionGroup. + */ + public String getName(); + + /** + * Sets the name assigned to this ConnectionGroup. + * + * @param identifier The name to assign. + */ + public void setName(String name); + /** * Returns the unique identifier assigned to this ConnectionGroup. * @return The unique identifier assigned to this ConnectionGroup. @@ -62,6 +75,19 @@ public interface ConnectionGroup { * @param identifier The identifier to assign. */ public void setIdentifier(String identifier); + + /** + * Sets whether this is a balancing ConnectionGroup. + * + * @param balancing whether this is a balancing ConnectionGroup. + */ + public void setBalancing(boolean balancing); + + /** + * Returns true if this is a balancing ConnectionGroup, false otherwise. + * @return true if this is a balancing ConnectionGroup, false otherwise. + */ + public boolean isBalancing(); /** * Retrieves a Directory which can be used to view and manipulate diff --git a/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/simple/SimpleConnection.java b/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/simple/SimpleConnection.java index 25f4fb49d..b9c1debf2 100644 --- a/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/simple/SimpleConnection.java +++ b/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/simple/SimpleConnection.java @@ -73,12 +73,16 @@ public class SimpleConnection extends AbstractConnection { * Creates a new SimpleConnection having the given identifier and * GuacamoleConfiguration. * - * @param identifier The identifier to associated with this connection. + * @param name The name to associate with this connection. + * @param identifier The identifier to associate with this connection. * @param config The configuration describing how to connect to this * connection. */ - public SimpleConnection(String identifier, + public SimpleConnection(String name, String identifier, GuacamoleConfiguration config) { + + // Set name + setName(name); // Set identifier setIdentifier(identifier); diff --git a/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/simple/SimpleConnectionDirectory.java b/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/simple/SimpleConnectionDirectory.java index 7787ef09c..54c55a60b 100644 --- a/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/simple/SimpleConnectionDirectory.java +++ b/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/simple/SimpleConnectionDirectory.java @@ -76,7 +76,8 @@ public class SimpleConnectionDirectory // Create connections for each config for (Entry entry : configs.entrySet()) connections.put(entry.getKey(), - new SimpleConnection(entry.getKey(), entry.getValue())); + new SimpleConnection(entry.getKey(), entry.getKey(), + entry.getValue())); } diff --git a/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/simple/SimpleConnectionGroup.java b/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/simple/SimpleConnectionGroup.java index 8a9e4a3f6..338bf255e 100644 --- a/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/simple/SimpleConnectionGroup.java +++ b/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/simple/SimpleConnectionGroup.java @@ -84,4 +84,15 @@ public class SimpleConnectionGroup extends AbstractConnectionGroup { throw new GuacamoleSecurityException("Permission denied."); } + @Override + public void setBalancing(boolean balancing) { + // All SimpleConnectionGroups are organizational only + } + + @Override + public boolean isBalancing() { + // All SimpleConnectionGroups are organizational only + return false; + } + }