diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java index a02012a90..017e6c297 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java @@ -57,6 +57,11 @@ import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionPermissionSet; import org.glyptodon.guacamole.auth.jdbc.permission.UserPermissionMapper; import org.glyptodon.guacamole.auth.jdbc.permission.UserPermissionService; import org.glyptodon.guacamole.auth.jdbc.permission.UserPermissionSet; +import org.glyptodon.guacamole.auth.jdbc.activeconnection.ActiveConnectionDirectory; +import org.glyptodon.guacamole.auth.jdbc.activeconnection.ActiveConnectionPermissionService; +import org.glyptodon.guacamole.auth.jdbc.activeconnection.ActiveConnectionPermissionSet; +import org.glyptodon.guacamole.auth.jdbc.activeconnection.ActiveConnectionService; +import org.glyptodon.guacamole.auth.jdbc.activeconnection.TrackedActiveConnection; import org.glyptodon.guacamole.environment.Environment; import org.mybatis.guice.MyBatisModule; import org.mybatis.guice.datasource.builtin.PooledDataSourceProvider; @@ -120,6 +125,8 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule { addMapperClass(UserPermissionMapper.class); // Bind core implementations of guacamole-ext classes + bind(ActiveConnectionDirectory.class); + bind(ActiveConnectionPermissionSet.class); bind(Environment.class).toInstance(environment); bind(ConnectionDirectory.class); bind(ConnectionGroupDirectory.class); @@ -131,11 +138,14 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule { bind(ModeledUser.class); bind(RootConnectionGroup.class); bind(SystemPermissionSet.class); + bind(TrackedActiveConnection.class); bind(UserContext.class); bind(UserDirectory.class); bind(UserPermissionSet.class); // Bind services + bind(ActiveConnectionService.class); + bind(ActiveConnectionPermissionService.class); bind(ConnectionGroupPermissionService.class); bind(ConnectionGroupService.class); bind(ConnectionPermissionService.class); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/ActiveConnectionDirectory.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/ActiveConnectionDirectory.java new file mode 100644 index 000000000..72d036813 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/ActiveConnectionDirectory.java @@ -0,0 +1,83 @@ +/* + * 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.auth.jdbc.activeconnection; + + +import com.google.inject.Inject; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject; +import org.glyptodon.guacamole.net.auth.ActiveConnection; +import org.glyptodon.guacamole.net.auth.Directory; + +/** + * Implementation of a Directory which contains all currently-active + * connections. + * + * @author Michael Jumper + */ +public class ActiveConnectionDirectory extends RestrictedObject + implements Directory { + + /** + * Service for retrieving and manipulating active connections. + */ + @Inject + private ActiveConnectionService activeConnectionService; + + @Override + public ActiveConnection get(String identifier) throws GuacamoleException { + return activeConnectionService.retrieveObject(getCurrentUser(), identifier); + } + + @Override + public Collection getAll(Collection identifiers) + throws GuacamoleException { + Collection objects = activeConnectionService.retrieveObjects(getCurrentUser(), identifiers); + return Collections.unmodifiableCollection(objects); + } + + @Override + public Set getIdentifiers() throws GuacamoleException { + return activeConnectionService.getIdentifiers(getCurrentUser()); + } + + @Override + public void add(ActiveConnection object) throws GuacamoleException { + activeConnectionService.createObject(getCurrentUser(), object); + } + + @Override + public void update(ActiveConnection object) throws GuacamoleException { + TrackedActiveConnection connection = (TrackedActiveConnection) object; + activeConnectionService.updateObject(getCurrentUser(), connection); + } + + @Override + public void remove(String identifier) throws GuacamoleException { + activeConnectionService.deleteObject(getCurrentUser(), identifier); + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/ActiveConnectionPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/ActiveConnectionPermissionService.java new file mode 100644 index 000000000..d5af87794 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/ActiveConnectionPermissionService.java @@ -0,0 +1,174 @@ +/* + * 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.auth.jdbc.activeconnection; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +import org.glyptodon.guacamole.auth.jdbc.permission.AbstractPermissionService; +import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionService; +import org.glyptodon.guacamole.auth.jdbc.tunnel.ActiveConnectionRecord; +import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.auth.jdbc.user.ModeledUser; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; + +/** + * Service which provides convenience methods for creating, retrieving, and + * manipulating active connections. + * + * @author Michael Jumper + */ +public class ActiveConnectionPermissionService + extends AbstractPermissionService + implements ObjectPermissionService { + + /** + * Service for creating and tracking tunnels. + */ + @Inject + private GuacamoleTunnelService tunnelService; + + /** + * Provider for active connection permission sets. + */ + @Inject + private Provider activeConnectionPermissionSetProvider; + + @Override + public ObjectPermission retrievePermission(AuthenticatedUser user, + ModeledUser targetUser, ObjectPermission.Type type, + String identifier) throws GuacamoleException { + + // Retrieve permissions + Set permissions = retrievePermissions(user, targetUser); + + // If retrieved permissions contains the requested permission, return it + ObjectPermission permission = new ObjectPermission(type, identifier); + if (permissions.contains(permission)) + return permission; + + // Otherwise, no such permission + return null; + + } + + @Override + public Set retrievePermissions(AuthenticatedUser user, + ModeledUser targetUser) throws GuacamoleException { + + // Retrieve permissions only if allowed + if (canReadPermissions(user, targetUser)) { + + // Only administrators may access active connections + if (!targetUser.isAdministrator()) + return Collections.EMPTY_SET; + + // Get all active connections + Collection records = tunnelService.getActiveConnections(user); + + // We have READ and DELETE on all active connections + Set permissions = new HashSet(); + for (ActiveConnectionRecord record : records) { + + // Add implicit READ and DELETE + String identifier = record.getUUID().toString(); + permissions.add(new ObjectPermission(ObjectPermission.Type.READ, identifier)); + permissions.add(new ObjectPermission(ObjectPermission.Type.DELETE, identifier)); + + } + + return permissions; + + } + + throw new GuacamoleSecurityException("Permission denied."); + + } + + @Override + public Collection retrieveAccessibleIdentifiers(AuthenticatedUser user, + ModeledUser targetUser, Collection permissionTypes, + Collection identifiers) throws GuacamoleException { + + Set permissions = retrievePermissions(user, targetUser); + 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 ObjectPermissionSet getPermissionSet(AuthenticatedUser user, + ModeledUser targetUser) throws GuacamoleException { + + // Create permission set for requested user + ActiveConnectionPermissionSet permissionSet = activeConnectionPermissionSetProvider.get(); + permissionSet.init(user, targetUser); + + return permissionSet; + + } + + @Override + public void createPermissions(AuthenticatedUser user, + ModeledUser targetUser, Collection permissions) + throws GuacamoleException { + + // Creating active connection permissions is not implemented + throw new GuacamoleSecurityException("Permission denied."); + + } + + @Override + public void deletePermissions(AuthenticatedUser user, + ModeledUser targetUser, Collection permissions) + throws GuacamoleException { + + // Deleting active connection permissions is not implemented + throw new GuacamoleSecurityException("Permission denied."); + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/ActiveConnectionPermissionSet.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/ActiveConnectionPermissionSet.java new file mode 100644 index 000000000..d60350a7e --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/ActiveConnectionPermissionSet.java @@ -0,0 +1,48 @@ +/* + * 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.auth.jdbc.activeconnection; + +import com.google.inject.Inject; +import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionService; +import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionSet; + +/** + * An implementation of ObjectPermissionSet which uses an injected service to + * query and manipulate the permissions associated with active connections. + * + * @author Michael Jumper + */ +public class ActiveConnectionPermissionSet extends ObjectPermissionSet { + + /** + * Service for querying and manipulating active connection permissions. + */ + @Inject + private ActiveConnectionPermissionService activeConnectionPermissionService; + + @Override + protected ObjectPermissionService getObjectPermissionService() { + return activeConnectionPermissionService; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/ActiveConnectionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/ActiveConnectionService.java new file mode 100644 index 000000000..41eceef60 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/ActiveConnectionService.java @@ -0,0 +1,151 @@ +/* + * 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.auth.jdbc.activeconnection; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectService; +import org.glyptodon.guacamole.auth.jdbc.tunnel.ActiveConnectionRecord; +import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; +import org.glyptodon.guacamole.net.GuacamoleTunnel; +import org.glyptodon.guacamole.net.auth.ActiveConnection; + +/** + * Service which provides convenience methods for creating, retrieving, and + * manipulating active connections. + * + * @author Michael Jumper + */ +public class ActiveConnectionService + implements DirectoryObjectService { + + /** + * Service for creating and tracking tunnels. + */ + @Inject + private GuacamoleTunnelService tunnelService; + + /** + * Provider for active connections. + */ + @Inject + private Provider trackedActiveConnectionProvider; + + @Override + public TrackedActiveConnection retrieveObject(AuthenticatedUser user, + String identifier) throws GuacamoleException { + + // Only administrators may retrieve active connections + if (!user.getUser().isAdministrator()) + throw new GuacamoleSecurityException("Permission denied."); + + // Retrieve record associated with requested connection + ActiveConnectionRecord record = tunnelService.getActiveConnection(user, identifier); + if (record == null) + return null; + + // Return tracked active connection using retrieved record + TrackedActiveConnection activeConnection = trackedActiveConnectionProvider.get(); + activeConnection.init(user, record); + return activeConnection; + + } + + @Override + public Collection retrieveObjects(AuthenticatedUser user, + Collection identifiers) throws GuacamoleException { + + // Build list of all active connections with given identifiers + Collection activeConnections = new ArrayList(identifiers.size()); + for (String identifier : identifiers) { + + // Add connection to list if it exists + TrackedActiveConnection activeConnection = retrieveObject(user, identifier); + if (activeConnection != null) + activeConnections.add(activeConnection); + + } + + return activeConnections; + + } + + @Override + public void deleteObject(AuthenticatedUser user, String identifier) + throws GuacamoleException { + + // Close connection, if it exists (and we have permission) + ActiveConnection activeConnection = retrieveObject(user, identifier); + if (activeConnection != null) { + + // Close connection if not already closed + GuacamoleTunnel tunnel = activeConnection.getTunnel(); + if (tunnel != null && tunnel.isOpen()) + tunnel.close(); + + } + + } + + @Override + public Set getIdentifiers(AuthenticatedUser user) + throws GuacamoleException { + + // Retrieve all visible connections (permissions enforced by tunnel service) + Collection records = tunnelService.getActiveConnections(user); + + // Build list of identifiers + Set identifiers = new HashSet(records.size()); + for (ActiveConnectionRecord record : records) + identifiers.add(record.getUUID().toString()); + + return identifiers; + + } + + @Override + public TrackedActiveConnection createObject(AuthenticatedUser user, + ActiveConnection object) throws GuacamoleException { + + // Updating active connections is not implemented + throw new GuacamoleSecurityException("Permission denied."); + + } + + @Override + public void updateObject(AuthenticatedUser user, TrackedActiveConnection object) + throws GuacamoleException { + + // Updating active connections is not implemented + throw new GuacamoleSecurityException("Permission denied."); + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/TrackedActiveConnection.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/TrackedActiveConnection.java new file mode 100644 index 000000000..8dc00fe27 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/TrackedActiveConnection.java @@ -0,0 +1,155 @@ +/* + * 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.auth.jdbc.activeconnection; + +import java.util.Date; +import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject; +import org.glyptodon.guacamole.auth.jdbc.tunnel.ActiveConnectionRecord; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.net.GuacamoleTunnel; +import org.glyptodon.guacamole.net.auth.ActiveConnection; + +/** + * An implementation of the ActiveConnection object which has an associated + * ActiveConnectionRecord. + * + * @author Michael Jumper + */ +public class TrackedActiveConnection extends RestrictedObject implements ActiveConnection { + + /** + * The identifier of this active connection. + */ + private String identifier; + + /** + * The identifier of the associated connection. + */ + private String connectionIdentifier; + + /** + * The date and time this active connection began. + */ + private Date startDate; + + /** + * The remote host that initiated this connection. + */ + private String remoteHost; + + /** + * The username of the user that initiated this connection. + */ + private String username; + + /** + * The underlying GuacamoleTunnel. + */ + private GuacamoleTunnel tunnel; + + /** + * Initializes this TrackedActiveConnection, copying the data associated + * with the given active connection record. + * + * @param currentUser + * The user that created or retrieved this object. + * + * @param activeConnectionRecord + * The active connection record to copy. + */ + public void init(AuthenticatedUser currentUser, + ActiveConnectionRecord activeConnectionRecord) { + + super.init(currentUser); + + // Copy all data from given record + this.connectionIdentifier = activeConnectionRecord.getConnection().getIdentifier(); + this.identifier = activeConnectionRecord.getUUID().toString(); + this.remoteHost = activeConnectionRecord.getRemoteHost(); + this.startDate = activeConnectionRecord.getStartDate(); + this.tunnel = activeConnectionRecord.getTunnel(); + this.username = activeConnectionRecord.getUsername(); + + } + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + @Override + public String getConnectionIdentifier() { + return connectionIdentifier; + } + + @Override + public void setConnectionIdentifier(String connnectionIdentifier) { + this.connectionIdentifier = connnectionIdentifier; + } + + @Override + public Date getStartDate() { + return startDate; + } + + @Override + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + @Override + public String getRemoteHost() { + return remoteHost; + } + + @Override + public void setRemoteHost(String remoteHost) { + this.remoteHost = remoteHost; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public void setUsername(String username) { + this.username = username; + } + + @Override + public GuacamoleTunnel getTunnel() { + return tunnel; + } + + @Override + public void setTunnel(GuacamoleTunnel tunnel) { + this.tunnel = tunnel; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/package-info.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/package-info.java new file mode 100644 index 000000000..018e6303f --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/activeconnection/package-info.java @@ -0,0 +1,26 @@ +/* + * 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. + */ + +/** + * Classes related to currently-active connections. + */ +package org.glyptodon.guacamole.auth.jdbc.activeconnection; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObjectService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObjectService.java index abe5ce1c3..7d755a68f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObjectService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObjectService.java @@ -22,23 +22,16 @@ package org.glyptodon.guacamole.auth.jdbc.base; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Set; import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.GuacamoleSecurityException; -import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionMapper; -import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionModel; -import org.glyptodon.guacamole.auth.jdbc.user.UserModel; -import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; -import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; /** * Service which provides convenience methods for creating, retrieving, and - * manipulating objects within directories. This service will automatically - * enforce the permissions of the current user. + * manipulating objects that have unique identifiers, such as the objects + * within directories. This service will automatically enforce the permissions + * of the current user. * * @author Michael Jumper * @param @@ -48,246 +41,8 @@ import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; * @param * The external interface or implementation of the type of object this * service provides access to, as defined by the guacamole-ext API. - * - * @param - * The underlying model object used to represent InternalType in the - * database. */ -public abstract class DirectoryObjectService, - ExternalType, ModelType extends ObjectModel> { - - /** - * All object permissions which are implicitly granted upon creation to the - * creator of the object. - */ - private static final ObjectPermission.Type[] IMPLICIT_OBJECT_PERMISSIONS = { - ObjectPermission.Type.READ, - ObjectPermission.Type.UPDATE, - ObjectPermission.Type.DELETE, - ObjectPermission.Type.ADMINISTER - }; - - /** - * Returns an instance of a mapper for the type of object used by this - * service. - * - * @return - * A mapper which provides access to the model objects associated with - * the objects used by this service. - */ - protected abstract DirectoryObjectMapper getObjectMapper(); - - /** - * Returns an instance of a mapper for the type of permissions that affect - * the type of object used by this service. - * - * @return - * A mapper which provides access to the model objects associated with - * the permissions that affect the objects used by this service. - */ - protected abstract ObjectPermissionMapper getPermissionMapper(); - - /** - * Returns an instance of an object which is backed by the given model - * object. - * - * @param currentUser - * The user for whom this object is being created. - * - * @param model - * The model object to use to back the returned object. - * - * @return - * An object which is backed by the given model object. - */ - protected abstract InternalType getObjectInstance(AuthenticatedUser currentUser, - ModelType model); - - /** - * Returns an instance of a model object which is based on the given - * object. - * - * @param currentUser - * The user for whom this model object is being created. - * - * @param object - * The object to use to produce the returned model object. - * - * @return - * A model object which is based on the given object. - */ - protected abstract ModelType getModelInstance(AuthenticatedUser currentUser, - ExternalType object); - - /** - * Returns whether the given user has permission to create the type of - * objects that this directory object service manages. - * - * @param user - * The user being checked. - * - * @return - * true if the user has object creation permission relevant to this - * directory object service, false otherwise. - * - * @throws GuacamoleException - * If permission to read the user's permissions is denied. - */ - protected abstract boolean hasCreatePermission(AuthenticatedUser user) - throws GuacamoleException; - - /** - * Returns whether the given user has permission to perform a certain - * action on a specific object managed by this directory object service. - * - * @param user - * The user being checked. - * - * @param identifier - * The identifier of the object to check. - * - * @param type - * The type of action that will be performed. - * - * @return - * true if the user has object permission relevant described, false - * otherwise. - * - * @throws GuacamoleException - * If permission to read the user's permissions is denied. - */ - protected boolean hasObjectPermission(AuthenticatedUser user, - String identifier, ObjectPermission.Type type) - throws GuacamoleException { - - // Get object permissions - ObjectPermissionSet permissionSet = getPermissionSet(user); - - // Return whether permission is granted - return user.getUser().isAdministrator() - || permissionSet.hasPermission(type, identifier); - - } - - /** - * Returns the permission set associated with the given user and related - * to the type of objects handled by this directory object service. - * - * @param user - * The user whose permissions are being retrieved. - * - * @return - * A permission set which contains the permissions associated with the - * given user and related to the type of objects handled by this - * directory object service. - * - * @throws GuacamoleException - * If permission to read the user's permissions is denied. - */ - protected abstract ObjectPermissionSet getPermissionSet(AuthenticatedUser user) - throws GuacamoleException; - - /** - * Returns a collection of objects which are backed by the models in the - * given collection. - * - * @param currentUser - * The user for whom these objects are being created. - * - * @param models - * The model objects to use to back the objects within the returned - * collection. - * - * @return - * A collection of objects which are backed by the models in the given - * collection. - */ - protected Collection getObjectInstances(AuthenticatedUser currentUser, - Collection models) { - - // Create new collection of objects by manually converting each model - Collection objects = new ArrayList(models.size()); - for (ModelType model : models) - objects.add(getObjectInstance(currentUser, model)); - - return objects; - - } - - /** - * Called before any object is created through this directory object - * service. This function serves as a final point of validation before - * the create operation occurs. In its default implementation, - * beforeCreate() performs basic permissions checks. - * - * @param user - * The user creating the object. - * - * @param model - * The model of the object being created. - * - * @throws GuacamoleException - * If the object is invalid, or an error prevents validating the given - * object. - */ - protected void beforeCreate(AuthenticatedUser user, - ModelType model ) throws GuacamoleException { - - // Verify permission to create objects - if (!user.getUser().isAdministrator() && !hasCreatePermission(user)) - throw new GuacamoleSecurityException("Permission denied."); - - } - - /** - * Called before any object is updated through this directory object - * service. This function serves as a final point of validation before - * the update operation occurs. In its default implementation, - * beforeUpdate() performs basic permissions checks. - * - * @param user - * The user updating the existing object. - * - * @param model - * The model of the object being updated. - * - * @throws GuacamoleException - * If the object is invalid, or an error prevents validating the given - * object. - */ - protected void beforeUpdate(AuthenticatedUser user, - ModelType model) throws GuacamoleException { - - // By default, do nothing. - if (!hasObjectPermission(user, model.getIdentifier(), ObjectPermission.Type.UPDATE)) - throw new GuacamoleSecurityException("Permission denied."); - - } - - /** - * Called before any object is deleted through this directory object - * service. This function serves as a final point of validation before - * the delete operation occurs. In its default implementation, - * beforeDelete() performs basic permissions checks. - * - * @param user - * The user deleting the existing object. - * - * @param identifier - * The identifier of the object being deleted. - * - * @throws GuacamoleException - * If the object is invalid, or an error prevents validating the given - * object. - */ - protected void beforeDelete(AuthenticatedUser user, - String identifier) throws GuacamoleException { - - // Verify permission to delete objects - if (!hasObjectPermission(user, identifier, ObjectPermission.Type.DELETE)) - throw new GuacamoleSecurityException("Permission denied."); - - } +public interface DirectoryObjectService { /** * Retrieves the single object that has the given identifier, if it exists @@ -306,24 +61,8 @@ public abstract class DirectoryObjectService objects = retrieveObjects(user, Collections.singleton(identifier)); - - // If no such object, return null - if (objects.isEmpty()) - return null; - - // The object collection will have exactly one element unless the - // database has seriously lost integrity - assert(objects.size() == 1); - - // Return first and only object - return objects.iterator().next(); - - } + InternalType retrieveObject(AuthenticatedUser user, String identifier) + throws GuacamoleException; /** * Retrieves all objects that have the identifiers in the given collection. @@ -341,73 +80,12 @@ public abstract class DirectoryObjectService retrieveObjects(AuthenticatedUser user, - Collection identifiers) throws GuacamoleException { - - // Do not query if no identifiers given - if (identifiers.isEmpty()) - return Collections.EMPTY_LIST; - - Collection objects; - - // Bypass permission checks if the user is a system admin - if (user.getUser().isAdministrator()) - objects = getObjectMapper().select(identifiers); - - // Otherwise only return explicitly readable identifiers - else - objects = getObjectMapper().selectReadable(user.getUser().getModel(), identifiers); - - // Return collection of requested objects - return getObjectInstances(user, objects); - - } + Collection retrieveObjects(AuthenticatedUser user, + Collection identifiers) throws GuacamoleException; /** - * Returns a collection of permissions that should be granted due to the - * creation of the given object. These permissions need not be granted - * solely to the user creating the object. - * - * @param user - * The user creating the object. - * - * @param model - * The object being created. - * - * @return - * The collection of implicit permissions that should be granted due to - * the creation of the given object. - */ - protected Collection getImplicitPermissions(AuthenticatedUser user, - ModelType model) { - - // Build list of implicit permissions - Collection implicitPermissions = - new ArrayList(IMPLICIT_OBJECT_PERMISSIONS.length); - - UserModel userModel = user.getUser().getModel(); - for (ObjectPermission.Type permission : IMPLICIT_OBJECT_PERMISSIONS) { - - // Create model which grants this permission to the current user - ObjectPermissionModel permissionModel = new ObjectPermissionModel(); - permissionModel.setUserID(userModel.getObjectID()); - permissionModel.setUsername(userModel.getIdentifier()); - permissionModel.setType(permission); - permissionModel.setObjectIdentifier(model.getIdentifier()); - - // Add permission - implicitPermissions.add(permissionModel); - - } - - return implicitPermissions; - - } - - /** - * Creates the given object within the database. If the object already - * exists, an error will be thrown. The internal model object will be - * updated appropriately to contain the new database ID. + * Creates the given object. If the object already exists, an error will be + * thrown. * * @param user * The user creating the object. @@ -422,21 +100,8 @@ public abstract class DirectoryObjectService getIdentifiers(AuthenticatedUser user) - throws GuacamoleException { - - // Bypass permission checks if the user is a system admin - if (user.getUser().isAdministrator()) - return getObjectMapper().selectIdentifiers(); - - // Otherwise only return explicitly readable identifiers - else - return getObjectMapper().selectReadableIdentifiers(user.getUser().getModel()); - - } + Set getIdentifiers(AuthenticatedUser user) throws GuacamoleException; } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObject.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledDirectoryObject.java similarity index 86% rename from extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObject.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledDirectoryObject.java index 8568d8995..8986f4f2c 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObject.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledDirectoryObject.java @@ -26,14 +26,15 @@ import org.glyptodon.guacamole.net.auth.Identifiable; /** * Common base class for objects that will ultimately be made available through - * the Directory class. All such objects will need the same base set of queries - * to fulfill the needs of the Directory class. + * the Directory class and are persisted to an underlying database model. All + * such objects will need the same base set of queries to fulfill the needs of + * the Directory class. * * @author Michael Jumper * @param * The type of model object that corresponds to this object. */ -public abstract class DirectoryObject +public abstract class ModeledDirectoryObject extends ModeledObject implements Identifiable { @Override diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObjectMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledDirectoryObjectMapper.java similarity index 98% rename from extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObjectMapper.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledDirectoryObjectMapper.java index d63cf3814..f3dc98582 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/DirectoryObjectMapper.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledDirectoryObjectMapper.java @@ -37,7 +37,7 @@ import org.apache.ibatis.annotations.Param; * The type of object contained within the directory whose objects are * mapped by this mapper. */ -public interface DirectoryObjectMapper { +public interface ModeledDirectoryObjectMapper { /** * Selects the identifiers of all objects, regardless of whether they diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledDirectoryObjectService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledDirectoryObjectService.java new file mode 100644 index 000000000..7d9137075 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledDirectoryObjectService.java @@ -0,0 +1,431 @@ +/* + * 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.auth.jdbc.base; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionMapper; +import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionModel; +import org.glyptodon.guacamole.auth.jdbc.user.UserModel; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; + +/** + * Service which provides convenience methods for creating, retrieving, and + * manipulating objects within directories. This service will automatically + * enforce the permissions of the current user. + * + * @author Michael Jumper + * @param + * The specific internal implementation of the type of object this service + * provides access to. + * + * @param + * The external interface or implementation of the type of object this + * service provides access to, as defined by the guacamole-ext API. + * + * @param + * The underlying model object used to represent InternalType in the + * database. + */ +public abstract class ModeledDirectoryObjectService, + ExternalType, ModelType extends ObjectModel> + implements DirectoryObjectService { + + /** + * All object permissions which are implicitly granted upon creation to the + * creator of the object. + */ + private static final ObjectPermission.Type[] IMPLICIT_OBJECT_PERMISSIONS = { + ObjectPermission.Type.READ, + ObjectPermission.Type.UPDATE, + ObjectPermission.Type.DELETE, + ObjectPermission.Type.ADMINISTER + }; + + /** + * Returns an instance of a mapper for the type of object used by this + * service. + * + * @return + * A mapper which provides access to the model objects associated with + * the objects used by this service. + */ + protected abstract ModeledDirectoryObjectMapper getObjectMapper(); + + /** + * Returns an instance of a mapper for the type of permissions that affect + * the type of object used by this service. + * + * @return + * A mapper which provides access to the model objects associated with + * the permissions that affect the objects used by this service. + */ + protected abstract ObjectPermissionMapper getPermissionMapper(); + + /** + * Returns an instance of an object which is backed by the given model + * object. + * + * @param currentUser + * The user for whom this object is being created. + * + * @param model + * The model object to use to back the returned object. + * + * @return + * An object which is backed by the given model object. + */ + protected abstract InternalType getObjectInstance(AuthenticatedUser currentUser, + ModelType model); + + /** + * Returns an instance of a model object which is based on the given + * object. + * + * @param currentUser + * The user for whom this model object is being created. + * + * @param object + * The object to use to produce the returned model object. + * + * @return + * A model object which is based on the given object. + */ + protected abstract ModelType getModelInstance(AuthenticatedUser currentUser, + ExternalType object); + + /** + * Returns whether the given user has permission to create the type of + * objects that this directory object service manages. + * + * @param user + * The user being checked. + * + * @return + * true if the user has object creation permission relevant to this + * directory object service, false otherwise. + * + * @throws GuacamoleException + * If permission to read the user's permissions is denied. + */ + protected abstract boolean hasCreatePermission(AuthenticatedUser user) + throws GuacamoleException; + + /** + * Returns whether the given user has permission to perform a certain + * action on a specific object managed by this directory object service. + * + * @param user + * The user being checked. + * + * @param identifier + * The identifier of the object to check. + * + * @param type + * The type of action that will be performed. + * + * @return + * true if the user has object permission relevant described, false + * otherwise. + * + * @throws GuacamoleException + * If permission to read the user's permissions is denied. + */ + protected boolean hasObjectPermission(AuthenticatedUser user, + String identifier, ObjectPermission.Type type) + throws GuacamoleException { + + // Get object permissions + ObjectPermissionSet permissionSet = getPermissionSet(user); + + // Return whether permission is granted + return user.getUser().isAdministrator() + || permissionSet.hasPermission(type, identifier); + + } + + /** + * Returns the permission set associated with the given user and related + * to the type of objects handled by this directory object service. + * + * @param user + * The user whose permissions are being retrieved. + * + * @return + * A permission set which contains the permissions associated with the + * given user and related to the type of objects handled by this + * directory object service. + * + * @throws GuacamoleException + * If permission to read the user's permissions is denied. + */ + protected abstract ObjectPermissionSet getPermissionSet(AuthenticatedUser user) + throws GuacamoleException; + + /** + * Returns a collection of objects which are backed by the models in the + * given collection. + * + * @param currentUser + * The user for whom these objects are being created. + * + * @param models + * The model objects to use to back the objects within the returned + * collection. + * + * @return + * A collection of objects which are backed by the models in the given + * collection. + */ + protected Collection getObjectInstances(AuthenticatedUser currentUser, + Collection models) { + + // Create new collection of objects by manually converting each model + Collection objects = new ArrayList(models.size()); + for (ModelType model : models) + objects.add(getObjectInstance(currentUser, model)); + + return objects; + + } + + /** + * Called before any object is created through this directory object + * service. This function serves as a final point of validation before + * the create operation occurs. In its default implementation, + * beforeCreate() performs basic permissions checks. + * + * @param user + * The user creating the object. + * + * @param model + * The model of the object being created. + * + * @throws GuacamoleException + * If the object is invalid, or an error prevents validating the given + * object. + */ + protected void beforeCreate(AuthenticatedUser user, + ModelType model ) throws GuacamoleException { + + // Verify permission to create objects + if (!user.getUser().isAdministrator() && !hasCreatePermission(user)) + throw new GuacamoleSecurityException("Permission denied."); + + } + + /** + * Called before any object is updated through this directory object + * service. This function serves as a final point of validation before + * the update operation occurs. In its default implementation, + * beforeUpdate() performs basic permissions checks. + * + * @param user + * The user updating the existing object. + * + * @param model + * The model of the object being updated. + * + * @throws GuacamoleException + * If the object is invalid, or an error prevents validating the given + * object. + */ + protected void beforeUpdate(AuthenticatedUser user, + ModelType model) throws GuacamoleException { + + // By default, do nothing. + if (!hasObjectPermission(user, model.getIdentifier(), ObjectPermission.Type.UPDATE)) + throw new GuacamoleSecurityException("Permission denied."); + + } + + /** + * Called before any object is deleted through this directory object + * service. This function serves as a final point of validation before + * the delete operation occurs. In its default implementation, + * beforeDelete() performs basic permissions checks. + * + * @param user + * The user deleting the existing object. + * + * @param identifier + * The identifier of the object being deleted. + * + * @throws GuacamoleException + * If the object is invalid, or an error prevents validating the given + * object. + */ + protected void beforeDelete(AuthenticatedUser user, + String identifier) throws GuacamoleException { + + // Verify permission to delete objects + if (!hasObjectPermission(user, identifier, ObjectPermission.Type.DELETE)) + throw new GuacamoleSecurityException("Permission denied."); + + } + + @Override + public InternalType retrieveObject(AuthenticatedUser user, + String identifier) throws GuacamoleException { + + // Pull objects having given identifier + Collection objects = retrieveObjects(user, Collections.singleton(identifier)); + + // If no such object, return null + if (objects.isEmpty()) + return null; + + // The object collection will have exactly one element unless the + // database has seriously lost integrity + assert(objects.size() == 1); + + // Return first and only object + return objects.iterator().next(); + + } + + @Override + public Collection retrieveObjects(AuthenticatedUser user, + Collection identifiers) throws GuacamoleException { + + // Do not query if no identifiers given + if (identifiers.isEmpty()) + return Collections.EMPTY_LIST; + + Collection objects; + + // Bypass permission checks if the user is a system admin + if (user.getUser().isAdministrator()) + objects = getObjectMapper().select(identifiers); + + // Otherwise only return explicitly readable identifiers + else + objects = getObjectMapper().selectReadable(user.getUser().getModel(), identifiers); + + // Return collection of requested objects + return getObjectInstances(user, objects); + + } + + /** + * Returns a collection of permissions that should be granted due to the + * creation of the given object. These permissions need not be granted + * solely to the user creating the object. + * + * @param user + * The user creating the object. + * + * @param model + * The object being created. + * + * @return + * The collection of implicit permissions that should be granted due to + * the creation of the given object. + */ + protected Collection getImplicitPermissions(AuthenticatedUser user, + ModelType model) { + + // Build list of implicit permissions + Collection implicitPermissions = + new ArrayList(IMPLICIT_OBJECT_PERMISSIONS.length); + + UserModel userModel = user.getUser().getModel(); + for (ObjectPermission.Type permission : IMPLICIT_OBJECT_PERMISSIONS) { + + // Create model which grants this permission to the current user + ObjectPermissionModel permissionModel = new ObjectPermissionModel(); + permissionModel.setUserID(userModel.getObjectID()); + permissionModel.setUsername(userModel.getIdentifier()); + permissionModel.setType(permission); + permissionModel.setObjectIdentifier(model.getIdentifier()); + + // Add permission + implicitPermissions.add(permissionModel); + + } + + return implicitPermissions; + + } + + @Override + public InternalType createObject(AuthenticatedUser user, ExternalType object) + throws GuacamoleException { + + ModelType model = getModelInstance(user, object); + beforeCreate(user, model); + + // Create object + getObjectMapper().insert(model); + + // Add implicit permissions + getPermissionMapper().insert(getImplicitPermissions(user, model)); + + return getObjectInstance(user, model); + + } + + @Override + public void deleteObject(AuthenticatedUser user, String identifier) + throws GuacamoleException { + + beforeDelete(user, identifier); + + // Delete object + getObjectMapper().delete(identifier); + + } + + @Override + public void updateObject(AuthenticatedUser user, InternalType object) + throws GuacamoleException { + + ModelType model = object.getModel(); + beforeUpdate(user, model); + + // Update object + getObjectMapper().update(model); + + } + + @Override + public Set getIdentifiers(AuthenticatedUser user) + throws GuacamoleException { + + // Bypass permission checks if the user is a system admin + if (user.getUser().isAdministrator()) + return getObjectMapper().selectIdentifiers(); + + // Otherwise only return explicitly readable identifiers + else + return getObjectMapper().selectReadableIdentifiers(user.getUser().getModel()); + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/GroupedDirectoryObject.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledGroupedDirectoryObject.java similarity index 95% rename from extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/GroupedDirectoryObject.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledGroupedDirectoryObject.java index 2b804be85..50c527f9d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/GroupedDirectoryObject.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledGroupedDirectoryObject.java @@ -33,8 +33,8 @@ import org.glyptodon.guacamole.auth.jdbc.connectiongroup.RootConnectionGroup; * @param * The type of model object that corresponds to this object. */ -public abstract class GroupedDirectoryObject - extends DirectoryObject { +public abstract class ModeledGroupedDirectoryObject + extends ModeledDirectoryObject { /** * Returns the identifier of the parent connection group, which cannot be diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/GroupedDirectoryObjectService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledGroupedDirectoryObjectService.java similarity index 97% rename from extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/GroupedDirectoryObjectService.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledGroupedDirectoryObjectService.java index 4fb169f35..1cfa734d0 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/GroupedDirectoryObjectService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/base/ModeledGroupedDirectoryObjectService.java @@ -49,9 +49,9 @@ import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; * The underlying model object used to represent InternalType in the * database. */ -public abstract class GroupedDirectoryObjectService, +public abstract class ModeledGroupedDirectoryObjectService, ExternalType, ModelType extends GroupedObjectModel> - extends DirectoryObjectService { + extends ModeledDirectoryObjectService { /** * Returns the set of parent connection groups that are modified by the diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionMapper.java index faa7f21d1..77c29046f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionMapper.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionMapper.java @@ -23,7 +23,7 @@ package org.glyptodon.guacamole.auth.jdbc.connection; import java.util.Set; -import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectMapper; +import org.glyptodon.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper; import org.glyptodon.guacamole.auth.jdbc.user.UserModel; import org.apache.ibatis.annotations.Param; @@ -32,7 +32,7 @@ import org.apache.ibatis.annotations.Param; * * @author Michael Jumper */ -public interface ConnectionMapper extends DirectoryObjectMapper { +public interface ConnectionMapper extends ModeledDirectoryObjectMapper { /** * Selects the identifiers of all connections within the given parent diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java index 8386d1a6d..27a74207c 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java @@ -32,12 +32,12 @@ import java.util.List; import java.util.Map; import java.util.Set; import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; -import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectMapper; +import org.glyptodon.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper; import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; import org.glyptodon.guacamole.GuacamoleClientException; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleSecurityException; -import org.glyptodon.guacamole.auth.jdbc.base.GroupedDirectoryObjectService; +import org.glyptodon.guacamole.auth.jdbc.base.ModeledGroupedDirectoryObjectService; import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionPermissionMapper; import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionMapper; import org.glyptodon.guacamole.net.GuacamoleTunnel; @@ -55,7 +55,7 @@ import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; * * @author Michael Jumper, James Muehlner */ -public class ConnectionService extends GroupedDirectoryObjectService { +public class ConnectionService extends ModeledGroupedDirectoryObjectService { /** * Mapper for accessing connections. @@ -94,7 +94,7 @@ public class ConnectionService extends GroupedDirectoryObjectService getObjectMapper() { + protected ModeledDirectoryObjectMapper getObjectMapper() { return connectionMapper; } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnection.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnection.java index 0502f2272..477b58175 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnection.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnection.java @@ -27,7 +27,7 @@ import com.google.inject.Provider; import java.util.List; import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.auth.jdbc.base.GroupedDirectoryObject; +import org.glyptodon.guacamole.auth.jdbc.base.ModeledGroupedDirectoryObject; import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionRecord; @@ -41,7 +41,7 @@ import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; * @author James Muehlner * @author Michael Jumper */ -public class ModeledConnection extends GroupedDirectoryObject +public class ModeledConnection extends ModeledGroupedDirectoryObject implements Connection { /** diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java index d00d19d13..83e472c73 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java @@ -24,7 +24,6 @@ package org.glyptodon.guacamole.auth.jdbc.connection; import java.util.Date; -import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.auth.ConnectionRecord; /** @@ -52,11 +51,6 @@ public class ModeledConnectionRecord implements ConnectionRecord { this.model = model; } - @Override - public String getIdentifier() { - return model.getConnectionIdentifier(); - } - @Override public Date getStartDate() { return model.getStartDate(); @@ -82,9 +76,4 @@ public class ModeledConnectionRecord implements ConnectionRecord { return false; } - @Override - public GuacamoleTunnel getTunnel() { - return null; - } - } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupMapper.java index a08ef7c20..ee4df9beb 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupMapper.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupMapper.java @@ -23,7 +23,7 @@ package org.glyptodon.guacamole.auth.jdbc.connectiongroup; import java.util.Set; -import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectMapper; +import org.glyptodon.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper; import org.glyptodon.guacamole.auth.jdbc.user.UserModel; import org.apache.ibatis.annotations.Param; @@ -32,7 +32,7 @@ import org.apache.ibatis.annotations.Param; * * @author Michael Jumper */ -public interface ConnectionGroupMapper extends DirectoryObjectMapper { +public interface ConnectionGroupMapper extends ModeledDirectoryObjectMapper { /** * Selects the identifiers of all connection groups within the given parent diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java index 4fa9ef9c8..954b5e0ac 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ConnectionGroupService.java @@ -26,13 +26,13 @@ import com.google.inject.Inject; import com.google.inject.Provider; import java.util.Set; import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; -import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectMapper; +import org.glyptodon.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper; import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; import org.glyptodon.guacamole.GuacamoleClientException; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleSecurityException; import org.glyptodon.guacamole.GuacamoleUnsupportedException; -import org.glyptodon.guacamole.auth.jdbc.base.GroupedDirectoryObjectService; +import org.glyptodon.guacamole.auth.jdbc.base.ModeledGroupedDirectoryObjectService; import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionGroupPermissionMapper; import org.glyptodon.guacamole.auth.jdbc.permission.ObjectPermissionMapper; import org.glyptodon.guacamole.net.GuacamoleTunnel; @@ -49,7 +49,7 @@ import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; * * @author Michael Jumper, James Muehlner */ -public class ConnectionGroupService extends GroupedDirectoryObjectService { /** @@ -77,7 +77,7 @@ public class ConnectionGroupService extends GroupedDirectoryObjectService getObjectMapper() { + protected ModeledDirectoryObjectMapper getObjectMapper() { return connectionGroupMapper; } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ModeledConnectionGroup.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ModeledConnectionGroup.java index 0b5d6c67c..01f09006f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ModeledConnectionGroup.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connectiongroup/ModeledConnectionGroup.java @@ -27,7 +27,7 @@ import java.util.Set; import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionService; import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.auth.jdbc.base.GroupedDirectoryObject; +import org.glyptodon.guacamole.auth.jdbc.base.ModeledGroupedDirectoryObject; import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.auth.ConnectionGroup; import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; @@ -38,7 +38,7 @@ import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; * * @author James Muehlner */ -public class ModeledConnectionGroup extends GroupedDirectoryObject +public class ModeledConnectionGroup extends ModeledGroupedDirectoryObject implements ConnectionGroup { /** diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/AbstractPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/AbstractPermissionService.java new file mode 100644 index 000000000..feff734ff --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/AbstractPermissionService.java @@ -0,0 +1,84 @@ +/* + * 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.auth.jdbc.permission; + +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.auth.jdbc.user.ModeledUser; +import org.glyptodon.guacamole.GuacamoleException; +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.PermissionSet; + +/** + * Abstract PermissionService implementation which provides additional + * convenience methods for enforcing the permission model. + * + * @author Michael Jumper + * @param + * The type of permission sets this service provides access to. + * + * @param + * The type of permission this service provides access to. + */ +public abstract class AbstractPermissionService, + PermissionType extends Permission> + implements PermissionService { + + /** + * Determines whether the given user can read the permissions currently + * granted to the given target user. If the reading user and the target + * user are not the same, then explicit READ or SYSTEM_ADMINISTER access is + * required. + * + * @param user + * The user attempting to read permissions. + * + * @param targetUser + * The user whose permissions are being read. + * + * @return + * true if permission is granted, false otherwise. + * + * @throws GuacamoleException + * If an error occurs while checking permission status, or if + * permission is denied to read the current user's permissions. + */ + protected boolean canReadPermissions(AuthenticatedUser user, + ModeledUser targetUser) throws GuacamoleException { + + // A user can always read their own permissions + if (user.getUser().getIdentifier().equals(targetUser.getIdentifier())) + return true; + + // A system adminstrator can do anything + if (user.getUser().isAdministrator()) + return true; + + // Can read permissions on target user if explicit READ is granted + ObjectPermissionSet userPermissionSet = user.getUser().getUserPermissions(); + return userPermissionSet.hasPermission(ObjectPermission.Type.READ, targetUser.getIdentifier()); + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionService.java index e64d6c71f..b16078efe 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionGroupPermissionService.java @@ -35,7 +35,7 @@ import org.glyptodon.guacamole.auth.jdbc.user.ModeledUser; * * @author Michael Jumper */ -public class ConnectionGroupPermissionService extends ObjectPermissionService { +public class ConnectionGroupPermissionService extends ModeledObjectPermissionService { /** * Mapper for connection group permissions. diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionService.java index 0903f71ab..d0bb6f7f9 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ConnectionPermissionService.java @@ -35,7 +35,7 @@ import org.glyptodon.guacamole.auth.jdbc.user.ModeledUser; * * @author Michael Jumper */ -public class ConnectionPermissionService extends ObjectPermissionService { +public class ConnectionPermissionService extends ModeledObjectPermissionService { /** * Mapper for connection permissions. diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ModeledObjectPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ModeledObjectPermissionService.java new file mode 100644 index 000000000..ad4f65859 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ModeledObjectPermissionService.java @@ -0,0 +1,210 @@ +/* + * 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.auth.jdbc.permission; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.auth.jdbc.user.ModeledUser; +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; + +/** + * Service which provides convenience methods for creating, retrieving, and + * deleting object permissions within a backend database model. This service + * will automatically enforce the permissions of the current user. + * + * @author Michael Jumper + */ +public abstract class ModeledObjectPermissionService + extends ModeledPermissionService + implements ObjectPermissionService { + + @Override + protected abstract ObjectPermissionMapper getPermissionMapper(); + + @Override + protected ObjectPermission getPermissionInstance(ObjectPermissionModel model) { + return new ObjectPermission(model.getType(), model.getObjectIdentifier()); + } + + @Override + protected ObjectPermissionModel getModelInstance(ModeledUser targetUser, + ObjectPermission permission) { + + ObjectPermissionModel model = new ObjectPermissionModel(); + + // Populate model object with data from user and permission + model.setUserID(targetUser.getModel().getObjectID()); + model.setUsername(targetUser.getModel().getIdentifier()); + model.setType(permission.getType()); + model.setObjectIdentifier(permission.getObjectIdentifier()); + + return model; + + } + + /** + * Determines whether the current user has permission to update the given + * target user, adding or removing the given permissions. Such permission + * depends on whether the current user is a system administrator, whether + * they have explicit UPDATE permission on the target user, and whether + * they have explicit ADMINISTER permission on all affected objects. + * + * @param user + * The user who is changing permissions. + * + * @param targetUser + * The user whose permissions are being changed. + * + * @param permissions + * The permissions that are being added or removed from the target + * user. + * + * @return + * true if the user has permission to change the target users + * permissions as specified, false otherwise. + * + * @throws GuacamoleException + * If an error occurs while checking permission status, or if + * permission is denied to read the current user's permissions. + */ + protected boolean canAlterPermissions(AuthenticatedUser user, ModeledUser targetUser, + Collection permissions) + throws GuacamoleException { + + // A system adminstrator can do anything + if (user.getUser().isAdministrator()) + return true; + + // Verify user has update permission on the target user + ObjectPermissionSet userPermissionSet = user.getUser().getUserPermissions(); + if (!userPermissionSet.hasPermission(ObjectPermission.Type.UPDATE, targetUser.getIdentifier())) + return false; + + // Produce collection of affected identifiers + Collection affectedIdentifiers = new HashSet(permissions.size()); + for (ObjectPermission permission : permissions) + affectedIdentifiers.add(permission.getObjectIdentifier()); + + // Determine subset of affected identifiers that we have admin access to + ObjectPermissionSet affectedPermissionSet = getPermissionSet(user, user.getUser()); + Collection allowedSubset = affectedPermissionSet.getAccessibleObjects( + Collections.singleton(ObjectPermission.Type.ADMINISTER), + affectedIdentifiers + ); + + // The permissions can be altered if and only if the set of objects we + // are allowed to administer is equal to the set of objects we will be + // affecting. + + return affectedIdentifiers.size() == allowedSubset.size(); + + } + + @Override + public void createPermissions(AuthenticatedUser user, ModeledUser targetUser, + Collection permissions) + throws GuacamoleException { + + // Create permissions only if user has permission to do so + if (canAlterPermissions(user, targetUser, permissions)) { + Collection models = getModelInstances(targetUser, permissions); + getPermissionMapper().insert(models); + return; + } + + // User lacks permission to create object permissions + throw new GuacamoleSecurityException("Permission denied."); + + } + + @Override + public void deletePermissions(AuthenticatedUser user, ModeledUser targetUser, + Collection permissions) + throws GuacamoleException { + + // Delete permissions only if user has permission to do so + if (canAlterPermissions(user, targetUser, permissions)) { + Collection models = getModelInstances(targetUser, permissions); + getPermissionMapper().delete(models); + return; + } + + // User lacks permission to delete object permissions + throw new GuacamoleSecurityException("Permission denied."); + + } + + @Override + public ObjectPermission retrievePermission(AuthenticatedUser user, + ModeledUser targetUser, ObjectPermission.Type type, + String identifier) throws GuacamoleException { + + // Retrieve permissions only if allowed + if (canReadPermissions(user, targetUser)) { + + // Read permission from database, return null if not found + ObjectPermissionModel model = getPermissionMapper().selectOne(targetUser.getModel(), type, identifier); + if (model == null) + return null; + + return getPermissionInstance(model); + + } + + // User cannot read this user's permissions + throw new GuacamoleSecurityException("Permission denied."); + + } + + @Override + public Collection retrieveAccessibleIdentifiers(AuthenticatedUser user, + ModeledUser targetUser, Collection permissions, + Collection identifiers) throws GuacamoleException { + + // Nothing is always accessible + if (identifiers.isEmpty()) + return identifiers; + + // Retrieve permissions only if allowed + if (canReadPermissions(user, targetUser)) { + + // If user is an admin, everything is accessible + if (user.getUser().isAdministrator()) + return identifiers; + + // Otherwise, return explicitly-retrievable identifiers + return getPermissionMapper().selectAccessibleIdentifiers(targetUser.getModel(), permissions, identifiers); + + } + + // User cannot read this user's permissions + throw new GuacamoleSecurityException("Permission denied."); + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ModeledPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ModeledPermissionService.java new file mode 100644 index 000000000..819ef005d --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ModeledPermissionService.java @@ -0,0 +1,158 @@ +/* + * 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.auth.jdbc.permission; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; +import org.glyptodon.guacamole.auth.jdbc.user.ModeledUser; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +import org.glyptodon.guacamole.net.auth.permission.Permission; +import org.glyptodon.guacamole.net.auth.permission.PermissionSet; + +/** + * Service which provides convenience methods for creating, retrieving, and + * deleting permissions within a backend database model, and for obtaining the + * permission sets that contain these permissions. This service will + * automatically enforce the permissions of the current user. + * + * @author Michael Jumper + * @param + * The type of permission sets this service provides access to. + * + * @param + * The type of permission this service provides access to. + * + * @param + * The underlying model object used to represent PermissionType in the + * database. + */ +public abstract class ModeledPermissionService, + PermissionType extends Permission, ModelType> + extends AbstractPermissionService { + + /** + * Returns an instance of a mapper for the type of permission used by this + * service. + * + * @return + * A mapper which provides access to the model objects associated with + * the permissions used by this service. + */ + protected abstract PermissionMapper getPermissionMapper(); + + /** + * Returns an instance of a permission which is based on the given model + * object. + * + * @param model + * The model object to use to produce the returned permission. + * + * @return + * A permission which is based on the given model object. + */ + protected abstract PermissionType getPermissionInstance(ModelType model); + + /** + * Returns a collection of permissions which are based on the models in + * the given collection. + * + * @param models + * The model objects to use to produce the permissions within the + * returned set. + * + * @return + * A set of permissions which are based on the models in the given + * collection. + */ + protected Set getPermissionInstances(Collection models) { + + // Create new collection of permissions by manually converting each model + Set permissions = new HashSet(models.size()); + for (ModelType model : models) + permissions.add(getPermissionInstance(model)); + + return permissions; + + } + + /** + * Returns an instance of a model object which is based on the given + * permission and target user. + * + * @param targetUser + * The user to whom this permission is granted. + * + * @param permission + * The permission to use to produce the returned model object. + * + * @return + * A model object which is based on the given permission and target + * user. + */ + protected abstract ModelType getModelInstance(ModeledUser targetUser, + PermissionType permission); + + /** + * Returns a collection of model objects which are based on the given + * permissions and target user. + * + * @param targetUser + * The user to whom this permission is granted. + * + * @param permissions + * The permissions to use to produce the returned model objects. + * + * @return + * A collection of model objects which are based on the given + * permissions and target user. + */ + protected Collection getModelInstances(ModeledUser targetUser, + Collection permissions) { + + // Create new collection of models by manually converting each permission + Collection models = new ArrayList(permissions.size()); + for (PermissionType permission : permissions) + models.add(getModelInstance(targetUser, permission)); + + return models; + + } + + @Override + public Set retrievePermissions(AuthenticatedUser user, + ModeledUser targetUser) throws GuacamoleException { + + // Retrieve permissions only if allowed + if (canReadPermissions(user, targetUser)) + return getPermissionInstances(getPermissionMapper().select(targetUser.getModel())); + + // User cannot read this user's permissions + throw new GuacamoleSecurityException("Permission denied."); + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ObjectPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ObjectPermissionService.java index 4e3beb0e1..04e66ad1d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ObjectPermissionService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/ObjectPermissionService.java @@ -23,12 +23,9 @@ package org.glyptodon.guacamole.auth.jdbc.permission; import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; import org.glyptodon.guacamole.auth.jdbc.user.ModeledUser; 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; @@ -39,124 +36,8 @@ import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; * * @author Michael Jumper */ -public abstract class ObjectPermissionService - extends PermissionService { - - @Override - protected abstract ObjectPermissionMapper getPermissionMapper(); - - @Override - protected ObjectPermission getPermissionInstance(ObjectPermissionModel model) { - return new ObjectPermission(model.getType(), model.getObjectIdentifier()); - } - - @Override - protected ObjectPermissionModel getModelInstance(ModeledUser targetUser, - ObjectPermission permission) { - - ObjectPermissionModel model = new ObjectPermissionModel(); - - // Populate model object with data from user and permission - model.setUserID(targetUser.getModel().getObjectID()); - model.setUsername(targetUser.getModel().getIdentifier()); - model.setType(permission.getType()); - model.setObjectIdentifier(permission.getObjectIdentifier()); - - return model; - - } - - /** - * Determines whether the current user has permission to update the given - * target user, adding or removing the given permissions. Such permission - * depends on whether the current user is a system administrator, whether - * they have explicit UPDATE permission on the target user, and whether - * they have explicit ADMINISTER permission on all affected objects. - * - * @param user - * The user who is changing permissions. - * - * @param targetUser - * The user whose permissions are being changed. - * - * @param permissions - * The permissions that are being added or removed from the target - * user. - * - * @return - * true if the user has permission to change the target users - * permissions as specified, false otherwise. - * - * @throws GuacamoleException - * If an error occurs while checking permission status, or if - * permission is denied to read the current user's permissions. - */ - protected boolean canAlterPermissions(AuthenticatedUser user, ModeledUser targetUser, - Collection permissions) - throws GuacamoleException { - - // A system adminstrator can do anything - if (user.getUser().isAdministrator()) - return true; - - // Verify user has update permission on the target user - ObjectPermissionSet userPermissionSet = user.getUser().getUserPermissions(); - if (!userPermissionSet.hasPermission(ObjectPermission.Type.UPDATE, targetUser.getIdentifier())) - return false; - - // Produce collection of affected identifiers - Collection affectedIdentifiers = new HashSet(permissions.size()); - for (ObjectPermission permission : permissions) - affectedIdentifiers.add(permission.getObjectIdentifier()); - - // Determine subset of affected identifiers that we have admin access to - ObjectPermissionSet affectedPermissionSet = getPermissionSet(user, user.getUser()); - Collection allowedSubset = affectedPermissionSet.getAccessibleObjects( - Collections.singleton(ObjectPermission.Type.ADMINISTER), - affectedIdentifiers - ); - - // The permissions can be altered if and only if the set of objects we - // are allowed to administer is equal to the set of objects we will be - // affecting. - - return affectedIdentifiers.size() == allowedSubset.size(); - - } - - @Override - public void createPermissions(AuthenticatedUser user, ModeledUser targetUser, - Collection permissions) - throws GuacamoleException { - - // Create permissions only if user has permission to do so - if (canAlterPermissions(user, targetUser, permissions)) { - Collection models = getModelInstances(targetUser, permissions); - getPermissionMapper().insert(models); - return; - } - - // User lacks permission to create object permissions - throw new GuacamoleSecurityException("Permission denied."); - - } - - @Override - public void deletePermissions(AuthenticatedUser user, ModeledUser targetUser, - Collection permissions) - throws GuacamoleException { - - // Delete permissions only if user has permission to do so - if (canAlterPermissions(user, targetUser, permissions)) { - Collection models = getModelInstances(targetUser, permissions); - getPermissionMapper().delete(models); - return; - } - - // User lacks permission to delete object permissions - throw new GuacamoleSecurityException("Permission denied."); - - } +public interface ObjectPermissionService + extends PermissionService { /** * Retrieves the permission of the given type associated with the given @@ -181,26 +62,9 @@ public abstract class ObjectPermissionService * @throws GuacamoleException * If an error occurs while retrieving the requested permission. */ - public ObjectPermission retrievePermission(AuthenticatedUser user, + ObjectPermission retrievePermission(AuthenticatedUser user, ModeledUser targetUser, ObjectPermission.Type type, - String identifier) throws GuacamoleException { - - // Retrieve permissions only if allowed - if (canReadPermissions(user, targetUser)) { - - // Read permission from database, return null if not found - ObjectPermissionModel model = getPermissionMapper().selectOne(targetUser.getModel(), type, identifier); - if (model == null) - return null; - - return getPermissionInstance(model); - - } - - // User cannot read this user's permissions - throw new GuacamoleSecurityException("Permission denied."); - - } + String identifier) throws GuacamoleException; /** * Retrieves the subset of the given identifiers for which the given user @@ -228,29 +92,8 @@ public abstract class ObjectPermissionService * @throws GuacamoleException * If an error occurs while retrieving permissions. */ - public Collection retrieveAccessibleIdentifiers(AuthenticatedUser user, + Collection retrieveAccessibleIdentifiers(AuthenticatedUser user, ModeledUser targetUser, Collection permissions, - Collection identifiers) throws GuacamoleException { - - // Nothing is always accessible - if (identifiers.isEmpty()) - return identifiers; - - // Retrieve permissions only if allowed - if (canReadPermissions(user, targetUser)) { - - // If user is an admin, everything is accessible - if (user.getUser().isAdministrator()) - return identifiers; - - // Otherwise, return explicitly-retrievable identifiers - return getPermissionMapper().selectAccessibleIdentifiers(targetUser.getModel(), permissions, identifiers); - - } - - // User cannot read this user's permissions - throw new GuacamoleSecurityException("Permission denied."); - - } + Collection identifiers) throws GuacamoleException; } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/PermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/PermissionService.java index b4c35ba5e..c350c3c95 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/PermissionService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/PermissionService.java @@ -47,137 +47,9 @@ import org.glyptodon.guacamole.net.auth.permission.PermissionSet; * * @param * The type of permission this service provides access to. - * - * @param - * The underlying model object used to represent PermissionType in the - * database. */ -public abstract class PermissionService, - PermissionType extends Permission, ModelType> { - - /** - * Returns an instance of a mapper for the type of permission used by this - * service. - * - * @return - * A mapper which provides access to the model objects associated with - * the permissions used by this service. - */ - protected abstract PermissionMapper getPermissionMapper(); - - /** - * Returns an instance of a permission which is based on the given model - * object. - * - * @param model - * The model object to use to produce the returned permission. - * - * @return - * A permission which is based on the given model object. - */ - protected abstract PermissionType getPermissionInstance(ModelType model); - - /** - * Returns a collection of permissions which are based on the models in - * the given collection. - * - * @param models - * The model objects to use to produce the permissions within the - * returned set. - * - * @return - * A set of permissions which are based on the models in the given - * collection. - */ - protected Set getPermissionInstances(Collection models) { - - // Create new collection of permissions by manually converting each model - Set permissions = new HashSet(models.size()); - for (ModelType model : models) - permissions.add(getPermissionInstance(model)); - - return permissions; - - } - - /** - * Returns an instance of a model object which is based on the given - * permission and target user. - * - * @param targetUser - * The user to whom this permission is granted. - * - * @param permission - * The permission to use to produce the returned model object. - * - * @return - * A model object which is based on the given permission and target - * user. - */ - protected abstract ModelType getModelInstance(ModeledUser targetUser, - PermissionType permission); - - /** - * Returns a collection of model objects which are based on the given - * permissions and target user. - * - * @param targetUser - * The user to whom this permission is granted. - * - * @param permissions - * The permissions to use to produce the returned model objects. - * - * @return - * A collection of model objects which are based on the given - * permissions and target user. - */ - protected Collection getModelInstances(ModeledUser targetUser, - Collection permissions) { - - // Create new collection of models by manually converting each permission - Collection models = new ArrayList(permissions.size()); - for (PermissionType permission : permissions) - models.add(getModelInstance(targetUser, permission)); - - return models; - - } - - /** - * Determines whether the given user can read the permissions currently - * granted to the given target user. If the reading user and the target - * user are not the same, then explicit READ or SYSTEM_ADMINISTER access is - * required. - * - * @param user - * The user attempting to read permissions. - * - * @param targetUser - * The user whose permissions are being read. - * - * @return - * true if permission is granted, false otherwise. - * - * @throws GuacamoleException - * If an error occurs while checking permission status, or if - * permission is denied to read the current user's permissions. - */ - protected boolean canReadPermissions(AuthenticatedUser user, - ModeledUser targetUser) throws GuacamoleException { - - // A user can always read their own permissions - if (user.getUser().getIdentifier().equals(targetUser.getIdentifier())) - return true; - - // A system adminstrator can do anything - if (user.getUser().isAdministrator()) - return true; - - // Can read permissions on target user if explicit READ is granted - ObjectPermissionSet userPermissionSet = user.getUser().getUserPermissions(); - return userPermissionSet.hasPermission(ObjectPermission.Type.READ, targetUser.getIdentifier()); - - } +public interface PermissionService, + PermissionType extends Permission> { /** * Returns a permission set that can be used to retrieve and manipulate the @@ -200,7 +72,7 @@ public abstract class PermissionService retrievePermissions(AuthenticatedUser user, - ModeledUser targetUser) throws GuacamoleException { - - // Retrieve permissions only if allowed - if (canReadPermissions(user, targetUser)) - return getPermissionInstances(getPermissionMapper().select(targetUser.getModel())); - - // User cannot read this user's permissions - throw new GuacamoleSecurityException("Permission denied."); - - } + Set retrievePermissions(AuthenticatedUser user, + ModeledUser targetUser) throws GuacamoleException; /** * Creates the given permissions within the database. If any permissions @@ -247,8 +110,7 @@ public abstract class PermissionService permissions) throws GuacamoleException; /** @@ -268,8 +130,7 @@ public abstract class PermissionService permissions) throws GuacamoleException; } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionService.java index 2a7837e58..47a0c1795 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/SystemPermissionService.java @@ -40,7 +40,7 @@ import org.glyptodon.guacamole.net.auth.permission.SystemPermission; * @author Michael Jumper */ public class SystemPermissionService - extends PermissionService { + extends ModeledPermissionService { /** * Mapper for system-level permissions. diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionService.java index 33497496a..c70d71706 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/permission/UserPermissionService.java @@ -35,7 +35,7 @@ import org.glyptodon.guacamole.auth.jdbc.user.ModeledUser; * * @author Michael Jumper */ -public class UserPermissionService extends ObjectPermissionService { +public class UserPermissionService extends ModeledObjectPermissionService { /** * Mapper for user permissions. diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java index 94521f51d..10adde197 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java @@ -49,7 +49,6 @@ import org.glyptodon.guacamole.net.GuacamoleSocket; import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionGroup; -import org.glyptodon.guacamole.net.auth.ConnectionRecord; import org.glyptodon.guacamole.protocol.ConfiguredGuacamoleSocket; import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; @@ -100,8 +99,8 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS /** * All active connections through the tunnel having a given UUID. */ - private final Map activeTunnels = - new ConcurrentHashMap(); + private final Map activeTunnels = + new ConcurrentHashMap(); /** * All active connections to a connection having a given identifier. @@ -446,7 +445,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS } @Override - public Collection getActiveConnections(AuthenticatedUser user) + public Collection getActiveConnections(AuthenticatedUser user) throws GuacamoleException { // Only administrators may see all active connections @@ -458,7 +457,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS } @Override - public ConnectionRecord getActiveConnection(AuthenticatedUser user, + public ActiveConnectionRecord getActiveConnection(AuthenticatedUser user, String tunnelUUID) throws GuacamoleException { // Only administrators may see all active connections @@ -482,7 +481,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS } @Override - public Collection getActiveConnections(Connection connection) { + public Collection getActiveConnections(Connection connection) { return activeConnections.get(connection.getIdentifier()); } @@ -507,7 +506,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS } @Override - public Collection getActiveConnections(ConnectionGroup connectionGroup) { + public Collection getActiveConnections(ConnectionGroup connectionGroup) { // If not a balancing group, assume no connections if (connectionGroup.getType() != ConnectionGroup.Type.BALANCING) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionMultimap.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionMultimap.java index a0590333d..4dd011622 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionMultimap.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionMultimap.java @@ -28,8 +28,6 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; -import org.glyptodon.guacamole.net.auth.ConnectionRecord; - /** * Mapping of object identifiers to lists of connection records. Records are @@ -44,8 +42,8 @@ public class ActiveConnectionMultimap { /** * All active connections to a connection having a given identifier. */ - private final Map> records = - new HashMap>(); + private final Map> records = + new HashMap>(); /** * Stores the given connection record in the list of active connections @@ -57,13 +55,13 @@ public class ActiveConnectionMultimap { * @param record * The record associated with the active connection. */ - public void put(String identifier, ConnectionRecord record) { + public void put(String identifier, ActiveConnectionRecord record) { synchronized (records) { // Get set of active connection records, creating if necessary - Set connections = records.get(identifier); + Set connections = records.get(identifier); if (connections == null) { - connections = Collections.synchronizedSet(Collections.newSetFromMap(new LinkedHashMap())); + connections = Collections.synchronizedSet(Collections.newSetFromMap(new LinkedHashMap())); records.put(identifier, connections); } @@ -83,11 +81,11 @@ public class ActiveConnectionMultimap { * @param record * The record associated with the active connection. */ - public void remove(String identifier, ConnectionRecord record) { + public void remove(String identifier, ActiveConnectionRecord record) { synchronized (records) { // Get set of active connection records - Set connections = records.get(identifier); + Set connections = records.get(identifier); assert(connections != null); // Remove old record @@ -114,11 +112,11 @@ public class ActiveConnectionMultimap { * the given identifier, or an empty collection if there are no such * records. */ - public Collection get(String identifier) { + public Collection get(String identifier) { synchronized (records) { // Get set of active connection records - Collection connections = records.get(identifier); + Collection connections = records.get(identifier); if (connections != null) return Collections.unmodifiableCollection(connections); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java index de2bffaf2..6401e705c 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java @@ -164,11 +164,6 @@ public class ActiveConnectionRecord implements ConnectionRecord { return balancingGroup != null; } - @Override - public String getIdentifier() { - return connection.getIdentifier(); - } - @Override public Date getStartDate() { return startDate; @@ -200,7 +195,14 @@ public class ActiveConnectionRecord implements ConnectionRecord { } - @Override + /** + * Returns the GuacamoleTunnel currently associated with the active + * connection represented by this connection record. + * + * @return + * The GuacamoleTunnel currently associated with the active connection + * represented by this connection record. + */ public GuacamoleTunnel getTunnel() { return tunnel; } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/GuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/GuacamoleTunnelService.java index f45d31426..0bb78419d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/GuacamoleTunnelService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/GuacamoleTunnelService.java @@ -30,7 +30,6 @@ import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.net.GuacamoleTunnel; import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionGroup; -import org.glyptodon.guacamole.net.auth.ConnectionRecord; import org.glyptodon.guacamole.protocol.GuacamoleClientInformation; @@ -57,7 +56,7 @@ public interface GuacamoleTunnelService { * If an error occurs while retrieving all active connections, or if * permission is denied. */ - public Collection getActiveConnections(AuthenticatedUser user) + public Collection getActiveConnections(AuthenticatedUser user) throws GuacamoleException; /** @@ -80,7 +79,7 @@ public interface GuacamoleTunnelService { * If an error occurs while retrieving all active connections, or if * permission is denied. */ - public ConnectionRecord getActiveConnection(AuthenticatedUser user, + public ActiveConnectionRecord getActiveConnection(AuthenticatedUser user, String tunnelUUID) throws GuacamoleException; @@ -114,7 +113,7 @@ public interface GuacamoleTunnelService { throws GuacamoleException; /** - * Returns a connection containing connection records representing all + * Returns a collection containing connection records representing all * currently-active connections using the given connection. These records * will have usernames and start dates, but no end date, and will be * sorted in ascending order by start date. @@ -126,7 +125,7 @@ public interface GuacamoleTunnelService { * A collection containing connection records representing all * currently-active connections. */ - public Collection getActiveConnections(Connection connection); + public Collection getActiveConnections(Connection connection); /** * Creates a socket for the given user which connects to the given @@ -171,6 +170,6 @@ public interface GuacamoleTunnelService { * A collection containing connection records representing all * currently-active connections. */ - public Collection getActiveConnections(ConnectionGroup connectionGroup); + public Collection getActiveConnections(ConnectionGroup connectionGroup); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/ModeledUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/ModeledUser.java index e9f79d2fd..1f1b4f508 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/ModeledUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/ModeledUser.java @@ -23,11 +23,12 @@ package org.glyptodon.guacamole.auth.jdbc.user; import com.google.inject.Inject; -import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObject; +import org.glyptodon.guacamole.auth.jdbc.base.ModeledDirectoryObject; import org.glyptodon.guacamole.auth.jdbc.security.PasswordEncryptionService; import org.glyptodon.guacamole.auth.jdbc.security.SaltService; import org.glyptodon.guacamole.auth.jdbc.permission.SystemPermissionService; import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.auth.jdbc.activeconnection.ActiveConnectionPermissionService; import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionGroupPermissionService; import org.glyptodon.guacamole.auth.jdbc.permission.ConnectionPermissionService; import org.glyptodon.guacamole.auth.jdbc.permission.UserPermissionService; @@ -42,7 +43,7 @@ import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; * @author James Muehlner * @author Michael Jumper */ -public class ModeledUser extends DirectoryObject implements User { +public class ModeledUser extends ModeledDirectoryObject implements User { /** * Service for hashing passwords. @@ -73,7 +74,13 @@ public class ModeledUser extends DirectoryObject implements User { */ @Inject private ConnectionGroupPermissionService connectionGroupPermissionService; - + + /** + * Service for retrieving active connection permissions. + */ + @Inject + private ActiveConnectionPermissionService activeConnectionPermissionService; + /** * Service for retrieving user permissions. */ @@ -160,6 +167,12 @@ public class ModeledUser extends DirectoryObject implements User { return connectionGroupPermissionService.getPermissionSet(getCurrentUser(), this); } + @Override + public ObjectPermissionSet getActiveConnectionPermissions() + throws GuacamoleException { + return activeConnectionPermissionService.getPermissionSet(getCurrentUser(), this); + } + @Override public ObjectPermissionSet getUserPermissions() throws GuacamoleException { diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java index ae06013ec..0b496240b 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java @@ -28,14 +28,12 @@ import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ConnectionGroupDirector import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionDirectory; import com.google.inject.Inject; import com.google.inject.Provider; -import java.util.ArrayList; -import java.util.Collection; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject; -import org.glyptodon.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; +import org.glyptodon.guacamole.auth.jdbc.activeconnection.ActiveConnectionDirectory; +import org.glyptodon.guacamole.net.auth.ActiveConnection; import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionGroup; -import org.glyptodon.guacamole.net.auth.ConnectionRecord; import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.User; @@ -49,12 +47,6 @@ import org.glyptodon.guacamole.net.auth.User; public class UserContext extends RestrictedObject implements org.glyptodon.guacamole.net.auth.UserContext { - /** - * Service for creating and tracking tunnels. - */ - @Inject - private GuacamoleTunnelService tunnelService; - /** * User directory restricted by the permissions of the user associated * with this context. @@ -76,6 +68,13 @@ public class UserContext extends RestrictedObject @Inject private ConnectionGroupDirectory connectionGroupDirectory; + /** + * ActiveConnection directory restricted by the permissions of the user + * associated with this context. + */ + @Inject + private ActiveConnectionDirectory activeConnectionDirectory; + /** * Provider for creating the root group. */ @@ -91,6 +90,7 @@ public class UserContext extends RestrictedObject userDirectory.init(currentUser); connectionDirectory.init(currentUser); connectionGroupDirectory.init(currentUser); + activeConnectionDirectory.init(currentUser); } @@ -114,6 +114,12 @@ public class UserContext extends RestrictedObject return connectionGroupDirectory; } + @Override + public Directory getActiveConnectionDirectory() + throws GuacamoleException { + return activeConnectionDirectory; + } + @Override public ConnectionGroup getRootConnectionGroup() throws GuacamoleException { @@ -124,29 +130,4 @@ public class UserContext extends RestrictedObject } - @Override - public Collection getActiveConnections() - throws GuacamoleException { - return tunnelService.getActiveConnections(getCurrentUser()); - } - - @Override - public Collection getActiveConnections(Collection tunnelUUIDs) - throws GuacamoleException { - - // Look up active connections for each given tunnel UUID - Collection records = new ArrayList(tunnelUUIDs.size()); - for (String tunnelUUID : tunnelUUIDs) { - - // Add corresponding record only if it exists - ConnectionRecord record = tunnelService.getActiveConnection(getCurrentUser(), tunnelUUID); - if (record != null) - records.add(record); - - } - - return records; - - } - } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.java index 5ef96cc2e..862739026 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserMapper.java @@ -22,7 +22,7 @@ package org.glyptodon.guacamole.auth.jdbc.user; -import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectMapper; +import org.glyptodon.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper; import org.apache.ibatis.annotations.Param; /** @@ -30,7 +30,7 @@ import org.apache.ibatis.annotations.Param; * * @author Michael Jumper */ -public interface UserMapper extends DirectoryObjectMapper { +public interface UserMapper extends ModeledDirectoryObjectMapper { /** * Returns the user having the given username, if any. If no such user diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserService.java index f552ce7a6..a84643ed7 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserService.java @@ -28,8 +28,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import org.glyptodon.guacamole.net.auth.Credentials; -import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectMapper; -import org.glyptodon.guacamole.auth.jdbc.base.DirectoryObjectService; +import org.glyptodon.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper; +import org.glyptodon.guacamole.auth.jdbc.base.ModeledDirectoryObjectService; import org.glyptodon.guacamole.GuacamoleClientException; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleUnsupportedException; @@ -49,7 +49,7 @@ import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; * * @author Michael Jumper, James Muehlner */ -public class UserService extends DirectoryObjectService { +public class UserService extends ModeledDirectoryObjectService { /** * All user permissions which are implicitly granted to the new user upon @@ -85,7 +85,7 @@ public class UserService extends DirectoryObjectService getObjectMapper() { + protected ModeledDirectoryObjectMapper getObjectMapper() { return userMapper; } diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AbstractActiveConnection.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AbstractActiveConnection.java new file mode 100644 index 000000000..7ce50aa61 --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/AbstractActiveConnection.java @@ -0,0 +1,120 @@ +/* + * 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; + +import java.util.Date; +import org.glyptodon.guacamole.net.GuacamoleTunnel; + +public abstract class AbstractActiveConnection implements ActiveConnection { + + /** + * The identifier of this active connection. + */ + private String identifier; + + /** + * The identifier of the associated connection. + */ + private String connectionIdentifier; + + /** + * The date and time this active connection began. + */ + private Date startDate; + + /** + * The remote host that initiated this connection. + */ + private String remoteHost; + + /** + * The username of the user that initiated this connection. + */ + private String username; + + /** + * The underlying GuacamoleTunnel. + */ + private GuacamoleTunnel tunnel; + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + @Override + public String getConnectionIdentifier() { + return connectionIdentifier; + } + + @Override + public void setConnectionIdentifier(String connnectionIdentifier) { + this.connectionIdentifier = connnectionIdentifier; + } + + @Override + public Date getStartDate() { + return startDate; + } + + @Override + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + @Override + public String getRemoteHost() { + return remoteHost; + } + + @Override + public void setRemoteHost(String remoteHost) { + this.remoteHost = remoteHost; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public void setUsername(String username) { + this.username = username; + } + + @Override + public GuacamoleTunnel getTunnel() { + return tunnel; + } + + @Override + public void setTunnel(GuacamoleTunnel tunnel) { + this.tunnel = tunnel; + } + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ActiveConnection.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ActiveConnection.java new file mode 100644 index 000000000..8258e59db --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ActiveConnection.java @@ -0,0 +1,122 @@ +/* + * 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; + +import java.util.Date; +import org.glyptodon.guacamole.net.GuacamoleTunnel; + +/** + * A pairing of username and GuacamoleTunnel representing an active usage of a + * particular connection. + * + * @author Michael Jumper + */ +public interface ActiveConnection extends Identifiable { + + /** + * Returns the identifier of the connection being actively used. + * + * @return + * The identifier of the connection being actively used. + */ + String getConnectionIdentifier(); + + /** + * Sets the identifier of the connection being actively used. + * + * @param connnectionIdentifier + * The identifier of the connection being actively used. + */ + void setConnectionIdentifier(String connnectionIdentifier); + + /** + * Returns the date and time the connection began. + * + * @return + * The date and time the connection began. + */ + Date getStartDate(); + + /** + * Sets the date and time the connection began. + * + * @param startDate + * The date and time the connection began. + */ + void setStartDate(Date startDate); + + /** + * Returns the hostname or IP address of the remote host that initiated the + * connection, if known. If the hostname or IP address is not known, null + * is returned. + * + * @return + * The hostname or IP address of the remote host, or null if this + * information is not available. + */ + String getRemoteHost(); + + /** + * Sets the hostname or IP address of the remote host that initiated the + * connection. + * + * @param remoteHost + * The hostname or IP address of the remote host, or null if this + * information is not available. + */ + void setRemoteHost(String remoteHost); + + /** + * Returns the name of the user who is using this connection. + * + * @return + * The name of the user who is using this connection. + */ + String getUsername(); + + /** + * Sets the name of the user who is using this connection. + * + * @param username + * The name of the user who is using this connection. + */ + void setUsername(String username); + + /** + * Returns the connected GuacamoleTunnel being used. This may be null if + * access to the underlying tunnel is denied. + * + * @return + * The connected GuacamoleTunnel, or null if permission is denied. + */ + GuacamoleTunnel getTunnel(); + + /** + * Sets the connected GuacamoleTunnel being used. + * + * @param tunnel + * The connected GuacamoleTunnel, or null if permission is denied. + */ + void setTunnel(GuacamoleTunnel tunnel); + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java index a4e2fcbe5..bb43565d5 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java @@ -23,7 +23,6 @@ package org.glyptodon.guacamole.net.auth; import java.util.Date; -import org.glyptodon.guacamole.net.GuacamoleTunnel; /** * A logging record describing when a user started and ended usage of a @@ -33,16 +32,6 @@ import org.glyptodon.guacamole.net.GuacamoleTunnel; */ public interface ConnectionRecord { - /** - * Returns the identifier of the connection associated with this connection - * record. - * - * @return - * The identifier of the connection associated with this connection - * record. - */ - public String getIdentifier(); - /** * Returns the date and time the connection began. * @@ -87,15 +76,4 @@ public interface ConnectionRecord { */ public boolean isActive(); - /** - * Returns the connected GuacamoleTunnel of the connection associated with - * this record, if any. If the connection is not active, or access to - * the socket is denied, null is returned. - * - * @return - * The connected GuacamoleTunnel, if any, or null if the connection is - * not active or permission is denied. - */ - public GuacamoleTunnel getTunnel(); - } 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 09e4c2e78..267ad1f0f 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 @@ -92,6 +92,21 @@ public interface User extends Identifiable { ObjectPermissionSet getConnectionGroupPermissions() throws GuacamoleException; + /** + * Returns all permissions given to this user regarding currently-active + * connections. + * + * @return + * An ObjectPermissionSet of all active connection permissions granted + * to this user. + * + * @throws GuacamoleException + * If an error occurs while retrieving permissions, or if reading all + * permissions is not allowed. + */ + ObjectPermissionSet getActiveConnectionPermissions() + throws GuacamoleException; + /** * Returns all user permissions given to this user. * 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 bad5901fe..38538bce1 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 @@ -22,7 +22,6 @@ package org.glyptodon.guacamole.net.auth; -import java.util.Collection; import org.glyptodon.guacamole.GuacamoleException; /** @@ -83,6 +82,21 @@ public interface UserContext { Directory getConnectionGroupDirectory() throws GuacamoleException; + /** + * Retrieves a Directory which can be used to view and manipulate + * active connections, 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 getActiveConnectionDirectory() + throws GuacamoleException; + /** * Retrieves a connection group which can be used to view and manipulate * connections, but only as allowed by the permissions given to the user of @@ -96,41 +110,4 @@ public interface UserContext { */ ConnectionGroup getRootConnectionGroup() throws GuacamoleException; - /** - * Returns a collection of connection records associated with all active - * connections to which the current user has access. For an administrative - * user, this may include connections associated with other users. - * - * @return - * A collection of all connection records associated with active - * connections to which the current user has access. - * - * @throws GuacamoleException - * If an error occurs while reading active connection records, or if - * permission is denied. - */ - Collection getActiveConnections() - throws GuacamoleException; - - /** - * Returns the connection records associated with the active connections - * having the tunnels with the given UUIDs. An active connection will only - * be returned if the current user has access. - * - * @param tunnelUUIDs - * The UUIDs of the tunnels whose associated connection records should - * be returned. - * - * @return - * A collection of all connection records associated with the active - * connections having the tunnels with the given UUIDs, if any, or an - * empty collection if no such connections exist. - * - * @throws GuacamoleException - * If an error occurs while reading active connection records, or if - * permission is denied. - */ - Collection getActiveConnections(Collection tunnelUUIDs) - throws GuacamoleException; - } 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 fa0899fa2..a9da2e939 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 @@ -130,4 +130,10 @@ public class SimpleUser extends AbstractUser { return new SimpleObjectPermissionSet(); } + @Override + public ObjectPermissionSet getActiveConnectionPermissions() + 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 3f6b45fe2..4ebc2d6a3 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 @@ -28,9 +28,9 @@ import java.util.Collections; import java.util.Map; import java.util.UUID; import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.auth.ActiveConnection; import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionGroup; -import org.glyptodon.guacamole.net.auth.ConnectionRecord; import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.User; import org.glyptodon.guacamole.net.auth.UserContext; @@ -169,15 +169,9 @@ public class SimpleUserContext implements UserContext { } @Override - public Collection getActiveConnections() + public Directory getActiveConnectionDirectory() throws GuacamoleException { - return Collections.EMPTY_LIST; - } - - @Override - public Collection getActiveConnections(Collection tunnelUUID) - throws GuacamoleException { - return Collections.EMPTY_LIST; + return new SimpleDirectory(); } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java index a2f0232f3..91cade593 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java @@ -31,7 +31,7 @@ import org.glyptodon.guacamole.net.basic.rest.clipboard.ClipboardRESTService; import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionRESTService; import org.glyptodon.guacamole.net.basic.rest.connectiongroup.ConnectionGroupRESTService; import org.glyptodon.guacamole.net.basic.rest.protocol.ProtocolRESTService; -import org.glyptodon.guacamole.net.basic.rest.tunnel.TunnelRESTService; +import org.glyptodon.guacamole.net.basic.rest.activeconnection.ActiveConnectionRESTService; import org.glyptodon.guacamole.net.basic.rest.user.UserRESTService; /** @@ -51,7 +51,7 @@ public class RESTServletModule extends ServletModule { bind(ProtocolRESTService.class); bind(UserRESTService.class); bind(TokenRESTService.class); - bind(TunnelRESTService.class); + bind(ActiveConnectionRESTService.class); // Set up the servlet and JSON mappings bind(GuiceContainer.class); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/APITunnel.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/activeconnection/APIActiveConnection.java similarity index 66% rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/APITunnel.java rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/activeconnection/APIActiveConnection.java index ca1a0604c..2065c39f6 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/APITunnel.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/activeconnection/APIActiveConnection.java @@ -20,22 +20,29 @@ * THE SOFTWARE. */ -package org.glyptodon.guacamole.net.basic.rest.tunnel; +package org.glyptodon.guacamole.net.basic.rest.activeconnection; import java.util.Date; -import org.glyptodon.guacamole.net.auth.ConnectionRecord; +import org.glyptodon.guacamole.net.auth.ActiveConnection; /** - * Tunnel-related information which may be exposed through the REST endpoints. + * Information related to active connections which may be exposed through the + * REST endpoints. * * @author Michael Jumper */ -public class APITunnel { +public class APIActiveConnection { /** - * The identifier of the connection associated with this tunnel. + * The identifier of the active connection itself. */ private final String identifier; + + /** + * The identifier of the connection associated with this + * active connection. + */ + private final String connectionIdentifier; /** * The date and time the connection began. @@ -53,26 +60,18 @@ public class APITunnel { private final String username; /** - * The UUID of the tunnel. - */ - private final String uuid; - - /** - * Creates a new APITunnel, copying the information from the given - * connection record. + * Creates a new APIActiveConnection, copying the information from the given + * active connection. * - * @param record - * The record to copy data from. - * - * @param uuid - * The UUID of the associated GuacamoleTunnel. + * @param connection + * The active connection to copy data from. */ - public APITunnel(ConnectionRecord record, String uuid) { - this.identifier = record.getIdentifier(); - this.startDate = record.getStartDate(); - this.remoteHost = record.getRemoteHost(); - this.username = record.getUsername(); - this.uuid = uuid; + public APIActiveConnection(ActiveConnection connection) { + this.identifier = connection.getIdentifier(); + this.connectionIdentifier = connection.getConnectionIdentifier(); + this.startDate = connection.getStartDate(); + this.remoteHost = connection.getRemoteHost(); + this.username = connection.getUsername(); } /** @@ -81,8 +80,8 @@ public class APITunnel { * @return * The identifier of the connection associated with this tunnel. */ - public String getIdentifier() { - return identifier; + public String getConnectionIdentifier() { + return connectionIdentifier; } /** @@ -117,14 +116,15 @@ public class APITunnel { } /** - * Returns the UUID of the underlying Guacamole tunnel. Absolutely every - * Guacamole tunnel has an associated UUID. + * Returns the identifier of the active connection itself. This is + * distinct from the connection identifier, and uniquely identifies a + * specific use of a connection. * * @return - * The UUID of the underlying Guacamole tunnel. + * The identifier of the active connection. */ - public String getUUID() { - return uuid; + public String getIdentifier() { + return identifier; } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/TunnelRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/activeconnection/ActiveConnectionRESTService.java similarity index 51% rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/TunnelRESTService.java rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/activeconnection/ActiveConnectionRESTService.java index 91641eadd..7553db7ad 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/tunnel/TunnelRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/activeconnection/ActiveConnectionRESTService.java @@ -20,10 +20,9 @@ * THE SOFTWARE. */ -package org.glyptodon.guacamole.net.basic.rest.tunnel; +package org.glyptodon.guacamole.net.basic.rest.activeconnection; import com.google.inject.Inject; -import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -37,9 +36,14 @@ import javax.ws.rs.core.MediaType; import org.glyptodon.guacamole.GuacamoleClientException; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleUnsupportedException; -import org.glyptodon.guacamole.net.GuacamoleTunnel; -import org.glyptodon.guacamole.net.auth.ConnectionRecord; +import org.glyptodon.guacamole.net.auth.ActiveConnection; +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.ObjectPermission; +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.APIPatch; import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; import org.glyptodon.guacamole.net.basic.rest.PATCH; @@ -52,15 +56,15 @@ import org.slf4j.LoggerFactory; * * @author Michael Jumper */ -@Path("/tunnels") +@Path("/activeConnections") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) -public class TunnelRESTService { +public class ActiveConnectionRESTService { /** * Logger for this class. */ - private static final Logger logger = LoggerFactory.getLogger(TunnelRESTService.class); + private static final Logger logger = LoggerFactory.getLogger(ActiveConnectionRESTService.class); /** * A service for authenticating users from auth tokens. @@ -69,61 +73,78 @@ public class TunnelRESTService { private AuthenticationService authenticationService; /** - * Retrieves the tunnels of all active connections visible to the current - * user. + * Gets a list of active connections in the system, filtering the returned + * list by the given permissions, if specified. * * @param authToken * The authentication token that is used to authenticate the user * performing the operation. * + * @param permissions + * The set of permissions to filter with. A user must have one or more + * of these permissions for a user to appear in the result. + * If null, no filtering will be performed. + * * @return - * A map of the tunnels of all active connections visible to the - * current user, where the key of each entry is the tunnel's UUID. - * + * A list of all active connections. If a permission was specified, + * this list will contain only those active connections for which the + * current user has that permission. + * * @throws GuacamoleException - * If an error occurs while retrieving the tunnels. + * If an error is encountered while retrieving active connections. */ @GET - @Path("/") @AuthProviderRESTExposure - public Map getTunnels(@QueryParam("token") String authToken) + public Map getActiveConnections(@QueryParam("token") String authToken, + @QueryParam("permission") List permissions) throws GuacamoleException { UserContext userContext = authenticationService.getUserContext(authToken); + User self = userContext.self(); + + // Do not filter on permissions if no permissions are specified + if (permissions != null && permissions.isEmpty()) + permissions = null; - // Retrieve all active tunnels - Map apiTunnels = new HashMap(); - for (ConnectionRecord record : userContext.getActiveConnections()) { + // An admin user has access to any user + SystemPermissionSet systemPermissions = self.getSystemPermissions(); + boolean isAdmin = systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER); - // Locate associated tunnel and UUID - GuacamoleTunnel tunnel = record.getTunnel(); - if (tunnel != null) { - APITunnel apiTunnel = new APITunnel(record, tunnel.getUUID().toString()); - apiTunnels.put(apiTunnel.getUUID(), apiTunnel); - } + // Get the directory + Directory activeConnectionDirectory = userContext.getActiveConnectionDirectory(); + // Filter users, if requested + Collection activeConnectionIdentifiers = activeConnectionDirectory.getIdentifiers(); + if (!isAdmin && permissions != null) { + ObjectPermissionSet activeConnectionPermissions = self.getActiveConnectionPermissions(); + activeConnectionIdentifiers = activeConnectionPermissions.getAccessibleObjects(permissions, activeConnectionIdentifiers); } + + // Retrieve all active connections , converting to API active connections + Map apiActiveConnections = new HashMap(); + for (ActiveConnection activeConnection : activeConnectionDirectory.getAll(activeConnectionIdentifiers)) + apiActiveConnections.put(activeConnection.getIdentifier(), new APIActiveConnection(activeConnection)); - return apiTunnels; + return apiActiveConnections; } /** - * Applies the given tunnel patches. This operation currently only supports - * deletion of tunnels through the "remove" patch operation. Deleting a - * tunnel effectively closing the tunnel and kills the associated - * connection. The path of each patch operation is of the form "/UUID" - * where UUID is the UUID of the tunnel being modified. + * Applies the given active connection patches. This operation currently + * only supports deletion of active connections through the "remove" patch + * operation. Deleting an active connection effectively kills the + * connection. The path of each patch operation is of the form "/ID" + * where ID is the identifier of the active connection being modified. * * @param authToken * The authentication token that is used to authenticate the user * performing the operation. * * @param patches - * The tunnel patches to apply for this request. + * The active connection patches to apply for this request. * * @throws GuacamoleException - * If an error occurs while deleting the tunnels. + * If an error occurs while deleting the active connections. */ @PATCH @Path("/") @@ -131,37 +152,28 @@ public class TunnelRESTService { public void patchTunnels(@QueryParam("token") String authToken, List> patches) throws GuacamoleException { - // Attempt to get all requested tunnels UserContext userContext = authenticationService.getUserContext(authToken); - // Build list of tunnels to delete - Collection tunnelUUIDs = new ArrayList(patches.size()); + // Get the directory + Directory activeConnectionDirectory = userContext.getActiveConnectionDirectory(); + + // Close each connection listed for removal for (APIPatch patch : patches) { // Only remove is supported if (patch.getOp() != APIPatch.Operation.remove) - throw new GuacamoleUnsupportedException("Only the \"remove\" operation is supported when patching tunnels."); + throw new GuacamoleUnsupportedException("Only the \"remove\" operation is supported when patching active connections."); // Retrieve and validate path String path = patch.getPath(); if (!path.startsWith("/")) throw new GuacamoleClientException("Patch paths must start with \"/\"."); - - // Add UUID - tunnelUUIDs.add(path.substring(1)); + + // Close connection + activeConnectionDirectory.remove(path.substring(1)); } - // Close each tunnel, if not already closed - Collection records = userContext.getActiveConnections(tunnelUUIDs); - for (ConnectionRecord record : records) { - - GuacamoleTunnel tunnel = record.getTunnel(); - if (tunnel != null && tunnel.isOpen()) - tunnel.close(); - - } - } } 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 697641086..d04dd0bb5 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 @@ -56,6 +56,12 @@ public class APIPermissionSet { private Map> connectionGroupPermissions = new HashMap>(); + /** + * Map of active connection ID to the set of granted permissions. + */ + private Map> activeConnectionPermissions = + new HashMap>(); + /** * Map of user ID to the set of granted permissions. */ @@ -149,10 +155,11 @@ public class APIPermissionSet { 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()); + addSystemPermissions(systemPermissions, user.getSystemPermissions()); + addObjectPermissions(connectionPermissions, user.getConnectionPermissions()); + addObjectPermissions(connectionGroupPermissions, user.getConnectionGroupPermissions()); + addObjectPermissions(activeConnectionPermissions, user.getActiveConnectionPermissions()); + addObjectPermissions(userPermissions, user.getUserPermissions()); } @@ -186,6 +193,21 @@ public class APIPermissionSet { return connectionGroupPermissions; } + /** + * Returns a map of active connection IDs to the set of permissions granted + * for that active connection. If no permissions are granted to a particular + * active connection, its ID will not be present as a key in the map. This + * map is mutable, and changes to this map will affect the permission set + * directly. + * + * @return + * A map of active connection IDs to the set of permissions granted for + * that active connection. + */ + public Map> getActiveConnectionPermissions() { + return activeConnectionPermissions; + } + /** * Returns a map of user IDs to the set of permissions granted for that * user. If no permissions are granted to a particular user, its ID will @@ -238,6 +260,19 @@ public class APIPermissionSet { this.connectionGroupPermissions = connectionGroupPermissions; } + /** + * Replaces the current map of active connection permissions with the give + * map, which must map active connection ID to its corresponding set of + * granted permissions. If an active connection has no permissions, its ID + * must not be present as a key in the map. + * + * @param activeConnectionPermissions + * The map which must replace the currently-stored map of permissions. + */ + public void setActiveConnectionPermissions(Map> activeConnectionPermissions) { + this.activeConnectionPermissions = activeConnectionPermissions; + } + /** * Replaces the current map of user permissions with the given map, which * must map user ID to its corresponding set of granted permissions. If a 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 52e5e707d..c1bc8b58a 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 @@ -95,4 +95,10 @@ public class APIUserWrapper implements User { throw new GuacamoleUnsupportedException("APIUserWrapper does not provide permission access."); } + @Override + public ObjectPermissionSet getActiveConnectionPermissions() + 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/UserRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java index 0167cf860..85f124476 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 @@ -92,6 +92,12 @@ public class UserRESTService { */ private static final String CONNECTION_GROUP_PERMISSION_PATCH_PATH_PREFIX = "/connectionGroupPermissions/"; + /** + * The prefix of any path within an operation of a JSON patch which + * modifies the permissions of a user regarding a specific active connection. + */ + private static final String ACTIVE_CONNECTION_PERMISSION_PATCH_PATH_PREFIX = "/activeConnectionPermissions/"; + /** * The prefix of any path within an operation of a JSON patch which * modifies the permissions of a user regarding another, specific user. @@ -503,10 +509,11 @@ public class UserRESTService { 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(); + PermissionSetPatch connectionPermissionPatch = new PermissionSetPatch(); + PermissionSetPatch connectionGroupPermissionPatch = new PermissionSetPatch(); + PermissionSetPatch activeConnectionPermissionPatch = new PermissionSetPatch(); + PermissionSetPatch userPermissionPatch = new PermissionSetPatch(); + PermissionSetPatch systemPermissionPatch = new PermissionSetPatch(); // Apply all patch operations individually for (APIPatch patch : patches) { @@ -539,6 +546,19 @@ public class UserRESTService { } + // Create active connection permission if path has active connection prefix + else if (path.startsWith(ACTIVE_CONNECTION_PERMISSION_PATCH_PATH_PREFIX)) { + + // Get identifier and type from patch operation + String identifier = path.substring(ACTIVE_CONNECTION_PERMISSION_PATCH_PATH_PREFIX.length()); + ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue()); + + // Create and update corresponding permission + ObjectPermission permission = new ObjectPermission(type, identifier); + updatePermissionSet(patch.getOp(), activeConnectionPermissionPatch, permission); + + } + // Create user permission if path has user prefix else if (path.startsWith(USER_PERMISSION_PATCH_PATH_PREFIX)) { @@ -573,6 +593,7 @@ public class UserRESTService { // Save the permission changes connectionPermissionPatch.apply(user.getConnectionPermissions()); connectionGroupPermissionPatch.apply(user.getConnectionGroupPermissions()); + activeConnectionPermissionPatch.apply(user.getActiveConnectionPermissions()); userPermissionPatch.apply(user.getUserPermissions()); systemPermissionPatch.apply(user.getSystemPermissions()); diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js b/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js index 3ae1e77e8..b4a63c61f 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js @@ -27,15 +27,15 @@ angular.module('manage').controller('manageSessionsController', ['$scope', '$inj function manageSessionsController($scope, $injector) { // Required types - var ActiveTunnelWrapper = $injector.get('ActiveTunnelWrapper'); - var ConnectionGroup = $injector.get('ConnectionGroup'); + var ActiveConnectionWrapper = $injector.get('ActiveConnectionWrapper'); + var ConnectionGroup = $injector.get('ConnectionGroup'); // Required services - var authenticationService = $injector.get('authenticationService'); - var connectionGroupService = $injector.get('connectionGroupService'); - var guacNotification = $injector.get('guacNotification'); - var permissionService = $injector.get('permissionService'); - var tunnelService = $injector.get('tunnelService'); + var activeConnectionService = $injector.get('activeConnectionService'); + var authenticationService = $injector.get('authenticationService'); + var connectionGroupService = $injector.get('connectionGroupService'); + var guacNotification = $injector.get('guacNotification'); + var permissionService = $injector.get('permissionService'); /** * The root connection group of the connection group hierarchy. @@ -53,10 +53,10 @@ angular.module('manage').controller('manageSessionsController', ['$scope', '$inj $scope.permissions = null; /** - * The ActiveTunnelWrappers of all active sessions accessible by the current - * user, or null if the tunnels have not yet been loaded. + * The ActiveConnectionWrappers of all active sessions accessible by the + * current user, or null if the active sessions have not yet been loaded. * - * @type ActiveTunnelWrapper[] + * @type ActiveConnectionWrapper[] */ $scope.wrappers = null; @@ -74,9 +74,9 @@ angular.module('manage').controller('manageSessionsController', ['$scope', '$inj $scope.connections = {}; /** - * Map of all currently-selected tunnel wrappers by UUID. + * Map of all currently-selected active connection wrappers by identifier. * - * @type Object. + * @type Object. */ var selectedWrappers = {}; @@ -122,12 +122,12 @@ angular.module('manage').controller('manageSessionsController', ['$scope', '$inj }); // Query active sessions - tunnelService.getActiveTunnels().success(function sessionsRetrieved(tunnels) { + activeConnectionService.getActiveConnections().success(function sessionsRetrieved(activeConnections) { - // Wrap all active tunnels for sake of display + // Wrap all active connections for sake of display $scope.wrappers = []; - for (var tunnelUUID in tunnels) { - $scope.wrappers.push(new ActiveTunnelWrapper(tunnels[tunnelUUID])); + for (var identifier in activeConnections) { + $scope.wrappers.push(new ActiveConnectionWrapper(activeConnections[identifier])); } }); @@ -192,12 +192,12 @@ angular.module('manage').controller('manageSessionsController', ['$scope', '$inj var deleteSessionsImmediately = function deleteSessionsImmediately() { // Perform deletion - tunnelService.deleteActiveTunnels(Object.keys(selectedWrappers)) - .success(function tunnelsDeleted() { + activeConnectionService.deleteActiveConnections(Object.keys(selectedWrappers)) + .success(function activeConnectionsDeleted() { - // Remove deleted tunnels from wrapper array - $scope.wrappers = $scope.wrappers.filter(function tunnelStillExists(wrapper) { - return !(wrapper.tunnel.uuid in selectedWrappers); + // Remove deleted connections from wrapper array + $scope.wrappers = $scope.wrappers.filter(function activeConnectionStillExists(wrapper) { + return !(wrapper.activeConnection.identifier in selectedWrappers); }); // Clear selection @@ -206,7 +206,7 @@ angular.module('manage').controller('manageSessionsController', ['$scope', '$inj }) // Notify of any errors - .error(function tunnelDeletionFailed(error) { + .error(function activeConnectionDeletionFailed(error) { guacNotification.showStatus({ 'className' : 'error', 'title' : 'MANAGE_SESSION.DIALOG_HEADER_ERROR', @@ -239,7 +239,7 @@ angular.module('manage').controller('manageSessionsController', ['$scope', '$inj $scope.canDeleteSessions = function canDeleteSessions() { // We can delete sessions if at least one is selected - for (var tunnelUUID in selectedWrappers) + for (var identifier in selectedWrappers) return true; return false; @@ -247,20 +247,20 @@ angular.module('manage').controller('manageSessionsController', ['$scope', '$inj }; /** - * Called whenever a tunnel wrapper changes selected status. + * Called whenever an active connection wrapper changes selected status. * - * @param {ActiveTunnelWrapper} wrapper + * @param {ActiveConnectionWrapper} wrapper * The wrapper whose selected status has changed. */ $scope.wrapperSelectionChange = function wrapperSelectionChange(wrapper) { // Add wrapper to map if selected if (wrapper.checked) - selectedWrappers[wrapper.tunnel.uuid] = wrapper; + selectedWrappers[wrapper.activeConnection.identifier] = wrapper; // Otherwise, remove wrapper from map else - delete selectedWrappers[wrapper.tunnel.uuid]; + delete selectedWrappers[wrapper.activeConnection.identifier]; }; diff --git a/guacamole/src/main/webapp/app/manage/templates/manageSessions.html b/guacamole/src/main/webapp/app/manage/templates/manageSessions.html index 741d47ce4..9b5ad2721 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageSessions.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageSessions.html @@ -52,10 +52,10 @@ THE SOFTWARE. - {{wrapper.tunnel.username}} - {{wrapper.tunnel.startDate | date:'short'}} - {{wrapper.tunnel.remoteHost}} - {{connections[wrapper.tunnel.identifier].name}} + {{wrapper.activeConnection.username}} + {{wrapper.activeConnection.startDate | date:'short'}} + {{wrapper.activeConnection.remoteHost}} + {{connections[wrapper.activeConnection.connectionIdentifier].name}} diff --git a/guacamole/src/main/webapp/app/manage/types/ActiveTunnelWrapper.js b/guacamole/src/main/webapp/app/manage/types/ActiveConnectionWrapper.js similarity index 67% rename from guacamole/src/main/webapp/app/manage/types/ActiveTunnelWrapper.js rename to guacamole/src/main/webapp/app/manage/types/ActiveConnectionWrapper.js index 88fe5e93b..b15a2929b 100644 --- a/guacamole/src/main/webapp/app/manage/types/ActiveTunnelWrapper.js +++ b/guacamole/src/main/webapp/app/manage/types/ActiveConnectionWrapper.js @@ -21,30 +21,30 @@ */ /** - * A service for defining the ActiveTunnelWrapper class. + * A service for defining the ActiveConnectionWrapper class. */ -angular.module('manage').factory('ActiveTunnelWrapper', [ - function defineActiveTunnelWrapper() { +angular.module('manage').factory('ActiveConnectionWrapper', [ + function defineActiveConnectionWrapper() { /** - * Wrapper for ActiveTunnel which adds display-specific + * Wrapper for ActiveConnection which adds display-specific * properties, such as a checked option. * * @constructor - * @param {ActiveTunnel} activeTunnel - * The ActiveTunnel to wrap. + * @param {ActiveConnection} activeConnection + * The ActiveConnection to wrap. */ - var ActiveTunnelWrapper = function ActiveTunnelWrapper(activeTunnel) { + var ActiveConnectionWrapper = function ActiveConnectionWrapper(activeConnection) { /** - * The wrapped ActiveTunnel. + * The wrapped ActiveConnection. * - * @type ActiveTunnel + * @type ActiveConnection */ - this.tunnel = activeTunnel; + this.activeConnection = activeConnection; /** - * A flag indicating that the tunnel has been selected. + * A flag indicating that the active connection has been selected. * * @type Boolean */ @@ -52,6 +52,6 @@ angular.module('manage').factory('ActiveTunnelWrapper', [ }; - return ActiveTunnelWrapper; + return ActiveConnectionWrapper; }]); \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/rest/services/tunnelService.js b/guacamole/src/main/webapp/app/rest/services/activeConnectionService.js similarity index 51% rename from guacamole/src/main/webapp/app/rest/services/tunnelService.js rename to guacamole/src/main/webapp/app/rest/services/activeConnectionService.js index 8ce18d867..a96ebdf3f 100644 --- a/guacamole/src/main/webapp/app/rest/services/tunnelService.js +++ b/guacamole/src/main/webapp/app/rest/services/activeConnectionService.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Glyptodon LLC + * 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 @@ -21,72 +21,84 @@ */ /** - * Service for operating on tunnels via the REST API. + * Service for operating on active connections via the REST API. */ -angular.module('rest').factory('tunnelService', ['$http', 'authenticationService', - function tunnelService($http, authenticationService) { +angular.module('rest').factory('activeConnectionService', ['$http', 'authenticationService', + function activeConnectionService($http, authenticationService) { var service = {}; /** * Makes a request to the REST API to get the list of active tunnels, - * returning a promise that provides a map of @link{ActiveTunnel} + * returning a promise that provides a map of @link{ActiveConnection} * objects if successful. * - * @returns {Promise.>} - * A promise which will resolve with a map of @link{ActiveTunnel} - * objects, where each key is the UUID of the corresponding tunnel. + * @param {String[]} [permissionTypes] + * The set of permissions to filter with. A user must have one or more + * of these permissions for an active connection to appear in the + * result. If null, no filtering will be performed. Valid values are + * listed within PermissionSet.ObjectType. + * + + * @returns {Promise.>} + * A promise which will resolve with a map of @link{ActiveConnection} + * objects, where each key is the identifier of the corresponding + * active connection. */ - service.getActiveTunnels = function getActiveTunnels() { + service.getActiveConnections = function getActiveConnections(permissionTypes) { // Build HTTP parameters set var httpParameters = { token : authenticationService.getCurrentToken() }; + // Add permission filter if specified + if (permissionTypes) + httpParameters.permission = permissionTypes; + // Retrieve tunnels return $http({ method : 'GET', - url : 'api/tunnels', + url : 'api/activeConnections', params : httpParameters }); }; /** - * Makes a request to the REST API to delete the tunnels having the given - * UUIDs, effectively disconnecting the tunnels, returning a promise that - * can be used for processing the results of the call. + * Makes a request to the REST API to delete the active connections having + * the given identifiers, effectively disconnecting them, returning a + * promise that can be used for processing the results of the call. * - * @param {String[]} uuids - * The UUIDs of the tunnels to delete. + * @param {String[]} identifiers + * The identifiers of the active connections to delete. * * @returns {Promise} * A promise for the HTTP call which will succeed if and only if the * delete operation is successful. */ - service.deleteActiveTunnels = function deleteActiveTunnels(uuids) { + service.deleteActiveConnections = function deleteActiveConnections(identifiers) { // Build HTTP parameters set var httpParameters = { token : authenticationService.getCurrentToken() }; - // Convert provided array of UUIDs to a patch - var tunnelPatch = []; - uuids.forEach(function addTunnelPatch(uuid) { - tunnelPatch.push({ + // Convert provided array of identifiers to a patch + var activeConnectionPatch = []; + identifiers.forEach(function addActiveConnectionPatch(identifier) { + activeConnectionPatch.push({ op : 'remove', - path : '/' + uuid + path : '/' + identifier }); }); - // Perform tunnel deletion via PATCH + // Perform active connection deletion via PATCH return $http({ method : 'PATCH', - url : 'api/tunnels', + url : 'api/activeConnections', params : httpParameters, - data : tunnelPatch + data : activeConnectionPatch }); }; diff --git a/guacamole/src/main/webapp/app/rest/services/permissionService.js b/guacamole/src/main/webapp/app/rest/services/permissionService.js index 57760191d..803afcf8e 100644 --- a/guacamole/src/main/webapp/app/rest/services/permissionService.js +++ b/guacamole/src/main/webapp/app/rest/services/permissionService.js @@ -153,6 +153,10 @@ angular.module('rest').factory('permissionService', ['$http', 'authenticationSer addObjectPatchOperations(patch, operation, "/connectionGroupPermissions", permissions.connectionGroupPermissions); + // Add active connection permission operations to patch + addObjectPatchOperations(patch, operation, "/activeConnectionPermissions", + permissions.activeConnectionPermissions); + // Add user permission operations to patch addObjectPatchOperations(patch, operation, "/userPermissions", permissions.userPermissions); diff --git a/guacamole/src/main/webapp/app/rest/types/ActiveTunnel.js b/guacamole/src/main/webapp/app/rest/types/ActiveConnection.js similarity index 69% rename from guacamole/src/main/webapp/app/rest/types/ActiveTunnel.js rename to guacamole/src/main/webapp/app/rest/types/ActiveConnection.js index 996105a64..b51e26245 100644 --- a/guacamole/src/main/webapp/app/rest/types/ActiveTunnel.js +++ b/guacamole/src/main/webapp/app/rest/types/ActiveConnection.js @@ -21,34 +21,44 @@ */ /** - * Service which defines the ActiveTunnel class. + * Service which defines the ActiveConnection class. */ -angular.module('rest').factory('ActiveTunnel', [function defineActiveTunnel() { +angular.module('rest').factory('ActiveConnection', [function defineActiveConnection() { /** * The object returned by REST API calls when representing the data - * associated with an active tunnel. Each tunnel denotes an active - * connection, uniquely identified by the tunnel UUID. + * associated with an active connection. Each active connection is + * effectively a pairing of a connection and the user currently using it, + * along with other information. * * @constructor - * @param {ActiveTunnel|Object} [template={}] + * @param {ActiveConnection|Object} [template={}] * The object whose properties should be copied within the new - * ActiveTunnel. + * ActiveConnection. */ - var ActiveTunnel = function ActiveTunnel(template) { + var ActiveConnection = function ActiveConnection(template) { // Use empty object by default template = template || {}; /** - * The identifier of the connection associated with this tunnel. - * + * The identifier which uniquely identifies this specific active + * connection. + * * @type String */ this.identifier = template.identifier; /** - * The time that the tunnel began, in seconds since + * The identifier of the connection associated with this active + * connection. + * + * @type String + */ + this.connectionIdentifier = template.connectionIdentifier; + + /** + * The time that the connection began, in seconds since * 1970-01-01 00:00:00 UTC. * * @type Number @@ -56,28 +66,21 @@ angular.module('rest').factory('ActiveTunnel', [function defineActiveTunnel() { this.startDate = template.startDate; /** - * The remote host that initiated the tunnel, if known. + * The remote host that initiated the connection, if known. * * @type String */ this.remoteHost = template.remoteHost; /** - * The username of the user associated with the tunnel. + * The username of the user associated with the connection. * * @type String */ this.username = template.username; - /** - * The UUID which uniquely identifies the tunnel. - * - * @type String - */ - this.uuid = template.uuid; - }; - return ActiveTunnel; + return ActiveConnection; }]); \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/rest/types/PermissionFlagSet.js b/guacamole/src/main/webapp/app/rest/types/PermissionFlagSet.js index 876f6bb20..598594812 100644 --- a/guacamole/src/main/webapp/app/rest/types/PermissionFlagSet.js +++ b/guacamole/src/main/webapp/app/rest/types/PermissionFlagSet.js @@ -90,6 +90,25 @@ angular.module('rest').factory('PermissionFlagSet', ['PermissionSet', 'ADMINISTER' : {} }; + /** + * The granted state of each permission for each active connection, as + * a map of object permission type string to permission map. The + * permission map is, in turn, a map of active connection identifier to + * boolean value. A particular permission is granted if its + * corresponding boolean value is set to true. Valid permission type + * strings are defined within PermissionSet.ObjectPermissionType. + * Permissions which are not granted may be set to false, but this is + * not required. + * + * @type Object.> + */ + this.activeConnectionPermissions = template.activeConnectionPermissions || { + 'READ' : {}, + 'UPDATE' : {}, + 'DELETE' : {}, + 'ADMINISTER' : {} + }; + /** * The granted state of each permission for each user, as a map of * object permission type string to permission map. The permission map @@ -110,6 +129,20 @@ angular.module('rest').factory('PermissionFlagSet', ['PermissionSet', }; + /** + * Iterates through all permissions in the given permission map, setting + * the corresponding permission flags in the given permission flag map. + * + * @param {Object.} permMap + * Map of object identifiers to the set of granted permissions. Each + * permission is represented by a string listed within + * PermissionSet.ObjectPermissionType. + * + * @param {Object.>} flagMap + * Map of permission type strings to identifier/flag pairs representing + * whether the permission of that type is granted for the object having + * having the associated identifier. + */ var addObjectPermissions = function addObjectPermissions(permMap, flagMap) { // For each defined identifier in the permission map @@ -158,6 +191,9 @@ angular.module('rest').factory('PermissionFlagSet', ['PermissionSet', // Add all granted connection group permissions addObjectPermissions(permissionSet.connectionGroupPermissions, permissionFlagSet.connectionGroupPermissions); + // Add all granted active connection permissions + addObjectPermissions(permissionSet.activeConnectionPermissions, permissionFlagSet.activeConnectionPermissions); + // Add all granted user permissions addObjectPermissions(permissionSet.userPermissions, permissionFlagSet.userPermissions); diff --git a/guacamole/src/main/webapp/app/rest/types/PermissionSet.js b/guacamole/src/main/webapp/app/rest/types/PermissionSet.js index 869f14523..7f61f3d02 100644 --- a/guacamole/src/main/webapp/app/rest/types/PermissionSet.js +++ b/guacamole/src/main/webapp/app/rest/types/PermissionSet.js @@ -57,6 +57,15 @@ angular.module('rest').factory('PermissionSet', [function definePermissionSet() */ this.connectionGroupPermissions = template.connectionGroupPermissions || {}; + /** + * Map of active connection identifiers to the corresponding array of + * granted permissions. Each permission is represented by a string + * listed within PermissionSet.ObjectPermissionType. + * + * @type Object. + */ + this.activeConnectionPermissions = template.activeConnectionPermissions || {}; + /** * Map of user identifiers to the corresponding array of granted * permissions. Each permission is represented by a string listed @@ -237,6 +246,28 @@ angular.module('rest').factory('PermissionSet', [function definePermissionSet() return hasPermission(permSet.connectionGroupPermissions, type, identifier); }; + /** + * Returns whether the given permission is granted for the active + * connection having the given ID. + * + * @param {PermissionSet|Object} permSet + * The permission set to check. + * + * @param {String} type + * The permission to search for, as defined by + * PermissionSet.ObjectPermissionType. + * + * @param {String} identifier + * The identifier of the active connection to which the permission + * applies. + * + * @returns {Boolean} + * true if the permission is present (granted), false otherwise. + */ + PermissionSet.hasActiveConnectionPermission = function hasActiveConnectionPermission(permSet, type, identifier) { + return hasPermission(permSet.activeConnectionPermissions, type, identifier); + }; + /** * Returns whether the given permission is granted for the user having the * given ID. @@ -507,6 +538,55 @@ angular.module('rest').factory('PermissionSet', [function definePermissionSet() return removeObjectPermission(permSet.connectionGroupPermissions, type, identifier); }; + /** + * Adds the given active connection permission applying to the connection + * group with the given ID to the given permission set, if not already + * present. If the permission is already present, this function has no + * effect. + * + * @param {PermissionSet} permSet + * The permission set to modify. + * + * @param {String} type + * The permission to add, as defined by + * PermissionSet.ObjectPermissionType. + * + * @param {String} identifier + * The identifier of the active connection to which the permission + * applies. + * + * @returns {Boolean} + * true if the permission was added, false if the permission was + * already present in the given permission set. + */ + PermissionSet.addActiveConnectionPermission = function addActiveConnectionPermission(permSet, type, identifier) { + return addObjectPermission(permSet.activeConnectionPermissions, type, identifier); + }; + + /** + * Removes the given active connection permission applying to the + * connection group with the given ID from the given permission set, if + * present. If the permission is not present, this function has no effect. + * + * @param {PermissionSet} permSet + * The permission set to modify. + * + * @param {String} type + * The permission to remove, as defined by + * PermissionSet.ObjectPermissionType. + * + * @param {String} identifier + * The identifier of the active connection to which the permission + * applies. + * + * @returns {Boolean} + * true if the permission was removed, false if the permission was not + * present in the given permission set. + */ + PermissionSet.removeActiveConnectionPermission = function removeActiveConnectionPermission(permSet, type, identifier) { + return removeObjectPermission(permSet.activeConnectionPermissions, type, identifier); + }; + /** * Adds the given user permission applying to the user with the given ID to * the given permission set, if not already present. If the permission is