diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/ObjectRetrievalService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/ObjectRetrievalService.java new file mode 100644 index 000000000..27cb6d9ef --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/ObjectRetrievalService.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2014 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.net.basic.rest; + +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleResourceNotFoundException; +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.basic.rest.connectiongroup.APIConnectionGroup; + +/** + * Provides easy access and automatic error handling for retrieval of objects, + * such as users, connections, or connection groups. REST API semantics, such + * as the special root connection group identifier, are also handled + * automatically. + */ +public class ObjectRetrievalService { + + /** + * Retrieves a single user from the given user context. + * + * @param userContext + * The user context to retrieve the user from. + * + * @param identifier + * The identifier of the user to retrieve. + * + * @return + * The user having the given identifier. + * + * @throws GuacamoleException + * If an error occurs while retrieving the user, or if the + * user does not exist. + */ + public User retrieveUser(UserContext userContext, + String identifier) throws GuacamoleException { + + // Get user directory + Directory directory = userContext.getUserDirectory(); + + // Pull specified user + User user = directory.get(identifier); + if (user == null) + throw new GuacamoleResourceNotFoundException("No such user: \"" + identifier + "\""); + + return user; + + } + + /** + * Retrieves a single connection from the given user context. + * + * @param userContext + * The user context to retrieve the connection from. + * + * @param identifier + * The identifier of the connection to retrieve. + * + * @return + * The connection having the given identifier. + * + * @throws GuacamoleException + * If an error occurs while retrieving the connection, or if the + * connection does not exist. + */ + public Connection retrieveConnection(UserContext userContext, + String identifier) throws GuacamoleException { + + // Get root directory + ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); + Directory directory = rootGroup.getConnectionDirectory(); + + // Pull specified connection + Connection connection = directory.get(identifier); + if (connection == null) + throw new GuacamoleResourceNotFoundException("No such connection: \"" + identifier + "\""); + + return connection; + + } + + /** + * Retrieves a single connection group from the given user context. If + * the given identifier the REST API root identifier, the root connection + * group will be returned. The underlying authentication provider may + * additionally use a different identifier for root. + * + * @param userContext + * The user context to retrieve the connection group from. + * + * @param identifier + * The identifier of the connection group to retrieve. + * + * @return + * The connection group having the given identifier, or the root + * connection group if the identifier the root identifier. + * + * @throws GuacamoleException + * If an error occurs while retrieving the connection group, or if the + * connection group does not exist. + */ + public ConnectionGroup retrieveConnectionGroup(UserContext userContext, + String identifier) throws GuacamoleException { + + ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); + + // Use root group if identifier is null (or the standard root identifier) + if (identifier != null && identifier.equals(APIConnectionGroup.ROOT_IDENTIFIER)) + return rootGroup; + + // Pull specified connection group otherwise + Directory directory = rootGroup.getConnectionGroupDirectory(); + ConnectionGroup connectionGroup = directory.get(identifier); + + if (connectionGroup == null) + throw new GuacamoleResourceNotFoundException("No such connection group: \"" + identifier + "\""); + + return connectionGroup; + + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java index 70fde2592..9b17d356a 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java @@ -38,6 +38,7 @@ public class RESTModule extends AbstractModule { // Bind generic low-level services bind(ProtocolRetrievalService.class); + bind(ObjectRetrievalService.class); } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java index 25ff2f5f6..e9633054b 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java @@ -44,8 +44,8 @@ import org.glyptodon.guacamole.net.auth.ConnectionRecord; import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.UserContext; import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; +import org.glyptodon.guacamole.net.basic.rest.ObjectRetrievalService; import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; -import org.glyptodon.guacamole.net.basic.rest.connectiongroup.APIConnectionGroup; import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,6 +71,12 @@ public class ConnectionRESTService { @Inject private AuthenticationService authenticationService; + /** + * Service for convenient retrieval of objects. + */ + @Inject + private ObjectRetrievalService retrievalService; + /** * Retrieves an individual connection. * @@ -95,17 +101,8 @@ public class ConnectionRESTService { UserContext userContext = authenticationService.getUserContext(authToken); - // Get the connection directory - ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); - Directory connectionDirectory = - rootGroup.getConnectionDirectory(); - - // Get the connection - Connection connection = connectionDirectory.get(connectionID); - if (connection == null) - throw new GuacamoleResourceNotFoundException("No such connection: \"" + connectionID + "\""); - - return new APIConnection(connection); + // Retrieve the requested connection + return new APIConnection(retrievalService.retrieveConnection(userContext, connectionID)); } @@ -138,10 +135,8 @@ public class ConnectionRESTService { Directory connectionDirectory = rootGroup.getConnectionDirectory(); - // Get the connection - Connection connection = connectionDirectory.get(connectionID); - if (connection == null) - throw new GuacamoleResourceNotFoundException("No such connection: \"" + connectionID + "\""); + // Retrieve the requested connection + Connection connection = retrievalService.retrieveConnection(userContext, connectionID); // Retrieve connection configuration GuacamoleConfiguration config = connection.getConfiguration(); @@ -181,11 +176,8 @@ public class ConnectionRESTService { Directory connectionDirectory = rootGroup.getConnectionDirectory(); - // Get the connection - Connection connection = connectionDirectory.get(connectionID); - if (connection == null) - throw new GuacamoleResourceNotFoundException("No such connection: \"" + connectionID + "\""); - + // Retrieve the requested connection's history + Connection connection = retrievalService.retrieveConnection(userContext, connectionID); return connection.getHistory(); } @@ -216,54 +208,11 @@ public class ConnectionRESTService { Directory connectionDirectory = rootGroup.getConnectionDirectory(); - // Make sure the connection is there before trying to delete - if (connectionDirectory.get(connectionID) == null) - throw new GuacamoleResourceNotFoundException("No such connection: \"" + connectionID + "\""); - - // Delete the connection + // Delete the specified connection connectionDirectory.remove(connectionID); } - /** - * Retrieves a single connection group from the given user context. If - * the given identifier is null or the root identifier, the root connection - * group will be returned. - * - * @param userContext - * The user context to retrieve the connection group from. - * - * @param identifier - * The identifier of the connection group to retrieve. - * - * @return - * The connection group having the given identifier, or the root - * connection group if the identifier is null or the root identifier. - * - * @throws GuacamoleException - * If an error occurs while retrieving the connection group, or if the - * connection group does not exist. - */ - private ConnectionGroup retrieveConnectionGroup(UserContext userContext, - String identifier) throws GuacamoleException { - - ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); - - // Use root group if identifier is null (or the standard root identifier) - if (identifier != null && identifier.equals(APIConnectionGroup.ROOT_IDENTIFIER)) - return rootGroup; - - // Pull specified connection group otherwise - Directory directory = rootGroup.getConnectionGroupDirectory(); - ConnectionGroup connectionGroup = directory.get(identifier); - - if (connectionGroup == null) - throw new GuacamoleResourceNotFoundException("No such connection group: \"" + identifier + "\""); - - return connectionGroup; - - } - /** * Creates a new connection and returns the identifier of the new * connection. @@ -294,7 +243,7 @@ public class ConnectionRESTService { // Retrieve parent group String parentID = connection.getParentIdentifier(); - ConnectionGroup parentConnectionGroup = retrieveConnectionGroup(userContext, parentID); + ConnectionGroup parentConnectionGroup = retrievalService.retrieveConnectionGroup(userContext, parentID); // Add the new connection Directory connectionDirectory = parentConnectionGroup.getConnectionDirectory(); @@ -341,10 +290,8 @@ public class ConnectionRESTService { rootGroup.getConnectionDirectory(); // Retrieve connection to update - Connection existingConnection = connectionDirectory.get(connectionID); - if (existingConnection == null) - throw new GuacamoleResourceNotFoundException("No such connection: \"" + connectionID + "\""); - + Connection existingConnection = retrievalService.retrieveConnection(userContext, connectionID); + // Build updated configuration GuacamoleConfiguration config = new GuacamoleConfiguration(); config.setProtocol(connection.getProtocol()); @@ -356,7 +303,7 @@ public class ConnectionRESTService { connectionDirectory.update(existingConnection); // Update connection parent - ConnectionGroup updatedParentGroup = retrieveConnectionGroup(userContext, connection.getParentIdentifier()); + ConnectionGroup updatedParentGroup = retrievalService.retrieveConnectionGroup(userContext, connection.getParentIdentifier()); connectionDirectory.move(connectionID, updatedParentGroup.getConnectionDirectory()); } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java index 46a37c5e0..226ba8f9d 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java @@ -46,6 +46,7 @@ import org.glyptodon.guacamole.net.auth.UserContext; import org.glyptodon.guacamole.net.auth.permission.ConnectionPermission; import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; +import org.glyptodon.guacamole.net.basic.rest.ObjectRetrievalService; import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; import org.glyptodon.guacamole.net.basic.rest.connection.APIConnection; import org.slf4j.Logger; @@ -72,6 +73,12 @@ public class ConnectionGroupRESTService { @Inject private AuthenticationService authenticationService; + /** + * Service for convenient retrieval of objects. + */ + @Inject + private ObjectRetrievalService retrievalService; + /** * Retrieves the given connection group from the user context, including * all descendant connections and groups if requested. @@ -105,24 +112,14 @@ public class ConnectionGroupRESTService { User self = userContext.self(); ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); - + + // Retrieve specified connection group ConnectionGroup connectionGroup; - - // Use root group if requested - if (identifier == null || identifier.equals(APIConnectionGroup.ROOT_IDENTIFIER)) - connectionGroup = rootGroup; - - // Otherwise, query requested group using root group directory - else { - - Directory connectionGroupDirectory = - rootGroup.getConnectionGroupDirectory(); - - // Get the connection group from root directory - connectionGroup = connectionGroupDirectory.get(identifier); - if (connectionGroup == null) - return null; - + try { + connectionGroup = retrievalService.retrieveConnectionGroup(userContext, identifier); + } + catch (GuacamoleResourceNotFoundException e) { + return null; } // Wrap queried connection group @@ -274,18 +271,9 @@ public class ConnectionGroupRESTService { // Get the connection group directory ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); - - // Use the root group if it was asked for - if (connectionGroupID != null && connectionGroupID.equals(APIConnectionGroup.ROOT_IDENTIFIER)) - connectionGroupID = rootGroup.getIdentifier(); - Directory connectionGroupDirectory = rootGroup.getConnectionGroupDirectory(); - // Make sure the connection is there before trying to delete - if (connectionGroupDirectory.get(connectionGroupID) == null) - throw new GuacamoleResourceNotFoundException("No such connection group: \"" + connectionGroupID + "\""); - // Delete the connection group connectionGroupDirectory.remove(connectionGroupID); @@ -321,22 +309,9 @@ public class ConnectionGroupRESTService { if (connectionGroup == null) throw new GuacamoleClientException("Connection group JSON must be submitted when creating connections groups."); + // Retrieve parent group String parentID = connectionGroup.getParentIdentifier(); - ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); - - // Use root group if no parent is specified - ConnectionGroup parentConnectionGroup; - if (parentID == null) - parentConnectionGroup = rootGroup; - - // Pull specified connection group otherwise - else { - Directory connectionGroupDirectory = rootGroup.getConnectionGroupDirectory(); - parentConnectionGroup = connectionGroupDirectory.get(parentID); - - if (parentConnectionGroup == null) - throw new GuacamoleResourceNotFoundException("No such connection group: \"" + parentID + "\""); - } + ConnectionGroup parentConnectionGroup = retrievalService.retrieveConnectionGroup(userContext, parentID); // Add the new connection group Directory connectionGroupDirectory = parentConnectionGroup.getConnectionGroupDirectory(); @@ -378,22 +353,22 @@ public class ConnectionGroupRESTService { if (connectionGroup == null) throw new GuacamoleClientException("Connection group JSON must be submitted when updating connection groups."); - // Get the connection directory + // Get the connection group directory ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); - - // Use the root group if it was asked for - if (connectionGroupID != null && connectionGroupID.equals(APIConnectionGroup.ROOT_IDENTIFIER)) - connectionGroupID = rootGroup.getIdentifier(); - Directory connectionGroupDirectory = rootGroup.getConnectionGroupDirectory(); - // Make sure the connection group is there before trying to update - if (connectionGroupDirectory.get(connectionGroupID) == null) - throw new GuacamoleResourceNotFoundException("No such connection group: \"" + connectionGroupID + "\""); - + // Retrieve connection group to update + ConnectionGroup existingConnectionGroup = connectionGroupDirectory.get(connectionGroupID); + // Update the connection group - connectionGroupDirectory.update(new APIConnectionGroupWrapper(connectionGroup)); + existingConnectionGroup.setName(connectionGroup.getName()); + existingConnectionGroup.setType(connectionGroup.getType()); + connectionGroupDirectory.update(existingConnectionGroup); + + // Update connection group parent + ConnectionGroup updatedParentGroup = retrievalService.retrieveConnectionGroup(userContext, connectionGroup.getParentIdentifier()); + connectionGroupDirectory.move(connectionGroupID, updatedParentGroup.getConnectionGroupDirectory()); } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java index c9e6a6fde..7a9f28b9b 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java @@ -54,6 +54,7 @@ import static org.glyptodon.guacamole.net.basic.rest.APIPatch.Operation.add; import static org.glyptodon.guacamole.net.basic.rest.APIPatch.Operation.remove; import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; import org.glyptodon.guacamole.net.basic.rest.HTTPException; +import org.glyptodon.guacamole.net.basic.rest.ObjectRetrievalService; import org.glyptodon.guacamole.net.basic.rest.PATCH; import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; import org.glyptodon.guacamole.net.basic.rest.permission.APIPermissionSet; @@ -105,6 +106,12 @@ public class UserRESTService { @Inject private AuthenticationService authenticationService; + /** + * Service for convenient retrieval of objects. + */ + @Inject + private ObjectRetrievalService retrievalService; + /** * Gets a list of users in the system, filtering the returned list by the * given permission, if specified. @@ -177,15 +184,8 @@ public class UserRESTService { UserContext userContext = authenticationService.getUserContext(authToken); - // Get the directory - Directory userDirectory = userContext.getUserDirectory(); - - // Get the user - User user = userDirectory.get(username); - if (user == null) - throw new GuacamoleResourceNotFoundException("No such user: \"" + username + "\""); - - // Return the user + // Retrieve the requested user + User user = retrievalService.retrieveUser(userContext, username); return new APIUser(user); } @@ -254,9 +254,7 @@ public class UserRESTService { "Username in path does not match username provided JSON data."); // Get the user - User existingUser = userDirectory.get(username); - if (existingUser == null) - throw new GuacamoleResourceNotFoundException("No such user: \"" + username + "\""); + User existingUser = retrievalService.retrieveUser(userContext, username); // Do not update the user password if no password was provided if (user.getPassword() != null)