From ac14cf0ff36a3d030d50ebee2fa20d709e34ba27 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 27 Feb 2015 14:00:45 -0800 Subject: [PATCH] GUAC-1101: Implement connection groups. --- .../auth/mysql/ConnectionGroupDirectory.java | 104 +++++++++ .../mysql/MySQLAuthenticationProvider.java | 6 + .../net/auth/mysql/MySQLConnectionGroup.java | 135 +++++++++++ .../auth/mysql/MySQLRootConnectionGroup.java | 13 +- .../net/auth/mysql/MySQLUserContext.java | 14 +- .../auth/mysql/dao/ConnectionGroupMapper.java | 75 ++++++ .../mysql/model/ConnectionGroupModel.java | 140 ++++++++++++ .../AbstractGuacamoleSocketService.java | 16 ++ .../mysql/service/ConnectionGroupService.java | 215 ++++++++++++++++++ .../auth/mysql/service/ConnectionService.java | 24 +- .../mysql/service/GuacamoleSocketService.java | 46 ++++ .../auth/mysql/dao/ConnectionGroupMapper.xml | 143 ++++++++++++ 12 files changed, 915 insertions(+), 16 deletions(-) create mode 100644 extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionGroupDirectory.java create mode 100644 extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnectionGroup.java create mode 100644 extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/dao/ConnectionGroupMapper.java create mode 100644 extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/model/ConnectionGroupModel.java create mode 100644 extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionGroupService.java create mode 100644 extensions/guacamole-auth-mysql/src/main/resources/net/sourceforge/guacamole/net/auth/mysql/dao/ConnectionGroupMapper.xml 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..28f1e4cc6 --- /dev/null +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionGroupDirectory.java @@ -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 { + + /** + * 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 getAll(Collection identifiers) throws GuacamoleException { + Collection objects = connectionGroupService.retrieveObjects(currentUser, identifiers); + return Collections.unmodifiableCollection(objects); + } + + @Override + @Transactional + public Set 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); + } + +} diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java index 34c7f6d43..6212374a0 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLAuthenticationProvider.java @@ -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); 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 new file mode 100644 index 000000000..cdc37fc92 --- /dev/null +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLConnectionGroup.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2013 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package 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 + 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 getConnectionIdentifiers() + throws GuacamoleException { + return connectionService.getIdentifiersWithin(getCurrentUser(), getIdentifier()); + } + + @Override + public Set getConnectionGroupIdentifiers() + throws GuacamoleException { + return connectionGroupService.getIdentifiersWithin(getCurrentUser(), getIdentifier()); + } + +} diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLRootConnectionGroup.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLRootConnectionGroup.java index fcd1157d9..5c9dbe488 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLRootConnectionGroup.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLRootConnectionGroup.java @@ -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; @@ -65,6 +65,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 getConnectionIdentifiers() throws GuacamoleException { - return connectionService.getRootIdentifiers(currentUser); + return connectionService.getIdentifiersWithin(currentUser, null); } @Override public Set getConnectionGroupIdentifiers() throws GuacamoleException { - /* STUB */ - return Collections.EMPTY_SET; + return connectionGroupService.getIdentifiersWithin(currentUser, null); } @Override diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUserContext.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUserContext.java index c5877d592..17754e283 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUserContext.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/MySQLUserContext.java @@ -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 getConnectionGroupDirectory() throws GuacamoleException { - /* STUB */ - return new SimpleConnectionGroupDirectory(Collections.singleton(getRootConnectionGroup())); + return connectionGroupDirectory; } @Override diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/dao/ConnectionGroupMapper.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/dao/ConnectionGroupMapper.java new file mode 100644 index 000000000..1a69a78a8 --- /dev/null +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/dao/ConnectionGroupMapper.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package 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 { + + /** + * Selects the identifiers of all connection groups within the given parent + * connection group, regardless of whether they are readable by any + * particular user. This should only be called on behalf of a system + * administrator. If identifiers are needed by a non-administrative user + * who must have explicit read rights, use + * selectReadableIdentifiersWithin() instead. + * + * @param parentIdentifier + * The identifier of the parent connection group, or null if the root + * connection group is to be queried. + * + * @return + * A Set containing all identifiers of all objects. + */ + Set selectIdentifiersWithin(@Param("parentIdentifier") String parentIdentifier); + + /** + * Selects the identifiers of all connection groups within the given parent + * connection group that are explicitly readable by the given user. If + * identifiers are needed by a system administrator (who, by definition, + * does not need explicit read rights), use selectIdentifiersWithin() + * instead. + * + * @param user + * The user whose permissions should determine whether an identifier + * is returned. + * + * @param parentIdentifier + * The identifier of the parent connection group, or null if the root + * connection group is to be queried. + * + * @return + * A Set containing all identifiers of all readable objects. + */ + Set selectReadableIdentifiersWithin(@Param("user") UserModel user, + @Param("parentIdentifier") String parentIdentifier); + +} \ No newline at end of file diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/model/ConnectionGroupModel.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/model/ConnectionGroupModel.java new file mode 100644 index 000000000..29242122f --- /dev/null +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/model/ConnectionGroupModel.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package 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."); + } + +} diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/AbstractGuacamoleSocketService.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/AbstractGuacamoleSocketService.java index d1d86cc2e..de973b9dd 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/AbstractGuacamoleSocketService.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/AbstractGuacamoleSocketService.java @@ -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; @@ -268,5 +270,19 @@ 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 getActiveConnections(ConnectionGroup connectionGroup) { + // STUB + return Collections.EMPTY_LIST; + } } 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 new file mode 100644 index 000000000..34e62ca7b --- /dev/null +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionGroupService.java @@ -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 { + + /** + * Mapper for accessing connection groups. + */ + @Inject + private ConnectionGroupMapper connectionGroupMapper; + + /** + * Provider for creating connection groups. + */ + @Inject + private Provider connectionGroupProvider; + + /** + * Service for creating and tracking sockets. + */ + @Inject + private GuacamoleSocketService socketService; + + @Override + protected DirectoryObjectMapper getObjectMapper() { + return connectionGroupMapper; + } + + @Override + protected 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 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."); + + } + +} 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 f4c1515f6..a9ca2c512 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 @@ -239,28 +239,38 @@ public class ConnectionService extends DirectoryObjectService getRootIdentifiers(AuthenticatedUser user) throws GuacamoleException { + public Set getIdentifiersWithin(AuthenticatedUser user, + String identifier) + throws GuacamoleException { // Bypass permission checks if the user is a system admin if (user.getUser().isAdministrator()) - return connectionMapper.selectIdentifiersWithin(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); } diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/GuacamoleSocketService.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/GuacamoleSocketService.java index b534cfc14..c14e0af8c 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/GuacamoleSocketService.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/GuacamoleSocketService.java @@ -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 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 getActiveConnections(ConnectionGroup connectionGroup); + } diff --git a/extensions/guacamole-auth-mysql/src/main/resources/net/sourceforge/guacamole/net/auth/mysql/dao/ConnectionGroupMapper.xml b/extensions/guacamole-auth-mysql/src/main/resources/net/sourceforge/guacamole/net/auth/mysql/dao/ConnectionGroupMapper.xml new file mode 100644 index 000000000..1a7c16b84 --- /dev/null +++ b/extensions/guacamole-auth-mysql/src/main/resources/net/sourceforge/guacamole/net/auth/mysql/dao/ConnectionGroupMapper.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DELETE FROM guacamole_connection_group + WHERE connection_group_id = #{identifier,jdbcType=VARCHAR} + + + + + + INSERT INTO guacamole_connection_group ( + connection_group_name, + parent_id, + type + ) + VALUES ( + #{object.name,jdbcType=VARCHAR}, + #{object.parentIdentifier,jdbcType=VARCHAR}, + #{object.type,jdbcType=VARCHAR} + ) + + + + + + UPDATE guacamole_connection_group + SET connection_group_name = #{object.name,jdbcType=VARCHAR}, + parent_id = #{object.parentIdentifier,jdbcType=VARCHAR}, + type = #{object.type,jdbcType=VARCHAR} + WHERE connection_group_id = #{object.objectID,jdbcType=INTEGER} + + + \ No newline at end of file