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

View File

@@ -22,13 +22,10 @@
package org.glyptodon.guacamole.net.basic.rest.connection; package org.glyptodon.guacamole.net.basic.rest.connection;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.Connection; 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.net.basic.rest.connectiongroup.APIConnectionGroup;
import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
@@ -60,15 +57,10 @@ public class APIConnection {
*/ */
private String protocol; 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. * 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. * Create an empty APIConnection.
@@ -76,7 +68,9 @@ public class APIConnection {
public 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. * @param connection The connection to create this APIConnection from.
* @throws GuacamoleException If a problem is encountered while * @throws GuacamoleException If a problem is encountered while
* instantiating this new APIConnection. * instantiating this new APIConnection.
@@ -84,22 +78,19 @@ public class APIConnection {
public APIConnection(Connection connection) public APIConnection(Connection connection)
throws GuacamoleException { throws GuacamoleException {
// Set identifying information
this.name = connection.getName(); this.name = connection.getName();
this.identifier = connection.getIdentifier(); 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) if (this.parentIdentifier == null)
this.parentIdentifier = APIConnectionGroup.ROOT_IDENTIFIER; this.parentIdentifier = APIConnectionGroup.ROOT_IDENTIFIER;
// Set protocol from configuration
GuacamoleConfiguration configuration = connection.getConfiguration(); GuacamoleConfiguration configuration = connection.getConfiguration();
this.protocol = configuration.getProtocol(); 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; 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. * Returns the parameter map for this connection.
* @return 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; package org.glyptodon.guacamole.net.basic.rest.connection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleException;
@@ -78,15 +80,16 @@ public class APIConnectionWrapper implements Connection {
@Override @Override
public GuacamoleConfiguration getConfiguration() { public GuacamoleConfiguration getConfiguration() {
// Create the GuacamoleConfiguration from the parameter map // Create the GuacamoleConfiguration with current protocol
GuacamoleConfiguration configuration = new GuacamoleConfiguration(); GuacamoleConfiguration configuration = new GuacamoleConfiguration();
configuration.setProtocol(apiConnection.getProtocol());
// Add parameters, if available
Map<String, String> parameters = apiConnection.getParameters(); Map<String, String> parameters = apiConnection.getParameters();
if (parameters != null) {
for (Map.Entry<String, String> entry : parameters.entrySet()) for (Map.Entry<String, String> entry : parameters.entrySet())
configuration.setParameter(entry.getKey(), entry.getValue()); configuration.setParameter(entry.getKey(), entry.getValue());
}
configuration.setProtocol(apiConnection.getProtocol());
return configuration; return configuration;
} }
@@ -95,12 +98,14 @@ public class APIConnectionWrapper implements Connection {
public void setConfiguration(GuacamoleConfiguration config) { public void setConfiguration(GuacamoleConfiguration config) {
// Create a parameter map from the GuacamoleConfiguration // Create a parameter map from the GuacamoleConfiguration
Map<String, String> parameters = apiConnection.getParameters(); Map<String, String> parameters = new HashMap<String, String>();
for (String key : config.getParameterNames()) for (String key : config.getParameterNames())
parameters.put(key, config.getParameter(key)); parameters.put(key, config.getParameter(key));
// Set the protocol // Set protocol and parameters
apiConnection.setProtocol(config.getProtocol()); apiConnection.setProtocol(config.getProtocol());
apiConnection.setParameters(parameters);
} }
@Override @Override
@@ -110,7 +115,7 @@ public class APIConnectionWrapper implements Connection {
@Override @Override
public List<? extends ConnectionRecord> getHistory() throws GuacamoleException { 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; package org.glyptodon.guacamole.net.basic.rest.connection;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE; import javax.ws.rs.DELETE;
import javax.ws.rs.GET; import javax.ws.rs.GET;
@@ -39,11 +41,13 @@ import org.glyptodon.guacamole.GuacamoleClientException;
import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.Connection;
import org.glyptodon.guacamole.net.auth.ConnectionGroup; 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.Directory;
import org.glyptodon.guacamole.net.auth.UserContext; import org.glyptodon.guacamole.net.auth.UserContext;
import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure;
import org.glyptodon.guacamole.net.basic.rest.HTTPException; import org.glyptodon.guacamole.net.basic.rest.HTTPException;
import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -68,52 +72,6 @@ public class ConnectionRESTService {
@Inject @Inject
private AuthenticationService authenticationService; 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. * Gets an individual connection.
* *
@@ -145,6 +103,91 @@ public class ConnectionRESTService {
} }
/**
* 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. * 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,26 +48,35 @@ angular.module('rest').factory('connectionService', ['$http', 'authenticationSer
}; };
/** /**
* Makes a request to the REST API to get the list of connections, * Makes a request to the REST API to get the usage history of a single
* returning a promise that provides an array of * connection, returning a promise that provides the corresponding
* @link{Connection} objects if successful. * array of @link{ConnectionHistoryEntry} objects if successful.
* *
* @param {String} [parentID=ConnectionGroup.ROOT_IDENTIFIER] * @param {String} id
* The ID of the connection group whose child connections should be * The identifier of the connection.
* returned. If not provided, the root connection group will be used
* by default.
* *
* @returns {Promise.<Connection[]>} * @returns {Promise.<ConnectionHistoryEntry[]>}
* A promise which will resolve with an array of @link{Connection} * A promise which will resolve with an array of
* objects upon success. * @link{ConnectionHistoryEntry} objects upon success.
*/ */
service.getConnections = function getConnections(parentID) { service.getConnectionHistory = function getConnectionHistory(id) {
return $http.get("api/connection/" + id + "/history?token=" + authenticationService.getCurrentToken());
};
var parentIDParam = ""; /**
if (parentID) * Makes a request to the REST API to get the parameters of a single
parentIDParam = "&parentID=" + parentID; * connection, returning a promise that provides the corresponding
* map of parameter name/value pairs if successful.
return $http.get("api/connection?token=" + authenticationService.getCurrentToken() + parentIDParam); *
* @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());
}; };
/** /**
@@ -85,18 +94,9 @@ angular.module('rest').factory('connectionService', ['$http', 'authenticationSer
*/ */
service.saveConnection = function saveConnection(connection) { 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 connection is new, add it and set the identifier automatically
if (!connectionToSave.identifier) { if (!connection.identifier) {
return $http.post("api/connection/?token=" + authenticationService.getCurrentToken(), connectionToSave).success( return $http.post("api/connection/?token=" + authenticationService.getCurrentToken(), connection).success(
// Set the identifier on the new connection // Set the identifier on the new connection
function setConnectionID(connectionID){ function setConnectionID(connectionID){
@@ -109,9 +109,9 @@ angular.module('rest').factory('connectionService', ['$http', 'authenticationSer
// Otherwise, update the existing connection // Otherwise, update the existing connection
else { else {
return $http.post( return $http.post(
"api/connection/" + connectionToSave.identifier + "api/connection/" + connection.identifier +
"?token=" + authenticationService.getCurrentToken(), "?token=" + authenticationService.getCurrentToken(),
connectionToSave); connection);
} }
}; };

View File

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