diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AbstractUser.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AbstractUser.java index 96cdb619e..d3232b6cb 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AbstractUser.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AbstractUser.java @@ -44,12 +44,12 @@ public abstract class AbstractUser implements User { private String password; @Override - public String getUsername() { + public String getIdentifier() { return username; } @Override - public void setUsername(String username) { + public void setIdentifier(String username) { this.username = username; } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Connectable.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Connectable.java new file mode 100644 index 000000000..61d2ebfd9 --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Connectable.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2013 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.auth; + +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.GuacamoleSocket; +import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; + +/** + * An object which Guacamole can connect to. + * + * @author Michael Jumper + */ +public interface Connectable { + + /** + * Establishes a connection to guacd using the information associated with + * this object. The connection will be provided the given client + * information. + * + * @param info + * Information associated with the connecting client. + * + * @return + * A fully-established GuacamoleSocket. + * + * @throws GuacamoleException + * If an error occurs while connecting to guacd, or if permission to + * connect is denied. + */ + public GuacamoleSocket connect(GuacamoleClientInformation info) + throws GuacamoleException; + + /** + * Returns the number of active connections associated with this object. + * Implementations may simply return 0 if this value is not tracked. + * + * @return + * The number of active connections associated with this object. + */ + public int getActiveConnections(); + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Connection.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Connection.java index cfd6447f2..6fac0809f 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Connection.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Connection.java @@ -24,8 +24,6 @@ package org.glyptodon.guacamole.net.auth; import java.util.List; import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.GuacamoleSocket; -import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; /** @@ -36,7 +34,7 @@ import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; * * @author Michael Jumper */ -public interface Connection { +public interface Connection extends Identifiable, Connectable { /** * Returns the name assigned to this Connection. @@ -51,24 +49,6 @@ public interface Connection { */ public void setName(String name); - /** - * Returns the unique identifier assigned to this Connection. All - * connections must have a deterministic, unique identifier which may not - * be null. - * - * @return - * The unique identifier assigned to this Connection, which may not be - * null. - */ - public String getIdentifier(); - - /** - * Sets the identifier assigned to this Connection. - * - * @param identifier The identifier to assign. - */ - public void setIdentifier(String identifier); - /** * Returns the unique identifier of the parent ConnectionGroup for * this Connection. @@ -104,21 +84,6 @@ public interface Connection { */ public void setConfiguration(GuacamoleConfiguration config); - /** - * Establishes a connection to guacd using the GuacamoleConfiguration - * associated with this Connection, and returns the resulting, connected - * GuacamoleSocket. The GuacamoleSocket will be pre-configured and will - * already have passed the handshake stage. - * - * @param info Information associated with the connecting client. - * @return A fully-established GuacamoleSocket. - * - * @throws GuacamoleException If an error occurs while connecting to guacd, - * or if permission to connect is denied. - */ - public GuacamoleSocket connect(GuacamoleClientInformation info) - throws GuacamoleException; - /** * Returns a list of ConnectionRecords representing the usage history * of this Connection, including any active users. ConnectionRecords diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionGroup.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionGroup.java index 6b5f84712..97c66ad02 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionGroup.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionGroup.java @@ -22,9 +22,8 @@ package org.glyptodon.guacamole.net.auth; +import java.util.Set; import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.GuacamoleSocket; -import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; /** * Represents a connection group, which can contain both other connection groups @@ -32,10 +31,29 @@ import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; * * @author James Muehlner */ -public interface ConnectionGroup { - +public interface ConnectionGroup extends Identifiable, Connectable { + + /** + * All legal types of connection group. + */ public enum Type { - ORGANIZATIONAL, BALANCING + + /** + * A connection group that purely organizes other connections or + * connection groups, serving only as a container. An organizational + * connection group is analogous to a directory or folder in a + * filesystem. + */ + ORGANIZATIONAL, + + /** + * A connection group that acts as a load balancer. A balancing + * connection group can be connected to in the same manner as a + * connection, and will transparently route to the least-used + * underlying connection. + */ + BALANCING + }; /** @@ -51,24 +69,6 @@ public interface ConnectionGroup { */ public void setName(String name); - /** - * Returns the unique identifier assigned to this ConnectionGroup. All - * connection groups must have a deterministic, unique identifier which may - * not be null. - * - * @return - * The unique identifier assigned to this ConnectionGroup, which may - * not be null. - */ - public String getIdentifier(); - - /** - * Sets the identifier assigned to this ConnectionGroup. - * - * @param identifier The identifier to assign. - */ - public void setIdentifier(String identifier); - /** * Returns the unique identifier of the parent ConnectionGroup for * this ConnectionGroup. @@ -101,45 +101,31 @@ public interface ConnectionGroup { public Type getType(); /** - * Retrieves a Directory which can be used to view and manipulate - * connections and their configurations, but only as allowed by the - * permissions given to the user. + * Returns the identifiers of all readable connections that are children + * of this connection group. * - * @return A Directory whose operations are bound by the permissions of - * the user. + * @return + * The set of identifiers of all readable connections that are children + * of this connection group. * - * @throws GuacamoleException If an error occurs while creating the - * Directory. + * @throws GuacamoleException + * If an error occurs while retrieving the identifiers. */ - Directory getConnectionDirectory() - throws GuacamoleException; + public Set getConnectionIdentifiers() throws GuacamoleException; /** - * Retrieves a Directory which can be used to view and manipulate - * connection groups and their members, but only as allowed by the - * permissions given to the user. + * Returns the identifiers of all readable connection groups that are + * children of this connection group. * - * @return A Directory whose operations are bound by the permissions of - * the user. + * @return + * The set of identifiers of all readable connection groups that are + * children of this connection group. * - * @throws GuacamoleException If an error occurs while creating the - * Directory. + * @throws GuacamoleException + * If an error occurs while retrieving the identifiers. */ - Directory getConnectionGroupDirectory() + + public Set getConnectionGroupIdentifiers() throws GuacamoleException; - /** - * Establishes a connection to guacd using a connection chosen from among - * the connections in this ConnectionGroup, and returns the resulting, - * connected GuacamoleSocket. - * - * @param info Information associated with the connecting client. - * @return A fully-established GuacamoleSocket. - * - * @throws GuacamoleException If an error occurs while connecting to guacd, - * or if permission to connect is denied. - */ - public GuacamoleSocket connect(GuacamoleClientInformation info) - throws GuacamoleException; - } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Directory.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Directory.java index e08446340..4dbb60ce9 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Directory.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Directory.java @@ -22,6 +22,7 @@ package org.glyptodon.guacamole.net.auth; +import java.util.Collection; import java.util.Set; import org.glyptodon.guacamole.GuacamoleException; @@ -33,11 +34,10 @@ import org.glyptodon.guacamole.GuacamoleException; * function. * * @author Michael Jumper - * @param The type of identifier used to identify objects - * stored within this Directory. - * @param The type of objects stored within this Directory. + * @param + * The type of objects stored within this Directory. */ -public interface Directory { +public interface Directory { /** * Returns the object having the given identifier. Note that changes to @@ -55,7 +55,30 @@ public interface Directory { * object, or if permission for retrieving the * object is denied. */ - ObjectType get(IdentifierType identifier) throws GuacamoleException; + ObjectType get(String identifier) throws GuacamoleException; + + /** + * Returns the objects having the given identifiers. Note that changes to + * any object returned will not necessarily affect the object stored within + * the Directory. To update an object stored within a + * Directory such that future calls to get() will return the updated + * object, you must call update() on the object after modification. + * + * @param identifiers + * The identifiers to use when locating the objects to return. + * + * @return + * The objects having the given identifiers. If any identifiers do not + * correspond to accessible objects, those identifiers will be ignored. + * If no objects correspond to any of the given identifiers, the + * returned collection will be empty. + * + * @throws GuacamoleException + * If an error occurs while retrieving the objects, or if permission + * to retrieve the requested objects is denied. + */ + Collection getAll(Collection identifiers) + throws GuacamoleException; /** * Returns a Set containing all identifiers for all objects within this @@ -65,7 +88,7 @@ public interface Directory { * @throws GuacamoleException If an error occurs while retrieving * the identifiers. */ - Set getIdentifiers() throws GuacamoleException; + Set getIdentifiers() throws GuacamoleException; /** * Adds the given object to the overall set. @@ -97,18 +120,6 @@ public interface Directory { * @throws GuacamoleException If an error occurs while removing the object, * or if removing object is not allowed. */ - void remove(IdentifierType identifier) throws GuacamoleException; - - /** - * Moves the object with the given identifier to the given directory. - * - * @param identifier The identifier of the object to remove. - * @param directory The directory to move the object to. - * - * @throws GuacamoleException If an error occurs while moving the object, - * or if moving object is not allowed. - */ - void move(IdentifierType identifier, Directory directory) - throws GuacamoleException; + void remove(String identifier) throws GuacamoleException; } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Identifiable.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Identifiable.java new file mode 100644 index 000000000..8490bbf7c --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/Identifiable.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2013 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.auth; + +/** + * An object which has a deterministic, unique identifier, which may not be + * null. + * + * @author Michael Jumper + */ +public interface Identifiable { + + /** + * Returns the unique identifier assigned to this object. All identifiable + * objects must have a deterministic, unique identifier which may not be + * null. + * + * @return + * The unique identifier assigned to this object, which may not be + * null. + */ + public String getIdentifier(); + + /** + * Sets the identifier assigned to this object. + * + * @param identifier + * The identifier to assign. + */ + public void setIdentifier(String identifier); + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/User.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/User.java index 8409844c1..09e4c2e78 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/User.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/User.java @@ -22,9 +22,9 @@ package org.glyptodon.guacamole.net.auth; -import java.util.Set; import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.net.auth.permission.Permission; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; +import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; /** @@ -32,24 +32,7 @@ import org.glyptodon.guacamole.net.auth.permission.Permission; * * @author Michael Jumper */ -public interface User { - - /** - * Returns the name of this user, which must be unique across all users. - * All users must have a deterministic, unique username which may not be - * null. - * - * @return - * The unique username of this user, which may not be null. - */ - public String getUsername(); - - /** - * Sets the name of this user, which must be unique across all users. - * - * @param username The name of this user. - */ - public void setUsername(String username); +public interface User extends Identifiable { /** * Returns this user's password. Note that the password returned may be @@ -69,48 +52,56 @@ public interface User { public void setPassword(String password); /** - * Lists all permissions given to this user. + * Returns all system-level permissions given to this user. * - * @return A Set of all permissions granted to this user. + * @return + * A SystemPermissionSet of all system-level permissions granted to + * this user. * - * @throws GuacamoleException If an error occurs while retrieving - * permissions, or if reading all permissions - * is not allowed. + * @throws GuacamoleException + * If an error occurs while retrieving permissions, or if reading all + * permissions is not allowed. */ - Set getPermissions() throws GuacamoleException; + SystemPermissionSet getSystemPermissions() throws GuacamoleException; /** - * Tests whether this user has the specified permission. + * Returns all connection permissions given to this user. * - * @param permission The permission to check. - * @return true if the permission is granted to this user, false otherwise. + * @return + * An ObjectPermissionSet of all connection permissions granted to this + * user. * - * @throws GuacamoleException If an error occurs while checking permissions, - * or if permissions cannot be checked due to - * lack of permissions to do so. + * @throws GuacamoleException + * If an error occurs while retrieving permissions, or if reading all + * permissions is not allowed. */ - boolean hasPermission(Permission permission) throws GuacamoleException; + ObjectPermissionSet getConnectionPermissions() + throws GuacamoleException; /** - * Adds the specified permission to this user. + * Returns all connection group permissions given to this user. * - * @param permission The permission to add. + * @return + * An ObjectPermissionSet of all connection group permissions granted + * to this user. * - * @throws GuacamoleException If an error occurs while adding the - * permission. or if permission to add - * permissions is denied. + * @throws GuacamoleException + * If an error occurs while retrieving permissions, or if reading all + * permissions is not allowed. */ - void addPermission(Permission permission) throws GuacamoleException; + ObjectPermissionSet getConnectionGroupPermissions() + throws GuacamoleException; /** - * Removes the specified permission from this specified user. + * Returns all user permissions given to this user. * - * @param permission The permission to remove. + * @return + * An ObjectPermissionSet of all user permissions granted to this user. * - * @throws GuacamoleException If an error occurs while removing the - * permission. or if permission to remove - * permissions is denied. + * @throws GuacamoleException + * If an error occurs while retrieving permissions, or if reading all + * permissions is not allowed. */ - void removePermission(Permission permission) throws GuacamoleException; + ObjectPermissionSet getUserPermissions() throws GuacamoleException; } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/UserContext.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/UserContext.java index 5912b38ec..c38d9a90f 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/UserContext.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/UserContext.java @@ -52,7 +52,35 @@ public interface UserContext { * @throws GuacamoleException If an error occurs while creating the * Directory. */ - Directory getUserDirectory() throws GuacamoleException; + Directory getUserDirectory() throws GuacamoleException; + + /** + * Retrieves a Directory which can be used to view and manipulate + * connections and their configurations, but only as allowed by the + * permissions given to the user. + * + * @return A Directory whose operations are bound by the permissions of + * the user. + * + * @throws GuacamoleException If an error occurs while creating the + * Directory. + */ + Directory getConnectionDirectory() + throws GuacamoleException; + + /** + * Retrieves a Directory which can be used to view and manipulate + * connection groups and their members, but only as allowed by the + * permissions given to the user. + * + * @return A Directory whose operations are bound by the permissions of + * the user. + * + * @throws GuacamoleException If an error occurs while creating the + * Directory. + */ + Directory getConnectionGroupDirectory() + throws GuacamoleException; /** * Retrieves a connection group which can be used to view and manipulate diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/ConnectionGroupPermission.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/ConnectionGroupPermission.java deleted file mode 100644 index f24e63b64..000000000 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/ConnectionGroupPermission.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2013 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.auth.permission; - - -/** - * A permission which controls operations that directly affect a specific - * ConnectionGroup. Note that this permission only refers to the - * ConnectionGroup by its identifier. The actual ConnectionGroup - * is not stored within. - * - * @author James Muehlner - */ -public class ConnectionGroupPermission - implements ObjectPermission { - - /** - * The identifier of the GuacamoleConfiguration associated with the - * operation affected by this permission. - */ - private String identifier; - - /** - * The type of operation affected by this permission. - */ - private ObjectPermission.Type type; - - /** - * Creates a new ConnectionGroupPermission having the given type - * and identifier. The identifier must be the unique identifier assigned - * to the ConnectionGroup by the AuthenticationProvider in use. - * - * @param type The type of operation affected by this permission. - * @param identifier The identifier of the ConnectionGroup associated - * with the operation affected by this permission. - */ - public ConnectionGroupPermission(ObjectPermission.Type type, String identifier) { - - this.identifier = identifier; - this.type = type; - - } - - @Override - public String getObjectIdentifier() { - return identifier; - } - - @Override - public ObjectPermission.Type getType() { - return type; - } - - @Override - public int hashCode() { - int hash = 5; - if (identifier != null) hash = 47 * hash + identifier.hashCode(); - if (type != null) hash = 47 * hash + type.hashCode(); - return hash; - } - - @Override - public boolean equals(Object obj) { - - // Not equal if null or wrong type - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - - final ConnectionGroupPermission other = - (ConnectionGroupPermission) obj; - - // Not equal if different type - if (this.type != other.type) - return false; - - // If null identifier, equality depends on whether other identifier - // is null - if (identifier == null) - return other.identifier == null; - - // Otherwise, equality depends entirely on identifier - return identifier.equals(other.identifier); - - } - -} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/ConnectionPermission.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/ConnectionPermission.java deleted file mode 100644 index 0802af5af..000000000 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/ConnectionPermission.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2013 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.auth.permission; - - -/** - * A permission which controls operations that directly affect a specific - * GuacamoleConfiguration. Note that this permission only refers to the - * GuacamoleConfiguration by its identifier. The actual GuacamoleConfiguration - * is not stored within. - * - * @author Michael Jumper - */ -public class ConnectionPermission - implements ObjectPermission { - - /** - * The identifier of the GuacamoleConfiguration associated with the - * operation affected by this permission. - */ - private String identifier; - - /** - * The type of operation affected by this permission. - */ - private Type type; - - /** - * Creates a new ConnectionPermission having the given type - * and identifier. The identifier must be the unique identifier assigned - * to the GuacamoleConfiguration by the AuthenticationProvider in use. - * - * @param type The type of operation affected by this permission. - * @param identifier The identifier of the GuacamoleConfiguration associated - * with the operation affected by this permission. - */ - public ConnectionPermission(Type type, String identifier) { - - this.identifier = identifier; - this.type = type; - - } - - @Override - public String getObjectIdentifier() { - return identifier; - } - - @Override - public Type getType() { - return type; - } - - @Override - public int hashCode() { - int hash = 5; - if (identifier != null) hash = 47 * hash + identifier.hashCode(); - if (type != null) hash = 47 * hash + type.hashCode(); - return hash; - } - - @Override - public boolean equals(Object obj) { - - // Not equal if null or wrong type - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - - final ConnectionPermission other = - (ConnectionPermission) obj; - - // Not equal if different type - if (this.type != other.type) - return false; - - // If null identifier, equality depends on whether other identifier - // is null - if (identifier == null) - return other.identifier == null; - - // Otherwise, equality depends entirely on identifier - return identifier.equals(other.identifier); - - } - -} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/ObjectPermission.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/ObjectPermission.java index f079ae4fc..e0927f30a 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/ObjectPermission.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/ObjectPermission.java @@ -28,9 +28,8 @@ package org.glyptodon.guacamole.net.auth.permission; * whole. * * @author Michael Jumper - * @param The type of identifier used by the object this permission affects. */ -public interface ObjectPermission extends Permission { +public class ObjectPermission implements Permission { /** * Specific types of object-level permissions. Each permission type is @@ -61,12 +60,80 @@ public interface ObjectPermission extends Permission { } /** + * The identifier of the GuacamoleConfiguration associated with the + * operation affected by this permission. + */ + private final String identifier; + + /** + * The type of operation affected by this permission. + */ + private final Type type; + + /** + * Creates a new ObjectPermission having the given type and identifier. + * The identifier must be the unique identifier assigned to the object + * associated with this permission by the AuthenticationProvider in use. + * + * @param type + * The type of operation affected by this permission. + * + * @param identifier + * The identifier of the object associated with the operation affected + * by this permission. + */ + public ObjectPermission(Type type, String identifier) { + + this.identifier = identifier; + this.type = type; + + } + + /** * Returns the identifier of the specific object affected by this * permission. * * @return The identifier of the specific object affected by this * permission. */ - public T getObjectIdentifier(); + public String getObjectIdentifier() { + return identifier; + } + + @Override + public Type getType() { + return type; + } + + @Override + public int hashCode() { + int hash = 5; + if (identifier != null) hash = 47 * hash + identifier.hashCode(); + if (type != null) hash = 47 * hash + type.hashCode(); + return hash; + } + + @Override + public boolean equals(Object obj) { + + // Not equal if null or wrong type + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + + final ObjectPermission other = (ObjectPermission) obj; + + // Not equal if different type + if (this.type != other.type) + return false; + + // If null identifier, equality depends on whether other identifier + // is null + if (identifier == null) + return other.identifier == null; + + // Otherwise, equality depends entirely on identifier + return identifier.equals(other.identifier); + + } } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/ObjectPermissionSet.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/ObjectPermissionSet.java new file mode 100644 index 000000000..7e6399a3d --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/ObjectPermissionSet.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015 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.auth.permission; + +import java.util.Collection; +import java.util.Set; +import org.glyptodon.guacamole.GuacamoleException; + + +/** + * A set of permissions which affect arbitrary objects, where each object has + * an associated unique identifier. + * + * @author Michael Jumper + */ +public interface ObjectPermissionSet extends PermissionSet { + + /** + * Tests whether the permission of the given type is granted for the + * object having the given identifier. + * + * @param permission + * The permission to check. + * + * @param identifier + * The identifier of the object affected by the permission being + * checked. + * + * @return + * true if the permission is granted, false otherwise. + * + * @throws GuacamoleException + * If an error occurs while checking permissions, or if permissions + * cannot be checked due to lack of permissions to do so. + */ + boolean hasPermission(ObjectPermission.Type permission, + String identifier) throws GuacamoleException; + + /** + * Adds the specified permission for the object having the given + * identifier. + * + * @param permission + * The permission to add. + * + * @param identifier + * The identifier of the object affected by the permission being + * added. + * + * @throws GuacamoleException + * If an error occurs while adding the permission, or if permission to + * add permissions is denied. + */ + void addPermission(ObjectPermission.Type permission, + String identifier) throws GuacamoleException; + + /** + * Removes the specified permission for the object having the given + * identifier. + * + * @param permission + * The permission to remove. + * + * @param identifier + * The identifier of the object affected by the permission being + * added. + * + * @throws GuacamoleException + * If an error occurs while removing the permission, or if permission + * to remove permissions is denied. + */ + void removePermission(ObjectPermission.Type permission, + String identifier) throws GuacamoleException; + + /** + * Tests whether this user has the specified permissions for the objects + * having the given identifiers. The identifier of an object is returned + * in a new collection if at least one of the specified permissions is + * granted for that object. + * + * @param permissions + * The permissions to check. An identifier will be included in the + * resulting collection if at least one of these permissions is granted + * for the associated object + * + * @param identifiers + * The identifiers of the objects affected by the permissions being + * checked. + * + * @return + * A collection containing the subset of identifiers for which at least + * one of the specified permissions is granted. + * + * @throws GuacamoleException + * If an error occurs while checking permissions, or if permissions + * cannot be checked due to lack of permissions to do so. + */ + Collection getAccessibleObjects( + Collection permissions, + Collection identifiers) throws GuacamoleException; + + @Override + Set getPermissions() + throws GuacamoleException; + + @Override + void addPermissions(Set permissions) + throws GuacamoleException; + + @Override + void removePermissions(Set permissions) + throws GuacamoleException; + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/PermissionSet.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/PermissionSet.java new file mode 100644 index 000000000..1bfa717f8 --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/PermissionSet.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 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.auth.permission; + +import java.util.Set; +import org.glyptodon.guacamole.GuacamoleException; + + +/** + * An arbitrary set of permissions. + * + * @author Michael Jumper + * @param + * The type of permission stored within this PermissionSet. + */ +public interface PermissionSet { + + /** + * Returns a Set which contains all permissions granted within this + * permission set. + * + * @return + * A Set containing all permissions granted within this permission set. + * + * @throws GuacamoleException + * If an error occurs while retrieving permissions, or if permissions + * cannot be retrieved due to lack of permissions to do so. + */ + Set getPermissions() throws GuacamoleException; + + /** + * Adds the specified permissions, if not already granted. If a specified + * permission is already granted, no operation is performed regarding that + * permission. + * + * @param permissions + * The permissions to add. + * + * @throws GuacamoleException + * If an error occurs while adding the permissions, or if permission to + * add permissions is denied. + */ + void addPermissions(Set permissions) + throws GuacamoleException; + + /** + * Removes each of the specified permissions, if granted. If a specified + * permission is not granted, no operation is performed regarding that + * permission. + * + * @param permissions + * The permissions to remove. + * + * @throws GuacamoleException + * If an error occurs while removing the permissions, or if permission + * to remove permissions is denied. + */ + void removePermissions(Set permissions) + throws GuacamoleException; + + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/SystemPermissionSet.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/SystemPermissionSet.java new file mode 100644 index 000000000..195c46e47 --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/SystemPermissionSet.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2015 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.auth.permission; + +import java.util.Set; +import org.glyptodon.guacamole.GuacamoleException; + + +/** + * A set of permissions which affects the system as a whole. + * + * @author Michael Jumper + */ +public interface SystemPermissionSet extends PermissionSet { + + /** + * Tests whether the permission of the given type is granted. + * + * @param permission + * The permission to check. + * + * @return + * true if the permission is granted, false otherwise. + * + * @throws GuacamoleException + * If an error occurs while checking permissions, or if permissions + * cannot be checked due to lack of permissions to do so. + */ + boolean hasPermission(SystemPermission.Type permission) + throws GuacamoleException; + + /** + * Adds the specified permission. + * + * @param permission + * The permission to add. + * + * @throws GuacamoleException + * If an error occurs while adding the permission, or if permission to + * add permissions is denied. + */ + void addPermission(SystemPermission.Type permission) + throws GuacamoleException; + + /** + * Removes the specified permission. + * + * @param permission + * The permission to remove. + * + * @throws GuacamoleException + * If an error occurs while removing the permission, or if permission + * to remove permissions is denied. + */ + void removePermission(SystemPermission.Type permission) + throws GuacamoleException; + + @Override + Set getPermissions() throws GuacamoleException; + + @Override + void addPermissions(Set permissions) + throws GuacamoleException; + + @Override + void removePermissions(Set permissions) + throws GuacamoleException; + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/UserPermission.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/UserPermission.java deleted file mode 100644 index 89aa085b8..000000000 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/permission/UserPermission.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2013 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.auth.permission; - - -/** - * A permission which controls operations that directly affect a specific - * User. - * - * @author Michael Jumper - */ -public class UserPermission implements ObjectPermission { - - /** - * The username of the User associated with the operation affected by this - * permission. - */ - private String identifier; - - /** - * The type of operation affected by this permission. - */ - private Type type; - - /** - * Creates a new UserPermission having the given type and identifier. The - * identifier must be the user's username. - * - * @param type The type of operation affected by this permission. - * @param identifier The username of the User associated with the operation - * affected by this permission. - */ - public UserPermission(Type type, String identifier) { - - this.identifier = identifier; - this.type = type; - - } - - @Override - public String getObjectIdentifier() { - return identifier; - } - - @Override - public Type getType() { - return type; - } - - @Override - public int hashCode() { - int hash = 5; - if (identifier != null) hash = 47 * hash + identifier.hashCode(); - if (type != null) hash = 47 * hash + type.hashCode(); - return hash; - } - - @Override - public boolean equals(Object obj) { - - // Not equal if null or wrong type - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - - final UserPermission other = (UserPermission) obj; - - // Not equal if different type - if (this.type != other.type) - return false; - - // If null identifier, equality depends on whether other identifier - // is null - if (identifier == null) - return other.identifier == null; - - // Otherwise, equality depends entirely on identifier - return identifier.equals(other.identifier); - - } - -} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnection.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnection.java index 5065ef5d7..fe5070097 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnection.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnection.java @@ -78,6 +78,11 @@ public class SimpleConnection extends AbstractConnection { } + @Override + public int getActiveConnections() { + return 0; + } + @Override public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException { diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionDirectory.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionDirectory.java index c5fe6594f..50e9d4ff5 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionDirectory.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionDirectory.java @@ -22,15 +22,10 @@ package org.glyptodon.guacamole.net.auth.simple; +import java.util.Collection; import java.util.HashMap; import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.GuacamoleSecurityException; import org.glyptodon.guacamole.net.auth.Connection; -import org.glyptodon.guacamole.net.auth.Directory; -import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; /** * An extremely simple read-only implementation of a Directory of @@ -39,77 +34,44 @@ import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; * * @author Michael Jumper */ -public class SimpleConnectionDirectory - implements Directory { +public class SimpleConnectionDirectory extends SimpleDirectory { /** * The Map of Connections to provide access to. */ - private Map connections = + private final Map connections = new HashMap(); /** - * Creates a new SimpleConnectionDirectory which provides - * access to the configurations contained within the given Map. + * Creates a new SimpleConnectionDirectory which provides access to the + * connections contained within the given Map. * - * @param configs The Map of GuacamoleConfigurations to provide access to. + * @param connections + * A Collection of all connections that should be present in this + * connection directory. */ - public SimpleConnectionDirectory( - Map configs) { + public SimpleConnectionDirectory(Collection connections) { - // Create connections for each config - for (Entry entry : configs.entrySet()) - connections.put(entry.getKey(), - new SimpleConnection(entry.getKey(), entry.getKey(), - entry.getValue())); + // Add all given connections + for (Connection connection : connections) + this.connections.put(connection.getIdentifier(), connection); + + // Use the connection map to back the underlying directory + super.setObjects(this.connections); } - @Override - public Connection get(String identifier) - throws GuacamoleException { - return connections.get(identifier); - } - - @Override - public Set getIdentifiers() throws GuacamoleException { - return connections.keySet(); - } - - @Override - public void add(Connection connection) - throws GuacamoleException { - throw new GuacamoleSecurityException("Permission denied."); - } - - @Override - public void update(Connection connection) - throws GuacamoleException { - throw new GuacamoleSecurityException("Permission denied."); - } - - @Override - public void remove(String identifier) throws GuacamoleException { - throw new GuacamoleSecurityException("Permission denied."); - } - - @Override - public void move(String identifier, Directory directory) - throws GuacamoleException { - throw new GuacamoleSecurityException("Permission denied."); - } - /** * An internal method for modifying the Connections in this Directory. * Returns the previous connection for the given identifier, if found. - * + * * @param connection The connection to add or update the Directory with. * @return The previous connection for the connection identifier, if found. */ public Connection putConnection(Connection connection) { return connections.put(connection.getIdentifier(), connection); } - + /** * An internal method for removing a Connection from this Directory. * @param identifier The identifier of the Connection to remove. diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionGroup.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionGroup.java index ffcf7a34a..71e73433e 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionGroup.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionGroup.java @@ -22,18 +22,19 @@ package org.glyptodon.guacamole.net.auth.simple; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleSecurityException; import org.glyptodon.guacamole.net.GuacamoleSocket; import org.glyptodon.guacamole.net.auth.AbstractConnectionGroup; -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.protocol.GuacamoleClientInformation; /** * An extremely simple read-only implementation of a ConnectionGroup which - * returns the connection and connection group directories it was constructed + * returns the connection and connection group identifiers it was constructed * with. Load balancing across this connection group is not allowed. * * @author James Muehlner @@ -41,31 +42,34 @@ import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; public class SimpleConnectionGroup extends AbstractConnectionGroup { /** - * Underlying connection directory, containing all connections within this - * group. + * The identifiers of all connections in this group. */ - private final Directory connectionDirectory; + private final Set connectionIdentifiers; /** - * Underlying connection group directory, containing all connections within - * this group. + * The identifiers of all connection groups in this group. */ - private final Directory connectionGroupDirectory; + private final Set connectionGroupIdentifiers; /** * Creates a new SimpleConnectionGroup having the given name and identifier - * which will expose the given directories as its contents. + * which will expose the given contents. * - * @param name The name to associate with this connection. - * @param identifier The identifier to associate with this connection. - * @param connectionDirectory The connection directory to expose when - * requested. - * @param connectionGroupDirectory The connection group directory to expose - * when requested. + * @param name + * The name to associate with this connection group. + * + * @param identifier + * The identifier to associate with this connection group. + * + * @param connectionIdentifiers + * The connection identifiers to expose when requested. + * + * @param connectionGroupIdentifiers + * The connection group identifiers to expose when requested. */ public SimpleConnectionGroup(String name, String identifier, - Directory connectionDirectory, - Directory connectionGroupDirectory) { + Collection connectionIdentifiers, + Collection connectionGroupIdentifiers) { // Set name setName(name); @@ -76,22 +80,25 @@ public class SimpleConnectionGroup extends AbstractConnectionGroup { // Set group type setType(ConnectionGroup.Type.ORGANIZATIONAL); - // Assign directories - this.connectionDirectory = connectionDirectory; - this.connectionGroupDirectory = connectionGroupDirectory; + // Populate contents + this.connectionIdentifiers = new HashSet(connectionIdentifiers); + this.connectionGroupIdentifiers = new HashSet(connectionGroupIdentifiers); } - - @Override - public Directory getConnectionDirectory() - throws GuacamoleException { - return connectionDirectory; - } @Override - public Directory getConnectionGroupDirectory() - throws GuacamoleException { - return connectionGroupDirectory; + public int getActiveConnections() { + return 0; + } + + @Override + public Set getConnectionIdentifiers() { + return connectionIdentifiers; + } + + @Override + public Set getConnectionGroupIdentifiers() { + return connectionGroupIdentifiers; } @Override diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionGroupDirectory.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionGroupDirectory.java index 0ebe52414..8e0a6d6f8 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionGroupDirectory.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionGroupDirectory.java @@ -25,11 +25,7 @@ package org.glyptodon.guacamole.net.auth.simple; import java.util.Collection; import java.util.HashMap; import java.util.Map; -import java.util.Set; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.GuacamoleSecurityException; import org.glyptodon.guacamole.net.auth.ConnectionGroup; -import org.glyptodon.guacamole.net.auth.Directory; /** * An extremely simple read-only implementation of a Directory of @@ -39,18 +35,18 @@ import org.glyptodon.guacamole.net.auth.Directory; * @author James Muehlner */ public class SimpleConnectionGroupDirectory - implements Directory { + extends SimpleDirectory { /** * The Map of ConnectionGroups to provide access to. */ - private Map connectionGroups = + private final Map connectionGroups = new HashMap(); /** * Creates a new SimpleConnectionGroupDirectory which contains the given * groups. - * + * * @param groups A Collection of all groups that should be present in this * connection group directory. */ @@ -60,46 +56,15 @@ public class SimpleConnectionGroupDirectory for (ConnectionGroup group : groups) connectionGroups.put(group.getIdentifier(), group); - } + // Use the connection group map to back the underlying AbstractDirectory + super.setObjects(connectionGroups); - @Override - public ConnectionGroup get(String identifier) - throws GuacamoleException { - return connectionGroups.get(identifier); - } - - @Override - public Set getIdentifiers() throws GuacamoleException { - return connectionGroups.keySet(); - } - - @Override - public void add(ConnectionGroup connectionGroup) - throws GuacamoleException { - throw new GuacamoleSecurityException("Permission denied."); - } - - @Override - public void update(ConnectionGroup connectionGroup) - throws GuacamoleException { - throw new GuacamoleSecurityException("Permission denied."); - } - - @Override - public void remove(String identifier) throws GuacamoleException { - throw new GuacamoleSecurityException("Permission denied."); - } - - @Override - public void move(String identifier, Directory directory) - throws GuacamoleException { - throw new GuacamoleSecurityException("Permission denied."); } /** * An internal method for modifying the ConnectionGroups in this Directory. * Returns the previous connection group for the given identifier, if found. - * + * * @param connectionGroup The connection group to add or update the * Directory with. * @return The previous connection group for the connection group @@ -108,10 +73,10 @@ public class SimpleConnectionGroupDirectory public ConnectionGroup putConnectionGroup(ConnectionGroup connectionGroup) { return connectionGroups.put(connectionGroup.getIdentifier(), connectionGroup); } - + /** * An internal method for removing a ConnectionGroup from this Directory. - * + * * @param identifier The identifier of the ConnectionGroup to remove. * @return The previous connection group for the given identifier, if found. */ diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleDirectory.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleDirectory.java new file mode 100644 index 000000000..caee07e2a --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleDirectory.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2015 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.auth.simple; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +import org.glyptodon.guacamole.net.auth.Directory; + +/** + * An extremely simple read-only implementation of a Directory which provides + * access to a pre-defined Map of arbitrary objects. Any changes to the Map + * will affect the available contents of this SimpleDirectory. + * + * @author Michael Jumper + * @param + * The type of objects stored within this SimpleDirectory. + */ +public class SimpleDirectory implements Directory { + + /** + * The Map of objects to provide access to. + */ + private Map objects = Collections.EMPTY_MAP; + + /** + * Creates a new empty SimpleDirectory which does not provide access to + * any objects. + */ + public SimpleDirectory() { + } + + /** + * Creates a new SimpleDirectory which provides access to the objects + * contained within the given Map. + * + * @param objects + * The Map of objects to provide access to. + */ + public SimpleDirectory(Map objects) { + this.objects = objects; + } + + /** + * Sets the Map which backs this SimpleDirectory. Future function calls + * which retrieve objects from this SimpleDirectory will use the provided + * Map. + * + * @param objects + * The Map of objects to provide access to. + */ + protected void setObjects(Map objects) { + this.objects = objects; + } + + /** + * Returns the Map which currently backs this SimpleDirectory. Changes to + * this Map will affect future function calls that retrieve objects from + * this SimpleDirectory. + * + * @return + * The Map of objects which currently backs this SimpleDirectory. + */ + protected Map getObjects() { + return objects; + } + + @Override + public ObjectType get(String identifier) + throws GuacamoleException { + return objects.get(identifier); + } + + @Override + public Collection getAll(Collection identifiers) + throws GuacamoleException { + + // Create collection which has an appropriate initial size + Collection foundObjects = new ArrayList(identifiers.size()); + + // Populate collection with matching objects + for (String identifier : identifiers) { + + // Add the object which has the current identifier, if any + ObjectType object = objects.get(identifier); + if (object != null) + foundObjects.add(object); + + } + + return foundObjects; + + } + + @Override + public Set getIdentifiers() throws GuacamoleException { + return objects.keySet(); + } + + @Override + public void add(ObjectType connection) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public void update(ObjectType connection) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public void remove(String identifier) throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleObjectPermissionSet.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleObjectPermissionSet.java new file mode 100644 index 000000000..b265ca5ca --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleObjectPermissionSet.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2015 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.auth.simple; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; + +/** + * A read-only implementation of ObjectPermissionSet which uses a backing Set + * of Permissions to determine which permissions are present. + * + * @author Michael Jumper + */ +public class SimpleObjectPermissionSet implements ObjectPermissionSet { + + /** + * The set of all permissions currently granted. + */ + private Set permissions = Collections.EMPTY_SET; + + /** + * Creates a new empty SimpleObjectPermissionSet. + */ + public SimpleObjectPermissionSet() { + } + + /** + * Creates a new SimpleObjectPermissionSet which contains the permissions + * within the given Set. + * + * @param permissions + * The Set of permissions this SimpleObjectPermissionSet should + * contain. + */ + public SimpleObjectPermissionSet(Set permissions) { + this.permissions = permissions; + } + + /** + * Sets the Set which backs this SimpleObjectPermissionSet. Future function + * calls on this SimpleObjectPermissionSet will use the provided Set. + * + * @param permissions + * The Set of permissions this SimpleObjectPermissionSet should + * contain. + */ + protected void setPermissions(Set permissions) { + this.permissions = permissions; + } + + @Override + public Set getPermissions() { + return permissions; + } + + @Override + public boolean hasPermission(ObjectPermission.Type permission, + String identifier) throws GuacamoleException { + + ObjectPermission objectPermission = + new ObjectPermission(permission, identifier); + + return permissions.contains(objectPermission); + + } + + @Override + public void addPermission(ObjectPermission.Type permission, + String identifier) throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public void removePermission(ObjectPermission.Type permission, + String identifier) throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public Collection getAccessibleObjects( + Collection permissionTypes, + Collection identifiers) throws GuacamoleException { + + Collection accessibleObjects = new ArrayList(permissions.size()); + + // For each identifier/permission combination + for (String identifier : identifiers) { + for (ObjectPermission.Type permissionType : permissionTypes) { + + // Add identifier if at least one requested permission is granted + ObjectPermission permission = new ObjectPermission(permissionType, identifier); + if (permissions.contains(permission)) { + accessibleObjects.add(identifier); + break; + } + + } + } + + return accessibleObjects; + + } + + @Override + public void addPermissions(Set permissions) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public void removePermissions(Set permissions) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleSystemPermissionSet.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleSystemPermissionSet.java new file mode 100644 index 000000000..bd6a5c2e0 --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleSystemPermissionSet.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2015 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.auth.simple; + +import java.util.Collections; +import java.util.Set; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +import org.glyptodon.guacamole.net.auth.permission.SystemPermission; +import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; + +/** + * A read-only implementation of SystemPermissionSet which uses a backing Set + * of Permissions to determine which permissions are present. + * + * @author Michael Jumper + */ +public class SimpleSystemPermissionSet implements SystemPermissionSet { + + /** + * The set of all permissions currently granted. + */ + private Set permissions = Collections.EMPTY_SET; + + /** + * Creates a new empty SimpleSystemPermissionSet. + */ + public SimpleSystemPermissionSet() { + } + + /** + * Creates a new SimpleSystemPermissionSet which contains the permissions + * within the given Set. + * + * @param permissions + * The Set of permissions this SimpleSystemPermissionSet should + * contain. + */ + public SimpleSystemPermissionSet(Set permissions) { + this.permissions = permissions; + } + + /** + * Sets the Set which backs this SimpleSystemPermissionSet. Future function + * calls on this SimpleSystemPermissionSet will use the provided Set. + * + * @param permissions + * The Set of permissions this SimpleSystemPermissionSet should + * contain. + */ + protected void setPermissions(Set permissions) { + this.permissions = permissions; + } + + @Override + public Set getPermissions() { + return permissions; + } + + @Override + public boolean hasPermission(SystemPermission.Type permission) + throws GuacamoleException { + + SystemPermission systemPermission = new SystemPermission(permission); + return permissions.contains(systemPermission); + + } + + @Override + public void addPermission(SystemPermission.Type permission) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public void removePermission(SystemPermission.Type permission) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public void addPermissions(Set permissions) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + + @Override + public void removePermissions(Set permissions) + throws GuacamoleException { + throw new GuacamoleSecurityException("Permission denied."); + } + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUser.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUser.java index abe447249..fa0899fa2 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUser.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUser.java @@ -24,17 +24,12 @@ package org.glyptodon.guacamole.net.auth.simple; import java.util.Collection; import java.util.HashSet; -import java.util.Map; import java.util.Set; import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.GuacamoleSecurityException; import org.glyptodon.guacamole.net.auth.AbstractUser; -import org.glyptodon.guacamole.net.auth.ConnectionGroup; -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.protocol.GuacamoleConfiguration; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; +import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; /** * An extremely basic User implementation. @@ -44,9 +39,16 @@ import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; public class SimpleUser extends AbstractUser { /** - * The set of all permissions available to this user. + * All connection permissions granted to this user. */ - private Set permissions = new HashSet(); + private final Set connectionPermissions = + new HashSet(); + + /** + * All connection group permissions granted to this user. + */ + private final Set connectionGroupPermissions = + new HashSet(); /** * Creates a completely uninitialized SimpleUser. @@ -55,67 +57,77 @@ public class SimpleUser extends AbstractUser { } /** - * Creates a new SimpleUser having the given username. + * Adds a new READ permission to the given set of permissions for each of + * the given identifiers. * - * @param username The username to assign to this SimpleUser. - * @param configs All configurations this user has read access to. - * @param groups All groups this user has read access to. + * @param permissions + * The set of permissions to add READ permissions to. + * + * @param identifiers + * The identifiers which should each have a corresponding READ + * permission added to the given set. */ - public SimpleUser(String username, - Map configs, - Collection groups) { + private void addReadPermissions(Set permissions, + Collection identifiers) { - // Set username - setUsername(username); - - // Add connection permissions - for (String identifier : configs.keySet()) { - - // Create permission - Permission permission = new ConnectionPermission( + // Add a READ permission to the set for each identifier given + for (String identifier : identifiers) { + permissions.add(new ObjectPermission ( ObjectPermission.Type.READ, identifier - ); - - // Add to set - permissions.add(permission); - - } - - // Add group permissions - for (ConnectionGroup group : groups) { - - // Create permission - Permission permission = new ConnectionGroupPermission( - ObjectPermission.Type.READ, - group.getIdentifier() - ); - - // Add to set - permissions.add(permission); - + )); } } + + /** + * Creates a new SimpleUser having the given username and READ access to + * the connections and groups having the given identifiers. + * + * @param username + * The username to assign to this SimpleUser. + * @param connectionIdentifiers + * The identifiers of all connections this user has READ access to. + * + * @param connectionGroupIdentifiers + * The identifiers of all connection groups this user has READ access + * to. + */ + public SimpleUser(String username, + Collection connectionIdentifiers, + Collection connectionGroupIdentifiers) { + + // Set username + setIdentifier(username); + + // Add permissions + addReadPermissions(connectionPermissions, connectionIdentifiers); + addReadPermissions(connectionGroupPermissions, connectionGroupIdentifiers); - @Override - public Set getPermissions() throws GuacamoleException { - return permissions; } @Override - public boolean hasPermission(Permission permission) throws GuacamoleException { - return permissions.contains(permission); + public SystemPermissionSet getSystemPermissions() + throws GuacamoleException { + return new SimpleSystemPermissionSet(); } @Override - public void addPermission(Permission permission) throws GuacamoleException { - throw new GuacamoleSecurityException("Permission denied."); + public ObjectPermissionSet getConnectionPermissions() + throws GuacamoleException { + return new SimpleObjectPermissionSet(connectionPermissions); } @Override - public void removePermission(Permission permission) throws GuacamoleException { - throw new GuacamoleSecurityException("Permission denied."); + public ObjectPermissionSet getConnectionGroupPermissions() + throws GuacamoleException { + return new SimpleObjectPermissionSet(connectionGroupPermissions); + } + + @Override + public ObjectPermissionSet getUserPermissions() + throws GuacamoleException { + return new SimpleObjectPermissionSet(); } } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserContext.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserContext.java index 2bb376747..8ee7e84c1 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserContext.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserContext.java @@ -22,10 +22,13 @@ package org.glyptodon.guacamole.net.auth.simple; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.UUID; import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionGroup; import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.User; @@ -41,6 +44,11 @@ import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; */ public class SimpleUserContext implements UserContext { + /** + * The unique identifier of the root connection group. + */ + private static final String ROOT_IDENTIFIER = "ROOT"; + /** * Reference to the user whose permissions dictate the configurations * accessible within this UserContext. @@ -51,13 +59,24 @@ public class SimpleUserContext implements UserContext { * The Directory with access only to the User associated with this * UserContext. */ - private final Directory userDirectory; + private final Directory userDirectory; /** - * The ConnectionGroup with access only to those Connections that the User - * associated with this UserContext has access to. + * The Directory with access only to the root group associated with this + * UserContext. */ - private final ConnectionGroup connectionGroup; + private final Directory connectionGroupDirectory; + + /** + * The Directory with access to all connections within the root group + * associated with this UserContext. + */ + private final Directory connectionDirectory; + + /** + * The root connection group. + */ + private final ConnectionGroup rootGroup; /** * Creates a new SimpleUserContext which provides access to only those @@ -82,17 +101,41 @@ public class SimpleUserContext implements UserContext { */ public SimpleUserContext(String username, Map configs) { - // Add root group that contains only configurations - this.connectionGroup = new SimpleConnectionGroup("ROOT", "ROOT", - new SimpleConnectionDirectory(configs), - new SimpleConnectionGroupDirectory(Collections.EMPTY_LIST)); + Collection connectionIdentifiers = new ArrayList(configs.size()); + Collection connectionGroupIdentifiers = Collections.singleton(ROOT_IDENTIFIER); + + // Produce collection of connections from given configs + Collection connections = new ArrayList(configs.size()); + for (Map.Entry configEntry : configs.entrySet()) { - // Build new user from credentials, giving the user an arbitrary name - this.self = new SimpleUser(username, - configs, Collections.singleton(connectionGroup)); + // Get connection identifier and configuration + String identifier = configEntry.getKey(); + GuacamoleConfiguration config = configEntry.getValue(); - // Create user directory for new user + // Add as simple connection + Connection connection = new SimpleConnection(identifier, identifier, config); + connection.setParentIdentifier(ROOT_IDENTIFIER); + connections.add(connection); + + // Add identifier to overall set of identifiers + connectionIdentifiers.add(identifier); + + } + + // Add root group that contains only the given configurations + this.rootGroup = new SimpleConnectionGroup( + ROOT_IDENTIFIER, ROOT_IDENTIFIER, + connectionIdentifiers, Collections.EMPTY_LIST + ); + + // Build new user from credentials + this.self = new SimpleUser(username, connectionIdentifiers, + connectionGroupIdentifiers); + + // Create directories for new user this.userDirectory = new SimpleUserDirectory(self); + this.connectionDirectory = new SimpleConnectionDirectory(connections); + this.connectionGroupDirectory = new SimpleConnectionGroupDirectory(Collections.singleton(this.rootGroup)); } @@ -102,14 +145,26 @@ public class SimpleUserContext implements UserContext { } @Override - public Directory getUserDirectory() + public Directory getUserDirectory() throws GuacamoleException { return userDirectory; } + @Override + public Directory getConnectionDirectory() + throws GuacamoleException { + return connectionDirectory; + } + + @Override + public Directory getConnectionGroupDirectory() + throws GuacamoleException { + return connectionGroupDirectory; + } + @Override public ConnectionGroup getRootConnectionGroup() throws GuacamoleException { - return connectionGroup; + return rootGroup; } } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserDirectory.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserDirectory.java index 97e6eefb3..e44f1897e 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserDirectory.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserDirectory.java @@ -23,10 +23,6 @@ package org.glyptodon.guacamole.net.auth.simple; import java.util.Collections; -import java.util.Set; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.GuacamoleSecurityException; -import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.User; /** @@ -35,12 +31,7 @@ import org.glyptodon.guacamole.net.auth.User; * * @author Michael Jumper */ -public class SimpleUserDirectory implements Directory { - - /** - * The only user to be contained within this directory. - */ - private User user; +public class SimpleUserDirectory extends SimpleDirectory { /** * Creates a new SimpleUserDirectory which provides access to the single @@ -49,45 +40,7 @@ public class SimpleUserDirectory implements Directory { * @param user The user to provide access to. */ public SimpleUserDirectory(User user) { - this.user = user; - } - - @Override - public User get(String username) throws GuacamoleException { - - // If username matches, return the user - if (user.getUsername().equals(username)) - return user; - - // Otherwise, not found - return null; - - } - - @Override - public Set getIdentifiers() throws GuacamoleException { - return Collections.singleton(user.getUsername()); - } - - @Override - public void add(User user) throws GuacamoleException { - throw new GuacamoleSecurityException("Permission denied."); - } - - @Override - public void update(User user) throws GuacamoleException { - throw new GuacamoleSecurityException("Permission denied."); - } - - @Override - public void remove(String username) throws GuacamoleException { - throw new GuacamoleSecurityException("Permission denied."); - } - - @Override - public void move(String identifier, Directory directory) - throws GuacamoleException { - throw new GuacamoleSecurityException("Permission denied."); + super(Collections.singletonMap(user.getIdentifier(), user)); } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelRequestService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelRequestService.java index 25e7c87fe..b0ba6ee39 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelRequestService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelRequestService.java @@ -210,44 +210,40 @@ public class TunnelRequestService { // Connection identifiers case CONNECTION: { - UserContext context = session.getUserContext(); - // Get connection directory - Directory directory = - context.getRootConnectionGroup().getConnectionDirectory(); + UserContext context = session.getUserContext(); + Directory directory = context.getConnectionDirectory(); // Get authorized connection Connection connection = directory.get(id); if (connection == null) { - logger.info("Connection \"{}\" does not exist for user \"{}\".", id, context.self().getUsername()); + logger.info("Connection \"{}\" does not exist for user \"{}\".", id, context.self().getIdentifier()); throw new GuacamoleSecurityException("Requested connection is not authorized."); } // Connect socket socket = connection.connect(info); - logger.info("User \"{}\" successfully connected to \"{}\".", context.self().getUsername(), id); + logger.info("User \"{}\" successfully connected to \"{}\".", context.self().getIdentifier(), id); break; } // Connection group identifiers case CONNECTION_GROUP: { - UserContext context = session.getUserContext(); - // Get connection group directory - Directory directory = - context.getRootConnectionGroup().getConnectionGroupDirectory(); + UserContext context = session.getUserContext(); + Directory directory = context.getConnectionGroupDirectory(); // Get authorized connection group ConnectionGroup group = directory.get(id); if (group == null) { - logger.info("Connection group \"{}\" does not exist for user \"{}\".", id, context.self().getUsername()); + logger.info("Connection group \"{}\" does not exist for user \"{}\".", id, context.self().getIdentifier()); throw new GuacamoleSecurityException("Requested connection group is not authorized."); } // Connect socket socket = group.connect(info); - logger.info("User \"{}\" successfully connected to group \"{}\".", context.self().getUsername(), id); + logger.info("User \"{}\" successfully connected to group \"{}\".", context.self().getIdentifier(), id); break; } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/ObjectRetrievalService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/ObjectRetrievalService.java index 27cb6d9ef..c72040493 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/ObjectRetrievalService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/ObjectRetrievalService.java @@ -59,7 +59,7 @@ public class ObjectRetrievalService { String identifier) throws GuacamoleException { // Get user directory - Directory directory = userContext.getUserDirectory(); + Directory directory = userContext.getUserDirectory(); // Pull specified user User user = directory.get(identifier); @@ -89,9 +89,8 @@ public class ObjectRetrievalService { public Connection retrieveConnection(UserContext userContext, String identifier) throws GuacamoleException { - // Get root directory - ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); - Directory directory = rootGroup.getConnectionDirectory(); + // Get connection directory + Directory directory = userContext.getConnectionDirectory(); // Pull specified connection Connection connection = directory.get(identifier); @@ -125,14 +124,12 @@ public class ObjectRetrievalService { public ConnectionGroup retrieveConnectionGroup(UserContext userContext, String identifier) throws GuacamoleException { - ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); - - // Use root group if identifier is null (or the standard root identifier) + // Use root group if identifier is the standard root identifier if (identifier != null && identifier.equals(APIConnectionGroup.ROOT_IDENTIFIER)) - return rootGroup; + return userContext.getRootConnectionGroup(); // Pull specified connection group otherwise - Directory directory = rootGroup.getConnectionGroupDirectory(); + Directory directory = userContext.getConnectionGroupDirectory(); ConnectionGroup connectionGroup = directory.get(identifier); if (connectionGroup == null) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java index e46b54d1a..3aac87e65 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenRESTService.java @@ -188,8 +188,8 @@ public class TokenRESTService { tokenSessionMap.put(authToken, new GuacamoleSession(credentials, userContext)); } - logger.debug("Login was successful for user \"{}\".", userContext.self().getUsername()); - return new APIAuthToken(authToken, userContext.self().getUsername()); + logger.debug("Login was successful for user \"{}\".", userContext.self().getIdentifier()); + return new APIAuthToken(authToken, userContext.self().getIdentifier()); } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java index 7eda11740..5b8da3f75 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java @@ -22,13 +22,10 @@ package org.glyptodon.guacamole.net.basic.rest.connection; -import java.util.List; import java.util.Map; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.net.auth.Connection; -import org.glyptodon.guacamole.net.auth.ConnectionRecord; -import org.glyptodon.guacamole.net.basic.rest.connectiongroup.APIConnectionGroup; import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; /** @@ -65,9 +62,9 @@ public class APIConnection { private Map parameters; /** - * The count of currently active users for this connection. + * The count of currently active connections using this connection. */ - private int activeUsers; + private int activeConnections; /** * Create an empty APIConnection. @@ -85,23 +82,12 @@ public class APIConnection { public APIConnection(Connection connection) throws GuacamoleException { - // Set identifying information + // Set connection information this.name = connection.getName(); this.identifier = connection.getIdentifier(); - - // Set proper parent identifier, using root identifier if needed this.parentIdentifier = connection.getParentIdentifier(); - if (this.parentIdentifier == null) - this.parentIdentifier = APIConnectionGroup.ROOT_IDENTIFIER; + this.activeConnections = connection.getActiveConnections(); - // Set the number of currently active users - this.activeUsers = 0; - - for (ConnectionRecord history : connection.getHistory()) { - if (history.isActive()) - this.activeUsers++; - } - // Set protocol from configuration GuacamoleConfiguration configuration = connection.getConfiguration(); this.protocol = configuration.getProtocol(); @@ -190,19 +176,24 @@ public class APIConnection { } /** - * Returns the number of currently active users for this connection. - * @return The number of currently active users for this connection. + * Returns the number of currently active connections using this + * connection. + * + * @return + * The number of currently active usages of this connection. */ - public int getActiveUsers() { - return activeUsers; + public int getActiveConnections() { + return activeConnections; } /** - * Set the number of currently active users for this connection. - * @param activeUsers The number of currently active users for this connection. + * Set the number of currently active connections using this connection. + * + * @param activeConnections + * The number of currently active usages of this connection. */ - public void setActiveUsers(int activeUsers) { - this.activeUsers = activeUsers; + public void setActiveUsers(int activeConnections) { + this.activeConnections = activeConnections; } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionWrapper.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionWrapper.java index 94bb5c0ed..33fe5047f 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionWrapper.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionWrapper.java @@ -40,8 +40,18 @@ import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; */ public class APIConnectionWrapper implements Connection { + /** + * The wrapped APIConnection. + */ private final APIConnection apiConnection; - + + /** + * Creates a new APIConnectionWrapper which wraps the given APIConnection + * as a Connection. + * + * @param apiConnection + * The APIConnection to wrap. + */ public APIConnectionWrapper(APIConnection apiConnection) { this.apiConnection = apiConnection; } @@ -76,6 +86,11 @@ public class APIConnectionWrapper implements Connection { apiConnection.setParentIdentifier(parentIdentifier); } + @Override + public int getActiveConnections() { + return apiConnection.getActiveConnections(); + } + @Override public GuacamoleConfiguration getConfiguration() { diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java index 67fb66258..8c827c11c 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java @@ -45,10 +45,10 @@ import org.glyptodon.guacamole.net.auth.ConnectionRecord; 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.auth.permission.Permission; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; import org.glyptodon.guacamole.net.auth.permission.SystemPermission; +import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; import org.glyptodon.guacamole.net.basic.rest.ObjectRetrievalService; import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; @@ -71,12 +71,6 @@ public class ConnectionRESTService { */ private static final Logger logger = LoggerFactory.getLogger(ConnectionRESTService.class); - /** - * System administration permission. - */ - private static final Permission SYSTEM_PERMISSION = - new SystemPermission(SystemPermission.Type.ADMINISTER); - /** * A service for authenticating users from auth tokens. */ @@ -143,9 +137,13 @@ public class ConnectionRESTService { UserContext userContext = authenticationService.getUserContext(authToken); User self = userContext.self(); + // Retrieve permission sets + SystemPermissionSet systemPermissions = self.getSystemPermissions(); + ObjectPermissionSet connectionPermissions = self.getConnectionPermissions(); + // Deny access if adminstrative or update permission is missing - if (!self.hasPermission(SYSTEM_PERMISSION) - && !self.hasPermission(new ConnectionPermission(ObjectPermission.Type.UPDATE, connectionID))) + if (!systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER) + && !connectionPermissions.hasPermission(ObjectPermission.Type.UPDATE, connectionID)) throw new GuacamoleSecurityException("Permission to read connection parameters denied."); // Retrieve the requested connection @@ -212,9 +210,7 @@ public class ConnectionRESTService { UserContext userContext = authenticationService.getUserContext(authToken); // Get the connection directory - ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); - Directory connectionDirectory = - rootGroup.getConnectionDirectory(); + Directory connectionDirectory = userContext.getConnectionDirectory(); // Delete the specified connection connectionDirectory.remove(connectionID); @@ -249,12 +245,8 @@ public class ConnectionRESTService { if (connection == null) throw new GuacamoleClientException("Connection JSON must be submitted when creating connections."); - // Retrieve parent group - String parentID = connection.getParentIdentifier(); - ConnectionGroup parentConnectionGroup = retrievalService.retrieveConnectionGroup(userContext, parentID); - // Add the new connection - Directory connectionDirectory = parentConnectionGroup.getConnectionDirectory(); + Directory connectionDirectory = userContext.getConnectionDirectory(); connectionDirectory.add(new APIConnectionWrapper(connection)); // Return the new connection identifier @@ -293,9 +285,7 @@ public class ConnectionRESTService { throw new GuacamoleClientException("Connection JSON must be submitted when updating connections."); // Get the connection directory - ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); - Directory connectionDirectory = - rootGroup.getConnectionDirectory(); + Directory connectionDirectory = userContext.getConnectionDirectory(); // Retrieve connection to update Connection existingConnection = retrievalService.retrieveConnection(userContext, connectionID); @@ -310,15 +300,6 @@ public class ConnectionRESTService { existingConnection.setName(connection.getName()); connectionDirectory.update(existingConnection); - // Get old and new parents - String oldParentIdentifier = existingConnection.getParentIdentifier(); - ConnectionGroup updatedParentGroup = retrievalService.retrieveConnectionGroup(userContext, connection.getParentIdentifier()); - - // Update connection parent, if changed - if ( (oldParentIdentifier != null && !oldParentIdentifier.equals(updatedParentGroup.getIdentifier())) - || (oldParentIdentifier == null && updatedParentGroup.getIdentifier() != null)) - connectionDirectory.move(connectionID, updatedParentGroup.getConnectionDirectory()); - } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroup.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroup.java index 45ca049e7..b980983b5 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroup.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroup.java @@ -61,6 +61,11 @@ public class APIConnectionGroup { */ private Type type; + /** + * The count of currently active connections using this connection group. + */ + private int activeConnections; + /** * All child connection groups. If children are not being queried, this may * be omitted. @@ -91,6 +96,7 @@ public class APIConnectionGroup { this.name = connectionGroup.getName(); this.type = connectionGroup.getType(); + this.activeConnections = connectionGroup.getActiveConnections(); } @@ -206,4 +212,26 @@ public class APIConnectionGroup { this.childConnections = childConnections; } + /** + * Returns the number of currently active connections using this + * connection group. + * + * @return + * The number of currently active usages of this connection group. + */ + public int getActiveConnections() { + return activeConnections; + } + + /** + * Set the number of currently active connections using this connection + * group. + * + * @param activeConnections + * The number of currently active usages of this connection group. + */ + public void setActiveUsers(int activeConnections) { + this.activeConnections = activeConnections; + } + } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroupWrapper.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroupWrapper.java index c38b22977..9007e75dd 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroupWrapper.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroupWrapper.java @@ -22,11 +22,10 @@ package org.glyptodon.guacamole.net.basic.rest.connectiongroup; +import java.util.Set; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.net.GuacamoleSocket; -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.protocol.GuacamoleClientInformation; /** @@ -92,12 +91,17 @@ public class APIConnectionGroupWrapper implements ConnectionGroup { } @Override - public Directory getConnectionDirectory() throws GuacamoleException { + public int getActiveConnections() { + return apiConnectionGroup.getActiveConnections(); + } + + @Override + public Set getConnectionIdentifiers() { throw new UnsupportedOperationException("Operation not supported."); } @Override - public Directory getConnectionGroupDirectory() throws GuacamoleException { + public Set getConnectionGroupIdentifiers() { throw new UnsupportedOperationException("Operation not supported."); } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java index 049a9d1bc..abb686ec2 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java @@ -23,9 +23,6 @@ package org.glyptodon.guacamole.net.basic.rest.connectiongroup; import com.google.inject.Inject; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.List; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -39,20 +36,13 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; 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.ConnectionGroupPermission; -import org.glyptodon.guacamole.net.auth.permission.ConnectionPermission; import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; -import org.glyptodon.guacamole.net.auth.permission.SystemPermission; import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; import org.glyptodon.guacamole.net.basic.rest.ObjectRetrievalService; 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; @@ -83,176 +73,6 @@ public class ConnectionGroupRESTService { @Inject private ObjectRetrievalService retrievalService; - /** - * Determines whether the given user has at least one of the given - * permissions for the connection having the given identifier. - * - * @param user - * The user to check permissions for. - * - * @param identifier - * The identifier of the connection to check permissions for. - * - * @param permissions - * The permissions to check. The given user must have one or more of - * these permissions for this function to return true. - * - * @return - * true if the user has at least one of the given permissions. - */ - private boolean hasConnectionPermission(User user, String identifier, - List permissions) throws GuacamoleException { - - // Determine whether user has at least one of the given permissions - for (ObjectPermission.Type permission : permissions) { - - ConnectionPermission connectionPermission = new ConnectionPermission(permission, identifier); - if (user.hasPermission(connectionPermission)) - return true; - - } - - // None of the given permissions were present - return false; - - } - - /** - * Determines whether the given user has at least one of the given - * permissions for the connection group having the given identifier. - * - * @param user - * The user to check permissions for. - * - * @param identifier - * The identifier of the connection group to check permissions for. - * - * @param permissions - * The permissions to check. The given user must have one or more of - * these permissions for this function to return true. - * - * @return - * true if the user has at least one of the given permissions. - */ - private boolean hasConnectionGroupPermission(User user, String identifier, - List permissions) throws GuacamoleException { - - // Determine whether user has at least one of the given permissions - for (ObjectPermission.Type permission : permissions) { - - ConnectionGroupPermission connectionGroupPermission = new ConnectionGroupPermission(permission, identifier); - if (user.hasPermission(connectionGroupPermission)) - return true; - - } - - // None of the given permissions were present - return false; - - } - - /** - * 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 permissions - * The set of permissions to filter with. A user must have one or more - * of these permissions for a connection to appear in the result. - * If null, no filtering 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. - */ - private APIConnectionGroup retrieveConnectionGroup(UserContext userContext, - String identifier, boolean includeDescendants, List permissions) - throws GuacamoleException { - - User self = userContext.self(); - - // An admin user has access to any connection or connection group - boolean isAdmin = self.hasPermission(new SystemPermission(SystemPermission.Type.ADMINISTER)); - - // Retrieve specified connection group - ConnectionGroup connectionGroup; - try { - connectionGroup = retrievalService.retrieveConnectionGroup(userContext, identifier); - } - catch (GuacamoleResourceNotFoundException e) { - return null; - } - - // Wrap queried connection group - APIConnectionGroup apiConnectionGroup = new APIConnectionGroup(connectionGroup); - - // Recursively query all descendants if necessary, only querying the - // descendants of balancing groups if we have admin permission on that - // group - if (includeDescendants - && (connectionGroup.getType() != ConnectionGroup.Type.BALANCING - || isAdmin - || hasConnectionGroupPermission(self, identifier, - Collections.singletonList(ObjectPermission.Type.ADMINISTER)))) { - - // Query all child connections - Collection apiConnections = new ArrayList(); - Directory 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 (isAdmin || permissions == null || hasConnectionPermission(self, childIdentifier, permissions)) - apiConnections.add(new APIConnection(childConnection)); - - } - - // Associate child connections with current connection group - apiConnectionGroup.setChildConnections(apiConnections); - - // Query all child connection groups - Collection apiConnectionGroups = new ArrayList(); - Directory 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, permissions); - if (childConnectionGroup == null) - continue; - - apiConnectionGroups.add(childConnectionGroup); - - } - - // Associate child groups with current connection group - apiConnectionGroup.setChildConnectionGroups(apiConnectionGroups); - - } - - // Return the connectiion group - return apiConnectionGroup; - - } - /** * Gets an individual connection group. * @@ -277,12 +97,8 @@ public class ConnectionGroupRESTService { UserContext userContext = authenticationService.getUserContext(authToken); - // Retrieve requested connection group only - APIConnectionGroup connectionGroup = retrieveConnectionGroup(userContext, connectionGroupID, false, null); - if (connectionGroup == null) - throw new GuacamoleResourceNotFoundException("No such connection group: \"" + connectionGroupID + "\""); - - return connectionGroup; + // Retrieve the requested connection group + return new APIConnectionGroup(retrievalService.retrieveConnectionGroup(userContext, connectionGroupID)); } @@ -319,16 +135,12 @@ public class ConnectionGroupRESTService { UserContext userContext = authenticationService.getUserContext(authToken); - // Do not filter on permissions if no permissions are specified - if (permissions != null && permissions.isEmpty()) - permissions = null; - - // Retrieve requested connection group and all descendants - APIConnectionGroup connectionGroup = retrieveConnectionGroup(userContext, connectionGroupID, true, permissions); - if (connectionGroup == null) - throw new GuacamoleResourceNotFoundException("No such connection group: \"" + connectionGroupID + "\""); + // Retrieve the requested tree, filtering by the given permissions + ConnectionGroup treeRoot = retrievalService.retrieveConnectionGroup(userContext, connectionGroupID); + ConnectionGroupTree tree = new ConnectionGroupTree(userContext, treeRoot, permissions); - return connectionGroup; + // Return tree as a connection group + return tree.getRootAPIConnectionGroup(); } @@ -354,9 +166,7 @@ public class ConnectionGroupRESTService { UserContext userContext = authenticationService.getUserContext(authToken); // Get the connection group directory - ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); - Directory connectionGroupDirectory = - rootGroup.getConnectionGroupDirectory(); + Directory connectionGroupDirectory = userContext.getConnectionGroupDirectory(); // Delete the connection group connectionGroupDirectory.remove(connectionGroupID); @@ -393,12 +203,8 @@ public class ConnectionGroupRESTService { if (connectionGroup == null) throw new GuacamoleClientException("Connection group JSON must be submitted when creating connections groups."); - // Retrieve parent group - String parentID = connectionGroup.getParentIdentifier(); - ConnectionGroup parentConnectionGroup = retrievalService.retrieveConnectionGroup(userContext, parentID); - // Add the new connection group - Directory connectionGroupDirectory = parentConnectionGroup.getConnectionGroupDirectory(); + Directory connectionGroupDirectory = userContext.getConnectionGroupDirectory(); connectionGroupDirectory.add(new APIConnectionGroupWrapper(connectionGroup)); // Return the new connection group identifier @@ -438,9 +244,7 @@ public class ConnectionGroupRESTService { throw new GuacamoleClientException("Connection group JSON must be submitted when updating connection groups."); // Get the connection group directory - ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); - Directory connectionGroupDirectory = - rootGroup.getConnectionGroupDirectory(); + Directory connectionGroupDirectory = userContext.getConnectionGroupDirectory(); // Retrieve connection group to update ConnectionGroup existingConnectionGroup = connectionGroupDirectory.get(connectionGroupID); @@ -450,15 +254,6 @@ public class ConnectionGroupRESTService { existingConnectionGroup.setType(connectionGroup.getType()); connectionGroupDirectory.update(existingConnectionGroup); - // Get old and new parents - String oldParentIdentifier = existingConnectionGroup.getParentIdentifier(); - ConnectionGroup updatedParentGroup = retrievalService.retrieveConnectionGroup(userContext, connectionGroup.getParentIdentifier()); - - // Update connection group parent, if changed - if ( (oldParentIdentifier != null && !oldParentIdentifier.equals(updatedParentGroup.getIdentifier())) - || (oldParentIdentifier == null && updatedParentGroup.getIdentifier() != null)) - connectionGroupDirectory.move(connectionGroupID, updatedParentGroup.getConnectionGroupDirectory()); - } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupTree.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupTree.java new file mode 100644 index 000000000..5d801525c --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupTree.java @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2015 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.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.auth.Connection; +import org.glyptodon.guacamole.net.auth.ConnectionGroup; +import org.glyptodon.guacamole.net.auth.UserContext; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; +import org.glyptodon.guacamole.net.basic.rest.connection.APIConnection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides access to the entire tree of connection groups and their + * connections. + * + * @author Michael Jumper + */ +public class ConnectionGroupTree { + + /** + * Logger for this 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. + */ + private final APIConnectionGroup rootAPIGroup; + + /** + * All connection groups that have been retrieved, stored by their + * identifiers. + */ + private final Map retrievedGroups = + new HashMap(); + + /** + * Adds each of the provided connections to the current tree as children + * of their respective parents. The parent connection groups must already + * be added. + * + * @param connections + * The connections to add to the tree. + * + * @throws GuacamoleException + * If an error occurs while adding the connection to the tree. + */ + private void addConnections(Collection connections) + throws GuacamoleException { + + // Add each connection to the tree + for (Connection connection : connections) { + + // Retrieve the connection's parent group + APIConnectionGroup parent = retrievedGroups.get(connection.getParentIdentifier()); + if (parent != null) { + + Collection children = parent.getChildConnections(); + + // Create child collection if it does not yet exist + if (children == null) { + children = new ArrayList(); + parent.setChildConnections(children); + } + + // Add child + children.add(new APIConnection(connection)); + + } + + // Warn of internal consistency issues + else + logger.debug("Connection \"{}\" cannot be added to the tree: parent \"{}\" does not actually exist.", + connection.getIdentifier(), + connection.getParentIdentifier()); + + } // end for each connection + + } + + /** + * Adds each of the provided connection groups to the current tree as + * children of their respective parents. The parent connection groups must + * already be added. + * + * @param connectionGroups + * The connection groups to add to the tree. + */ + private void addConnectionGroups(Collection connectionGroups) { + + // Add each connection group to the tree + for (ConnectionGroup connectionGroup : connectionGroups) { + + // Retrieve the connection group's parent group + APIConnectionGroup parent = retrievedGroups.get(connectionGroup.getParentIdentifier()); + if (parent != null) { + + Collection children = parent.getChildConnectionGroups(); + + // Create child collection if it does not yet exist + if (children == null) { + children = new ArrayList(); + parent.setChildConnectionGroups(children); + } + + // Add child + APIConnectionGroup apiConnectionGroup = new APIConnectionGroup(connectionGroup); + retrievedGroups.put(connectionGroup.getIdentifier(), apiConnectionGroup); + children.add(apiConnectionGroup); + + } + + // Warn of internal consistency issues + else + logger.debug("Connection group \"{}\" cannot be added to the tree: parent \"{}\" does not actually exist.", + connectionGroup.getIdentifier(), + connectionGroup.getParentIdentifier()); + + } // end for each connection group + + } + + /** + * Adds all descendants of the given parent groups to their corresponding + * parents already stored under root. + * + * @param parents + * The parents whose descendants should be added to the tree. + * + * @throws GuacamoleException + * If an error occurs while retrieving the descendants. + */ + private void addDescendants(Collection parents) + throws GuacamoleException { + + // If no parents, nothing to do + if (parents.isEmpty()) + return; + + Collection childConnectionIdentifiers = new ArrayList(); + Collection childConnectionGroupIdentifiers = new ArrayList(); + + // Build lists of identifiers for retrieval + for (ConnectionGroup parent : parents) { + childConnectionIdentifiers.addAll(parent.getConnectionIdentifiers()); + childConnectionGroupIdentifiers.addAll(parent.getConnectionGroupIdentifiers()); + } + + // Retrieve child connections + if (!childConnectionIdentifiers.isEmpty()) { + Collection childConnections = userContext.getConnectionDirectory().getAll(childConnectionIdentifiers); + addConnections(childConnections); + } + + // Retrieve child connection groups + if (!childConnectionGroupIdentifiers.isEmpty()) { + Collection childConnectionGroups = userContext.getConnectionGroupDirectory().getAll(childConnectionGroupIdentifiers); + addConnectionGroups(childConnectionGroups); + addDescendants(childConnectionGroups); + } + + } + + /** + * Creates a new connection group tree using the given connection group as + * the tree root. + * + * @param userContext + * The context of the user obtaining the connection group tree. + * + * @param root + * The connection group to use as the root of this connection group + * tree. + * + * @param permissions + * If specified and non-empty, limit the contents of the tree to only + * those connections for which the current user has any of the given + * permissions. Otherwise, all visible connections are returned. + * Connection groups are unaffected by this parameter. + * + * @throws GuacamoleException + * If an error occurs while retrieving the tree of connection groups + * and their descendants. + */ + 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); + + // Add all descendants + addDescendants(Collections.singleton(root)); + + } + + /** + * Returns the entire connection group tree as an APIConnectionGroup. The + * returned APIConnectionGroup is the root group and will contain all + * descendant connection groups and connections, arranged hierarchically. + * + * @return + * The root connection group, containing the entire connection group + * tree and all connections. + */ + public APIConnectionGroup getRootAPIConnectionGroup() { + return rootAPIGroup; + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermissionSet.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermissionSet.java index 450cbc823..697641086 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermissionSet.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermissionSet.java @@ -24,17 +24,14 @@ 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.User; import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; -import org.glyptodon.guacamole.net.auth.permission.Permission; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; import org.glyptodon.guacamole.net.auth.permission.SystemPermission; -import org.glyptodon.guacamole.net.auth.permission.UserPermission; +import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; /** * The set of permissions which are granted to a specific user, organized by @@ -50,97 +47,26 @@ public class APIPermissionSet { /** * Map of connection ID to the set of granted permissions. */ - private Map> connectionPermissions = new HashMap>(); + private Map> connectionPermissions = + new HashMap>(); /** * Map of connection group ID to the set of granted permissions. */ - private Map> connectionGroupPermissions = new HashMap>(); + private Map> connectionGroupPermissions = + new HashMap>(); /** * Map of user ID to the set of granted permissions. */ - private Map> userPermissions = new HashMap>(); + private Map> userPermissions = + new HashMap>(); /** * Set of all granted system-level permissions. */ - private EnumSet 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> permissions, ObjectPermission permission) { - - // Pull set of permissions for given object - String id = permission.getObjectIdentifier(); - EnumSet 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 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."); - - } + private Set systemPermissions = + EnumSet.noneOf(SystemPermission.Type.class); /** * Creates a new permission set which contains no granted permissions. Any @@ -151,37 +77,83 @@ public class APIPermissionSet { } /** - * Creates a new permission set having the given permissions. + * Adds the system permissions from the given SystemPermissionSet to the + * Set of system permissions provided. * * @param permissions - * The permissions to initially store within the permission set. + * The Set to add system permissions to. + * + * @param permSet + * The SystemPermissionSet containing the system permissions to add. * * @throws GuacamoleException - * If any of the given permissions are of an unsupported type. + * If an error occurs while retrieving system permissions from the + * SystemPermissionSet. */ - public APIPermissionSet(Iterable permissions) throws GuacamoleException { + private void addSystemPermissions(Set permissions, + SystemPermissionSet permSet) throws GuacamoleException { - // Add all provided permissions - for (Permission permission : permissions) - addPermission(permission); + // Add all provided system permissions + for (SystemPermission permission : permSet.getPermissions()) + permissions.add(permission.getType()); } - + /** - * Creates a new permission set having the given permissions. + * Adds the object permissions from the given ObjectPermissionSet to the + * Map of object permissions provided. * * @param permissions - * The permissions to initially store within the permission set. + * The Map to add object permissions to. + * + * @param permSet + * The ObjectPermissionSet containing the object permissions to add. * * @throws GuacamoleException - * If any of the given permissions are of an unsupported type. + * If an error occurs while retrieving object permissions from the + * ObjectPermissionSet. */ - public APIPermissionSet(Permission... permissions) throws GuacamoleException { + private void addObjectPermissions(Map> permissions, + ObjectPermissionSet permSet) throws GuacamoleException { - // Add all provided permissions - for (Permission permission : permissions) - addPermission(permission); + // Add all provided object permissions + for (ObjectPermission permission : permSet.getPermissions()) { + // Get associated set of permissions + String identifier = permission.getObjectIdentifier(); + Set objectPermissions = permissions.get(identifier); + + // Create new set if none yet exists + if (objectPermissions == null) + permissions.put(identifier, EnumSet.of(permission.getType())); + + // Otherwise add to existing set + else + objectPermissions.add(permission.getType()); + + } + + } + + /** + * Creates a new permission set containing all permissions currently + * granted to the given user. + * + * @param user + * The user whose permissions should be stored within this permission + * set. + * + * @throws GuacamoleException + * If an error occurs while retrieving the user's permissions. + */ + public APIPermissionSet(User user) throws GuacamoleException { + + // Add all permissions from the provided user + addSystemPermissions(systemPermissions, user.getSystemPermissions()); + addObjectPermissions(connectionPermissions, user.getConnectionPermissions()); + addObjectPermissions(connectionGroupPermissions, user.getConnectionGroupPermissions()); + addObjectPermissions(userPermissions, user.getUserPermissions()); + } /** @@ -195,7 +167,7 @@ public class APIPermissionSet { * A map of connection IDs to the set of permissions granted for that * connection. */ - public Map> getConnectionPermissions() { + public Map> getConnectionPermissions() { return connectionPermissions; } @@ -210,7 +182,7 @@ public class APIPermissionSet { * A map of connection group IDs to the set of permissions granted for * that connection group. */ - public Map> getConnectionGroupPermissions() { + public Map> getConnectionGroupPermissions() { return connectionGroupPermissions; } @@ -223,7 +195,7 @@ public class APIPermissionSet { * @return * A map of user IDs to the set of permissions granted for that user. */ - public Map> getUserPermissions() { + public Map> getUserPermissions() { return userPermissions; } @@ -236,7 +208,7 @@ public class APIPermissionSet { * @return * The set of granted system-level permissions. */ - public EnumSet getSystemPermissions() { + public Set getSystemPermissions() { return systemPermissions; } @@ -249,7 +221,7 @@ public class APIPermissionSet { * @param connectionPermissions * The map which must replace the currently-stored map of permissions. */ - public void setConnectionPermissions(Map> connectionPermissions) { + public void setConnectionPermissions(Map> connectionPermissions) { this.connectionPermissions = connectionPermissions; } @@ -262,7 +234,7 @@ public class APIPermissionSet { * @param connectionGroupPermissions * The map which must replace the currently-stored map of permissions. */ - public void setConnectionGroupPermissions(Map> connectionGroupPermissions) { + public void setConnectionGroupPermissions(Map> connectionGroupPermissions) { this.connectionGroupPermissions = connectionGroupPermissions; } @@ -274,7 +246,7 @@ public class APIPermissionSet { * @param userPermissions * The map which must replace the currently-stored map of permissions. */ - public void setUserPermissions(Map> userPermissions) { + public void setUserPermissions(Map> userPermissions) { this.userPermissions = userPermissions; } @@ -286,7 +258,7 @@ public class APIPermissionSet { * @param systemPermissions * The set which must replace the currently-stored set of permissions. */ - public void setSystemPermissions(EnumSet systemPermissions) { + public void setSystemPermissions(Set systemPermissions) { this.systemPermissions = systemPermissions; } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUser.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUser.java index 2efcb6992..6351f2942 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUser.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUser.java @@ -55,7 +55,7 @@ public class APIUser { * @param user The User to construct the APIUser from. */ public APIUser(User user) { - this.username = user.getUsername(); + this.username = user.getIdentifier(); this.password = user.getPassword(); } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUserWrapper.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUserWrapper.java index dec5a9a65..52e5e707d 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUserWrapper.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUserWrapper.java @@ -22,15 +22,17 @@ package org.glyptodon.guacamole.net.basic.rest.user; -import java.util.Collections; -import java.util.Set; import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleUnsupportedException; import org.glyptodon.guacamole.net.auth.User; -import org.glyptodon.guacamole.net.auth.permission.Permission; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; +import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; /** - * A wrapper to make an APIConnection look like a User. Useful where a - * org.glyptodon.guacamole.net.auth.User is required. + * A wrapper to make an APIUser look like a User. Useful where an + * org.glyptodon.guacamole.net.auth.User is required. As a simple wrapper for + * APIUser, access to permissions is not provided. Any attempt to access or + * manipulate permissions on an APIUserWrapper will result in an exception. * * @author James Muehlner */ @@ -41,12 +43,6 @@ public class APIUserWrapper implements User { */ private final APIUser apiUser; - /** - * The set of permissions for this user. - * NOTE: Not exposed by the REST endpoints. - */ - private Set permissionSet = Collections.EMPTY_SET; - /** * Wrap a given APIUser to expose as a User. * @param apiUser The APIUser to wrap. @@ -55,23 +51,13 @@ public class APIUserWrapper implements User { this.apiUser = apiUser; } - /** - * Wrap a given APIUser to expose as a User, with the given permission set. - * @param apiUser The APIUser to wrap. - * @param permissionSet The set of permissions for the wrapped user. - */ - public APIUserWrapper(APIUser apiUser, Set permissionSet) { - this.apiUser = apiUser; - this.permissionSet = permissionSet; - } - @Override - public String getUsername() { + public String getIdentifier() { return apiUser.getUsername(); } @Override - public void setUsername(String username) { + public void setIdentifier(String username) { apiUser.setUsername(username); } @@ -86,23 +72,27 @@ public class APIUserWrapper implements User { } @Override - public Set getPermissions() throws GuacamoleException { - return permissionSet; + public SystemPermissionSet getSystemPermissions() + throws GuacamoleException { + throw new GuacamoleUnsupportedException("APIUserWrapper does not provide permission access."); } @Override - public boolean hasPermission(Permission permission) throws GuacamoleException { - return permissionSet.contains(permission); + public ObjectPermissionSet getConnectionPermissions() + throws GuacamoleException { + throw new GuacamoleUnsupportedException("APIUserWrapper does not provide permission access."); } @Override - public void addPermission(Permission permission) throws GuacamoleException { - throw new UnsupportedOperationException("Operation not supported."); + public ObjectPermissionSet getConnectionGroupPermissions() + throws GuacamoleException { + throw new GuacamoleUnsupportedException("APIUserWrapper does not provide permission access."); } @Override - public void removePermission(Permission permission) throws GuacamoleException { - throw new UnsupportedOperationException("Operation not supported."); + public ObjectPermissionSet getUserPermissions() + throws GuacamoleException { + throw new GuacamoleUnsupportedException("APIUserWrapper does not provide permission access."); } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/PermissionSetPatch.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/PermissionSetPatch.java new file mode 100644 index 000000000..cb887e64e --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/PermissionSetPatch.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2015 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.user; + +import java.util.HashSet; +import java.util.Set; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.auth.permission.Permission; +import org.glyptodon.guacamole.net.auth.permission.PermissionSet; + +/** + * A set of changes to be applied to a PermissionSet, describing the set of + * permissions being added and removed. + * + * @author Michael Jumper + * @param + * The type of permissions being added and removed. + */ +public class PermissionSetPatch { + + /** + * The set of all permissions being added. + */ + private final Set addedPermissions = + new HashSet(); + + /** + * The set of all permissions being removed. + */ + private final Set removedPermissions = + new HashSet(); + + /** + * Queues the given permission to be added. The add operation will be + * performed only when apply() is called. + * + * @param permission + * The permission to add. + */ + public void addPermission(PermissionType permission) { + addedPermissions.add(permission); + } + + /** + * Queues the given permission to be removed. The remove operation will be + * performed only when apply() is called. + * + * @param permission + * The permission to remove. + */ + public void removePermission(PermissionType permission) { + removedPermissions.add(permission); + } + + /** + * Applies all queued changes to the given permission set. + * + * @param permissionSet + * The permission set to add and remove permissions from. + * + * @throws GuacamoleException + * If an error occurs while manipulating the permissions of the given + * permission set. + */ + public void apply(PermissionSet permissionSet) + throws GuacamoleException { + + // Add any added permissions + if (!addedPermissions.isEmpty()) + permissionSet.addPermissions(addedPermissions); + + // Remove any removed permissions + if (!removedPermissions.isEmpty()) + permissionSet.removePermissions(removedPermissions); + + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java index 8f12aff47..d8fc5f15a 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java @@ -24,6 +24,7 @@ package org.glyptodon.guacamole.net.basic.rest.user; import com.google.inject.Inject; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.UUID; import javax.ws.rs.Consumes; @@ -43,12 +44,11 @@ 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.ObjectPermissionSet; 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.auth.permission.SystemPermissionSet; 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; @@ -112,40 +112,6 @@ public class UserRESTService { @Inject private ObjectRetrievalService retrievalService; - /** - * Determines whether the given user has at least one of the given - * permissions for the user having the given username. - * - * @param user - * The user to check permissions for. - * - * @param username - * The username of the user to check permissions for. - * - * @param permissions - * The permissions to check. The given user must have one or more of - * these permissions for this function to return true. - * - * @return - * true if the user has at least one of the given permissions. - */ - private boolean hasUserPermission(User user, String username, - List permissions) throws GuacamoleException { - - // Determine whether user has at least one of the given permissions - for (ObjectPermission.Type permission : permissions) { - - UserPermission userPermission = new UserPermission(permission, username); - if (user.hasPermission(userPermission)) - return true; - - } - - // None of the given permissions were present - return false; - - } - /** * Gets a list of users in the system, filtering the returned list by the * given permission, if specified. @@ -181,23 +147,26 @@ public class UserRESTService { permissions = null; // An admin user has access to any user - boolean isAdmin = self.hasPermission(new SystemPermission(SystemPermission.Type.ADMINISTER)); + SystemPermissionSet systemPermissions = self.getSystemPermissions(); + boolean isAdmin = systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER); // Get the directory - Directory userDirectory = userContext.getUserDirectory(); - - List users = new ArrayList(); - - // Add all users matching the given permission filter - for (String username : userDirectory.getIdentifiers()) { - - if (isAdmin || permissions == null || hasUserPermission(self, username, permissions)) - users.add(new APIUser(userDirectory.get(username))); + Directory userDirectory = userContext.getUserDirectory(); + // Filter users, if requested + Collection userIdentifiers = userDirectory.getIdentifiers(); + if (!isAdmin && permissions != null) { + ObjectPermissionSet userPermissions = self.getUserPermissions(); + userIdentifiers = userPermissions.getAccessibleObjects(permissions, userIdentifiers); } - - // Return the user directory listing - return users; + + // Retrieve all users, converting to API users + List apiUsers = new ArrayList(); + for (User user : userDirectory.getAll(userIdentifiers)) + apiUsers.add(new APIUser(user)); + + // Return the converted user list + return apiUsers; } @@ -248,7 +217,7 @@ public class UserRESTService { UserContext userContext = authenticationService.getUserContext(authToken); // Get the directory - Directory userDirectory = userContext.getUserDirectory(); + Directory userDirectory = userContext.getUserDirectory(); // Randomly set the password if it wasn't provided if (user.getPassword() == null) @@ -287,7 +256,7 @@ public class UserRESTService { UserContext userContext = authenticationService.getUserContext(authToken); // Get the directory - Directory userDirectory = userContext.getUserDirectory(); + Directory userDirectory = userContext.getUserDirectory(); // Validate data and path are sane if (!user.getUsername().equals(username)) @@ -329,7 +298,7 @@ public class UserRESTService { UserContext userContext = authenticationService.getUserContext(authToken); // Get the directory - Directory userDirectory = userContext.getUserDirectory(); + Directory userDirectory = userContext.getUserDirectory(); // Get the user User existingUser = userDirectory.get(username); @@ -369,7 +338,7 @@ public class UserRESTService { User user; // If username is own username, just use self - might not have query permissions - if (userContext.self().getUsername().equals(username)) + if (userContext.self().getIdentifier().equals(username)) user = userContext.self(); // If not self, query corresponding user from directory @@ -379,7 +348,50 @@ public class UserRESTService { throw new GuacamoleResourceNotFoundException("No such user: \"" + username + "\""); } - return new APIPermissionSet(user.getPermissions()); + return new APIPermissionSet(user); + + } + + /** + * Updates the given permission set patch by queuing an add or remove + * operation for the given permission based on the given patch operation. + * + * @param + * The type of permission stored within the permission set. + * + * @param operation + * The patch operation to perform. + * + * @param permissionSetPatch + * The permission set patch being modified. + * + * @param permission + * The permission being added or removed from the set. + */ + private void updatePermissionSet( + APIPatch.Operation operation, + PermissionSetPatch permissionSetPatch, + PermissionType permission) { + + // Add or remove permission based on operation + switch (operation) { + + // Add permission + case add: + permissionSetPatch.addPermission(permission); + break; + + // Remove permission + case remove: + permissionSetPatch.removePermission(permission); + break; + + // Unsupported patch operation + default: + throw new HTTPException(Status.BAD_REQUEST, + "Unsupported patch operation: \"" + operation + "\""); + + } } @@ -412,19 +424,20 @@ public class UserRESTService { UserContext userContext = authenticationService.getUserContext(authToken); - // Get the user directory - Directory userDirectory = userContext.getUserDirectory(); - // Get the user User user = userContext.getUserDirectory().get(username); if (user == null) throw new GuacamoleResourceNotFoundException("No such user: \"" + username + "\""); + // Permission patches for all types of permissions + PermissionSetPatch connectionPermissionPatch = new PermissionSetPatch(); + PermissionSetPatch connectionGroupPermissionPatch = new PermissionSetPatch(); + PermissionSetPatch userPermissionPatch = new PermissionSetPatch(); + PermissionSetPatch systemPermissionPatch = new PermissionSetPatch(); + // Apply all patch operations individually for (APIPatch patch : patches) { - Permission permission; - String path = patch.getPath(); // Create connection permission if path has connection prefix @@ -434,8 +447,9 @@ public class UserRESTService { 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 and update corresponding permission + ObjectPermission permission = new ObjectPermission(type, identifier); + updatePermissionSet(patch.getOp(), connectionPermissionPatch, permission); } @@ -446,8 +460,9 @@ public class UserRESTService { 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 and update corresponding permission + ObjectPermission permission = new ObjectPermission(type, identifier); + updatePermissionSet(patch.getOp(), connectionGroupPermissionPatch, permission); } @@ -458,19 +473,21 @@ public class UserRESTService { 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 and update corresponding permission + ObjectPermission permission = new ObjectPermission(type, identifier); + updatePermissionSet(patch.getOp(), userPermissionPatch, permission); + } // Create system permission if path is system path - else if (path.startsWith(SYSTEM_PERMISSION_PATCH_PATH)) { + else if (path.equals(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); + // Create and update corresponding permission + SystemPermission permission = new SystemPermission(type); + updatePermissionSet(patch.getOp(), systemPermissionPatch, permission); } @@ -478,30 +495,13 @@ public class UserRESTService { 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); + connectionPermissionPatch.apply(user.getConnectionPermissions()); + connectionGroupPermissionPatch.apply(user.getConnectionGroupPermissions()); + userPermissionPatch.apply(user.getUserPermissions()); + systemPermissionPatch.apply(user.getSystemPermissions()); } diff --git a/guacamole/src/main/webapp/app/groupList/types/GroupListItem.js b/guacamole/src/main/webapp/app/groupList/types/GroupListItem.js index 3f5260359..a13e0f98f 100644 --- a/guacamole/src/main/webapp/app/groupList/types/GroupListItem.js +++ b/guacamole/src/main/webapp/app/groupList/types/GroupListItem.js @@ -102,12 +102,12 @@ angular.module('groupList').factory('GroupListItem', ['ConnectionGroup', functio this.isExpanded = template.isExpanded; /** - * The number of currently active users for this connection. This field - * has no meaning for a connection group, and may be null or undefined. + * The number of currently active users for this connection or + * connection group, if known. * * @type Number */ - this.activeUsers = template.activeUsers; + this.activeConnections = template.activeConnections; /** * The connection or connection group whose data is exposed within @@ -143,8 +143,8 @@ angular.module('groupList').factory('GroupListItem', ['ConnectionGroup', functio isConnection : true, isConnectionGroup : false, - // Count of currently active users - activeUsers : connection.activeUsers, + // Count of currently active connections using this connection + activeConnections : connection.activeConnections, // Wrapped item wrappedItem : connection @@ -198,6 +198,9 @@ angular.module('groupList').factory('GroupListItem', ['ConnectionGroup', functio // Already-converted children children : children, + // Count of currently active connection groups using this connection + activeConnections : connectionGroup.activeConnections, + // Wrapped item wrappedItem : connectionGroup diff --git a/guacamole/src/main/webapp/app/home/templates/connection.html b/guacamole/src/main/webapp/app/home/templates/connection.html index 6e8bfcc28..4e91fd62d 100644 --- a/guacamole/src/main/webapp/app/home/templates/connection.html +++ b/guacamole/src/main/webapp/app/home/templates/connection.html @@ -21,7 +21,7 @@ THE SOFTWARE. --> -
+
@@ -32,8 +32,8 @@ {{item.name}} - - {{'HOME.INFO_ACTIVE_USER_COUNT' | translate:'{USERS: item.activeUsers}'}} + + {{'HOME.INFO_ACTIVE_USER_COUNT' | translate:'{USERS: item.activeConnections}'}}
diff --git a/guacamole/src/main/webapp/app/manage/templates/connection.html b/guacamole/src/main/webapp/app/manage/templates/connection.html index a1daded4e..2b65ee7d6 100644 --- a/guacamole/src/main/webapp/app/manage/templates/connection.html +++ b/guacamole/src/main/webapp/app/manage/templates/connection.html @@ -21,7 +21,7 @@ THE SOFTWARE. --> -
+
@@ -32,8 +32,8 @@ {{item.name}} - - {{'MANAGE.INFO_ACTIVE_USER_COUNT' | translate:'{USERS: item.activeUsers}'}} + + {{'MANAGE.INFO_ACTIVE_USER_COUNT' | translate:'{USERS: item.activeConnections}'}}
diff --git a/guacamole/src/main/webapp/app/rest/types/Connection.js b/guacamole/src/main/webapp/app/rest/types/Connection.js index 1a0459d7a..3c3efb87a 100644 --- a/guacamole/src/main/webapp/app/rest/types/Connection.js +++ b/guacamole/src/main/webapp/app/rest/types/Connection.js @@ -81,13 +81,13 @@ angular.module('rest').factory('Connection', [function defineConnection() { this.parameters = template.parameters; /** - * The count of currently active users for this connection. This field - * will be returned from the REST API during a get operation, - * but may not be set when doing an update or create operation. + * The count of currently active connections using this connection. + * This field will be returned from the REST API during a get + * operation, but manually setting this field will have no effect. * * @type Number */ - this.activeUsers = template.activeUsers; + this.activeConnections = template.activeConnections; }; diff --git a/guacamole/src/main/webapp/app/rest/types/ConnectionGroup.js b/guacamole/src/main/webapp/app/rest/types/ConnectionGroup.js index 89e53c6eb..cdb3fe232 100644 --- a/guacamole/src/main/webapp/app/rest/types/ConnectionGroup.js +++ b/guacamole/src/main/webapp/app/rest/types/ConnectionGroup.js @@ -91,6 +91,15 @@ angular.module('rest').factory('ConnectionGroup', [function defineConnectionGrou */ this.childConnectionGroups = template.childConnectionGroups; + /** + * The count of currently active connections using this connection + * group. This field will be returned from the REST API during a get + * operation, but manually setting this field will have no effect. + * + * @type Number + */ + this.activeConnections = template.activeConnections; + }; /**