From 60152e38411e40fc9d059dda9bf9f8c33eb91773 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 4 Aug 2016 14:05:32 -0700 Subject: [PATCH] GUACAMOLE-5: Include sharing profiles within connection tree. --- .../rest/connection/APIConnection.java | 32 ++++ .../connectiongroup/ConnectionGroupTree.java | 172 ++++++++++++++++-- .../main/webapp/app/rest/types/Connection.js | 9 + 3 files changed, 193 insertions(+), 20 deletions(-) diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connection/APIConnection.java b/guacamole/src/main/java/org/apache/guacamole/rest/connection/APIConnection.java index 639318ff0..cd0441331 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/connection/APIConnection.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/connection/APIConnection.java @@ -19,12 +19,14 @@ package org.apache.guacamole.rest.connection; +import java.util.Collection; import java.util.Map; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.protocol.GuacamoleConfiguration; +import org.apache.guacamole.rest.sharingprofile.APISharingProfile; /** * A simple connection to expose through the REST endpoints. @@ -65,6 +67,12 @@ public class APIConnection { */ private Map attributes; + /** + * All associated sharing profiles. If sharing profiles are not being + * queried, this may be omitted. + */ + private Collection sharingProfiles; + /** * The count of currently active connections using this connection. */ @@ -227,4 +235,28 @@ public class APIConnection { 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 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 sharingProfiles) { + this.sharingProfiles = sharingProfiles; + } + } diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupTree.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupTree.java index d2f6f6fb3..2541fa2db 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupTree.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupTree.java @@ -28,16 +28,20 @@ import java.util.Map; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.auth.Connection; 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.permission.ObjectPermission; import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; import org.apache.guacamole.rest.connection.APIConnection; +import org.apache.guacamole.rest.sharingprofile.APISharingProfile; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Provides access to the entire tree of connection groups and their - * connections. + * Provides access to the entire tree of connection groups, their + * connections, and any associated sharing profiles. * * @author Michael Jumper */ @@ -48,16 +52,38 @@ public class ConnectionGroupTree { */ 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. */ 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 connectionDirectory; + + /** + * The directory of all connection groups visible to the user obtaining this + * tree. + */ + private final Directory connectionGroupDirectory; + + /** + * The directory of all sharing profiles visible to the user obtaining this + * tree. + */ + private final Directory sharingProfileDirectory; + /** * All connection groups that have been retrieved, stored by their * identifiers. @@ -65,6 +91,12 @@ public class ConnectionGroupTree { private final Map retrievedGroups = new HashMap(); + /** + * All connections that have been retrieved, stored by their identifiers. + */ + private final Map retrievedConnections = + new HashMap(); + /** * Adds each of the provided connections to the current tree as children * of their respective parents. The parent connection groups must already @@ -95,7 +127,9 @@ public class ConnectionGroupTree { } // 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 } - + + /** + * 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 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 children = primaryConnection.getSharingProfiles(); + + // Create child collection if it does not yet exist + if (children == null) { + children = new ArrayList(); + 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 * parents already stored under root. @@ -167,7 +249,7 @@ public class ConnectionGroupTree { * @throws GuacamoleException * If an error occurs while retrieving the descendants. */ - private void addDescendants(Collection parents, + private void addConnectionGroupDescendants(Collection parents, List permissions) throws GuacamoleException { @@ -185,22 +267,64 @@ public class ConnectionGroupTree { } // Filter identifiers based on permissions, if requested - if (permissions != null && !permissions.isEmpty()) { - ObjectPermissionSet permissionSet = userContext.self().getConnectionPermissions(); - childConnectionIdentifiers = permissionSet.getAccessibleObjects(permissions, childConnectionIdentifiers); - } + if (permissions != null && !permissions.isEmpty()) + childConnectionIdentifiers = connectionPermissions.getAccessibleObjects( + permissions, childConnectionIdentifiers); // Retrieve child connections if (!childConnectionIdentifiers.isEmpty()) { - Collection childConnections = userContext.getConnectionDirectory().getAll(childConnectionIdentifiers); + Collection childConnections = connectionDirectory.getAll(childConnectionIdentifiers); addConnections(childConnections); + addConnectionDescendants(childConnections, permissions); } // Retrieve child connection groups if (!childConnectionGroupIdentifiers.isEmpty()) { - Collection childConnectionGroups = userContext.getConnectionGroupDirectory().getAll(childConnectionGroupIdentifiers); + Collection childConnectionGroups = connectionGroupDirectory.getAll(childConnectionGroupIdentifiers); 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 connections, + List permissions) + throws GuacamoleException { + + // If no connections, nothing to do + if (connections.isEmpty()) + return; + + // Build lists of sharing profile identifiers for retrieval + Collection identifiers = new ArrayList(); + 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 sharingProfiles = sharingProfileDirectory.getAll(identifiers); + addSharingProfiles(sharingProfiles); } } @@ -229,14 +353,22 @@ public class ConnectionGroupTree { public ConnectionGroupTree(UserContext userContext, ConnectionGroup root, List permissions) throws GuacamoleException { - this.userContext = userContext; - // Store root of tree this.rootAPIGroup = new APIConnectionGroup(root); 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 - addDescendants(Collections.singleton(root), permissions); + addConnectionGroupDescendants(Collections.singleton(root), permissions); } diff --git a/guacamole/src/main/webapp/app/rest/types/Connection.js b/guacamole/src/main/webapp/app/rest/types/Connection.js index e000761d8..b4639b23a 100644 --- a/guacamole/src/main/webapp/app/rest/types/Connection.js +++ b/guacamole/src/main/webapp/app/rest/types/Connection.js @@ -95,6 +95,15 @@ angular.module('rest').factory('Connection', [function defineConnection() { */ 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;