Merge pull request #22 from glyptodon/cleanup-angular

Rewrite an absolute ton of Angular and REST code
This commit is contained in:
James Muehlner
2014-12-18 21:37:49 -08:00
66 changed files with 3359 additions and 2178 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
*~
target/
nb-configuration.xml
guacamole/customs.json

View File

@@ -162,4 +162,31 @@ public class GuacamoleConfiguration implements Serializable {
return Collections.unmodifiableSet(parameters.keySet());
}
/**
* Returns a map which contains parameter name/value pairs as key/value
* pairs. Changes to this map will affect the parameters stored within
* this configuration.
*
* @return
* A map which contains all parameter name/value pairs as key/value
* pairs.
*/
public Map<String, String> getParameters() {
return parameters;
}
/**
* Replaces all current parameters with the parameters defined within the
* given map. Key/value pairs within the map represent parameter name/value
* pairs.
*
* @param parameters
* A map which contains all parameter name/value pairs as key/value
* pairs.
*/
public void setParameters(Map<String, String> parameters) {
this.parameters.clear();
this.parameters.putAll(parameters);
}
}

View File

@@ -23,11 +23,7 @@
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.connectiongroup.ConnectionGroupService;
import org.glyptodon.guacamole.net.basic.rest.permission.PermissionService;
import org.glyptodon.guacamole.net.basic.rest.protocol.ProtocolRetrievalService;
import org.glyptodon.guacamole.net.basic.rest.user.UserService;
/**
* A Guice Module for setting up dependency injection for the
@@ -41,10 +37,6 @@ public class RESTModule extends AbstractModule {
protected void configure() {
// Bind generic low-level services
bind(ConnectionService.class);
bind(ConnectionGroupService.class);
bind(PermissionService.class);
bind(UserService.class);
bind(ProtocolRetrievalService.class);
}

View File

@@ -30,7 +30,6 @@ import org.glyptodon.guacamole.net.basic.rest.auth.TokenRESTService;
import org.glyptodon.guacamole.net.basic.rest.clipboard.ClipboardRESTService;
import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionRESTService;
import org.glyptodon.guacamole.net.basic.rest.connectiongroup.ConnectionGroupRESTService;
import org.glyptodon.guacamole.net.basic.rest.permission.PermissionRESTService;
import org.glyptodon.guacamole.net.basic.rest.protocol.ProtocolRESTService;
import org.glyptodon.guacamole.net.basic.rest.user.UserRESTService;
@@ -48,7 +47,6 @@ public class RESTServletModule extends ServletModule {
bind(ClipboardRESTService.class);
bind(ConnectionRESTService.class);
bind(ConnectionGroupRESTService.class);
bind(PermissionRESTService.class);
bind(ProtocolRESTService.class);
bind(UserRESTService.class);
bind(TokenRESTService.class);

View File

@@ -48,7 +48,7 @@ import org.slf4j.LoggerFactory;
*
* @author James Muehlner
*/
@Path("/token")
@Path("/tokens")
@Produces(MediaType.APPLICATION_JSON)
public class TokenRESTService {

View File

@@ -22,14 +22,11 @@
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.APIConstants;
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();
// Set proper parent identifier, using root identifier if needed
this.parentIdentifier = connection.getParentIdentifier();
this.history = connection.getHistory();
// Use the explicit ROOT group ID
if (this.parentIdentifier == null)
this.parentIdentifier = APIConstants.ROOT_CONNECTION_GROUP_IDENTIFIER;
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,7 @@
package org.glyptodon.guacamole.net.basic.rest.connection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.glyptodon.guacamole.GuacamoleException;
@@ -78,15 +79,14 @@ 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)
configuration.setParameters(parameters);
return configuration;
}
@@ -94,13 +94,10 @@ public class APIConnectionWrapper implements Connection {
@Override
public void setConfiguration(GuacamoleConfiguration config) {
// Create a parameter map from the GuacamoleConfiguration
Map<String, String> parameters = apiConnection.getParameters();
for(String key : config.getParameterNames())
parameters.put(key, config.getParameter(key));
// Set the protocol
// Set protocol and parameters
apiConnection.setProtocol(config.getProtocol());
apiConnection.setParameters(config.getParameters());
}
@Override
@@ -110,7 +107,7 @@ public class APIConnectionWrapper implements Connection {
@Override
public List<? extends ConnectionRecord> getHistory() throws GuacamoleException {
return apiConnection.getHistory();
return Collections.EMPTY_LIST;
}
}

View File

@@ -24,6 +24,7 @@ package org.glyptodon.guacamole.net.basic.rest.connection;
import com.google.inject.Inject;
import java.util.List;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
@@ -34,16 +35,18 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response.Status;
import org.glyptodon.guacamole.GuacamoleClientException;
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.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.net.basic.rest.connectiongroup.APIConnectionGroup;
import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -52,7 +55,7 @@ import org.slf4j.LoggerFactory;
*
* @author James Muehlner
*/
@Path("/connection")
@Path("/connections")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ConnectionRESTService {
@@ -69,59 +72,20 @@ public class ConnectionRESTService {
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.
* Retrieves an individual connection.
*
* @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.
*
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param connectionID The ID of the Connection..
* @return The connection.
* @throws GuacamoleException If a problem is encountered while retrieving the connection.
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param connectionID
* The identifier of the connection to retrieve.
*
* @return
* The connection having the given identifier.
*
* @throws GuacamoleException
* If an error occurs while retrieving the connection.
*/
@GET
@Path("/{connectionID}")
@@ -139,19 +103,105 @@ public class ConnectionRESTService {
// Get the connection
Connection connection = connectionDirectory.get(connectionID);
if (connection == null)
throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided ID.");
throw new GuacamoleResourceNotFoundException("No such connection: \"" + connectionID + "\"");
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 identifier 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 GuacamoleResourceNotFoundException("No such connection: \"" + connectionID + "\"");
// Retrieve connection configuration
GuacamoleConfiguration config = connection.getConfiguration();
// Return parameter map
return config.getParameters();
}
/**
* 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 identifier 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 GuacamoleResourceNotFoundException("No such connection: \"" + connectionID + "\"");
return connection.getHistory();
}
/**
* Deletes an individual connection.
*
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param connectionID The ID of the Connection to delete.
* @throws GuacamoleException If a problem is encountered while deleting the connection.
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param connectionID
* The identifier of the connection to delete.
*
* @throws GuacamoleException
* If an error occurs while deleting the connection.
*/
@DELETE
@Path("/{connectionID}")
@@ -168,72 +218,112 @@ public class ConnectionRESTService {
// Make sure the connection is there before trying to delete
if (connectionDirectory.get(connectionID) == null)
throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided ID.");
throw new GuacamoleResourceNotFoundException("No such connection: \"" + connectionID + "\"");
// Delete the connection
connectionDirectory.remove(connectionID);
}
/**
* Creates a new connection and returns the identifier of the new connection.
* If a parentID is provided, the connection will be created in the
* connection group with the parentID. Otherwise, the root connection group
* will be used.
* 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<String, ConnectionGroup> 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.
*
* @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.
* @param connection The connection to create.
* @return The identifier of the new connection.
* @throws GuacamoleException If a problem is encountered while creating the connection.
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param connection
* The connection to create.
*
* @return
* The identifier of the new connection.
*
* @throws GuacamoleException
* If an error occurs while creating the connection.
*/
@POST
@AuthProviderRESTExposure
public String createConnection(@QueryParam("token") String authToken,
@QueryParam("parentID") String parentID, APIConnection connection) throws GuacamoleException {
public String createConnection(@QueryParam("token") String authToken,
APIConnection connection) throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
// Validate that connection data was provided
if (connection == null)
throw new GuacamoleClientException("A connection is required for this request.");
throw new GuacamoleClientException("Connection JSON must be submitted when creating connections.");
// If the parent connection group is passed in, try to find it.
ConnectionGroup parentConnectionGroup;
if (parentID == null)
parentConnectionGroup = userContext.getRootConnectionGroup();
// Retrieve parent group
String parentID = connection.getParentIdentifier();
ConnectionGroup parentConnectionGroup = retrieveConnectionGroup(userContext, parentID);
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();
// Create the connection
// Add the new connection
Directory<String, Connection> connectionDirectory = parentConnectionGroup.getConnectionDirectory();
connectionDirectory.add(new APIConnectionWrapper(connection));
// Return the new connection identifier
return connection.getIdentifier();
}
/**
* Updates a connection.
* Updates an existing connection. If the parent identifier of the
* connection is changed, the connection will also be moved to the new
* parent group.
*
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param connectionID The ID of the Connection to move.
* @param connection The connection to update.
* @throws GuacamoleException If a problem is encountered while updating the connection.
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param connectionID
* The identifier of the connection to update.
*
* @param connection
* The connection data to update the specified connection with.
*
* @throws GuacamoleException
* If an error occurs while updating the connection.
*/
@POST
@PUT
@Path("/{connectionID}")
@AuthProviderRESTExposure
public void updateConnection(@QueryParam("token") String authToken,
@@ -241,62 +331,30 @@ public class ConnectionRESTService {
UserContext userContext = authenticationService.getUserContext(authToken);
// Validate that connection data was provided
if (connection == null)
throw new GuacamoleClientException("A connection is required for this request.");
throw new GuacamoleClientException("Connection JSON must be submitted when updating connections.");
// Get the connection directory
ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
Directory<String, Connection> connectionDirectory =
rootGroup.getConnectionDirectory();
Connection connectionFromAuthProvider = connectionDirectory.get(connectionID);
// Make sure the connection is there before trying to update
if (connectionFromAuthProvider == null)
throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided ID.");
Connection existingConnection = connectionDirectory.get(connectionID);
if (existingConnection == null)
throw new GuacamoleResourceNotFoundException("No such connection: \"" + connectionID + "\"");
// Copy the information from this connection over to an object from the Auth Provider
APIConnectionWrapper wrappedConnection = new APIConnectionWrapper(connection);
connectionFromAuthProvider.setConfiguration(wrappedConnection.getConfiguration());
connectionFromAuthProvider.setName(wrappedConnection.getName());
// Retrieve connection configuration
GuacamoleConfiguration config = new GuacamoleConfiguration();
config.setProtocol(connection.getProtocol());
config.setParameters(connection.getParameters());
// Update the connection
connectionDirectory.update(connectionFromAuthProvider);
existingConnection.setConfiguration(config);
existingConnection.setName(connection.getName());
connectionDirectory.update(existingConnection);
}
/**
* Moves an individual connection to a different connection group.
*
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param connectionID The ID of the Connection to move.
* @param parentID The ID of the ConnectionGroup the connection is to be moved to.
* @throws GuacamoleException If a problem is encountered while moving the connection.
*/
@PUT
@Path("/{connectionID}")
@AuthProviderRESTExposure
public void moveConnection(@QueryParam("token") String authToken,
@PathParam("connectionID") String connectionID, @QueryParam("parentID") String parentID)
throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
// Get the connection directory
ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
Directory<String, Connection> connectionDirectory =
rootGroup.getConnectionDirectory();
// Find the new parent connection group
Directory<String, ConnectionGroup> connectionGroupDirectory = rootGroup.getConnectionGroupDirectory();
ConnectionGroup parentConnectionGroup = connectionGroupDirectory.get(parentID);
if (parentConnectionGroup == null)
throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID.");
// Move the connection
connectionDirectory.move(connectionID, parentConnectionGroup.getConnectionDirectory());
}
}

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

@@ -22,10 +22,11 @@
package org.glyptodon.guacamole.net.basic.rest.connectiongroup;
import java.util.Collection;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.glyptodon.guacamole.net.auth.ConnectionGroup;
import org.glyptodon.guacamole.net.auth.ConnectionGroup.Type;
import org.glyptodon.guacamole.net.basic.rest.APIConstants;
import org.glyptodon.guacamole.net.basic.rest.connection.APIConnection;
/**
* A simple connection group to expose through the REST endpoints.
@@ -35,6 +36,11 @@ import org.glyptodon.guacamole.net.basic.rest.APIConstants;
@JsonIgnoreProperties(ignoreUnknown = true)
public class APIConnectionGroup {
/**
* The identifier of the root connection group.
*/
public static final String ROOT_IDENTIFIER = "ROOT";
/**
* The name of this connection group.
*/
@@ -54,6 +60,18 @@ public class APIConnectionGroup {
* The type of this connection group.
*/
private Type type;
/**
* All child connection groups. If children are not being queried, this may
* be omitted.
*/
private Collection<APIConnectionGroup> childConnectionGroups;
/**
* All child connections. If children are not being queried, this may be
* omitted.
*/
private Collection<APIConnection> childConnections;
/**
* Create an empty APIConnectionGroup.
@@ -73,7 +91,7 @@ public class APIConnectionGroup {
// Use the explicit ROOT group ID
if (this.parentIdentifier == null)
this.parentIdentifier = APIConstants.ROOT_CONNECTION_GROUP_IDENTIFIER;
this.parentIdentifier = ROOT_IDENTIFIER;
this.name = connectionGroup.getName();
this.type = connectionGroup.getType();
@@ -144,4 +162,52 @@ public class APIConnectionGroup {
this.type = type;
}
/**
* Returns a collection of all child connection groups, or null if children
* have not been queried.
*
* @return
* A collection of all child connection groups, or null if children
* have not been queried.
*/
public Collection<APIConnectionGroup> getChildConnectionGroups() {
return childConnectionGroups;
}
/**
* Sets the collection of all child connection groups to the given
* collection, which may be null if children have not been queried.
*
* @param childConnectionGroups
* The collection containing all child connection groups of this
* connection group, or null if children have not been queried.
*/
public void setChildConnectionGroups(Collection<APIConnectionGroup> childConnectionGroups) {
this.childConnectionGroups = childConnectionGroups;
}
/**
* Returns a collection of all child connections, or null if children have
* not been queried.
*
* @return
* A collection of all child connections, or null if children have not
* been queried.
*/
public Collection<APIConnection> getChildConnections() {
return childConnections;
}
/**
* Sets the collection of all child connections to the given collection,
* which may be null if children have not been queried.
*
* @param childConnections
* The collection containing all child connections of this connection
* group, or null if children have not been queried.
*/
public void setChildConnections(Collection<APIConnection> childConnections) {
this.childConnections = childConnections;
}
}

View File

@@ -23,7 +23,8 @@
package org.glyptodon.guacamole.net.basic.rest.connectiongroup;
import com.google.inject.Inject;
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
@@ -34,15 +35,19 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response.Status;
import org.glyptodon.guacamole.GuacamoleClientException;
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.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.HTTPException;
import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
import org.glyptodon.guacamole.net.basic.rest.connection.APIConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -51,7 +56,7 @@ import org.slf4j.LoggerFactory;
*
* @author James Muehlner
*/
@Path("/connectionGroup")
@Path("/connectionGroups")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ConnectionGroupRESTService {
@@ -68,63 +73,124 @@ public class ConnectionGroupRESTService {
private AuthenticationService authenticationService;
/**
* A service for managing the REST endpoint APIConnection objects.
*/
@Inject
private ConnectionGroupService connectionGroupService;
/**
* The ID that will be guaranteed to refer to the root connection group.
*/
private static final String ROOT_CONNECTION_GROUP_ID = "ROOT";
/**
* Gets a list of connection groups with the given ConnectionGroup parentID.
* If no parentID is provided, returns the connection groups from the root group.
* Retrieves the given connection group from the user context, including
* all descendant connections and groups if requested.
*
* @param userContext
* The user context from which to retrieve the connection group.
*
* @param identifier
* The unique identifier of the connection group to retrieve.
*
* @param includeDescendants
* Whether the descendant connections and groups of the given
* connection group should also be retrieved.
*
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param parentID The ID of the ConnectionGroup the connection groups
* belong to. If null, the root connection group will be used.
* @return The connection list.
* @throws GuacamoleException If a problem is encountered while listing connection groups.
* @param permission
* The permission the current user must have for a connection or
* connection group to be returned in the results, if any. If null
* is specified, no filtering by permission will be performed.
*
* @return
* The requested connection group, or null if no such connection group
* exists.
*
* @throws GuacamoleException
* If an error occurs while retrieving the requested connection group
* or any of its descendants.
*/
@GET
@AuthProviderRESTExposure
public List<APIConnectionGroup> getConnectionGroups(@QueryParam("token") String authToken, @QueryParam("parentID") String parentID)
private APIConnectionGroup retrieveConnectionGroup(UserContext userContext,
String identifier, boolean includeDescendants, ObjectPermission.Type permission)
throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
User self = userContext.self();
ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
// If the parent connection group is passed in, try to find it.
ConnectionGroup parentConnectionGroup;
if (parentID == null)
parentConnectionGroup = userContext.getRootConnectionGroup();
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 {
ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
Directory<String, ConnectionGroup> connectionGroupDirectory = rootGroup.getConnectionGroupDirectory();
parentConnectionGroup = connectionGroupDirectory.get(parentID);
Directory<String, ConnectionGroup> connectionGroupDirectory =
rootGroup.getConnectionGroupDirectory();
// Get the connection group from root directory
connectionGroup = connectionGroupDirectory.get(identifier);
if (connectionGroup == null)
return null;
}
if (parentConnectionGroup == null)
throw new HTTPException(Status.NOT_FOUND, "No connection group found with the provided parentID.");
// Wrap queried connection group
APIConnectionGroup apiConnectionGroup = new APIConnectionGroup(connectionGroup);
Directory<String, ConnectionGroup> connectionGroupDirectory =
parentConnectionGroup.getConnectionGroupDirectory();
// Recursively query all descendants if necessary
if (includeDescendants) {
// Query all child connections
Collection<APIConnection> apiConnections = new ArrayList<APIConnection>();
Directory<String, Connection> connectionDirectory = connectionGroup.getConnectionDirectory();
for (String childIdentifier : connectionDirectory.getIdentifiers()) {
// Pull current connection - silently ignore if connection was removed prior to read
Connection childConnection = connectionDirectory.get(childIdentifier);
if (childConnection == null)
continue;
// Filter based on permission, if requested
if (permission == null || self.hasPermission(new ConnectionPermission(permission, childIdentifier)))
apiConnections.add(new APIConnection(childConnection));
}
// Associate child connections with current connection group
apiConnectionGroup.setChildConnections(apiConnections);
// Query all child connection groups
Collection<APIConnectionGroup> apiConnectionGroups = new ArrayList<APIConnectionGroup>();
Directory<String, ConnectionGroup> groupDirectory = connectionGroup.getConnectionGroupDirectory();
for (String childIdentifier : groupDirectory.getIdentifiers()) {
// Pull current connection group - silently ignore if connection group was removed prior to read
APIConnectionGroup childConnectionGroup = retrieveConnectionGroup(userContext, childIdentifier, true, permission);
if (childConnectionGroup == null)
continue;
apiConnectionGroups.add(childConnectionGroup);
}
// Associate child groups with current connection group
apiConnectionGroup.setChildConnectionGroups(apiConnectionGroups);
}
// Return the connectiion group
return apiConnectionGroup;
// Return the converted connection group list
return connectionGroupService.convertConnectionGroupList(connectionGroupDirectory);
}
/**
* Gets an individual connection group.
*
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param connectionGroupID The ID of the ConnectionGroup.
* @return The connection group.
* @throws GuacamoleException If a problem is encountered while retrieving the connection group.
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param connectionGroupID
* The ID of the connection group to retrieve.
*
* @return
* The connection group, without any descendants.
*
* @throws GuacamoleException
* If a problem is encountered while retrieving the connection group.
*/
@GET
@Path("/{connectionGroupID}")
@@ -133,34 +199,70 @@ public class ConnectionGroupRESTService {
@PathParam("connectionGroupID") String connectionGroupID) throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
// Get the connection group directory
ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
// Return the root group if it was asked for
if (connectionGroupID != null && connectionGroupID.equals(ROOT_CONNECTION_GROUP_ID))
return new APIConnectionGroup(rootGroup);
Directory<String, ConnectionGroup> connectionGroupDirectory =
rootGroup.getConnectionGroupDirectory();
// Get the connection group
ConnectionGroup connectionGroup = connectionGroupDirectory.get(connectionGroupID);
// Retrieve requested connection group only
APIConnectionGroup connectionGroup = retrieveConnectionGroup(userContext, connectionGroupID, false, null);
if (connectionGroup == null)
throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided ID.");
throw new GuacamoleResourceNotFoundException("No such connection group: \"" + connectionGroupID + "\"");
// Return the connectiion group
return new APIConnectionGroup(connectionGroup);
return connectionGroup;
}
/**
* Gets an individual connection group and all children.
*
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param connectionGroupID
* The ID of the connection group to retrieve.
*
* @param permission
* If specified, limit the returned list to only those connections for
* which the current user has the given permission. Otherwise, all
* visible connections are returned. Connection groups are unaffected
* by this parameter.
*
* @return
* The requested connection group, including all descendants.
*
* @throws GuacamoleException
* If a problem is encountered while retrieving the connection group or
* its descendants.
*/
@GET
@Path("/{connectionGroupID}/tree")
@AuthProviderRESTExposure
public APIConnectionGroup getConnectionGroupTree(@QueryParam("token") String authToken,
@PathParam("connectionGroupID") String connectionGroupID,
@QueryParam("permission") ObjectPermission.Type permission)
throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
// Retrieve requested connection group and all descendants
APIConnectionGroup connectionGroup = retrieveConnectionGroup(userContext, connectionGroupID, true, permission);
if (connectionGroup == null)
throw new GuacamoleResourceNotFoundException("No such connection group: \"" + connectionGroupID + "\"");
return connectionGroup;
}
/**
* Deletes an individual connection group.
*
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param connectionGroupID The ID of the ConnectionGroup to delete.
* @throws GuacamoleException If a problem is encountered while deleting the connection group.
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param connectionGroupID
* The identifier of the connection group to delete.
*
* @throws GuacamoleException
* If an error occurs while deleting the connection group.
*/
@DELETE
@Path("/{connectionGroupID}")
@@ -174,7 +276,7 @@ public class ConnectionGroupRESTService {
ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
// Use the root group if it was asked for
if (connectionGroupID != null && connectionGroupID.equals(ROOT_CONNECTION_GROUP_ID))
if (connectionGroupID != null && connectionGroupID.equals(APIConnectionGroup.ROOT_IDENTIFIER))
connectionGroupID = rootGroup.getIdentifier();
Directory<String, ConnectionGroup> connectionGroupDirectory =
@@ -182,7 +284,7 @@ public class ConnectionGroupRESTService {
// Make sure the connection is there before trying to delete
if (connectionGroupDirectory.get(connectionGroupID) == null)
throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided ID.");
throw new GuacamoleResourceNotFoundException("No such connection group: \"" + connectionGroupID + "\"");
// Delete the connection group
connectionGroupDirectory.remove(connectionGroupID);
@@ -195,42 +297,49 @@ public class ConnectionGroupRESTService {
* connection group with the parentID. Otherwise, the root connection group
* will be used.
*
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param parentID The ID of the ConnectionGroup the connection groups
* belong to. If null, the root connection group will be used.
* @param connectionGroup The connection group to create.
* @return The identifier of the new connection group.
* @throws GuacamoleException If a problem is encountered while creating the connection group.
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param connectionGroup
* The connection group to create.
*
* @return
* The identifier of the new connection group.
*
* @throws GuacamoleException
* If an error occurs while creating the connection group.
*/
@POST
@AuthProviderRESTExposure
public String createConnectionGroup(@QueryParam("token") String authToken,
@QueryParam("parentID") String parentID, APIConnectionGroup connectionGroup) throws GuacamoleException {
public String createConnectionGroup(@QueryParam("token") String authToken,
APIConnectionGroup connectionGroup) throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
// Validate that connection group data was provided
if (connectionGroup == null)
throw new GuacamoleClientException("A connection group is required for this request.");
throw new GuacamoleClientException("Connection group JSON must be submitted when creating connections groups.");
// If the parent connection group is passed in, try to find it.
String parentID = connectionGroup.getParentIdentifier();
ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
// Use root group if no parent is specified
ConnectionGroup parentConnectionGroup;
if (parentID == null)
parentConnectionGroup = userContext.getRootConnectionGroup();
parentConnectionGroup = rootGroup;
// Pull specified connection group otherwise
else {
ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
Directory<String, ConnectionGroup> connectionGroupDirectory = rootGroup.getConnectionGroupDirectory();
parentConnectionGroup = connectionGroupDirectory.get(parentID);
if (parentConnectionGroup == null)
throw new GuacamoleResourceNotFoundException("No such connection group: \"" + parentID + "\"");
}
if (parentConnectionGroup == null)
throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID.");
Directory<String, ConnectionGroup> connectionGroupDirectory =
parentConnectionGroup.getConnectionGroupDirectory();
// Create the connection group
// Add the new connection group
Directory<String, ConnectionGroup> connectionGroupDirectory = parentConnectionGroup.getConnectionGroupDirectory();
connectionGroupDirectory.add(new APIConnectionGroupWrapper(connectionGroup));
// Return the new connection group identifier
@@ -239,15 +348,24 @@ public class ConnectionGroupRESTService {
}
/**
* Updates a connection group.
* Updates a connection group. If the parent identifier of the
* connection group is changed, the connection group will also be moved to
* the new parent group.
*
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param connectionGroupID The ID of the ConnectionGroup to update.
* @param connectionGroup The connection group to update.
* @throws GuacamoleException If a problem is encountered while updating the connection group.
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param connectionGroupID
* The identifier of the existing connection group to update.
*
* @param connectionGroup
* The data to update the existing connection group with.
*
* @throws GuacamoleException
* If an error occurs while updating the connection group.
*/
@POST
@PUT
@Path("/{connectionGroupID}")
@AuthProviderRESTExposure
public void updateConnectionGroup(@QueryParam("token") String authToken,
@@ -256,14 +374,15 @@ public class ConnectionGroupRESTService {
UserContext userContext = authenticationService.getUserContext(authToken);
// Validate that connection group data was provided
if (connectionGroup == null)
throw new GuacamoleClientException("A connection group is required for this request.");
throw new GuacamoleClientException("Connection group JSON must be submitted when updating connection groups.");
// Get the connection directory
ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
// Use the root group if it was asked for
if (connectionGroupID != null && connectionGroupID.equals(ROOT_CONNECTION_GROUP_ID))
if (connectionGroupID != null && connectionGroupID.equals(APIConnectionGroup.ROOT_IDENTIFIER))
connectionGroupID = rootGroup.getIdentifier();
Directory<String, ConnectionGroup> connectionGroupDirectory =
@@ -271,51 +390,11 @@ public class ConnectionGroupRESTService {
// Make sure the connection group is there before trying to update
if (connectionGroupDirectory.get(connectionGroupID) == null)
throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided ID.");
throw new GuacamoleResourceNotFoundException("No such connection group: \"" + connectionGroupID + "\"");
// Update the connection group
connectionGroupDirectory.update(new APIConnectionGroupWrapper(connectionGroup));
}
/**
* Moves an individual connection group to a different connection group.
*
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param connectionGroupID The ID of the ConnectionGroup to move.
* @param parentID The ID of the ConnectionGroup the connection group is to be moved to.
* @throws GuacamoleException If a problem is encountered while moving the connection group.
*/
@PUT
@Path("/{connectionGroupID}")
@AuthProviderRESTExposure
public void moveConnectionGroup(@QueryParam("token") String authToken,
@PathParam("connectionGroupID") String connectionGroupID,
@QueryParam("parentID") String parentID) throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
// Get the connection group directory
ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
// Use the root group if it was asked for
if (connectionGroupID != null && connectionGroupID.equals(ROOT_CONNECTION_GROUP_ID))
connectionGroupID = rootGroup.getIdentifier();
Directory<String, ConnectionGroup> connectionGroupDirectory =
rootGroup.getConnectionGroupDirectory();
// Find the new parent connection group
Directory<String, ConnectionGroup> newConnectionGroupDirectory = rootGroup.getConnectionGroupDirectory();
ConnectionGroup parentConnectionGroup = newConnectionGroupDirectory.get(parentID);
if (parentConnectionGroup == null)
throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID.");
// Move the connection group
connectionGroupDirectory.move(connectionGroupID, parentConnectionGroup.getConnectionGroupDirectory());
}
}

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.connectiongroup;
import java.util.ArrayList;
import java.util.List;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.ConnectionGroup;
import org.glyptodon.guacamole.net.auth.Directory;
/**
* A service for performing useful manipulations on REST ConnectionGroups.
*
* @author James Muehlner
*/
public class ConnectionGroupService {
/**
* Converts a ConnectionGroup directory to a list of APIConnectionGroup
* objects for exposing with the REST endpoints.
*
* @param connectionGroupDirectory The ConnectionGroup Directory to convert for REST endpoint use.
* @return A List of APIConnectionGroup objects for use with the REST endpoint.
* @throws GuacamoleException If an error occurs while converting the
* connection group directory.
*/
public List<APIConnectionGroup> convertConnectionGroupList(
Directory<String, ConnectionGroup> connectionGroupDirectory) throws GuacamoleException {
List<APIConnectionGroup> restConnectionGroups = new ArrayList<APIConnectionGroup>();
for (String connectionGroupID : connectionGroupDirectory.getIdentifiers())
restConnectionGroups.add(new APIConnectionGroup(connectionGroupDirectory.get(connectionGroupID)));
return restConnectionGroups;
}
}

View File

@@ -1,228 +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.permission;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.glyptodon.guacamole.net.auth.permission.ConnectionGroupPermission;
import org.glyptodon.guacamole.net.auth.permission.ConnectionPermission;
import org.glyptodon.guacamole.net.auth.permission.ObjectPermission;
import org.glyptodon.guacamole.net.auth.permission.Permission;
import org.glyptodon.guacamole.net.auth.permission.SystemPermission;
import org.glyptodon.guacamole.net.auth.permission.UserPermission;
/**
* A simple user permission to expose through the REST endpoints.
*
* @author James Muehlner
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
public class APIPermission {
/**
* Create an empty APIPermission.
*/
public APIPermission() {}
/**
* The type of object that this permission refers to.
*/
private ObjectType objectType;
/**
* The type of object that a permission can refer to.
*/
public enum ObjectType {
/**
* A normal connection.
*/
CONNECTION,
/**
* A connection group.
*/
CONNECTION_GROUP,
/**
* A Guacamole user.
*/
USER,
/**
* The Guacamole system itself.
*/
SYSTEM
}
/**
* The identifier of the object that this permission refers to.
*/
private String objectIdentifier;
/**
* The object permission type for this APIPermission, if relevant. This is
* only used if this.objectType is CONNECTION, CONNECTION_GROUP, or USER.
*/
private ObjectPermission.Type objectPermissionType;
/**
* The system permission type for this APIPermission, if relevant. This is
* only used if this.objectType is SYSTEM.
*/
private SystemPermission.Type systemPermissionType;
/**
* Create an APIConnection from a Connection record.
*
* @param permission The permission to create this APIPermission from.
*/
public APIPermission(Permission permission) {
// Connection permission
if (permission instanceof ConnectionPermission) {
this.objectType = ObjectType.CONNECTION;
this.objectPermissionType = ((ConnectionPermission) permission).getType();
this.objectIdentifier = ((ConnectionPermission) permission).getObjectIdentifier();
}
// Connection group permission
else if (permission instanceof ConnectionGroupPermission) {
this.objectType = ObjectType.CONNECTION_GROUP;
this.objectPermissionType = ((ConnectionGroupPermission) permission).getType();
this.objectIdentifier = ((ConnectionGroupPermission) permission).getObjectIdentifier();
}
// User permission
else if (permission instanceof UserPermission) {
this.objectType = ObjectType.USER;
this.objectPermissionType = ((UserPermission) permission).getType();
this.objectIdentifier = ((UserPermission) permission).getObjectIdentifier();
}
// System permission
else if (permission instanceof SystemPermission) {
this.objectType = ObjectType.SYSTEM;
this.systemPermissionType = ((SystemPermission) permission).getType();
}
}
/**
* Returns the type of object that this permission refers to.
*
* @return The type of object that this permission refers to.
*/
public ObjectType getObjectType() {
return objectType;
}
/**
* Set the type of object that this permission refers to.
* @param objectType The type of object that this permission refers to.
*/
public void setObjectType(ObjectType objectType) {
this.objectType = objectType;
}
/**
* Returns a string representation of the permission type.
*
* @return A string representation of the permission type.
*/
public String getPermissionType() {
switch(this.objectType) {
case CONNECTION:
case CONNECTION_GROUP:
case USER:
return this.objectPermissionType.toString();
case SYSTEM:
return this.systemPermissionType.toString();
default:
return null;
}
}
/**
* Set the permission type from a string representation of that type.
* Since it's not clear at this point whether this is an object permission or
* system permission, try to set both of them.
*
* @param permissionType The string representation of the permission type.
*/
public void setPermissionType(String permissionType) {
try {
this.objectPermissionType = ObjectPermission.Type.valueOf(permissionType);
} catch(IllegalArgumentException e) {}
try {
this.systemPermissionType = SystemPermission.Type.valueOf(permissionType);
} catch(IllegalArgumentException e) {}
}
/**
* Returns the identifier of the object that this permission refers to.
*
* @return The identifier of the object that this permission refers to.
*/
public String getObjectIdentifier() {
return objectIdentifier;
}
/**
* Set the identifier of the object that this permission refers to.
*
* @param objectIdentifier The identifier of the object that this permission refers to.
*/
public void setObjectIdentifier(String objectIdentifier) {
this.objectIdentifier = objectIdentifier;
}
/**
* Returns an org.glyptodon.guacamole.net.auth.permission.Permission
* representation of this APIPermission.
*
* @return An org.glyptodon.guacamole.net.auth.permission.Permission
* representation of this APIPermission.
*/
public Permission toPermission() {
switch(this.objectType) {
case CONNECTION:
return new ConnectionPermission
(this.objectPermissionType, this.objectIdentifier);
case CONNECTION_GROUP:
return new ConnectionGroupPermission
(this.objectPermissionType, this.objectIdentifier);
case USER:
return new UserPermission
(this.objectPermissionType, this.objectIdentifier);
case SYSTEM:
return new SystemPermission(this.systemPermissionType);
default:
return null;
}
}
}

View File

@@ -0,0 +1,293 @@
/*
* 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.permission;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.GuacamoleServerException;
import org.glyptodon.guacamole.net.auth.permission.ConnectionGroupPermission;
import org.glyptodon.guacamole.net.auth.permission.ConnectionPermission;
import org.glyptodon.guacamole.net.auth.permission.ObjectPermission;
import org.glyptodon.guacamole.net.auth.permission.Permission;
import org.glyptodon.guacamole.net.auth.permission.SystemPermission;
import org.glyptodon.guacamole.net.auth.permission.UserPermission;
/**
* The set of permissions which are granted to a specific user, organized by
* object type and, if applicable, identifier. This object can be constructed
* with arbitrary permissions present, or manipulated after creation through
* the manipulation or replacement of its collections of permissions, but is
* otherwise not intended for internal use as a data structure for permissions.
* Its primary purpose is as a hierarchical format for exchanging granted
* permissions with REST clients.
*/
public class APIPermissionSet {
/**
* Map of connection ID to the set of granted permissions.
*/
private Map<String, EnumSet<ObjectPermission.Type>> connectionPermissions = new HashMap<String, EnumSet<ObjectPermission.Type>>();
/**
* Map of connection group ID to the set of granted permissions.
*/
private Map<String, EnumSet<ObjectPermission.Type>> connectionGroupPermissions = new HashMap<String, EnumSet<ObjectPermission.Type>>();
/**
* Map of user ID to the set of granted permissions.
*/
private Map<String, EnumSet<ObjectPermission.Type>> userPermissions = new HashMap<String, EnumSet<ObjectPermission.Type>>();
/**
* Set of all granted system-level permissions.
*/
private EnumSet<SystemPermission.Type> systemPermissions = EnumSet.noneOf(SystemPermission.Type.class);
/**
* Adds the given object permission to the given map of object identifier
* to permission set.
*
* @param permissions
* The map to add the given permission to.
*
* @param permission
* The permission to add.
*/
private void addPermission(Map<String, EnumSet<ObjectPermission.Type>> permissions, ObjectPermission<String> permission) {
// Pull set of permissions for given object
String id = permission.getObjectIdentifier();
EnumSet<ObjectPermission.Type> types = permissions.get(id);
// If set does not yet exist, create it
if (types == null) {
types = EnumSet.of(permission.getType());
permissions.put(id, types);
}
// Otherwise, add the specified permission
else
types.add(permission.getType());
}
/**
* Adds the given system-level permission to the given set of granted
* system permissions.
*
* @param permissions
* The set of system permissions to add the given permission to.
*
* @param permission
* The permission to add.
*/
private void addPermission(EnumSet<SystemPermission.Type> permissions, SystemPermission permission) {
permissions.add(permission.getType());
}
/**
* Adds the given permission to the appropriate type-specific set or map of
* permissions based on the permission class. Only connection, connection
* group, user, and system permissions are supported. Unsupported
* permission types will result in a GuacamoleException being thrown.
*
* @param permission The permission to add.
* @throws GuacamoleException If the permission is of an unsupported type.
*/
private void addPermission(Permission<?> permission) throws GuacamoleException {
// Connection permissions
if (permission instanceof ConnectionPermission)
addPermission(connectionPermissions, (ConnectionPermission) permission);
// Connection group permissions
else if (permission instanceof ConnectionGroupPermission)
addPermission(connectionGroupPermissions, (ConnectionGroupPermission) permission);
// User permissions
else if (permission instanceof UserPermission)
addPermission(userPermissions, (UserPermission) permission);
// System permissions
else if (permission instanceof SystemPermission)
addPermission(systemPermissions, (SystemPermission) permission);
// Unknown / unsupported permission type
else
throw new GuacamoleServerException("Serialization of permission type \"" + permission.getClass() + "\" not implemented.");
}
/**
* Creates a new permission set which contains no granted permissions. Any
* permissions must be added by manipulating or replacing the applicable
* permission collection.
*/
public APIPermissionSet() {
}
/**
* Creates a new permission set having the given permissions.
*
* @param permissions
* The permissions to initially store within the permission set.
*
* @throws GuacamoleException
* If any of the given permissions are of an unsupported type.
*/
public APIPermissionSet(Iterable<Permission> permissions) throws GuacamoleException {
// Add all provided permissions
for (Permission permission : permissions)
addPermission(permission);
}
/**
* Creates a new permission set having the given permissions.
*
* @param permissions
* The permissions to initially store within the permission set.
*
* @throws GuacamoleException
* If any of the given permissions are of an unsupported type.
*/
public APIPermissionSet(Permission... permissions) throws GuacamoleException {
// Add all provided permissions
for (Permission permission : permissions)
addPermission(permission);
}
/**
* Returns a map of connection IDs to the set of permissions granted for
* that connection. If no permissions are granted to a particular
* connection, its ID will not be present as a key in the map. This map is
* mutable, and changes to this map will affect the permission set
* directly.
*
* @return
* A map of connection IDs to the set of permissions granted for that
* connection.
*/
public Map<String, EnumSet<ObjectPermission.Type>> getConnectionPermissions() {
return connectionPermissions;
}
/**
* Returns a map of connection group IDs to the set of permissions granted
* for that connection group. If no permissions are granted to a particular
* connection group, its ID will not be present as a key in the map. This
* map is mutable, and changes to this map will affect the permission set
* directly.
*
* @return
* A map of connection group IDs to the set of permissions granted for
* that connection group.
*/
public Map<String, EnumSet<ObjectPermission.Type>> getConnectionGroupPermissions() {
return connectionGroupPermissions;
}
/**
* Returns a map of user IDs to the set of permissions granted for that
* user. If no permissions are granted to a particular user, its ID will
* not be present as a key in the map. This map is mutable, and changes to
* to this map will affect the permission set directly.
*
* @return
* A map of user IDs to the set of permissions granted for that user.
*/
public Map<String, EnumSet<ObjectPermission.Type>> getUserPermissions() {
return userPermissions;
}
/**
* Returns the set of granted system-level permissions. If no permissions
* are granted at the system level, this will be an empty set. This set is
* mutable, and changes to this set will affect the permission set
* directly.
*
* @return
* The set of granted system-level permissions.
*/
public EnumSet<SystemPermission.Type> getSystemPermissions() {
return systemPermissions;
}
/**
* Replaces the current map of connection permissions with the given map,
* which must map connection ID to its corresponding set of granted
* permissions. If a connection has no permissions, its ID must not be
* present as a key in the map.
*
* @param connectionPermissions
* The map which must replace the currently-stored map of permissions.
*/
public void setConnectionPermissions(Map<String, EnumSet<ObjectPermission.Type>> connectionPermissions) {
this.connectionPermissions = connectionPermissions;
}
/**
* Replaces the current map of connection group permissions with the given
* map, which must map connection group ID to its corresponding set of
* granted permissions. If a connection group has no permissions, its ID
* must not be present as a key in the map.
*
* @param connectionGroupPermissions
* The map which must replace the currently-stored map of permissions.
*/
public void setConnectionGroupPermissions(Map<String, EnumSet<ObjectPermission.Type>> connectionGroupPermissions) {
this.connectionGroupPermissions = connectionGroupPermissions;
}
/**
* Replaces the current map of user permissions with the given map, which
* must map user ID to its corresponding set of granted permissions. If a
* user has no permissions, its ID must not be present as a key in the map.
*
* @param userPermissions
* The map which must replace the currently-stored map of permissions.
*/
public void setUserPermissions(Map<String, EnumSet<ObjectPermission.Type>> userPermissions) {
this.userPermissions = userPermissions;
}
/**
* Replaces the current set of system-level permissions with the given set.
* If no system-level permissions are granted, the empty set must be
* specified.
*
* @param systemPermissions
* The set which must replace the currently-stored set of permissions.
*/
public void setSystemPermissions(EnumSet<SystemPermission.Type> systemPermissions) {
this.systemPermissions = systemPermissions;
}
}

View File

@@ -1,217 +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.permission;
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.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response.Status;
import org.glyptodon.guacamole.GuacamoleException;
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.permission.Permission;
import org.glyptodon.guacamole.net.basic.rest.APIPatch;
import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure;
import org.glyptodon.guacamole.net.basic.rest.HTTPException;
import org.glyptodon.guacamole.net.basic.rest.PATCH;
import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A REST Service for handling connection CRUD operations.
*
* @author James Muehlner
*/
@Path("/permission")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class PermissionRESTService {
/**
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(PermissionRESTService.class);
/**
* A service for authenticating users from auth tokens.
*/
@Inject
private AuthenticationService authenticationService;
/**
* A service for managing the REST endpoint APIPermission objects.
*/
@Inject
private PermissionService permissionService;
/**
* Gets a list of permissions for the user with the given userID.
*
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param userID The ID of the user to retrieve permissions for.
* @return The permission list.
* @throws GuacamoleException If a problem is encountered while listing permissions.
*/
@GET
@Path("/{userID}")
@AuthProviderRESTExposure
public List<APIPermission> getPermissions(@QueryParam("token") String authToken, @PathParam("userID") String userID)
throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
// Get the user
User user = userContext.getUserDirectory().get(userID);
if (user == null)
throw new HTTPException(Status.NOT_FOUND, "User not found with the provided userID.");
return permissionService.convertPermissionList(user.getPermissions());
}
/**
* Adds a permissions for a user with the given userID.
*
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param userID The user ID to add the permission for.
* @param permission The permission to add for the user with the given userID.
* @throws GuacamoleException If a problem is encountered while adding the permission.
*/
@POST
@Path("/{userID}")
@AuthProviderRESTExposure
public void addPermission(@QueryParam("token") String authToken,
@PathParam("userID") String userID, APIPermission permission)
throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
// Get the user
User user = userContext.getUserDirectory().get(userID);
if (user == null)
throw new HTTPException(Status.NOT_FOUND, "User not found with the provided userID.");
// Add the new permission
user.addPermission(permission.toPermission());
userContext.getUserDirectory().update(user);
}
/**
* Removes a permissions for a user with the given userID.
*
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param userID The user ID to remove the permission for.
* @param permission The permission to remove for the user with the given userID.
* @throws GuacamoleException If a problem is encountered while removing the permission.
*/
@POST
@Path("/remove/{userID}/")
@AuthProviderRESTExposure
public void removePermission(@QueryParam("token") String authToken,
@PathParam("userID") String userID, APIPermission permission)
throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
// Get the user
User user = userContext.getUserDirectory().get(userID);
if (user == null)
throw new HTTPException(Status.NOT_FOUND, "User not found with the provided userID.");
// Remove the permission
user.removePermission(permission.toPermission());
userContext.getUserDirectory().update(user);
}
/**
* Applies a given list of permission patches.
*
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param patches The permission patches to apply for this request.
* @throws GuacamoleException If a problem is encountered while removing the permission.
*/
@PATCH
@AuthProviderRESTExposure
public void patchPermissions(@QueryParam("token") String authToken,
List<APIPatch<APIPermission>> patches) throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
// Get the user directory
Directory<String, User> userDirectory = userContext.getUserDirectory();
// All users who have had permissions added or removed
Map<String, User> modifiedUsers = new HashMap<String, User>();
for (APIPatch<APIPermission> patch : patches) {
String userID = patch.getPath();
Permission permission = patch.getValue().toPermission();
// See if we've already modified this user in this request
User user = modifiedUsers.get(userID);
if (user == null)
user = userDirectory.get(userID);
if (user == null)
throw new HTTPException(Status.NOT_FOUND, "User not found with userID " + userID + ".");
// Only the add and remove operations are supported for permissions
switch(patch.getOp()) {
case add:
user.addPermission(permission);
modifiedUsers.put(userID, user);
break;
case remove:
user.removePermission(permission);
modifiedUsers.put(userID, user);
break;
}
}
// Save the permission changes for all modified users
for (User user : modifiedUsers.values())
userDirectory.update(user);
}
}

View File

@@ -1,74 +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.permission;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.glyptodon.guacamole.net.auth.permission.Permission;
/**
* A service for performing useful manipulations on REST Permissions.
*
* @author James Muehlner
*/
public class PermissionService {
/**
* Converts a list of Permission to a list of APIPermission objects for
* exposing with the REST endpoints.
*
* @param permissions The Connections to convert for REST endpoint use.
* @return A List of APIPermission objects for use with the REST endpoint.
*/
public List<APIPermission> convertPermissionList(Iterable<? extends Permission> permissions) {
List<APIPermission> restPermissions = new ArrayList<APIPermission>();
for(Permission permission : permissions)
restPermissions.add(new APIPermission(permission));
return restPermissions;
}
/**
* Converts a list of APIPermission to a set of Permission objects for internal
* Guacamole use.
*
* @param restPermissions The APIPermission objects from the REST endpoints.
* @return a List of Permission objects for internal Guacamole use.
*/
public Set<Permission> convertAPIPermissionList(Iterable<APIPermission> restPermissions) {
Set<Permission> permissions = new HashSet<Permission>();
for(APIPermission restPermission : restPermissions)
permissions.add(restPermission.toPermission());
return permissions;
}
}

View File

@@ -24,14 +24,15 @@ package org.glyptodon.guacamole.net.basic.rest.protocol;
import com.google.inject.Inject;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.basic.ProtocolInfo;
import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure;
import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,9 +41,8 @@ import org.slf4j.LoggerFactory;
*
* @author James Muehlner
*/
@Path("/protocol")
@Path("/protocols")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ProtocolRESTService {
/**
@@ -50,6 +50,12 @@ public class ProtocolRESTService {
*/
private static final Logger logger = LoggerFactory.getLogger(ProtocolRESTService.class);
/**
* A service for authenticating users from auth tokens.
*/
@Inject
private AuthenticationService authenticationService;
/**
* Service for retrieving protocol definitions.
*/
@@ -59,15 +65,27 @@ public class ProtocolRESTService {
/**
* Gets a map of protocols defined in the system - protocol name to protocol.
*
* @return The protocol map.
* @throws GuacamoleException If a problem is encountered while listing protocols.
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @return
* A map of protocol information, where each key is the unique name
* associated with that protocol.
*
* @throws GuacamoleException
* If an error occurs while retrieving the available protocols.
*/
@GET
@AuthProviderRESTExposure
public Map<String, ProtocolInfo> getProtocols() throws GuacamoleException {
public Map<String, ProtocolInfo> getProtocols(@QueryParam("token") String authToken) throws GuacamoleException {
// Verify the given auth token is valid
authenticationService.getUserContext(authToken);
// Get and return a map of all protocols.
return protocolRetrievalservice.getProtocolMap();
}
}

View File

@@ -23,25 +23,40 @@
package org.glyptodon.guacamole.net.basic.rest.user;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.GuacamoleResourceNotFoundException;
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.permission.ConnectionGroupPermission;
import org.glyptodon.guacamole.net.auth.permission.ConnectionPermission;
import org.glyptodon.guacamole.net.auth.permission.ObjectPermission;
import org.glyptodon.guacamole.net.auth.permission.Permission;
import org.glyptodon.guacamole.net.auth.permission.SystemPermission;
import org.glyptodon.guacamole.net.auth.permission.UserPermission;
import org.glyptodon.guacamole.net.basic.rest.APIPatch;
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.PATCH;
import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
import org.glyptodon.guacamole.net.basic.rest.permission.APIPermissionSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -50,7 +65,7 @@ import org.slf4j.LoggerFactory;
*
* @author James Muehlner
*/
@Path("/user")
@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserRESTService {
@@ -59,6 +74,30 @@ public class UserRESTService {
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(UserRESTService.class);
/**
* The prefix of any path within an operation of a JSON patch which
* modifies the permissions of a user regarding a specific connection.
*/
private static final String CONNECTION_PERMISSION_PATCH_PATH_PREFIX = "/connectionPermissions/";
/**
* The prefix of any path within an operation of a JSON patch which
* modifies the permissions of a user regarding a specific connection group.
*/
private static final String CONNECTION_GROUP_PERMISSION_PATCH_PATH_PREFIX = "/connectionGroupPermissions/";
/**
* The prefix of any path within an operation of a JSON patch which
* modifies the permissions of a user regarding another, specific user.
*/
private static final String USER_PERMISSION_PATCH_PATH_PREFIX = "/userPermissions/";
/**
* The path of any operation within a JSON patch which modifies the
* permissions of a user regarding the entire system.
*/
private static final String SYSTEM_PERMISSION_PATCH_PATH = "/systemPermissions";
/**
* A service for authenticating users from auth tokens.
@@ -67,44 +106,73 @@ public class UserRESTService {
private AuthenticationService authenticationService;
/**
* A service for managing the REST endpoint APIPermission objects.
*/
@Inject
private UserService userService;
/**
* Gets a list of users in the system.
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @return The user list.
* @throws GuacamoleException If a problem is encountered while listing users.
* Gets a list of users in the system, filtering the returned list by the
* given permission, if specified.
*
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param permission
* If specified, limit the returned list to only those users for whom
* the current user has the given permission. Otherwise, all visible
* users are returned.
*
* @return
* A list of all visible users. If a permission was specified, this
* list will contain only those users for whom the current user has
* that permission.
*
* @throws GuacamoleException
* If an error is encountered while retrieving users.
*/
@GET
@AuthProviderRESTExposure
public List<APIUser> getUsers(@QueryParam("token") String authToken) throws GuacamoleException {
public List<APIUser> getUsers(@QueryParam("token") String authToken,
@QueryParam("permission") UserPermission.Type permission)
throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
User self = userContext.self();
// Get the directory
Directory<String, User> userDirectory = userContext.getUserDirectory();
// Convert and return the user directory listing
return userService.convertUserList(userDirectory);
List<APIUser> users = new ArrayList<APIUser>();
// Add all users matching the given permission filter
for (String username : userDirectory.getIdentifiers()) {
if (permission == null || self.hasPermission(new UserPermission(permission, username)))
users.add(new APIUser(userDirectory.get(username)));
}
// Return the user directory listing
return users;
}
/**
* Gets an individual user.
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param userID The ID of the user to retrieve.
* @return user The user.
* @throws GuacamoleException If a problem is encountered while retrieving the user.
* Retrieves an individual user.
*
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param username
* The username of the user to retrieve.
*
* @return user
* The user having the given username.
*
* @throws GuacamoleException
* If an error occurs while retrieving the user.
*/
@GET
@Path("/{userID}")
@Path("/{username}")
@AuthProviderRESTExposure
public APIUser getUser(@QueryParam("token") String authToken, @PathParam("userID") String userID)
public APIUser getUser(@QueryParam("token") String authToken, @PathParam("username") String username)
throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
@@ -113,9 +181,9 @@ public class UserRESTService {
Directory<String, User> userDirectory = userContext.getUserDirectory();
// Get the user
User user = userDirectory.get(userID);
User user = userDirectory.get(username);
if (user == null)
throw new HTTPException(Response.Status.NOT_FOUND, "User not found with the provided userID.");
throw new GuacamoleResourceNotFoundException("No such user: \"" + username + "\"");
// Return the user
return new APIUser(user);
@@ -154,16 +222,25 @@ public class UserRESTService {
/**
* Updates an individual existing user.
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param userID The unique identifier of the user to update.
* @param user The updated user.
* @throws GuacamoleException If a problem is encountered while updating the user.
*
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param username
* The username of the user to update.
*
* @param user
* The data to update the user with.
*
* @throws GuacamoleException
* If an error occurs while updating the user.
*/
@POST
@Path("/{userID}")
@PUT
@Path("/{username}")
@AuthProviderRESTExposure
public void updateUser(@QueryParam("token") String authToken, @PathParam("userID") String userID, APIUser user)
public void updateUser(@QueryParam("token") String authToken,
@PathParam("username") String username, APIUser user)
throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
@@ -171,37 +248,43 @@ public class UserRESTService {
// Get the directory
Directory<String, User> userDirectory = userContext.getUserDirectory();
if (!user.getUsername().equals(userID))
throw new HTTPException(Response.Status.BAD_REQUEST, "Username does not match provided userID.");
// Validate data and path are sane
if (!user.getUsername().equals(username))
throw new HTTPException(Response.Status.BAD_REQUEST,
"Username in path does not match username provided JSON data.");
// Get the user
User existingUser = userDirectory.get(userID);
User existingUser = userDirectory.get(username);
if (existingUser == null)
throw new HTTPException(Response.Status.NOT_FOUND, "User not found with the provided userID.");
throw new GuacamoleResourceNotFoundException("No such user: \"" + username + "\"");
// Do not update the user password if no password was provided
if (user.getPassword() != null) {
/*
* Update the user with the permission set from the existing user
* since the user REST endpoints do not expose permissions.
*/
if (user.getPassword() != null)
existingUser.setPassword(user.getPassword());
userDirectory.update(existingUser);
}
// Update the user
userDirectory.update(existingUser);
}
/**
* Deletes an individual existing user.
* @param authToken The authentication token that is used to authenticate
* the user performing the operation.
* @param userID The unique identifier of the user to delete.
* @throws GuacamoleException If a problem is encountered while deleting the user.
*
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param username
* The username of the user to delete.
*
* @throws GuacamoleException
* If an error occurs while deleting the user.
*/
@DELETE
@Path("/{userID}")
@Path("/{username}")
@AuthProviderRESTExposure
public void deleteUser(@QueryParam("token") String authToken, @PathParam("userID") String userID)
public void deleteUser(@QueryParam("token") String authToken,
@PathParam("username") String username)
throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
@@ -210,12 +293,168 @@ public class UserRESTService {
Directory<String, User> userDirectory = userContext.getUserDirectory();
// Get the user
User existingUser = userDirectory.get(userID);
User existingUser = userDirectory.get(username);
if (existingUser == null)
throw new HTTPException(Response.Status.NOT_FOUND, "User not found with the provided userID.");
throw new GuacamoleResourceNotFoundException("No such user: \"" + username + "\"");
// Delete the user
userDirectory.remove(userID);
userDirectory.remove(username);
}
/**
* Gets a list of permissions for the user with the given username.
*
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param username
* The username of the user to retrieve permissions for.
*
* @return
* A list of all permissions granted to the specified user.
*
* @throws GuacamoleException
* If an error occurs while retrieving permissions.
*/
@GET
@Path("/{username}/permissions")
@AuthProviderRESTExposure
public APIPermissionSet getPermissions(@QueryParam("token") String authToken,
@PathParam("username") String username)
throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
// Get the user
User user = userContext.getUserDirectory().get(username);
if (user == null)
throw new GuacamoleResourceNotFoundException("No such user: \"" + username + "\"");
return new APIPermissionSet(user.getPermissions());
}
/**
* Applies a given list of permission patches. Each patch specifies either
* an "add" or a "remove" operation for a permission type, represented by
* a string. Valid permission types depend on the path of each patch
* operation, as the path dictates the permission being modified, such as
* "/connectionPermissions/42" or "/systemPermissions".
*
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param username
* The username of the user to modify the permissions of.
*
* @param patches
* The permission patches to apply for this request.
*
* @throws GuacamoleException
* If a problem is encountered while modifying permissions.
*/
@PATCH
@Path("/{username}/permissions")
@AuthProviderRESTExposure
public void patchPermissions(@QueryParam("token") String authToken,
@PathParam("username") String username,
List<APIPatch<String>> patches) throws GuacamoleException {
UserContext userContext = authenticationService.getUserContext(authToken);
// Get the user directory
Directory<String, User> userDirectory = userContext.getUserDirectory();
// Get the user
User user = userContext.getUserDirectory().get(username);
if (user == null)
throw new GuacamoleResourceNotFoundException("No such user: \"" + username + "\"");
// Apply all patch operations individually
for (APIPatch<String> patch : patches) {
Permission permission;
String path = patch.getPath();
// Create connection permission if path has connection prefix
if (path.startsWith(CONNECTION_PERMISSION_PATCH_PATH_PREFIX)) {
// Get identifier and type from patch operation
String identifier = path.substring(CONNECTION_PERMISSION_PATCH_PATH_PREFIX.length());
ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue());
// Create corresponding permission
permission = new ConnectionPermission(type, identifier);
}
// Create connection group permission if path has connection group prefix
else if (path.startsWith(CONNECTION_GROUP_PERMISSION_PATCH_PATH_PREFIX)) {
// Get identifier and type from patch operation
String identifier = path.substring(CONNECTION_GROUP_PERMISSION_PATCH_PATH_PREFIX.length());
ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue());
// Create corresponding permission
permission = new ConnectionGroupPermission(type, identifier);
}
// Create user permission if path has user prefix
else if (path.startsWith(USER_PERMISSION_PATCH_PATH_PREFIX)) {
// Get identifier and type from patch operation
String identifier = path.substring(USER_PERMISSION_PATCH_PATH_PREFIX.length());
ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue());
// Create corresponding permission
permission = new UserPermission(type, identifier);
}
// Create system permission if path is system path
else if (path.startsWith(SYSTEM_PERMISSION_PATCH_PATH)) {
// Get identifier and type from patch operation
SystemPermission.Type type = SystemPermission.Type.valueOf(patch.getValue());
// Create corresponding permission
permission = new SystemPermission(type);
}
// Otherwise, the path is not supported
else
throw new HTTPException(Status.BAD_REQUEST, "Unsupported patch path: \"" + path + "\"");
// Add or remove permission based on operation
switch (patch.getOp()) {
// Add permission
case add:
user.addPermission(permission);
break;
// Remove permission
case remove:
user.removePermission(permission);
break;
// Unsupported patch operation
default:
throw new HTTPException(Status.BAD_REQUEST,
"Unsupported patch operation: \"" + patch.getOp() + "\"");
}
} // end for each patch operation
// Save the permission changes
userDirectory.update(user);
}

View File

@@ -28,20 +28,33 @@ angular.module('auth').factory('authenticationService', ['$http', '$cookieStore'
var service = {};
/**
* The unique identifier of the local cookie which stores the user's
* current authentication token and user ID.
*
* @type String
*/
var AUTH_COOKIE_ID = "GUAC_AUTH";
/**
* Makes a request to authenticate a user using the token REST API endpoint,
* returning a promise that can be used for processing the results of the call.
* returning a promise that succeeds only if the login operation was
* successful. The resulting authentication data can be retrieved later
* via getCurrentToken() or getCurrentUserID().
*
* @param {String} username The username to log in with.
* @param {String} password The password to log in with.
* @returns {Promise} A promise for the HTTP call.
* @param {String} username
* The username to log in with.
*
* @param {String} password
* The password to log in with.
*
* @returns {Promise}
* A promise which succeeds only if the login operation was successful.
*/
service.login = function login(username, password) {
return $http({
method: 'POST',
url: 'api/token',
url: 'api/tokens',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
@@ -59,14 +72,17 @@ angular.module('auth').factory('authenticationService', ['$http', '$cookieStore'
/**
* Makes a request to logout a user using the login REST API endpoint,
* returning a promise that can be used for processing the results of the call.
* returning a promise succeeds only if the logout operation was
* successful.
*
* @returns {Promise} A promise for the HTTP call.
* @returns {Promise}
* A promise which succeeds only if the logout operation was
* successful.
*/
service.logout = function logout() {
return $http({
method: 'DELETE',
url: 'api/token/' + encodeURIComponent(service.getCurrentToken())
url: 'api/tokens/' + encodeURIComponent(service.getCurrentToken())
});
};

View File

@@ -23,4 +23,4 @@
/**
* The module for code used to connect to a connection or balancing group.
*/
angular.module('client', ['auth', 'history']);
angular.module('client', ['auth', 'history', 'rest']);

View File

@@ -132,10 +132,10 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
remaining: 15
};
// Get DAO for reading connections and groups
var connectionGroupDAO = $injector.get('connectionGroupDAO');
var connectionService = $injector.get('connectionService');
var ClientProperties = $injector.get('ClientProperties');
// Get services for reading connections and groups
var connectionGroupService = $injector.get('connectionGroupService');
var connectionService = $injector.get('connectionService');
var ClientProperties = $injector.get('ClientProperties');
// Client settings and state
$scope.clientProperties = new ClientProperties();
@@ -176,7 +176,7 @@ angular.module('home').controller('clientController', ['$scope', '$routeParams',
// Connection group
case 'g':
connectionGroupDAO.getConnectionGroup($routeParams.id).success(function (group) {
connectionGroupService.getConnectionGroup($routeParams.id).success(function (group) {
$scope.connectionName = $scope.page.title = group.name;
});
break;

View File

@@ -315,15 +315,18 @@ angular.module('client').directive('guacClient', [function guacClient() {
* CONNECT / RECONNECT
*/
// Connect to given ID whenever ID changes
$scope.$watch('id', function(id, previousID) {
// If a client is already attached, ensure it is disconnected
if (client)
client.disconnect();
/**
* Store the thumbnail of the currently connected client within
* the connection history under the given ID. If the client is not
* connected, or if no ID is given, this function has no effect.
*
* @param {String} id
* The ID of the history entry to update.
*/
var updateHistoryEntry = function updateHistoryEntry(id) {
// Update stored thumbnail of previous connection
if (previousID && display && display.getWidth() > 0 && display.getHeight() > 0) {
if (id && display && display.getWidth() > 0 && display.getHeight() > 0) {
// Get screenshot
var canvas = display.flatten();
@@ -343,10 +346,22 @@ angular.module('client').directive('guacClient', [function guacClient() {
0, 0, thumbnail.width, thumbnail.height
);
guacHistory.updateThumbnail(previousID, thumbnail.toDataURL("image/png"));
guacHistory.updateThumbnail(id, thumbnail.toDataURL("image/png"));
}
};
// Connect to given ID whenever ID changes
$scope.$watch('id', function(id, previousID) {
// If a client is already attached, ensure it is disconnected
if (client)
client.disconnect();
// Update stored thumbnail of previous connection
updateHistoryEntry(previousID);
// Only proceed if a new client is attached
if (!id)
return;
@@ -385,6 +400,14 @@ angular.module('client').directive('guacClient', [function guacClient() {
});
// Clean up when client directive is destroyed
$scope.$on('$destroy', function destroyClient() {
// Update stored thumbnail of current connection
updateHistoryEntry($scope.id);
});
/*
* MOUSE EMULATION
*/

View File

@@ -33,10 +33,7 @@
<div id="text-input"><div id="text-input-field"><div id="sent-history"></div><textarea rows="1" id="target"></textarea></div><div id="text-input-buttons"><button class="key" data-keysym="0xFFE3" data-sticky="true">{{'client.ctrl' | translate}}</button><button class="key" data-keysym="0xFFE9" data-sticky="true">{{'client.alt' | translate}}</button><button class="key" data-keysym="0xFF1B">{{'client.esc' | translate}}</button><button class="key" data-keysym="0xFF09">{{'client.tab' | translate}}</button></div></div>
<!-- Dimensional clone of viewport -->
<div id="viewportClone"/>
<!-- Notification area -->
<div id="notificationArea"/>
<div id="viewportClone"></div>
<!-- Menu -->
<div ng-class="{open: menuShown}" id="menu">
@@ -108,8 +105,8 @@
<!-- Images which should be preloaded -->
<div id="preload">
<img src="images/action-icons/guac-close.png"/>
<img src="images/progress.png"/>
<img src="images/action-icons/guac-close.png" alt=""/>
<img src="images/progress.png" alt=""/>
</div>
<ng-include src="app/client/template/clientError.html"/>

View File

@@ -1,148 +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.
*/
/**
* Service for operating on connections via the REST API.
*/
angular.module('connection').factory('connectionService', ['$http', 'authenticationService',
function connectionService($http, authenticationService) {
var service = {};
/**
* Makes a request to the REST API to get a single connection, returning a
* promise that provides the corresponding @link{Connection} if successful.
*
* @param {String} id The ID of the connection.
* @returns {Promise.<Connection>}
* A promise which will resolve with a @link{Connection} upon success.
*
* @example
*
* connectionService.getConnection('myConnection').success(function(connection) {
* // Do something with the connection
* });
*/
service.getConnection = function getConnection(id) {
return $http.get("api/connection/" + id + "?token=" + authenticationService.getCurrentToken());
};
/**
* Makes a request to the REST API to get the list of connections,
* returning a promise that can be used for processing the results of the
* call.
*
* @param {string} parentID The parent ID for the connection.
* If not passed in, it will query a list of the
* connections in the root group.
*
* @returns {Promise.<Connection[]>}
* A promise which will resolve with an array of @link{Connection}
* objects upon success.
*/
service.getConnections = function getConnections(parentID) {
var parentIDParam = "";
if(parentID !== undefined)
parentIDParam = "&parentID=" + parentID;
return $http.get("api/connection?token=" + authenticationService.getCurrentToken() + parentIDParam);
};
/**
* 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.
*
* @param {Connection} connection The connection to update
*
* @returns {Promise}
* A promise for the HTTP call which will succeed if and only if the
* save operation is successful.
*/
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;
// This is a new connection
if(!connectionToSave.identifier) {
return $http.post("api/connection/?token=" + authenticationService.getCurrentToken(), connectionToSave).success(
function setConnectionID(connectionID){
// Set the identifier on the new connection
connection.identifier = connectionID; // FIXME: Functions with side effects = bad
return connectionID; // FIXME: Why? Where does this value go?
});
} else {
return $http.post(
"api/connection/" + connectionToSave.identifier +
"?token=" + authenticationService.getCurrentToken(),
connectionToSave);
}
};
/**
* FIXME: Why is this different from save?
*
* Makes a request to the REST API to move a connection to a different
* group, returning a promise that can be used for processing the results
* of the call.
*
* @param {Connection} connection The connection to move.
*
* @returns {Promise}
* A promise for the HTTP call which will succeed if and only if the
* move operation is successful.
*/
service.moveConnection = function moveConnection(connection) {
return $http.put(
"api/connection/" + connection.identifier +
"?token=" + authenticationService.getCurrentToken() +
"&parentID=" + connection.parentIdentifier,
connection);
};
/**
* Makes a request to the REST API to delete a connection,
* returning a promise that can be used for processing the results of the call.
*
* @param {Connection} connection The connection to delete
*
* @returns {Promise}
* A promise for the HTTP call which will succeed if and only if the
* delete operation is successful.
*/
service.deleteConnection = function deleteConnection(connection) {
return $http['delete'](
"api/connection/" + connection.identifier +
"?token=" + authenticationService.getCurrentToken());
};
return service;
}]);

View File

@@ -1,130 +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.
*/
/**
* The DAO for connection group operations agains the REST API.
*/
angular.module('connectionGroup').factory('connectionGroupDAO', ['$http', 'authenticationService',
function connectionGrouDAO($http, authenticationService) {
/**
* The ID of the root connection group.
*/
var ROOT_CONNECTION_GROUP_ID = "ROOT";
var service = {};
/**
* Makes a request to the REST API to get the list of connection groups,
* returning a promise that can be used for processing the results of the call.
*
* @param {string} parentID The parent ID for the connection group.
* If not passed in, it will query a list of the
* connection groups in the root group.
*
* @returns {promise} A promise for the HTTP call.
*/
service.getConnectionGroups = function getConnectionGroups(parentID) {
var parentIDParam = "";
if(parentID !== undefined)
parentIDParam = "&parentID=" + parentID;
return $http.get("api/connectionGroup?token=" + authenticationService.getCurrentToken() + parentIDParam);
};
/**
* Makes a request to the REST API to get an individual connection group,
* returning a promise that can be used for processing the results of the call.
*
* @param {string} connectionGroupID The ID for the connection group.
* If not passed in, it will query the
* root connection group.
*
* @returns {promise} A promise for the HTTP call.
*/
service.getConnectionGroup = function getConnectionGroup(connectionGroupID) {
// Use the root connection group ID if no ID is passed in
connectionGroupID = connectionGroupID || ROOT_CONNECTION_GROUP_ID;
return $http.get("api/connectionGroup/" + connectionGroupID + "?token=" + authenticationService.getCurrentToken());
};
/**
* Makes a request to the REST API to save a connection group,
* returning a promise that can be used for processing the results of the call.
*
* @param {object} connectionGroup The connection group to update
*
* @returns {promise} A promise for the HTTP call.
*/
service.saveConnectionGroup = function saveConnectionGroup(connectionGroup) {
// This is a new connection group
if(!connectionGroup.identifier) {
return $http.post("api/connectionGroup/?token=" + authenticationService.getCurrentToken(), connectionGroup).success(
function setConnectionGroupID(connectionGroupID){
// Set the identifier on the new connection
connectionGroup.identifier = connectionGroupID;
return connectionGroupID;
});
} else {
return $http.post(
"api/connectionGroup/" + connectionGroup.identifier +
"?token=" + authenticationService.getCurrentToken(),
connectionGroup);
}
};
/**
* Makes a request to the REST API to move a connection group to a different group,
* returning a promise that can be used for processing the results of the call.
*
* @param {object} connectionGroup The connection group to move.
*
* @returns {promise} A promise for the HTTP call.
*/
service.moveConnectionGroup = function moveConnectionGroup(connectionGroup) {
return $http.put(
"api/connectionGroup/" + connectionGroup.identifier +
"?token=" + authenticationService.getCurrentToken() +
"&parentID=" + connectionGroup.parentIdentifier,
connectionGroup);
};
/**
* Makes a request to the REST API to delete a connection group,
* returning a promise that can be used for processing the results of the call.
*
* @param {object} connectionGroup The connection group to delete
*
* @returns {promise} A promise for the HTTP call.
*/
service.deleteConnectionGroup = function deleteConnectionGroup(connectionGroup) {
return $http['delete'](
"api/connectionGroup/" + connectionGroup.identifier +
"?token=" + authenticationService.getCurrentToken());
};
return service;
}]);

View File

@@ -1,231 +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.
*/
/**
* A service for performing useful connection group related functionaltiy.
*/
angular.module('connectionGroup').factory('connectionGroupService', ['$injector', function connectionGroupService($injector) {
var connectionGroupDAO = $injector.get('connectionGroupDAO');
var connectionService = $injector.get('connectionService');
var permissionCheckService = $injector.get('permissionCheckService');
var $q = $injector.get('$q');
var displayObjectPreparationService = $injector.get('displayObjectPreparationService');
var service = {};
// Add all groups from this group to the parent group child list
function addToParent(connectionGroup, parentGroup, context, includeConnections) {
// Include connections by default
if(typeof includeConnections === 'undefined')
includeConnections = true;
parentGroup.children.push(connectionGroup);
// Prepare this group for display
displayObjectPreparationService.prepareConnectionGroup(connectionGroup);
if(includeConnections) {
// Get all connections in the group and add them under this connection group
context.openRequest();
connectionService.getConnections(connectionGroup.identifier).success(function fetchConnections(connections) {
for(var i = 0; i < connections.length; i++) {
connections[i].isConnection = true;
connectionGroup.children.push(connections[i]);
}
context.closeRequest();
});
}
// Get all connection groups in the group and repeat
context.openRequest();
connectionGroupDAO.getConnectionGroups(connectionGroup.identifier).success(function fetchConnectionGroups(connectionGroups) {
for(var i = 0; i < connectionGroups.length; i++) {
addToParent(connectionGroups[i], connectionGroup, context, includeConnections);
}
context.closeRequest();
});
}
/**
* Queries all connections and connection groups under the connection group
* with the provided parent ID, and returns them in a heirarchical structure
* with convinient display properties set on the objects.
*
* @param {array} items The root list of connections and groups. Should be an
* initally empty array that will get filled in as the
* connections and groups are loaded.
*
* @param {string} parentID The parent ID for the connection group.
* If not passed in, it will begin with
* the root connection group.
*
* @param {boolean} includeConnections Whether or not to include connections
* in the structure. Defaults to true.
*
* @param {boolean} includeRoot Whether or not to include the root connection group
* in the structure. Defaults to false.
*
* @return {promise} A promise that will be fulfilled when all connections
* and groups have been loaded.
*/
service.getAllGroupsAndConnections = function getAllGroupsAndConnections(items, parentID, includeConnections, includeRoot) {
// Include connections by default
if(typeof includeConnections === 'undefined')
includeConnections = true;
var context = {
// The number of requets to the server currently open
openRequests : 0,
// Create the promise
finishedFetching : $q.defer(),
// Notify the caller that the promise has been completed
complete : function complete() {
this.finishedFetching.resolve(items);
},
/**
* Indicate that a request has been started.
*/
openRequest : function openRequest() {
this.openRequests++;
},
/**
* Indicate that a request has been completed. If this was the last
* open request, fulfill the promise.
*/
closeRequest : function closeRequest() {
if(--this.openRequests === 0)
this.complete();
}
};
// Include the root only if it was asked for
if(includeRoot) {
context.openRequest();
connectionGroupDAO.getConnectionGroup(parentID).success(function setRootGroup (rootGroup) {
items.push(rootGroup);
rootGroup.children = [];
getChildrenOfRootGroup(rootGroup.children);
context.closeRequest();
});
} else {
getChildrenOfRootGroup(items);
}
// Get the children of the root group
function getChildrenOfRootGroup(children) {
context.openRequest();
connectionGroupDAO.getConnectionGroups(parentID).success(function fetchRootConnectionGroups(connectionGroups) {
for(var i = 0; i < connectionGroups.length; i++) {
addToParent(connectionGroups[i], {children: children}, context, includeConnections);
}
if(includeConnections) {
// Get all connections in the root group and add them under this connection group
context.openRequest();
connectionService.getConnections().success(function fetchRootConnections(connections) {
for(var i = 0; i < connections.length; i++) {
// Prepare this connection for display
displayObjectPreparationService.prepareConnection(connections[i]);
children.push(connections[i]);
}
context.closeRequest();
});
}
context.closeRequest();
});
}
// Return the promise
return context.finishedFetching.promise;
};
/**
* Filters the list of connections and groups using the provided permissions.
*
* @param {array} items The heirarchical list of groups and connections.
*
* @param {object} permissionList The list of permissions to use
* when filtering.
*
* @param {object} permissionCriteria A map of object type to permission type(s)
* required for that object type.
*
* @return {array} The filtered list.
*/
service.filterConnectionsAndGroupByPermission = function filterConnectionsAndGroupByPermission(items, permissionList, permissionCriteria) {
var requiredConnectionPermission = permissionCriteria.CONNECTION;
var requiredConnectionGroupPermission = permissionCriteria.CONNECTION_GROUP;
for(var i = 0; i < items.length; i++) {
var item = items[i];
if(item.isConnection && requiredConnectionPermission) {
/*
* If item is a connection and a permission is required for this
* item, check now to see if the permission exists. If not,
* remove the item.
*/
if(!permissionCheckService.checkPermission(permissionList,
"CONNECTION", item.identifier, requiredConnectionPermission)) {
items.splice(i, 1);
continue;
}
}
else {
/*
* If item is a group and a permission is required for this
* item, check now to see if the permission exists. If not,
* remove the item.
*/
if(requiredConnectionGroupPermission) {
if(!permissionCheckService.checkPermission(permissionList,
"CONNECTION_GROUP", item.identifier, requiredConnectionGroupPermission)) {
items.splice(i, 1);
continue;
}
}
// Filter the children of this connection group as well
if(item.children && item.children.length)
service.filterConnectionsAndGroupByPermission(items.children);
}
}
return items;
};
return service;
}]);

View File

@@ -0,0 +1,72 @@
/*
* 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.
*/
/**
* A directive which displays the contents of a connection group.
*/
angular.module('groupList').directive('guacGroupList', [function guacGroupList() {
return {
restrict: 'E',
replace: true,
scope: {
/**
* The connection group to display.
*
* @type ConnectionGroup|Object
*/
connectionGroup : '='
},
templateUrl: 'app/groupList/templates/guacGroupList.html',
controller: ['$scope', '$injector', '$interval', function guacGroupListController($scope, $injector, $interval) {
// Get required types
var GroupListItem = $injector.get('GroupListItem');
// Set contents whenever the connection group is assigned or changed
$scope.$watch("connectionGroup", function setContents(connectionGroup) {
if (connectionGroup)
$scope.rootItem = GroupListItem.fromConnectionGroup(connectionGroup);
else
$scope.rootItem = null;
});
/**
* Toggle the open/closed status of a group list item.
*
* @param {GroupListItem} groupListItem
* The list item to expand, which should represent a
* connection group.
*/
$scope.toggleExpanded = function toggleExpanded(groupListItem) {
groupListItem.expanded = !groupListItem.expanded;
};
}]
};
}]);

View File

@@ -21,6 +21,7 @@
*/
/**
* The module for code relating to connection groups.
* Module for displaying the contents of a connection group, allowing the user
* to select individual connections or groups.
*/
angular.module('connectionGroup', ['auth', 'util', 'connection']);
angular.module('groupList', ['rest']);

View File

@@ -19,8 +19,3 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* A module for code relating to users.
*/
angular.module('user', ['auth']);

View File

@@ -0,0 +1,71 @@
<div class="group-list">
<!--
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.
-->
<script type="text/ng-template" id="nestedGroup.html">
<!-- Connection -->
<div class="connection" ng-show="item.isConnection">
<a ng-href="#/client/c/{{item.identifier}}">
<div class="caption">
<!-- Connection icon -->
<div class="protocol">
<div class="icon type" ng-class="item.protocol"></div>
</div>
<!-- Connection name -->
<span class="name">{{item.name}}</span>
</div>
</a>
</div>
<!-- Connection group -->
<div class="group" ng-show="item.isConnectionGroup">
<div class="caption">
<!-- Connection group icon -->
<div class="icon group type" ng-click="toggleExpanded(item)"
ng-class="{expanded: item.isExpanded, empty: !item.children.length, balancer: item.isBalancing}"></div>
<!-- Connection group name -->
<span class="name">
<a ng-show="item.isBalancing" ng-href="#/client/g/{{item.identifier}}">{{item.name}}</a>
<span ng-show="!item.isBalancing">{{item.name}}</span>
</span>
</div>
<!-- Children of this group -->
<div class="children" ng-show="item.isExpanded">
<div class="list-item" ng-repeat="item in item.children | orderBy : 'name'" ng-include="'nestedGroup.html'">
</div>
</div>
</script>
<div class="list-item" ng-repeat="item in rootItem.children | orderBy : 'name'" ng-include="'nestedGroup.html'"></div>
</div>

View File

@@ -0,0 +1,181 @@
/*
* 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.
*/
/**
* Provides the GroupListItem class definition.
*/
angular.module('groupList').factory('GroupListItem', ['ConnectionGroup', function defineGroupListItem(ConnectionGroup) {
/**
* Creates a new GroupListItem, initializing the properties of that
* GroupListItem with the corresponding properties of the given template.
*
* @constructor
* @param {GroupListItem|Object} [template={}]
* The object whose properties should be copied within the new
* GroupListItem.
*/
var GroupListItem = function GroupListItem(template) {
// Use empty object by default
template = template || {};
/**
* The unique identifier associated with the connection or connection
* group this item represents.
*
* @type String
*/
this.identifier = template.identifier;
/**
* The human-readable display name of this item.
*
* @type String
*/
this.name = template.name;
/**
* The unique identifier of the protocol, if this item represents a
* connection. If this item does not represent a connection, this
* property is not applicable.
*
* @type String
*/
this.protocol = template.protocol;
/**
* All children items of this item. If this item contains no children,
* this will be an empty array.
*
* @type GroupListItem[]
*/
this.children = template.children || [];
/**
* Whether this item represents a connection. If this item represents
* a connection group, this MUST be false.
*
* @type Boolean
*/
this.isConnection = template.isConnection;
/**
* Whether this item represents a connection group. If this item
* represents a connection, this MUST be false.
*
* @type Boolean
*/
this.isConnectionGroup = template.isConnectionGroup;
/**
* Whether this item represents a balancing connection group.
*
* @type Boolean
*/
this.isBalancing = template.isBalancing;
/**
* Whether the children items should be displayed.
*
* @type Boolean
*/
this.isExpanded = template.isExpanded;
};
/**
* Creates a new GroupListItem using the contents of the given connection.
*
* @param {ConnectionGroup} connection
* The connection whose contents should be represented by the new
* GroupListItem.
*
* @returns {GroupListItem}
* A new GroupListItem which represents the given connection.
*/
GroupListItem.fromConnection = function fromConnection(connection) {
// Return item representing the given connection
return new GroupListItem({
// Identifying information
name : connection.name,
identifier : connection.identifier,
protocol : connection.protocol,
// Type information
isConnection : true,
isConnectionGroup : false
});
};
/**
* Creates a new GroupListItem using the contents and descendants of the
* given connection group.
*
* @param {ConnectionGroup} connectionGroup
* The connection group whose contents and descendants should be
* represented by the new GroupListItem and its descendants.
*
* @returns {GroupListItem}
* A new GroupListItem which represents the given connection group,
* including all descendants.
*/
GroupListItem.fromConnectionGroup = function fromConnectionGroup(connectionGroup) {
var children = [];
// Add any child connections
connectionGroup.childConnections.forEach(function addChildConnection(child) {
children.push(GroupListItem.fromConnection(child));
});
// Add any child groups
connectionGroup.childConnectionGroups.forEach(function addChildGroup(child) {
children.push(GroupListItem.fromConnectionGroup(child));
});
// Return item representing the given connection group
return new GroupListItem({
// Identifying information
name : connectionGroup.name,
identifier : connectionGroup.identifier,
// Type information
isConnection : false,
isConnectionGroup : true,
isBalancing : connectionGroup.type === ConnectionGroup.Type.BALANCING,
// Already-converted children
children : children
});
};
return GroupListItem;
}]);

View File

@@ -37,7 +37,8 @@ angular.module('history').factory('HistoryEntry', [function defineHistoryEntry()
var HistoryEntry = function HistoryEntry(id, thumbnail) {
/**
* The ID of the connection associated with this history entry.
* The ID of the connection associated with this history entry,
* including type prefix.
*/
this.id = id;

View File

@@ -25,71 +25,23 @@
*/
angular.module('home').controller('homeController', ['$scope', '$injector',
function homeController($scope, $injector) {
// Get required types
var ConnectionGroup = $injector.get("ConnectionGroup");
// The parameter name for getting the history from local storage
var GUAC_HISTORY_STORAGE_KEY = "GUAC_HISTORY";
// Get the dependencies commonJS style
// Get required services
var connectionGroupService = $injector.get("connectionGroupService");
var guacHistory = $injector.get("guacHistory");
// All the connections and connection groups in root
$scope.connectionsAndGroups = [];
// All valid recent connections
$scope.recentConnections = [];
// Set status to loading until we have all the connections and groups loaded
$scope.loading = true;
/* Fetch all connections and groups, then find which recent connections
* still refer to valid connections and groups.
*/
connectionGroupService.getAllGroupsAndConnections($scope.connectionsAndGroups)
.then(function findRecentConnections() {
// TODONT: Munch the guacHistory recentConnections list into a legacy-style object
var recentConnections = {};
for (var i=0; i < guacHistory.recentConnections.length; i++) {
var entry = guacHistory.recentConnections[i];
recentConnections[encodeURIComponent(entry.id)] = {
id : entry.id,
thumbnail : entry.thumbnail
};
}
// Figure out which recent connection entries are valid
$scope.connectionsAndGroups.forEach(function findValidEntries (connectionOrGroup) {
var type = connectionOrGroup.isConnection ? "c" : "g";
// Find the unique ID to index into the recent connections
var uniqueId = encodeURIComponent(
type + "/" + connectionOrGroup.identifier
);
/*
* If it's a valid recent connection, add it to the list,
* along with enough information to make a connection url.
*/
var recentConnection = recentConnections[uniqueId];
if(recentConnection) {
recentConnection.type = type;
recentConnection.id = connectionOrGroup.identifier;
$scope.recentConnections.push(recentConnection);
}
});
// Retrieve root group and all descendants
connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER)
.success(function rootGroupRetrieved(rootConnectionGroup) {
$scope.rootConnectionGroup = rootConnectionGroup;
$scope.loading = false;
});
/**
* Toggle the open/closed status of the connectionGroup.
*
* @param {object} connectionGroup The connection group to toggle.
*/
$scope.toggleExpanded = function toggleExpanded(connectionGroup) {
connectionGroup.expanded = !connectionGroup.expanded;
};
}]);

View File

@@ -0,0 +1,115 @@
/*
* 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.
*/
/**
* A directive which displays the contents of a connection group.
*/
angular.module('home').directive('guacRecentConnections', [function guacRecentConnections() {
return {
restrict: 'E',
replace: true,
scope: {
/**
* The root connection group, and all visible descendants.
* Recent connections will only be shown if they exist within this
* hierarchy, regardless of their existence within the history.
*
* @type ConnectionGroup
*/
rootGroup : '='
},
templateUrl: 'app/home/templates/guacRecentConnections.html',
controller: ['$scope', '$injector', 'guacHistory', 'RecentConnection',
function guacRecentConnectionsController($scope, $injector, guacHistory, RecentConnection) {
var visibleObjects = {};
/**
* Adds the given connection to the internal set of visible
* objects.
*
* @param {Connection} connection
* The connection to add to the internal set of visible objects.
*/
var addVisibleConnection = function addVisibleConnection(connection) {
// Add given connection to set of visible objects
visibleObjects['c/' + connection.identifier] = connection;
};
/**
* Adds the given connection group to the internal set of visible
* objects, along with any descendants.
*
* @param {ConnectionGroup} connectionGroup
* The connection group to add to the internal set of visible
* objects, along with any descendants.
*/
var addVisibleConnectionGroup = function addVisibleConnectionGroup(connectionGroup) {
// Add given connection group to set of visible objects
visibleObjects['g/' + connectionGroup.identifier] = connectionGroup;
// Add all child connections
if (connectionGroup.childConnections)
connectionGroup.childConnections.forEach(addVisibleConnection);
// Add all child connection groups
if (connectionGroup.childConnectionGroups)
connectionGroup.childConnectionGroups.forEach(addVisibleConnectionGroup);
};
// Update visible objects when root group is set
$scope.$watch("rootGroup", function setRootGroup(rootGroup) {
$scope.recentConnections = [];
// Produce collection of visible objects
visibleObjects = {};
if (rootGroup)
addVisibleConnectionGroup(rootGroup);
// Add any recent connections that are visible
guacHistory.recentConnections.forEach(function addRecentConnection(historyEntry) {
// Add recent connections for history entries with associated visible objects
if (historyEntry.id in visibleObjects) {
var object = visibleObjects[historyEntry.id];
$scope.recentConnections.push(new RecentConnection(object.name, historyEntry));
}
});
}); // end rootGroup scope watch
}]
};
}]);

View File

@@ -20,4 +20,4 @@
* THE SOFTWARE.
*/
angular.module('home', ['connection', 'connectionGroup', 'history', 'user', 'permission']);
angular.module('home', ['history', 'groupList', 'rest']);

View File

@@ -0,0 +1,44 @@
<div>
<!--
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.
-->
<!-- Text displayed if no recent connections exist -->
<p class="no-recent" ng-hide="recentConnections.length">{{'home.noRecentConnections' | translate}}</p>
<!-- All recent connections -->
<div ng-repeat="recentConnection in recentConnections" class="connection">
<a href="#/client/{{recentConnection.entry.id}}/{{recentConnection.name}}">
<!-- Connection thumbnail -->
<div class="thumbnail">
<img alt="{{recentConnection.name}}" ng-src="{{recentConnection.entry.thumbnail}}"/>
</div>
<!-- Connection name -->
<div class="caption">
<span class="name">{{recentConnection.name}}</span>
</div>
</a>
</div>
</div>

View File

@@ -20,31 +20,6 @@
THE SOFTWARE.
-->
<script type="text/ng-template" id="nestedGroup.html">
<div class="connection" ng-show="item.isConnection">
<a ng-href="#/client/c/{{item.identifier}}">
<div class="caption">
<div class="protocol">
<div class="icon type" ng-class="item.protocol"></div>
</div>
<span class="name">{{item.name}}</span>
</div>
</a>
</div>
<div class="group" ng-show="!item.isConnection">
<div class="caption">
<div class="icon group type" ng-click="toggleExpanded(item)" ng-class="{expanded: item.expanded, empty: !item.children.length, balancer: item.balancer && !item.children.length}"></div>
<span class="name">
<a ng-show="item.balancer" ng-href="#/client/g/{{item.identifier}}">{{item.name}}</a>
<span ng-show="!item.balancer">{{item.name}}</span>
</span>
</div>
<div class="children" ng-show="item.expanded">
<div class="list-item" ng-repeat="item in item.children | orderBy : 'name'" ng-include="'nestedGroup.html'">
</div>
</div>
</script>
<div class="connection-list-ui">
<div class="logout-panel">
@@ -54,25 +29,14 @@
<!-- The recent connections for this user -->
<h2>{{'home.recentConnections' | translate}}</h2>
<div class="recent-connections" ng-hide="recentConnections.length">
<p class="no-recent">{{'home.noRecentConnections' | translate}}</p>
</div>
<div class="recent-connections" ng-show="recentConnections.length">
<div ng-repeat="recentConnection in recentConnections" class="connection">
<a href="#/client/{{recentConnection.type}}/{{recentConnection.id}}/{{recentConnection.name}}">
<div class="thumbnail">
<img alt="{{recentConnection.name}}" ng-src="{{recentConnection.thumbnail}}"/>
</div>
<div class="caption">
<span class="name">{{recentConnection.name}}</span>
</div>
</a>
</div>
<div class="recent-connections" ng-class="{loading: loading}">
<guac-recent-connections root-group="rootConnectionGroup"/>
</div>
<!-- All connections for this user -->
<h2>{{'home.allConnections' | translate}}</h2>
<div class="all-connections" ng-class="{loading: loading}">
<div class="list-item" ng-repeat="item in connectionsAndGroups | orderBy : 'name'" ng-include="'nestedGroup.html'"></div>
<guac-group-list connection-group="rootConnectionGroup"/>
</div>
</div>

View File

@@ -20,18 +20,36 @@
* THE SOFTWARE.
*/
package org.glyptodon.guacamole.net.basic.rest;
/**
* Useful constants for the REST API.
*
* @author James Muehlner
* Provides the RecentConnection class used by the guacRecentConnections
* directive.
*/
public class APIConstants {
angular.module('home').factory('RecentConnection', [function defineRecentConnection() {
/**
* The identifier of the ROOT connection group.
* A recently-user connection, visible to the current user, with an
* associated history entry.
*
* @constructor
*/
public static final String ROOT_CONNECTION_GROUP_IDENTIFIER = "ROOT";
}
var RecentConnection = function RecentConnection(name, entry) {
/**
* The human-readable name of this connection.
*
* @type String
*/
this.name = name;
/**
* The history entry associated with this recent connection.
*
* @type HistoryEntry
*/
this.entry = entry;
};
return RecentConnection;
}]);

View File

@@ -25,10 +25,12 @@
*/
angular.module('index').controller('indexController', ['$scope', '$injector',
function indexController($scope, $injector) {
// Get the dependencies commonJS style
var permissionDAO = $injector.get("permissionDAO"),
permissionCheckService = $injector.get("permissionCheckService"),
// Get class dependencies
var PermissionSet = $injector.get("PermissionSet");
// Get services
var permissionService = $injector.get("permissionService"),
authenticationService = $injector.get("authenticationService"),
$q = $injector.get("$q"),
$document = $injector.get("$document"),
@@ -166,16 +168,18 @@ angular.module('index').controller('indexController', ['$scope', '$injector',
// Allow the permissions to be reloaded elsewhere if needed
$scope.loadBasicPermissions = function loadBasicPermissions() {
permissionDAO.getPermissions($scope.currentUserID).success(function fetchCurrentUserPermissions(permissions) {
permissionService.getPermissions($scope.currentUserID).success(function fetchCurrentUserPermissions(permissions) {
$scope.currentUserPermissions = permissions;
// Will be true if the user is an admin
$scope.currentUserIsAdmin = permissionCheckService.checkPermission($scope.currentUserPermissions, "SYSTEM", undefined, "ADMINISTER");
// Whether the user has system-wide admin permission
$scope.currentUserIsAdmin = PermissionSet.hasSystemPermission($scope.currentUserPermissions, PermissionSet.SystemPermissionType.ADMINISTER);
// Whether the user can update at least one object
$scope.currentUserHasUpdate = $scope.currentUserIsAdmin
|| PermissionSet.hasConnectionPermission($scope.currentUserPermissions, "UPDATE")
|| PermissionSet.hasConnectionGroupPermission($scope.currentUserPermissions, "UPDATE")
|| PermissionSet.hasUserPermission($scope.currentUserPermissions, "UPDATE");
// Will be true if the user is an admin or has update access to any object
$scope.currentUserHasUpdate = $scope.currentUserIsAdmin ||
permissionCheckService.checkPermission($scope.currentUserPermissions, undefined, undefined, "UPDATE");
permissionsLoaded.resolve();
});
};

View File

@@ -24,4 +24,4 @@
* The module for the root of the application.
*/
angular.module('index', ['ngRoute', 'pascalprecht.translate',
'auth', 'home', 'manage', 'login', 'client', 'notification']);
'auth', 'home', 'manage', 'login', 'client', 'notification', 'rest']);

View File

@@ -260,7 +260,7 @@ div.section {
border-left: 1px dotted rgba(0, 0, 0, 0.25);
}
.group.balancer > .caption .icon.type {
.group.balancer:not(.empty) > .caption .icon.type {
display: inline-block;
background-image: url('images/protocol-icons/guac-monitor.png');
}

View File

@@ -27,7 +27,7 @@ angular.module('manage').controller('connectionGroupEditModalController', ['$sco
function connectionEditModalController($scope, $injector) {
var connectionGroupEditModal = $injector.get('connectionGroupEditModal');
var connectionGroupDAO = $injector.get('connectionGroupDAO');
var connectionGroupService = $injector.get('connectionGroupService');
var displayObjectPreparationService = $injector.get('displayObjectPreparationService');
// Make a copy of the old connection group so that we can copy over the changes when done
@@ -64,7 +64,7 @@ angular.module('manage').controller('connectionGroupEditModalController', ['$sco
* Save the connection and close the modal.
*/
$scope.save = function save() {
connectionGroupDAO.saveConnectionGroup($scope.connectionGroup).success(function successfullyUpdatedConnectionGroup() {
connectionGroupService.saveConnectionGroup($scope.connectionGroup).success(function successfullyUpdatedConnectionGroup() {
// Prepare this connection group for display
displayObjectPreparationService.prepareConnectionGroup($scope.connectionGroup);
@@ -79,7 +79,7 @@ angular.module('manage').controller('connectionGroupEditModalController', ['$sco
if(newConnectionGroup && newParentID === $scope.rootGroup.identifier) {
$scope.moveItem($scope.connectionGroup, oldParentID, newParentID);
} else {
connectionGroupDAO.moveConnectionGroup($scope.connectionGroup).then(function moveConnectionGroup() {
connectionGroupService.moveConnectionGroup($scope.connectionGroup).then(function moveConnectionGroup() {
$scope.moveItem($scope.connectionGroup, oldParentID, newParentID);
});
}
@@ -99,7 +99,7 @@ angular.module('manage').controller('connectionGroupEditModalController', ['$sco
// Close the modal
connectionGroupEditModal.deactivate();
connectionGroupDAO.deleteConnectionGroup($scope.connectionGroup).success(function successfullyDeletedConnectionGroup() {
connectionGroupService.deleteConnectionGroup($scope.connectionGroup).success(function successfullyDeletedConnectionGroup() {
var oldParentID = oldConnectionGroup.parentIdentifier;
// We have to remove this connection group from the heirarchy

View File

@@ -25,60 +25,39 @@
*/
angular.module('manage').controller('manageController', ['$scope', '$injector',
function manageController($scope, $injector) {
// Get the dependencies commonJS style
// Required types
var PermissionSet = $injector.get('PermissionSet');
var ConnectionGroup = $injector.get('ConnectionGroup');
// Required services
var connectionGroupService = $injector.get('connectionGroupService');
var connectionEditModal = $injector.get('connectionEditModal');
var connectionGroupEditModal = $injector.get('connectionGroupEditModal');
var userEditModal = $injector.get('userEditModal');
var protocolDAO = $injector.get('protocolDAO');
var userDAO = $injector.get('userDAO');
var protocolService = $injector.get('protocolService');
var userService = $injector.get('userService');
// Set status to loading until we have all the connections, groups, and users have loaded
$scope.loadingUsers = true;
$scope.loadingConnections = true;
// All the connections and connection groups in root
$scope.connectionsAndGroups = [];
// All users that the current user has permission to edit
$scope.users = [];
$scope.basicPermissionsLoaded.then(function basicPermissionsHaveBeenLoaded() {
connectionGroupService.getAllGroupsAndConnections([], undefined, true, true).then(function filterConnectionsAndGroups(rootGroupList) {
$scope.rootGroup = rootGroupList[0];
$scope.connectionsAndGroups = $scope.rootGroup.children;
// Filter the items to only include ones that we have UPDATE for
if(!$scope.currentUserIsAdmin) {
connectionGroupService.filterConnectionsAndGroupByPermission(
$scope.connectionsAndGroups,
$scope.currentUserPermissions,
{
'CONNECTION': 'UPDATE',
'CONNECTION_GROUP': 'UPDATE'
}
);
}
// Retrieve all users for whom we have UPDATE permission
connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER, PermissionSet.ObjectPermissionType.UPDATE)
.success(function connectionGroupReceived(rootGroup) {
$scope.rootGroup = rootGroup;
$scope.loadingConnections = false;
});
userDAO.getUsers().success(function filterEditableUsers(users) {
// Retrieve all users for whom we have UPDATE permission
userService.getUsers(PermissionSet.ObjectPermissionType.UPDATE)
.success(function usersReceived(users) {
$scope.users = users;
// Filter the users to only include ones that we have UPDATE for
if(!$scope.currentUserIsAdmin) {
userService.filterUsersByPermission(
$scope.users,
$scope.currentUserPermissions,
'UPDATE'
);
}
$scope.loadingUsers = false;
});
});
/**
@@ -132,7 +111,7 @@ angular.module('manage').controller('manageController', ['$scope', '$injector',
$scope.protocols = {};
// Get the protocol information from the server and copy it into the scope
protocolDAO.getProtocols().success(function fetchProtocols(protocols) {
protocolService.getProtocols().success(function fetchProtocols(protocols) {
angular.extend($scope.protocols, protocols);
});
@@ -236,7 +215,7 @@ angular.module('manage').controller('manageController', ['$scope', '$injector',
username: $scope.newUsername
};
userDAO.createUser(newUser).success(function addUserToList() {
userService.createUser(newUser).success(function addUserToList() {
$scope.users.push(newUser);
});

View File

@@ -26,9 +26,9 @@
angular.module('manage').controller('userEditModalController', ['$scope', '$injector',
function userEditModalController($scope, $injector) {
var userEditModal = $injector.get('userEditModal');
var userDAO = $injector.get('userDAO');
var permissionDAO = $injector.get('permissionDAO');
var userEditModal = $injector.get('userEditModal');
var userService = $injector.get('userService');
var permissionService = $injector.get('permissionService');
// Make a copy of the old user so that we can copy over the changes when done
var oldUser = $scope.user;
@@ -73,7 +73,7 @@ angular.module('manage').controller('userEditModalController', ['$scope', '$inje
return;
}
userDAO.saveUser($scope.user).success(function successfullyUpdatedUser() {
userService.saveUser($scope.user).success(function successfullyUpdatedUser() {
//Figure out what permissions have changed
var connectionPermissionsToCreate = [],
@@ -182,7 +182,7 @@ angular.module('manage').controller('userEditModalController', ['$scope', '$inje
if(permissionsToAdd.length || permissionsToRemove.length) {
// Make the call to update the permissions
permissionDAO.patchPermissions(
permissionService.patchPermissions(
$scope.user.username, permissionsToAdd, permissionsToRemove)
.success(completeSaveProcess).error(handleFailure);
} else {
@@ -205,7 +205,7 @@ angular.module('manage').controller('userEditModalController', ['$scope', '$inje
originalSystemPermissions;
// Get the permissions for the user we are editing
permissionDAO.getPermissions($scope.user.username).success(function gotPermissions(permissions) {
permissionService.getPermissions($scope.user.username).success(function gotPermissions(permissions) {
$scope.permissions = permissions;
// Figure out if the user has any system level permissions
@@ -239,7 +239,7 @@ angular.module('manage').controller('userEditModalController', ['$scope', '$inje
* Delete the user and close the modal.
*/
$scope['delete'] = function deleteUser() {
userDAO.deleteUser($scope.user).success(function successfullyDeletedUser() {
userService.deleteUser($scope.user).success(function successfullyDeletedUser() {
// Remove the user from the list
$scope.removeUser($scope.user);

View File

@@ -23,5 +23,5 @@
/**
* The module for the administration functionality.
*/
angular.module('manage', ['btford.modal', 'protocol', 'connection', 'connectionGroup', 'util']);
angular.module('manage', ['btford.modal', 'groupList', 'rest', 'util']);

View File

@@ -20,28 +20,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<script type="text/ng-template" id="nestedGroup.html">
<div class="connection" ng-click="editConnection(item)" ng-show="item.isConnection">
<div class="caption">
<div class="protocol">
<div class="icon type" ng-class="item.protocol"></div>
</div>
<span class="name">{{item.name}}</span>
</div>
</div>
<div class="group" ng-show="!item.isConnection">
<div class="caption">
<div class="icon group type" ng-click="toggleExpanded(item)" ng-class="{expanded: item.expanded, empty: !item.children.length, balancer: item.balancer && !item.children.length}"></div>
<span ng-click="editConnectionGroup(item)" class="name">
<span>{{item.name}}</span>
</span>
</div>
<div class="children" ng-show="item.expanded">
<div class="list-item" ng-repeat="item in item.children | orderBy : 'name'" ng-include="'nestedGroup.html'">
</div>
</div>
</script>
<div class="logout-panel">
<a class="back button" href="#/">{{'manage.back' | translate}}</a>
<a class="logout button" ng-click="logout()">{{'home.logout' | translate}}</a>
@@ -85,7 +63,7 @@ THE SOFTWARE.
<!-- List of connections and groups this user has access to -->
<div class="connection-list" ng-class="{loading: loadingConnections}">
<div class="list-item" ng-repeat="item in connectionsAndGroups | orderBy : 'name'" ng-include="'nestedGroup.html'"></div>
<guac-group-list connection-group="rootGroup"/>
</div>
</div>

View File

@@ -1,26 +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.
*/
/**
* A module for code relating to permissions.
*/
angular.module('permission', ['auth']);

View File

@@ -1,73 +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.
*/
/**
* A service for checking if a specific permission exists
* in a given list of permissions.
*/
angular.module('permission').factory('permissionCheckService', [
function permissionCheckService() {
var service = {};
/**
* A service for checking if the given permission list contains the given
* permission, defined by the objectType, objectID, and permissionType.
* If the objectType or objectID are not passed, they will not be checked.
*
* For example, checkPermission(list, "CONNECTION", undefined, "READ") would
* check if the permission list contains permission to read any connection.
*
* @param {array} permissions The array of permissions to check.
* @param {string} objectType The object type for the permission.
* If not passed, this will not be checked.
* @param {string} objectID The ID of the object the permission is for.
* If not passed, this will not be checked.
* @param {string} permissionType The actual permission type to check for.
* @returns {boolean} True if the given permissions contain the requested permission, false otherwise.
*/
service.checkPermission = function checkPermission(permissions, objectType, objectID, permissionType) {
// Loop through all the permissions and check if any of them match the given parameters
for(var i = 0; i < permissions.length; i++) {
var permission = permissions[i];
if(objectType === "SYSTEM") {
// System permissions have no object ID, we only need to check the type.
if(permission.permissionType === permissionType)
return true;
}
else {
// Object permissions need to match the object ID and type if given.
if(permission.permissionType === permissionType &&
(!objectType || permission.objectType === objectType) &&
(!objectID || permission.objectID === objectID))
return true;
}
}
// Didn't find any that matched
return false;
}
return service;
}]);

View File

@@ -1,115 +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.
*/
/**
* The DAO for permission operations agains the REST API.
*/
angular.module('permission').factory('permissionDAO', ['$http', 'authenticationService',
function permissionDAO($http, authenticationService) {
var service = {};
/**
* Makes a request to the REST API to get the list of permissions for a given user,
* returning a promise that can be used for processing the results of the call.
*
* @param {string} userID The ID of the user to retrieve the permissions for.
*
* @returns {promise} A promise for the HTTP call.
*/
service.getPermissions = function getPermissions(userID) {
return $http.get("api/permission/" + userID + "/?token=" + authenticationService.getCurrentToken());
};
/**
* Makes a request to the REST API to add a permission for a given user,
* returning a promise that can be used for processing the results of the call.
*
* @param {string} userID The ID of the user to add the permission for.
* @param {object} permission The permission to add.
*
* @returns {promise} A promise for the HTTP call.
*/
service.addPermission = function addPermission(userID, permission) {
return $http.post("api/permission/" + userID + "/?token=" + authenticationService.getCurrentToken(), permission);
};
/**
* Makes a request to the REST API to remove a permission for a given user,
* returning a promise that can be used for processing the results of the call.
*
* @param {string} userID The ID of the user to remove the permission for.
* @param {object} permission The permission to remove.
*
* @returns {promise} A promise for the HTTP call.
*/
service.removePermission = function removePermission(userID, permission) {
return $http.post("api/permission/remove/" + userID + "/?token=" + authenticationService.getCurrentToken(), permission);
};
/**
* Makes a request to the REST API to modify the permissions for a given user,
* returning a promise that can be used for processing the results of the call.
*
* @param {string} userID The ID of the user to remove the permission for.
* @param {array} permissionsToAdd The permissions to add.
* @param {array} permissionsToRemove The permissions to remove.
*
* @returns {promise} A promise for the HTTP call.
*/
service.patchPermissions = function patchPermissions(userID, permissionsToAdd, permissionsToRemove) {
var permissionPatch = [];
// Add all the add operations to the patch
for(var i = 0; i < permissionsToAdd.length; i++ ) {
permissionPatch.push({
op : "add",
path : userID,
value : permissionsToAdd[i]
});
}
// Add all the remove operations to the patch
for(var i = 0; i < permissionsToRemove.length; i++ ) {
permissionPatch.push({
op : "remove",
path : userID,
value : permissionsToRemove[i]
});
}
// Make the HTTP call
return $http({
method : 'PATCH',
url : "api/permission/?token=" + authenticationService.getCurrentToken(),
data : permissionPatch
});
}
return service;
}]);

View File

@@ -21,6 +21,7 @@
*/
/**
* The module for the protocol functionality.
* The module for code relating to communication with the REST API of the
* Guacamole web application.
*/
angular.module('protocol', []);
angular.module('rest', ['auth']);

View File

@@ -0,0 +1,181 @@
/*
* 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.
*/
/**
* Service for operating on connection groups via the REST API.
*/
angular.module('rest').factory('connectionGroupService', ['$http', 'authenticationService', 'ConnectionGroup',
function connectionGroupService($http, authenticationService, ConnectionGroup) {
var service = {};
/**
* Makes a request to the REST API to get an individual connection group
* and all descendants, returning a promise that provides the corresponding
* @link{ConnectionGroup} if successful. Descendant groups and connections
* will be stored as children of that connection group. If a permission
* type is specified, the result will be filtering by that permission.
*
* @param {String} [connectionGroupID=ConnectionGroup.ROOT_IDENTIFIER]
* The ID of the connection group to retrieve. If not provided, the
* root connection group will be retrieved by default.
*
* @param {String} [permissionType]
* The permission type string of the permission that the current user
* must have for a given connection or connection group to appear
* within the result. Valid values are listed within
* PermissionSet.ObjectType.
*
* @returns {Promise.ConnectionGroup}
* A promise which will resolve with a @link{ConnectionGroup} upon
* success.
*/
service.getConnectionGroupTree = function getConnectionGroupTree(connectionGroupID, permissionType) {
// Use the root connection group ID if no ID is passed in
connectionGroupID = connectionGroupID || ConnectionGroup.ROOT_IDENTIFIER;
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Add permission filter if specified
if (permissionType)
httpParameters.permission = permissionType;
// Retrieve connection group
return $http({
method : 'GET',
url : 'api/connectionGroups/' + encodeURIComponent(connectionGroupID) + '/tree',
params : httpParameters
});
};
/**
* Makes a request to the REST API to get an individual connection group,
* returning a promise that provides the corresponding
* @link{ConnectionGroup} if successful.
*
* @param {String} [connectionGroupID=ConnectionGroup.ROOT_IDENTIFIER]
* The ID of the connection group to retrieve. If not provided, the
* root connection group will be retrieved by default.
*
* @returns {Promise.<ConnectionGroup>} A promise for the HTTP call.
* A promise which will resolve with a @link{ConnectionGroup} upon
* success.
*/
service.getConnectionGroup = function getConnectionGroup(connectionGroupID) {
// Use the root connection group ID if no ID is passed in
connectionGroupID = connectionGroupID || ConnectionGroup.ROOT_IDENTIFIER;
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Retrieve connection group
return $http({
method : 'GET',
url : 'api/connectionGroups/' + encodeURIComponent(connectionGroupID),
params : httpParameters
});
};
/**
* Makes a request to the REST API to save a connection group, returning a
* promise that can be used for processing the results of the call. If the
* connection group is new, and thus does not yet have an associated
* identifier, the identifier will be automatically set in the provided
* connection group upon success.
*
* @param {ConnectionGroup} connectionGroup The connection group to update.
*
* @returns {Promise}
* A promise for the HTTP call which will succeed if and only if the
* save operation is successful.
*/
service.saveConnectionGroup = function saveConnectionGroup(connectionGroup) {
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// If connection group is new, add it and set the identifier automatically
if (!connectionGroup.identifier) {
return $http({
method : 'POST',
url : 'api/connectionGroups',
params : httpParameters,
data : connectionGroup
})
// Set the identifier on the new connection group
.success(function connectionGroupCreated(identifier){
connectionGroup.identifier = identifier;
});
}
// Otherwise, update the existing connection group
else {
return $http({
method : 'PUT',
url : 'api/connectionGroups/' + encodeURIComponent(connectionGroup.identifier),
params : httpParameters,
data : connectionGroup
});
}
};
/**
* Makes a request to the REST API to delete a connection group, returning
* a promise that can be used for processing the results of the call.
*
* @param {ConnectionGroup} connectionGroup The connection group to delete.
*
* @returns {Promise}
* A promise for the HTTP call which will succeed if and only if the
* delete operation is successful.
*/
service.deleteConnectionGroup = function deleteConnectionGroup(connectionGroup) {
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Delete connection group
return $http({
method : 'DELETE',
url : 'api/connectionGroups/' + encodeURIComponent(connectionGroup.identifier),
params : httpParameters
});
};
return service;
}]);

View File

@@ -0,0 +1,192 @@
/*
* 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.
*/
/**
* Service for operating on connections via the REST API.
*/
angular.module('rest').factory('connectionService', ['$http', 'authenticationService',
function connectionService($http, authenticationService) {
var service = {};
/**
* Makes a request to the REST API to get a single connection, returning a
* promise that provides the corresponding @link{Connection} if successful.
*
* @param {String} id The ID of the connection.
*
* @returns {Promise.<Connection>}
* A promise which will resolve with a @link{Connection} upon success.
*
* @example
*
* connectionService.getConnection('myConnection').success(function(connection) {
* // Do something with the connection
* });
*/
service.getConnection = function getConnection(id) {
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Retrieve connection
return $http({
method : 'GET',
url : 'api/connections/' + encodeURIComponent(id),
params : httpParameters
});
};
/**
* 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} id
* The identifier of the connection.
*
* @returns {Promise.<ConnectionHistoryEntry[]>}
* A promise which will resolve with an array of
* @link{ConnectionHistoryEntry} objects upon success.
*/
service.getConnectionHistory = function getConnectionHistory(id) {
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Retrieve connection history
return $http({
method : 'GET',
url : 'api/connections/' + encodeURIComponent(id) + '/history',
params : httpParameters
});
};
/**
* 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) {
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Retrieve connection parameters
return $http({
method : 'GET',
url : 'api/connections/' + encodeURIComponent(id) + '/parameters',
params : httpParameters
});
};
/**
* 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
* connection is new, and thus does not yet have an associated identifier,
* the identifier will be automatically set in the provided connection
* upon success.
*
* @param {Connection} connection The connection to update.
*
* @returns {Promise}
* A promise for the HTTP call which will succeed if and only if the
* save operation is successful.
*/
service.saveConnection = function saveConnection(connection) {
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// If connection is new, add it and set the identifier automatically
if (!connection.identifier) {
return $http({
method : 'POST',
url : 'api/connections',
params : httpParameters,
data : connection
})
// Set the identifier on the new connection
.success(function connectionCreated(identifier){
connection.identifier = identifier;
});
}
// Otherwise, update the existing connection
else {
return $http({
method : 'PUT',
url : 'api/connections/' + encodeURIComponent(connection.identifier),
params : httpParameters,
data : connection
});
}
};
/**
* Makes a request to the REST API to delete a connection,
* returning a promise that can be used for processing the results of the call.
*
* @param {Connection} connection The connection to delete.
*
* @returns {Promise}
* A promise for the HTTP call which will succeed if and only if the
* delete operation is successful.
*/
service.deleteConnection = function deleteConnection(connection) {
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Delete connection
return $http({
method : 'DELETE',
url : 'api/connections/' + encodeURIComponent(connection.identifier),
params : httpParameters
});
};
return service;
}]);

View File

@@ -0,0 +1,216 @@
/*
* 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.
*/
/**
* Service for operating on user permissions via the REST API.
*/
angular.module('rest').factory('permissionService', ['$http', 'authenticationService', 'PermissionPatch',
function permissionService($http, authenticationService, PermissionPatch) {
var service = {};
/**
* Makes a request to the REST API to get the list of permissions for a
* given user, returning a promise that provides an array of
* @link{Permission} objects if successful.
*
* @param {String} userID
* The ID of the user to retrieve the permissions for.
*
* @returns {Promise.<PermissionSet>}
* A promise which will resolve with a @link{PermissionSet} upon
* success.
*/
service.getPermissions = function getPermissions(userID) {
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Retrieve user permissions
return $http({
method : 'GET',
url : 'api/users/' + encodeURIComponent(userID) + '/permissions',
params : httpParameters
});
};
/**
* Makes a request to the REST API to add permissions for a given user,
* returning a promise that can be used for processing the results of the
* call.
*
* @param {String} userID
* The ID of the user to modify the permissions of.
*
* @param {PermissionSet} permissions
* The set of permissions to add.
*
* @returns {Promise}
* A promise for the HTTP call which will succeed if and only if the
* add operation is successful.
*/
service.addPermissions = function addPermissions(userID, permissions) {
return service.patchPermissions(userID, permissions, null);
};
/**
* Makes a request to the REST API to remove permissions for a given user,
* returning a promise that can be used for processing the results of the
* call.
*
* @param {String} userID
* The ID of the user to modify the permissions of.
*
* @param {PermissionSet} permissions
* The set of permissions to remove.
*
* @returns {Promise}
* A promise for the HTTP call which will succeed if and only if the
* remove operation is successful.
*/
service.removePermissions = function removePermissions(userID, permissions) {
return service.patchPermissions(userID, null, permissions);
};
/**
* Adds patches for modifying the permissions associated with specific
* objects to the given array of patches.
*
* @param {PermissionPatch[]} patch
* The array of patches to add new patches to.
*
* @param {String} operation
* The operation to specify within each of the patches. Valid values
* for this are defined within PermissionPatch.Operation.
*
* @param {String} path
* The path of the permissions being patched. The path is a JSON path
* describing the position of the permissions within a PermissionSet.
*
* @param {Object.<String, String[]>} permissions
* A map of object identifiers to arrays of permission type strings,
* where each type string is a value from
* PermissionSet.ObjectPermissionType.
*/
var addObjectPatchOperations = function addObjectPatchOperations(patch, operation, path, permissions) {
// Add object permission operations to patch
for (var identifier in permissions) {
permissions[identifier].forEach(function addObjectPatch(type) {
patch.push({
op : operation,
path : path + "/" + identifier,
value : type
});
});
}
};
/**
* Adds patches for modifying any permission that can be stored within a
* @link{PermissionSet}.
*
* @param {PermissionPatch[]} patch
* The array of patches to add new patches to.
*
* @param {String} operation
* The operation to specify within each of the patches. Valid values
* for this are defined within PermissionPatch.Operation.
*
* @param {PermissionSet} permissions
* The set of permissions for which patches should be added.
*/
var addPatchOperations = function addPatchOperations(patch, operation, permissions) {
// Add connection permission operations to patch
addObjectPatchOperations(patch, operation, "/connectionPermissions",
permissions.connectionPermissions);
// Add connection group permission operations to patch
addObjectPatchOperations(patch, operation, "/connectionGroupPermissions",
permissions.connectionGroupPermissions);
// Add user permission operations to patch
addObjectPatchOperations(patch, operation, "/userPermissions",
permissions.userPermissions);
// Add system operations to patch
permissions.systemPermissions.forEach(function addSystemPatch(type) {
patch.push({
op : operation,
path : "/systemPermissions",
value : type
});
});
};
/**
* Makes a request to the REST API to modify the permissions for a given
* user, returning a promise that can be used for processing the results of
* the call.
*
* @param {String} userID
* The ID of the user to modify the permissions of.
*
* @param {PermissionSet} [permissionsToAdd]
* The set of permissions to add, if any.
*
* @param {PermissionSet} [permissionsToRemove]
* The set of permissions to remove, if any.
*
* @returns {Promise}
* A promise for the HTTP call which will succeed if and only if the
* patch operation is successful.
*/
service.patchPermissions = function patchPermissions(userID, permissionsToAdd, permissionsToRemove) {
var permissionPatch = [];
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Add all the add operations to the patch
addPatchOperations(permissionPatch, PermissionPatch.Operation.ADD, permissionsToAdd);
// Add all the remove operations to the patch
addPatchOperations(permissionPatch, PermissionPatch.Operation.REMOVE, permissionsToRemove);
// Patch user permissions
return $http({
method : 'PATCH',
url : 'api/users/' + encodeURIComponent(userID) + '/permissions',
params : httpParameters,
data : permissionPatch
});
};
return service;
}]);

View File

@@ -21,21 +21,38 @@
*/
/**
* The DAO for protocol operations agains the REST API.
* Service for operating on protocol metadata via the REST API.
*/
angular.module('protocol').factory('protocolDAO', ['$http', function protocolDAO($http) {
angular.module('rest').factory('protocolService', ['$http', 'authenticationService',
function protocolService($http, authenticationService) {
var service = {};
/**
* Makes a request to the REST API to get the list of protocols,
* returning a promise that can be used for processing the results of the call.
* Makes a request to the REST API to get the list of protocols, returning
* a promise that provides an array of @link{Protocol} objects if
* successful.
*
* @returns {promise} A promise for the HTTP call.
* @returns {Promise.<Protocol[]>}
* A promise which will resolve with an array of @link{Protocol}
* objects upon success.
*/
service.getProtocols = function getProtocols() {
return $http.get("api/protocol");
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Retrieve available protocols
return $http({
method : 'GET',
url : 'api/protocols',
params : httpParameters
});
};
return service;
}]);

View File

@@ -0,0 +1,177 @@
/*
* 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.
*/
/**
* Service for operating on users via the REST API.
*/
angular.module('rest').factory('userService', ['$http', 'authenticationService',
function userService($http, authenticationService) {
var service = {};
/**
* Makes a request to the REST API to get the list of users,
* returning a promise that provides an array of @link{User} objects if
* successful.
*
* @param {String} [permissionType]
* The permission type string of the permission that the current user
* must have for a given user to appear within the list. Valid values
* are listed within PermissionSet.ObjectType.
*
* @returns {Promise.<User[]>}
* A promise which will resolve with an array of @link{User} objects
* upon success.
*/
service.getUsers = function getUsers(permissionType) {
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Add permission filter if specified
if (permissionType)
httpParameters.permission = permissionType;
// Retrieve users
return $http({
method : 'GET',
url : 'api/users',
params : httpParameters
});
};
/**
* Makes a request to the REST API to get the user having the given
* username, returning a promise that provides the corresponding
* @link{User} if successful.
*
* @param {String} username
* The username of the user to retrieve.
*
* @returns {Promise.<User>}
* A promise which will resolve with a @link{User} upon success.
*/
service.getUser = function getUser(username) {
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Retrieve user
return $http({
method : 'GET',
url : 'api/users/' + encodeURIComponent(username),
params : httpParameters
});
};
/**
* Makes a request to the REST API to delete a user, returning a promise
* that can be used for processing the results of the call.
*
* @param {User} user
* The user to delete.
*
* @returns {Promise}
* A promise for the HTTP call which will succeed if and only if the
* delete operation is successful.
*/
service.deleteUser = function deleteUser(user) {
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Retrieve user
return $http({
method : 'DELETE',
url : 'api/users/' + encodeURIComponent(user.username),
params : httpParameters
});
};
/**
* Makes a request to the REST API to create a user, returning a promise
* that can be used for processing the results of the call.
*
* @param {User} user
* The user to create.
*
* @returns {Promise}
* A promise for the HTTP call which will succeed if and only if the
* create operation is successful.
*/
service.createUser = function createUser(user) {
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Retrieve user
return $http({
method : 'POST',
url : 'api/users',
params : httpParameters,
data : user
});
};
/**
* Makes a request to the REST API to save a user, returning a promise that
* can be used for processing the results of the call.
*
* @param {User} user
* The user to update.
*
* @returns {Promise}
* A promise for the HTTP call which will succeed if and only if the
* save operation is successful.
*/
service.saveUser = function saveUser(user) {
// Build HTTP parameters set
var httpParameters = {
token : authenticationService.getCurrentToken()
};
// Retrieve user
return $http({
method : 'PUT',
url : 'api/users/' + encodeURIComponent(user.username),
params : httpParameters,
data : user
});
};
return service;
}]);

View File

@@ -23,7 +23,7 @@
/**
* Service which defines the Connection class.
*/
angular.module('connection').factory('Connection', [function defineConnection() {
angular.module('rest').factory('Connection', [function defineConnection() {
/**
* The object returned by REST API calls when representing the data
@@ -70,24 +70,15 @@ angular.module('connection').factory('Connection', [function defineConnection()
*/
this.protocol = template.protocol;
/**
* All previous and current usages of this connection, along with
* associated users.
*
* @type ConnectionHistoryEntry[]
*/
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>
*/
this.parameters = template.parameters || {};
this.parameters = template.parameters;
};

View File

@@ -0,0 +1,127 @@
/*
* 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.
*/
/**
* Service which defines the ConnectionGroup class.
*/
angular.module('rest').factory('ConnectionGroup', [function defineConnectionGroup() {
/**
* The object returned by REST API calls when representing the data
* associated with a connection group.
*
* @constructor
* @param {ConnectionGroup|Object} [template={}]
* The object whose properties should be copied within the new
* ConnectionGroup.
*/
var ConnectionGroup = function ConnectionGroup(template) {
// Use empty object by default
template = template || {};
/**
* The unique identifier associated with this connection group.
*
* @type String
*/
this.identifier = template.identifier;
/**
* The unique identifier of the connection group that contains this
* connection group.
*
* @type String
* @default ConnectionGroup.ROOT_IDENTIFIER
*/
this.parentIdentifier = template.parentIdentifier || ConnectionGroup.ROOT_IDENTIFIER;
/**
* The human-readable name of this connection group, which is not
* necessarily unique.
*
* @type String
*/
this.name = template.name;
/**
* The type of this connection group, which may be either
* ConnectionGroup.Type.ORGANIZATIONAL or
* ConnectionGroup.Type.BALANCING.
*
* @type String
* @default ConnectionGroup.Type.ORGANIZATIONAL
*/
this.type = template.type || ConnectionGroup.Type.ORGANIZATIONAL;
/**
* An array of all child connections, if known. This property may be
* null or undefined if children have not been queried, and thus the
* child connections are unknown.
*
* @type Connection[]
*/
this.childConnections = template.childConnections;
/**
* An array of all child connection groups, if known. This property may
* be null or undefined if children have not been queried, and thus the
* child connection groups are unknown.
*
* @type ConnectionGroup[]
*/
this.childConnectionGroups = template.childConnectionGroups;
};
/**
* The reserved identifier which always represents the root connection
* group.
*
* @type String
*/
ConnectionGroup.ROOT_IDENTIFIER = "ROOT";
/**
* All valid connection group types.
*/
ConnectionGroup.Type = {
/**
* The type string associated with balancing connection groups.
*
* @type String
*/
BALANCING : "BALANCING",
/**
* The type string associated with organizational connection groups.
*
* @type String
*/
ORGANIZATIONAL : "ORGANIZATIONAL"
};
return ConnectionGroup;
}]);

View File

@@ -23,7 +23,7 @@
/**
* Service which defines the ConnectionHistoryEntry class.
*/
angular.module('connection').factory('ConnectionHistoryEntry', [function defineConnectionHistoryEntry() {
angular.module('rest').factory('ConnectionHistoryEntry', [function defineConnectionHistoryEntry() {
/**
* The object returned by REST API calls when representing the data

View File

@@ -0,0 +1,94 @@
/*
* 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.
*/
/**
* Service which defines the PermissionPatch class.
*/
angular.module('rest').factory('PermissionPatch', [function definePermissionPatch() {
/**
* The object returned by REST API calls when representing changes to the
* permissions granted to a specific user.
*
* @constructor
* @param {PermissionPatch|Object} [template={}]
* The object whose properties should be copied within the new
* PermissionPatch.
*/
var PermissionPatch = function PermissionPatch(template) {
// Use empty object by default
template = template || {};
/**
* The operation to apply to the permissions indicated by the path.
* Valid operation values are defined within PermissionPatch.Operation.
*
* @type String
*/
this.op = template.op;
/**
* The path of the permissions to modify. Depending on the type of the
* permission, this will be either "/connectionPermissions/ID",
* "/connectionGroupPermissions/ID", "/userPermissions/ID", or
* "/systemPermissions", where "ID" is the identifier of the object
* to which the permissions apply, if any.
*
* @type String
*/
this.path = template.path;
/**
* The permissions being added or removed. If the permission applies to
* an object, such as a connection or connection group, this will be a
* value from PermissionSet.ObjectPermissionType. If the permission
* applies to the system as a whole (the path is "/systemPermissions"),
* this will be a value from PermissionSet.SystemPermissionType.
*
* @type String
*/
this.value = template.value;
};
/**
* All valid patch operations for permissions. Currently, only add and
* remove are supported.
*/
PermissionPatch.Operation = {
/**
* Adds (grants) the specified permission.
*/
ADD : "add",
/**
* Removes (revokes) the specified permission.
*/
REMOVE : "remove"
};
return PermissionPatch;
}]);

View File

@@ -0,0 +1,280 @@
/*
* 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.
*/
/**
* Service which defines the PermissionSet class.
*/
angular.module('rest').factory('PermissionSet', [function definePermissionSet() {
/**
* The object returned by REST API calls when representing the permissions
* granted to a specific user.
*
* @constructor
* @param {PermissionSet|Object} [template={}]
* The object whose properties should be copied within the new
* PermissionSet.
*/
var PermissionSet = function PermissionSet(template) {
// Use empty object by default
template = template || {};
/**
* Map of connection identifiers to the corresponding array of granted
* permissions. Each permission is represented by a string listed
* within PermissionSet.ObjectPermissionType.
*
* @type Object.<String, String[]>
*/
this.connectionPermissions = template.connectionPermissions || {};
/**
* Map of connection group identifiers to the corresponding array of
* granted permissions. Each permission is represented by a string
* listed within PermissionSet.ObjectPermissionType.
*
* @type Object.<String, String[]>
*/
this.connectionGroupPermissions = template.connectionGroupPermissions || {};
/**
* Map of user identifiers to the corresponding array of granted
* permissions. Each permission is represented by a string listed
* within PermissionSet.ObjectPermissionType.
*
* @type Object.<String, String[]>
*/
this.userPermissions = template.userPermissions || {};
/**
* Array of granted system permissions. Each permission is represented
* by a string listed within PermissionSet.SystemPermissionType.
*
* @type String[]
*/
this.systemPermissions = template.systemPermissions || [];
};
/**
* Valid object permission type strings.
*/
PermissionSet.ObjectPermissionType = {
/**
* Permission to read from the specified object.
*/
READ : "READ",
/**
* Permission to update the specified object.
*/
UPDATE : "UPDATE",
/**
* Permission to delete the specified object.
*/
DELETE : "DELETE",
/**
* Permission to administer the specified object
*/
ADMINISTER : "ADMINISTER"
};
/**
* Valid system permission type strings.
*/
PermissionSet.SystemPermissionType = {
/**
* Permission to administer the entire system.
*/
ADMINISTER : "ADMINISTER",
/**
* Permission to create new users.
*/
CREATE_USER : "CREATE_USER",
/**
* Permission to create new connections.
*/
CREATE_CONNECTION : "CREATE_CONNECTION",
/**
* Permission to create new connection groups.
*/
CREATE_CONNECTION_GROUP : "CREATE_CONNECTION_GROUP"
};
/**
* Returns whether the given permission is granted for at least one
* arbitrary object, regardless of ID.
*
* @param {Object.<String, String[]>} permMap
* The permission map to check, where each entry maps an object
* identifer to the array of granted permissions.
*
* @param {String} type
* The permission to search for, as defined by
* PermissionSet.ObjectPermissionType.
*
* @returns {Boolean}
* true if the permission is present (granted), false otherwise.
*/
var containsPermission = function containsPermission(permMap, type) {
// Search all identifiers for given permission
for (var identifier in permMap) {
// If permission is granted, then no further searching is necessary
if (permMap[identifier].indexOf(type) !== -1)
return true;
}
// No such permission exists
return false;
};
/**
* Returns whether the given permission is granted for the arbitrary
* object having the given ID. If no ID is given, this function determines
* whether the permission is granted at all for any such arbitrary object.
*
* @param {Object.<String, String[]>} permMap
* The permission map to check, where each entry maps an object
* identifer to the array of granted permissions.
*
* @param {String} type
* The permission to search for, as defined by
* PermissionSet.ObjectPermissionType.
*
* @param {String} [identifier]
* The identifier of the object to which the permission applies.
*
* @returns {Boolean}
* true if the permission is present (granted), false otherwise.
*/
var hasPermission = function hasPermission(permMap, type, identifier) {
// If no identifier given, search ignoring the identifier
if (!identifier)
return containsPermission(permMap, type);
// If identifier not present at all, there are no such permissions
if (!(identifier in permMap))
return false;
return permMap[identifier].indexOf(type) !== -1;
};
/**
* Returns whether the given permission is granted for the connection
* having the given ID.
*
* @param {PermissionSet|Object} permSet
* The permission set to check.
*
* @param {String} type
* The permission to search for, as defined by
* PermissionSet.ObjectPermissionType.
*
* @param {String} identifier
* The identifier of the connection to which the permission applies.
*
* @returns {Boolean}
* true if the permission is present (granted), false otherwise.
*/
PermissionSet.hasConnectionPermission = function hasConnectionPermission(permSet, type, identifier) {
return hasPermission(permSet.connectionPermissions, type, identifier);
};
/**
* Returns whether the given permission is granted for the connection group
* having the given ID.
*
* @param {PermissionSet|Object} permSet
* The permission set to check.
*
* @param {String} type
* The permission to search for, as defined by
* PermissionSet.ObjectPermissionType.
*
* @param {String} identifier
* The identifier of the connection group to which the permission
* applies.
*
* @returns {Boolean}
* true if the permission is present (granted), false otherwise.
*/
PermissionSet.hasConnectionGroupPermission = function hasConnectionGroupPermission(permSet, type, identifier) {
return hasPermission(permSet.connectionGroupPermissions, type, identifier);
};
/**
* Returns whether the given permission is granted for the user having the
* given ID.
*
* @param {PermissionSet|Object} permSet
* The permission set to check.
*
* @param {String} type
* The permission to search for, as defined by
* PermissionSet.ObjectPermissionType.
*
* @param {String} identifier
* The identifier of the user to which the permission applies.
*
* @returns {Boolean}
* true if the permission is present (granted), false otherwise.
*/
PermissionSet.hasUserPermission = function hasUserPermission(permSet, type, identifier) {
return hasPermission(permSet.userPermissions, type, identifier);
};
/**
* Returns whether the given permission is granted at the system level.
*
* @param {PermissionSet|Object} permSet
* The permission set to check.
*
* @param {String} type
* The permission to search for, as defined by
* PermissionSet.SystemPermissionType.
*
* @returns {Boolean}
* true if the permission is present (granted), false otherwise.
*/
PermissionSet.hasSystemPermission = function hasSystemPermission(permSet, type) {
return permSet.systemPermissions.indexOf(type) !== -1;
};
return PermissionSet;
}]);

View File

@@ -21,37 +21,49 @@
*/
/**
* A service for performing useful user related functionaltiy.
* Service which defines the Protocol class.
*/
angular.module('user').factory('userService', ['$injector', function userService($injector) {
angular.module('rest').factory('Protocol', [function defineProtocol() {
var permissionCheckService = $injector.get('permissionCheckService');
var service = {};
/**
* Filters the list of users using the provided permissions.
* The object returned by REST API calls when representing the data
* associated with a supported remote desktop protocol.
*
* @param {array} users The user list.
*
* @param {object} permissionList The list of permissions to use
* when filtering.
*
* @param {object} permissionCriteria The required permission for each user.
*
* @return {array} The filtered list.
* @constructor
* @param {Protocol|Object} [template={}]
* The object whose properties should be copied within the new
* Protocol.
*/
service.filterUsersByPermission = function filterUsersByPermission(users, permissionList, permissionCriteria) {
for(var i = 0; i < users.length; i++) {
if(!permissionCheckService.checkPermission(permissionList,
"USER", user.username, permissionCriteria)) {
items.splice(i, 1);
continue;
}
}
return users;
var Protocol = function Protocol(template) {
// Use empty object by default
template = template || {};
/**
* The name which uniquely identifies this protocol.
*
* @type String
*/
this.name = template.name;
/**
* A human-readable name for this protocol.
*
* @type String
*/
this.title = template.title;
/**
* An array of all known parameters for this protocol, their types,
* and other information.
*
* @type ProtocolParameter[]
* @default []
*/
this.parameters = template.parameters || [];
};
return service;
}]);
return Protocol;
}]);

View File

@@ -0,0 +1,152 @@
/*
* 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.
*/
/**
* Service which defines the ProtocolParameter class.
*/
angular.module('rest').factory('ProtocolParameter', [function defineProtocolParameter() {
/**
* The object returned by REST API calls when representing the data
* associated with a configuration parameter of a remote desktop protocol.
*
* @constructor
* @param {ProtocolParameter|Object} [template={}]
* The object whose properties should be copied within the new
* ProtocolParameter.
*/
var ProtocolParameter = function ProtocolParameter(template) {
// Use empty object by default
template = template || {};
/**
* The name which uniquely identifies this parameter.
*
* @type String
*/
this.name = template.name;
/**
* A human-readable name for this parameter.
*
* @type String
*/
this.title = template.title;
/**
* The type string defining which values this parameter may contain,
* as well as what properties are applicable. Valid types are listed
* within ProtocolParameter.Type.
*
* @type String
* @default ProtocolParameter.Type.TEXT
*/
this.type = template.type || ProtocolParameter.Type.TEXT;
/**
* The value to set the parameter to, in the case of a BOOLEAN
* parameter, to enable that parameter's effect.
*
* @type String
*/
this.value = template.value;
/**
* All possible legal values for this parameter. This property is only
* applicable to ENUM type parameters.
*
* @type ProtocolParameterOption[]
*/
this.options = template.options;
};
/**
* All valid protocol parameter types.
*/
ProtocolParameter.Type = {
/**
* The type string associated with parameters that may contain a single
* line of arbitrary text.
*
* @type String
*/
TEXT : "TEXT",
/**
* The type string associated with parameters that may contain an
* arbitrary string, where that string represents the username of the
* user authenticating with the remote desktop service.
*
* @type String
*/
USERNAME : "USERNAME",
/**
* The type string associated with parameters that may contain an
* arbitrary string, where that string represents the password of the
* user authenticating with the remote desktop service.
*
* @type String
*/
PASSWORD : "PASSWORD",
/**
* The type string associated with parameters that may contain only
* numeric values.
*
* @type String
*/
NUMERIC : "NUMERIC",
/**
* The type string associated with parameters that may contain only a
* single possible value, where that value enables the parameter's
* effect.
*
* @type String
*/
BOOLEAN : "BOOLEAN",
/**
* The type string associated with parameters that may contain a
* strictly-defined set of possible values.
*
* @type String
*/
ENUM : "ENUM",
/**
* The type string associated with parameters that may contain any
* number of lines of arbitrary text.
*
* @type String
*/
MULTILINE : "MULTILINE"
};
return ProtocolParameter;
}]);

View File

@@ -20,40 +20,42 @@
* THE SOFTWARE.
*/
package org.glyptodon.guacamole.net.basic.rest.user;
import java.util.ArrayList;
import java.util.List;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.Directory;
import org.glyptodon.guacamole.net.auth.User;
/**
* A service for performing useful manipulations on REST Users.
*
* @author James Muehlner
* Service which defines the ProtocolParameterOption class.
*/
public class UserService {
/**
* Converts a user directory to a list of APIUser objects for
* exposing with the REST endpoints.
*
* @param userDirectory The user directory to convert for REST endpoint use.
* @return A List of APIUser objects for use with the REST endpoint.
* @throws GuacamoleException If an error occurs while converting the
* user directory.
*/
public List<APIUser> convertUserList(Directory<String, User> userDirectory)
throws GuacamoleException {
List<APIUser> restUsers = new ArrayList<APIUser>();
for(String username : userDirectory.getIdentifiers())
restUsers.add(new APIUser(userDirectory.get(username)));
angular.module('rest').factory('ProtocolParameterOption', [function defineProtocolParameterOption() {
return restUsers;
/**
* The object returned by REST API calls when representing a single possible
* legal value of a configuration parameter of a remote desktop protocol.
*
* @constructor
* @param {ProtocolParameterOption|Object} [template={}]
* The object whose properties should be copied within the new
* ProtocolParameterOption.
*/
var ProtocolParameterOption = function ProtocolParameterOption(template) {
}
}
// Use empty object by default
template = template || {};
/**
* A human-readable name for this parameter value.
*
* @type String
*/
this.title = template.title;
/**
* The actual value to set the parameter to, if this option is
* selected.
*
* @type String
*/
this.value = template.value;
};
return ProtocolParameterOption;
}]);

View File

@@ -21,6 +21,43 @@
*/
/**
* The module for code relating to connections.
* Service which defines the User class.
*/
angular.module('connection', ['auth']);
angular.module('rest').factory('User', [function defineUser() {
/**
* The object returned by REST API calls when representing the data
* associated with a user.
*
* @constructor
* @param {User|Object} [template={}]
* The object whose properties should be copied within the new
* User.
*/
var User = function User(template) {
// Use empty object by default
template = template || {};
/**
* The name which uniquely identifies this user.
*
* @type String
*/
this.username = template.username;
/**
* This user's password. Note that the REST API may not populate this
* property for the sake of security. In most cases, it's not even
* possible for the authentication layer to retrieve the user's true
* password.
*
* @type String
*/
this.password = template.password;
};
return User;
}]);

View File

@@ -1,100 +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.
*/
/**
* The DAO for connection operations agains the REST API.
*/
angular.module('user').factory('userDAO', ['$http', 'authenticationService',
function userDAO($http, authenticationService) {
var service = {};
/**
* Makes a request to the REST API to get the list of users,
* returning a promise that can be used for processing the results of the call.
*
* @returns {promise} A promise for the HTTP call.
*/
service.getUsers = function getUsers() {
return $http.get("api/user?token=" + authenticationService.getCurrentToken());
};
/**
* Makes a request to the REST API to get the list of users,
* returning a promise that can be used for processing the results of the call.
*
* @param {string} userID The ID of the user to retrieve.
*
* @returns {promise} A promise for the HTTP call.
*/
service.getUser = function getUser(userID) {
return $http.get("api/user/" + userID + "/?token=" + authenticationService.getCurrentToken());
};
/**
* Makes a request to the REST API to delete a user,
* returning a promise that can be used for processing the results of the call.
*
* @param {object} user The user to delete.
*
* @returns {promise} A promise for the HTTP call.
*/
service.deleteUser = function deleteUser(user) {
return $http['delete'](
"api/user/" + user.username +
"?token=" + authenticationService.getCurrentToken());
};
/**
* Makes a request to the REST API to create a user,
* returning a promise that can be used for processing the results of the call.
*
* @param {object} user The user to create.
*
* @returns {promise} A promise for the HTTP call.
*/
service.createUser = function createUser(user) {
return $http.post(
"api/user/"
+ "?token=" + authenticationService.getCurrentToken(),
user
);
}
/**
* Makes a request to the REST API to save a user,
* returning a promise that can be used for processing the results of the call.
*
* @param {object} user The user to update.
*
* @returns {promise} A promise for the HTTP call.
*/
service.saveUser = function saveUser(user) {
return $http.post(
"api/user/" + user.username +
"?token=" + authenticationService.getCurrentToken(),
user);
};
return service;
}]);

View File

@@ -48,7 +48,7 @@ THE SOFTWARE.
<div id="notificationArea">
<div ng-repeat="wrapper in notifications">
<guac-notification notification="wrapper.notification"/>
<div>
</div>
</div>
<script type="text/javascript" src="guacamole.min.js"></script>