GUAC-932: Do not return parameters and history for all connections. Provide explicit endpoints for connection parameters and history.

This commit is contained in:
Michael Jumper
2014-12-17 01:03:42 -08:00
parent c4057baa42
commit 15f7fedd53
7 changed files with 150 additions and 191 deletions

View File

@@ -23,7 +23,6 @@
package org.glyptodon.guacamole.net.basic.rest;
import com.google.inject.AbstractModule;
import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionService;
import org.glyptodon.guacamole.net.basic.rest.protocol.ProtocolRetrievalService;
/**
@@ -38,7 +37,6 @@ public class RESTModule extends AbstractModule {
protected void configure() {
// Bind generic low-level services
bind(ConnectionService.class);
bind(ProtocolRetrievalService.class);
}

View File

@@ -22,13 +22,10 @@
package org.glyptodon.guacamole.net.basic.rest.connection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.Connection;
import org.glyptodon.guacamole.net.auth.ConnectionRecord;
import org.glyptodon.guacamole.net.basic.rest.connectiongroup.APIConnectionGroup;
import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
@@ -60,15 +57,10 @@ public class APIConnection {
*/
private String protocol;
/**
* The history records associated with this connection.
*/
private List<? extends ConnectionRecord> history;
/**
* Map of all associated parameter values, indexed by parameter name.
*/
private Map<String, String> parameters = new HashMap<String, String>();
private Map<String, String> parameters;
/**
* Create an empty APIConnection.
@@ -76,7 +68,9 @@ public class APIConnection {
public APIConnection() {}
/**
* Create an APIConnection from a Connection record.
* Create an APIConnection from a Connection record. Parameters for the
* connection will not be included.
*
* @param connection The connection to create this APIConnection from.
* @throws GuacamoleException If a problem is encountered while
* instantiating this new APIConnection.
@@ -84,21 +78,18 @@ public class APIConnection {
public APIConnection(Connection connection)
throws GuacamoleException {
// Set identifying information
this.name = connection.getName();
this.identifier = connection.getIdentifier();
this.parentIdentifier = connection.getParentIdentifier();
this.history = connection.getHistory();
// Use the explicit ROOT group ID
// Set proper parent identifier, using root identifier if needed
this.parentIdentifier = connection.getParentIdentifier();
if (this.parentIdentifier == null)
this.parentIdentifier = APIConnectionGroup.ROOT_IDENTIFIER;
// Set protocol from configuration
GuacamoleConfiguration configuration = connection.getConfiguration();
this.protocol = configuration.getProtocol();
for (String key: configuration.getParameterNames())
this.parameters.put(key, configuration.getParameter(key));
}
@@ -151,14 +142,6 @@ public class APIConnection {
this.parentIdentifier = parentIdentifier;
}
/**
* Returns the history records associated with this connection.
* @return The history records associated with this connection.
*/
public List<? extends ConnectionRecord> getHistory() {
return history;
}
/**
* Returns the parameter map for this connection.
* @return The parameter map for this connection.

View File

@@ -22,6 +22,8 @@
package org.glyptodon.guacamole.net.basic.rest.connection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.glyptodon.guacamole.GuacamoleException;
@@ -78,15 +80,16 @@ public class APIConnectionWrapper implements Connection {
@Override
public GuacamoleConfiguration getConfiguration() {
// Create the GuacamoleConfiguration from the parameter map
// Create the GuacamoleConfiguration with current protocol
GuacamoleConfiguration configuration = new GuacamoleConfiguration();
Map<String, String> parameters = apiConnection.getParameters();
for(Map.Entry<String, String> entry : parameters.entrySet())
configuration.setParameter(entry.getKey(), entry.getValue());
configuration.setProtocol(apiConnection.getProtocol());
// Add parameters, if available
Map<String, String> parameters = apiConnection.getParameters();
if (parameters != null) {
for (Map.Entry<String, String> entry : parameters.entrySet())
configuration.setParameter(entry.getKey(), entry.getValue());
}
return configuration;
}
@@ -95,12 +98,14 @@ public class APIConnectionWrapper implements Connection {
public void setConfiguration(GuacamoleConfiguration config) {
// Create a parameter map from the GuacamoleConfiguration
Map<String, String> parameters = apiConnection.getParameters();
for(String key : config.getParameterNames())
Map<String, String> parameters = new HashMap<String, String>();
for (String key : config.getParameterNames())
parameters.put(key, config.getParameter(key));
// Set the protocol
// Set protocol and parameters
apiConnection.setProtocol(config.getProtocol());
apiConnection.setParameters(parameters);
}
@Override
@@ -110,7 +115,7 @@ public class APIConnectionWrapper implements Connection {
@Override
public List<? extends ConnectionRecord> getHistory() throws GuacamoleException {
return apiConnection.getHistory();
return Collections.EMPTY_LIST;
}
}

View File

@@ -23,7 +23,9 @@
package org.glyptodon.guacamole.net.basic.rest.connection;
import com.google.inject.Inject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
@@ -39,11 +41,13 @@ import org.glyptodon.guacamole.GuacamoleClientException;
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.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.HTTPException;
import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -68,52 +72,6 @@ public class ConnectionRESTService {
@Inject
private AuthenticationService authenticationService;
/**
* A service for managing the REST endpoint APIConnection objects.
*/
@Inject
private ConnectionService connectionService;
/**
* Gets a list of connections with the given ConnectionGroup parentID.
* If no parentID is provided, returns the connections from the root group.
*
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param parentID The ID of the ConnectionGroup the connections
* belong to. If null, the root connection group will be used.
* @return The connection list.
* @throws GuacamoleException If a problem is encountered while listing connections.
*/
@GET
@AuthProviderRESTExposure
public List<APIConnection> getConnections(@QueryParam("token") String authToken, @QueryParam("parentID") String parentID)
throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
// If the parent connection group is passed in, try to find it.
ConnectionGroup parentConnectionGroup;
if (parentID == null)
parentConnectionGroup = userContext.getRootConnectionGroup();
else {
ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
Directory<String, ConnectionGroup> connectionGroupDirectory = rootGroup.getConnectionGroupDirectory();
parentConnectionGroup = connectionGroupDirectory.get(parentID);
}
if (parentConnectionGroup == null)
throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID.");
Directory<String, Connection> connectionDirectory =
parentConnectionGroup.getConnectionDirectory();
// Return the converted connection directory
return connectionService.convertConnectionList(connectionDirectory);
}
/**
* Gets an individual connection.
*
@@ -144,7 +102,92 @@ public class ConnectionRESTService {
return new APIConnection(connection);
}
/**
* Retrieves the parameters associated with a single connection.
*
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param connectionID
* The ID of the connection.
*
* @return
* A map of parameter name/value pairs.
*
* @throws GuacamoleException
* If an error occurs while retrieving the connection parameters.
*/
@GET
@Path("/{connectionID}/parameters")
@AuthProviderRESTExposure
public Map<String, String> getConnectionParameters(@QueryParam("token") String authToken,
@PathParam("connectionID") String connectionID) throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
// Get the connection directory
ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
Directory<String, Connection> connectionDirectory =
rootGroup.getConnectionDirectory();
// Get the connection
Connection connection = connectionDirectory.get(connectionID);
if (connection == null)
throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided ID.");
// Retrieve connection configuration
GuacamoleConfiguration config = connection.getConfiguration();
// Convert parameters to map
Map<String, String> parameters = new HashMap<String, String>();
for (String key : config.getParameterNames())
parameters.put(key, config.getParameter(key));
return parameters;
}
/**
* Retrieves the usage history of a single connection.
*
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param connectionID
* The ID of the connection.
*
* @return
* A list of connection records, describing the start and end times of
* various usages of this connection.
*
* @throws GuacamoleException
* If an error occurs while retrieving the connection history.
*/
@GET
@Path("/{connectionID}/history")
@AuthProviderRESTExposure
public List<? extends ConnectionRecord> getConnectionHistory(@QueryParam("token") String authToken,
@PathParam("connectionID") String connectionID) throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
// Get the connection directory
ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
Directory<String, Connection> connectionDirectory =
rootGroup.getConnectionDirectory();
// Get the connection
Connection connection = connectionDirectory.get(connectionID);
if (connection == null)
throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided ID.");
return connection.getHistory();
}
/**
* Deletes an individual connection.
*

View File

@@ -1,59 +0,0 @@
/*
* 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.connection;
import java.util.ArrayList;
import java.util.List;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.Connection;
import org.glyptodon.guacamole.net.auth.Directory;
/**
* A service for performing useful manipulations on REST Connections.
*
* @author James Muehlner
*/
public class ConnectionService {
/**
* Converts a Connection Directory to a list of APIConnection objects for
* exposing with the REST endpoints.
*
* @param connectionDirectory The Connection Directory to convert for REST endpoint use.
* @return A List of APIConnection objects for use with the REST endpoint.
* @throws GuacamoleException If an error occurs while converting the
* connection directory.
*/
public List<APIConnection> convertConnectionList(Directory<String, Connection> connectionDirectory)
throws GuacamoleException {
List<APIConnection> restConnections = new ArrayList<APIConnection>();
for (String connectionID : connectionDirectory.getIdentifiers())
restConnections.add(new APIConnection(connectionDirectory.get(connectionID)));
return restConnections;
}
}

View File

@@ -48,28 +48,37 @@ angular.module('rest').factory('connectionService', ['$http', 'authenticationSer
};
/**
* Makes a request to the REST API to get the list of connections,
* returning a promise that provides an array of
* @link{Connection} objects if successful.
* Makes a request to the REST API to get the usage history of a single
* connection, returning a promise that provides the corresponding
* array of @link{ConnectionHistoryEntry} objects if successful.
*
* @param {String} [parentID=ConnectionGroup.ROOT_IDENTIFIER]
* The ID of the connection group whose child connections should be
* returned. If not provided, the root connection group will be used
* by default.
*
* @returns {Promise.<Connection[]>}
* A promise which will resolve with an array of @link{Connection}
* objects upon success.
* @param {String} id
* The identifier of the connection.
*
* @returns {Promise.<ConnectionHistoryEntry[]>}
* A promise which will resolve with an array of
* @link{ConnectionHistoryEntry} objects upon success.
*/
service.getConnections = function getConnections(parentID) {
var parentIDParam = "";
if (parentID)
parentIDParam = "&parentID=" + parentID;
return $http.get("api/connection?token=" + authenticationService.getCurrentToken() + parentIDParam);
service.getConnectionHistory = function getConnectionHistory(id) {
return $http.get("api/connection/" + id + "/history?token=" + authenticationService.getCurrentToken());
};
/**
* Makes a request to the REST API to get the parameters of a single
* connection, returning a promise that provides the corresponding
* map of parameter name/value pairs if successful.
*
* @param {String} id
* The identifier of the connection.
*
* @returns {Promise.<Object.<String, String>>}
* A promise which will resolve with an map of parameter name/value
* pairs upon success.
*/
service.getConnectionParameters = function getConnectionParameters(id) {
return $http.get("api/connection/" + id + "/parameters?token=" + authenticationService.getCurrentToken());
};
/**
* Makes a request to the REST API to save a connection, returning a
* promise that can be used for processing the results of the call. If the
@@ -85,18 +94,9 @@ angular.module('rest').factory('connectionService', ['$http', 'authenticationSer
*/
service.saveConnection = function saveConnection(connection) {
/*
* FIXME: This should not be necessary. Perhaps the need for this is a
* sign that history should be queried separately, and not reside as
* part of the connection object?
*/
// Do not try to save the connection history records
var connectionToSave = angular.copy(connection);
delete connectionToSave.history;
// If connection is new, add it and set the identifier automatically
if (!connectionToSave.identifier) {
return $http.post("api/connection/?token=" + authenticationService.getCurrentToken(), connectionToSave).success(
if (!connection.identifier) {
return $http.post("api/connection/?token=" + authenticationService.getCurrentToken(), connection).success(
// Set the identifier on the new connection
function setConnectionID(connectionID){
@@ -109,9 +109,9 @@ angular.module('rest').factory('connectionService', ['$http', 'authenticationSer
// Otherwise, update the existing connection
else {
return $http.post(
"api/connection/" + connectionToSave.identifier +
"api/connection/" + connection.identifier +
"?token=" + authenticationService.getCurrentToken(),
connectionToSave);
connection);
}
};

View File

@@ -70,26 +70,15 @@ angular.module('rest').factory('Connection', [function defineConnection() {
*/
this.protocol = template.protocol;
/**
* All previous and current usages of this connection, along with
* associated users.
*
* @type ConnectionHistoryEntry[]
* @default []
*/
this.history = template.history || [];
/**
* Connection configuration parameters, as dictated by the protocol in
* use, arranged as name/value pairs. This information may not be
* available if the current user lacks permission to update
* connection parameters, even if they otherwise have permission to
* read and use the connection.
* available until directly queried. If this information is
* unavailable, this property will be null or undefined.
*
* @type Object.<String, String>
* @default {}
*/
this.parameters = template.parameters || {};
this.parameters = template.parameters;
};