GUACAMOLE-5: Include sharing profiles within connection tree.

This commit is contained in:
Michael Jumper
2016-08-04 14:05:32 -07:00
parent 841bc4761a
commit 60152e3841
3 changed files with 193 additions and 20 deletions

View File

@@ -19,12 +19,14 @@
package org.apache.guacamole.rest.connection; package org.apache.guacamole.rest.connection;
import java.util.Collection;
import java.util.Map; import java.util.Map;
import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.annotate.JsonSerialize; import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.protocol.GuacamoleConfiguration; import org.apache.guacamole.protocol.GuacamoleConfiguration;
import org.apache.guacamole.rest.sharingprofile.APISharingProfile;
/** /**
* A simple connection to expose through the REST endpoints. * A simple connection to expose through the REST endpoints.
@@ -65,6 +67,12 @@ public class APIConnection {
*/ */
private Map<String, String> attributes; private Map<String, String> attributes;
/**
* All associated sharing profiles. If sharing profiles are not being
* queried, this may be omitted.
*/
private Collection<APISharingProfile> sharingProfiles;
/** /**
* The count of currently active connections using this connection. * The count of currently active connections using this connection.
*/ */
@@ -227,4 +235,28 @@ public class APIConnection {
this.attributes = attributes; this.attributes = attributes;
} }
/**
* Returns a collection of all associated sharing profiles, or null if
* sharing profiles have not been queried.
*
* @return
* A collection of all associated sharing profiles, or null if sharing
* profiles have not been queried.
*/
public Collection<APISharingProfile> getSharingProfiles() {
return sharingProfiles;
}
/**
* Sets the collection of all associated sharing profiles to the given
* collection, which may be null if sharing profiles have not been queried.
*
* @param sharingProfiles
* The collection containing all sharing profiles associated with this
* connection, or null if sharing profiles have not been queried.
*/
public void setSharingProfiles(Collection<APISharingProfile> sharingProfiles) {
this.sharingProfiles = sharingProfiles;
}
} }

View File

@@ -28,16 +28,20 @@ import java.util.Map;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.ConnectionGroup; import org.apache.guacamole.net.auth.ConnectionGroup;
import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.SharingProfile;
import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.UserContext; import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.net.auth.permission.ObjectPermission; import org.apache.guacamole.net.auth.permission.ObjectPermission;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
import org.apache.guacamole.rest.connection.APIConnection; import org.apache.guacamole.rest.connection.APIConnection;
import org.apache.guacamole.rest.sharingprofile.APISharingProfile;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* Provides access to the entire tree of connection groups and their * Provides access to the entire tree of connection groups, their
* connections. * connections, and any associated sharing profiles.
* *
* @author Michael Jumper * @author Michael Jumper
*/ */
@@ -48,16 +52,38 @@ public class ConnectionGroupTree {
*/ */
private static final Logger logger = LoggerFactory.getLogger(ConnectionGroupTree.class); private static final Logger logger = LoggerFactory.getLogger(ConnectionGroupTree.class);
/**
* The context of the user obtaining this tree.
*/
private final UserContext userContext;
/** /**
* The root connection group as an APIConnectionGroup. * The root connection group as an APIConnectionGroup.
*/ */
private final APIConnectionGroup rootAPIGroup; private final APIConnectionGroup rootAPIGroup;
/**
* All connection permissions granted to the user obtaining this tree.
*/
private final ObjectPermissionSet connectionPermissions;
/**
* All sharing profile permissions granted to the user obtaining this tree.
*/
private final ObjectPermissionSet sharingProfilePermissions;
/**
* The directory of all connections visible to the user obtaining this tree.
*/
private final Directory<Connection> connectionDirectory;
/**
* The directory of all connection groups visible to the user obtaining this
* tree.
*/
private final Directory<ConnectionGroup> connectionGroupDirectory;
/**
* The directory of all sharing profiles visible to the user obtaining this
* tree.
*/
private final Directory<SharingProfile> sharingProfileDirectory;
/** /**
* All connection groups that have been retrieved, stored by their * All connection groups that have been retrieved, stored by their
* identifiers. * identifiers.
@@ -65,6 +91,12 @@ public class ConnectionGroupTree {
private final Map<String, APIConnectionGroup> retrievedGroups = private final Map<String, APIConnectionGroup> retrievedGroups =
new HashMap<String, APIConnectionGroup>(); new HashMap<String, APIConnectionGroup>();
/**
* All connections that have been retrieved, stored by their identifiers.
*/
private final Map<String, APIConnection> retrievedConnections =
new HashMap<String, APIConnection>();
/** /**
* Adds each of the provided connections to the current tree as children * Adds each of the provided connections to the current tree as children
* of their respective parents. The parent connection groups must already * of their respective parents. The parent connection groups must already
@@ -95,7 +127,9 @@ public class ConnectionGroupTree {
} }
// Add child // Add child
children.add(new APIConnection(connection)); APIConnection apiConnection = new APIConnection(connection);
retrievedConnections.put(connection.getIdentifier(), apiConnection);
children.add(apiConnection);
} }
@@ -150,7 +184,55 @@ public class ConnectionGroupTree {
} // end for each connection group } // end for each connection group
} }
/**
* Adds each of the provided sharing profiles to the current tree as
* children of their respective primary connections. The primary connections
* must already be added.
*
* @param sharingProfiles
* The sharing profiles to add to the tree.
*
* @throws GuacamoleException
* If an error occurs while adding the sharing profiles to the tree.
*/
private void addSharingProfiles(Collection<SharingProfile> sharingProfiles)
throws GuacamoleException {
// Add each sharing profile to the tree
for (SharingProfile sharingProfile : sharingProfiles) {
// Retrieve the sharing profile's associated connection
String primaryConnectionIdentifier = sharingProfile.getPrimaryConnectionIdentifier();
APIConnection primaryConnection = retrievedConnections.get(primaryConnectionIdentifier);
// Add the sharing profile as a child of the primary connection
if (primaryConnection != null) {
Collection<APISharingProfile> children = primaryConnection.getSharingProfiles();
// Create child collection if it does not yet exist
if (children == null) {
children = new ArrayList<APISharingProfile>();
primaryConnection.setSharingProfiles(children);
}
// Add child
children.add(new APISharingProfile(sharingProfile));
}
// Warn of internal consistency issues
else
logger.debug("Sharing profile \"{}\" cannot be added to the "
+ "tree: primary connection \"{}\" does not actually "
+ "exist.", sharingProfile.getIdentifier(),
primaryConnectionIdentifier);
} // end for each sharing profile
}
/** /**
* Adds all descendants of the given parent groups to their corresponding * Adds all descendants of the given parent groups to their corresponding
* parents already stored under root. * parents already stored under root.
@@ -167,7 +249,7 @@ public class ConnectionGroupTree {
* @throws GuacamoleException * @throws GuacamoleException
* If an error occurs while retrieving the descendants. * If an error occurs while retrieving the descendants.
*/ */
private void addDescendants(Collection<ConnectionGroup> parents, private void addConnectionGroupDescendants(Collection<ConnectionGroup> parents,
List<ObjectPermission.Type> permissions) List<ObjectPermission.Type> permissions)
throws GuacamoleException { throws GuacamoleException {
@@ -185,22 +267,64 @@ public class ConnectionGroupTree {
} }
// Filter identifiers based on permissions, if requested // Filter identifiers based on permissions, if requested
if (permissions != null && !permissions.isEmpty()) { if (permissions != null && !permissions.isEmpty())
ObjectPermissionSet permissionSet = userContext.self().getConnectionPermissions(); childConnectionIdentifiers = connectionPermissions.getAccessibleObjects(
childConnectionIdentifiers = permissionSet.getAccessibleObjects(permissions, childConnectionIdentifiers); permissions, childConnectionIdentifiers);
}
// Retrieve child connections // Retrieve child connections
if (!childConnectionIdentifiers.isEmpty()) { if (!childConnectionIdentifiers.isEmpty()) {
Collection<Connection> childConnections = userContext.getConnectionDirectory().getAll(childConnectionIdentifiers); Collection<Connection> childConnections = connectionDirectory.getAll(childConnectionIdentifiers);
addConnections(childConnections); addConnections(childConnections);
addConnectionDescendants(childConnections, permissions);
} }
// Retrieve child connection groups // Retrieve child connection groups
if (!childConnectionGroupIdentifiers.isEmpty()) { if (!childConnectionGroupIdentifiers.isEmpty()) {
Collection<ConnectionGroup> childConnectionGroups = userContext.getConnectionGroupDirectory().getAll(childConnectionGroupIdentifiers); Collection<ConnectionGroup> childConnectionGroups = connectionGroupDirectory.getAll(childConnectionGroupIdentifiers);
addConnectionGroups(childConnectionGroups); addConnectionGroups(childConnectionGroups);
addDescendants(childConnectionGroups, permissions); addConnectionGroupDescendants(childConnectionGroups, permissions);
}
}
/**
* Adds all descendant sharing profiles of the given connections to their
* corresponding primary connections already stored under root.
*
* @param connections
* The connections whose descendant sharing profiles should be added to
* the tree.
*
* @param permissions
* If specified and non-empty, limit added sharing profiles to only
* those for which the current user has any of the given
* permissions. Otherwise, all visible sharing profiles are added.
*
* @throws GuacamoleException
* If an error occurs while retrieving the descendants.
*/
private void addConnectionDescendants(Collection<Connection> connections,
List<ObjectPermission.Type> permissions)
throws GuacamoleException {
// If no connections, nothing to do
if (connections.isEmpty())
return;
// Build lists of sharing profile identifiers for retrieval
Collection<String> identifiers = new ArrayList<String>();
for (Connection connection : connections)
identifiers.addAll(connection.getSharingProfileIdentifiers());
// Filter identifiers based on permissions, if requested
if (permissions != null && !permissions.isEmpty())
identifiers = sharingProfilePermissions.getAccessibleObjects(
permissions, identifiers);
// Retrieve and add all associated sharing profiles
if (!identifiers.isEmpty()) {
Collection<SharingProfile> sharingProfiles = sharingProfileDirectory.getAll(identifiers);
addSharingProfiles(sharingProfiles);
} }
} }
@@ -229,14 +353,22 @@ public class ConnectionGroupTree {
public ConnectionGroupTree(UserContext userContext, ConnectionGroup root, public ConnectionGroupTree(UserContext userContext, ConnectionGroup root,
List<ObjectPermission.Type> permissions) throws GuacamoleException { List<ObjectPermission.Type> permissions) throws GuacamoleException {
this.userContext = userContext;
// Store root of tree // Store root of tree
this.rootAPIGroup = new APIConnectionGroup(root); this.rootAPIGroup = new APIConnectionGroup(root);
retrievedGroups.put(root.getIdentifier(), this.rootAPIGroup); retrievedGroups.put(root.getIdentifier(), this.rootAPIGroup);
// Store user's current permissions
User self = userContext.self();
this.connectionPermissions = self.getConnectionPermissions();
this.sharingProfilePermissions = self.getSharingProfilePermissions();
// Store required directories
this.connectionDirectory = userContext.getConnectionDirectory();
this.connectionGroupDirectory = userContext.getConnectionGroupDirectory();
this.sharingProfileDirectory = userContext.getSharingProfileDirectory();
// Add all descendants // Add all descendants
addDescendants(Collections.singleton(root), permissions); addConnectionGroupDescendants(Collections.singleton(root), permissions);
} }

View File

@@ -95,6 +95,15 @@ angular.module('rest').factory('Connection', [function defineConnection() {
*/ */
this.activeConnections = template.activeConnections; this.activeConnections = template.activeConnections;
/**
* An array of all associated sharing profiles, if known. This property
* may be null or undefined if sharing profiles have not been queried,
* and thus the sharing profiles are unknown.
*
* @type SharingProfile[]
*/
this.sharingProfiles = template.sharingProfiles;
}; };
return Connection; return Connection;