diff --git a/guacamole/pom.xml b/guacamole/pom.xml index e737bb189..c0b6b3bc3 100644 --- a/guacamole/pom.xml +++ b/guacamole/pom.xml @@ -290,6 +290,11 @@ guice 3.0 + + com.google.inject.extensions + guice-assistedinject + 3.0 + diff --git a/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java b/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java index 06530c2f1..41832cb34 100644 --- a/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java +++ b/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java @@ -26,6 +26,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.apache.guacamole.environment.Environment; import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.auth.AuthenticatedUser; +import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.UserContext; import org.apache.guacamole.tunnel.StreamInterceptingTunnel; import org.slf4j.Logger; @@ -126,6 +127,44 @@ public class GuacamoleSession { return Collections.unmodifiableList(userContexts); } + /** + * Returns the UserContext associated with this session that originated + * from the AuthenticationProvider with the given identifier. If no such + * UserContext exists, an exception is thrown. + * + * @param authProviderIdentifier + * The unique identifier of the AuthenticationProvider that created the + * UserContext being retrieved. + * + * @return + * The UserContext that was created by the AuthenticationProvider + * having the given identifier. + * + * @throws GuacamoleException + * If no such UserContext exists. + */ + public UserContext getUserContext(String authProviderIdentifier) + throws GuacamoleException { + + // Locate and return the UserContext associated with the + // AuthenticationProvider having the given identifier, if any + for (UserContext userContext : getUserContexts()) { + + // Get AuthenticationProvider associated with current UserContext + AuthenticationProvider authProvider = userContext.getAuthenticationProvider(); + + // If AuthenticationProvider identifier matches, done + if (authProvider.getIdentifier().equals(authProviderIdentifier)) + return userContext; + + } + + throw new GuacamoleResourceNotFoundException("Session not associated " + + "with authentication provider \"" + authProviderIdentifier + "\"."); + + + } + /** * Replaces all UserContexts associated with this session with the given * List of UserContexts. diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/APIException.java b/guacamole/src/main/java/org/apache/guacamole/rest/APIException.java index 0a004bef6..ea4cf5d5d 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/APIException.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/APIException.java @@ -21,6 +21,7 @@ package org.apache.guacamole.rest; import java.util.Collection; import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.apache.guacamole.form.Field; import org.apache.guacamole.protocol.GuacamoleStatus; @@ -44,7 +45,10 @@ public class APIException extends WebApplicationException { * The error that occurred. */ public APIException(APIError error) { - super(Response.status(error.getType().getStatus()).entity(error).build()); + super(Response.status(error.getType().getStatus()) + .type(MediaType.APPLICATION_JSON) + .entity(error) + .build()); } /** diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/ObjectRetrievalService.java b/guacamole/src/main/java/org/apache/guacamole/rest/ObjectRetrievalService.java deleted file mode 100644 index d0e403ca8..000000000 --- a/guacamole/src/main/java/org/apache/guacamole/rest/ObjectRetrievalService.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.guacamole.rest; - -import java.util.List; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.GuacamoleResourceNotFoundException; -import org.apache.guacamole.net.auth.AuthenticationProvider; -import org.apache.guacamole.net.auth.Connection; -import org.apache.guacamole.net.auth.ConnectionGroup; -import org.apache.guacamole.net.auth.Directory; -import org.apache.guacamole.net.auth.User; -import org.apache.guacamole.net.auth.UserContext; -import org.apache.guacamole.GuacamoleSession; -import org.apache.guacamole.rest.connectiongroup.APIConnectionGroup; - -/** - * Provides easy access and automatic error handling for retrieval of objects, - * such as users, connections, or connection groups. REST API semantics, such - * as the special root connection group identifier, are also handled - * automatically. - */ -public class ObjectRetrievalService { - - /** - * Retrieves a single UserContext from the given GuacamoleSession, which - * may contain multiple UserContexts. - * - * @param session - * The GuacamoleSession to retrieve the UserContext from. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider that created the - * UserContext being retrieved. Only one UserContext per User per - * AuthenticationProvider can exist. - * - * @return - * The UserContext that was created by the AuthenticationProvider - * having the given identifier. - * - * @throws GuacamoleException - * If an error occurs while retrieving the UserContext, or if the - * UserContext does not exist. - */ - public UserContext retrieveUserContext(GuacamoleSession session, - String authProviderIdentifier) throws GuacamoleException { - - // Get list of UserContexts - List userContexts = session.getUserContexts(); - - // Locate and return the UserContext associated with the - // AuthenticationProvider having the given identifier, if any - for (UserContext userContext : userContexts) { - - // Get AuthenticationProvider associated with current UserContext - AuthenticationProvider authProvider = userContext.getAuthenticationProvider(); - - // If AuthenticationProvider identifier matches, done - if (authProvider.getIdentifier().equals(authProviderIdentifier)) - return userContext; - - } - - throw new GuacamoleResourceNotFoundException("Session not associated with authentication provider \"" + authProviderIdentifier + "\"."); - - } - - /** - * Retrieves a single user from the given user context. - * - * @param userContext - * The user context to retrieve the user from. - * - * @param identifier - * The identifier of the user to retrieve. - * - * @return - * The user having the given identifier. - * - * @throws GuacamoleException - * If an error occurs while retrieving the user, or if the - * user does not exist. - */ - public User retrieveUser(UserContext userContext, - String identifier) throws GuacamoleException { - - // Get user directory - Directory directory = userContext.getUserDirectory(); - - // Pull specified user - User user = directory.get(identifier); - if (user == null) - throw new GuacamoleResourceNotFoundException("No such user: \"" + identifier + "\""); - - return user; - - } - - /** - * Retrieves a single user from the given GuacamoleSession. - * - * @param session - * The GuacamoleSession to retrieve the user from. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider that created the - * UserContext from which the user should be retrieved. Only one - * UserContext per User per AuthenticationProvider can exist. - * - * @param identifier - * The identifier of the user to retrieve. - * - * @return - * The user having the given identifier. - * - * @throws GuacamoleException - * If an error occurs while retrieving the user, or if the - * user does not exist. - */ - public User retrieveUser(GuacamoleSession session, String authProviderIdentifier, - String identifier) throws GuacamoleException { - - UserContext userContext = retrieveUserContext(session, authProviderIdentifier); - return retrieveUser(userContext, identifier); - - } - - /** - * Retrieves a single connection from the given user context. - * - * @param userContext - * The user context to retrieve the connection from. - * - * @param identifier - * The identifier of the connection to retrieve. - * - * @return - * The connection having the given identifier. - * - * @throws GuacamoleException - * If an error occurs while retrieving the connection, or if the - * connection does not exist. - */ - public Connection retrieveConnection(UserContext userContext, - String identifier) throws GuacamoleException { - - // Get connection directory - Directory directory = userContext.getConnectionDirectory(); - - // Pull specified connection - Connection connection = directory.get(identifier); - if (connection == null) - throw new GuacamoleResourceNotFoundException("No such connection: \"" + identifier + "\""); - - return connection; - - } - - /** - * Retrieves a single connection from the given GuacamoleSession. - * - * @param session - * The GuacamoleSession to retrieve the connection from. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider that created the - * UserContext from which the connection should be retrieved. Only one - * UserContext per User per AuthenticationProvider can exist. - * - * @param identifier - * The identifier of the connection to retrieve. - * - * @return - * The connection having the given identifier. - * - * @throws GuacamoleException - * If an error occurs while retrieving the connection, or if the - * connection does not exist. - */ - public Connection retrieveConnection(GuacamoleSession session, - String authProviderIdentifier, String identifier) - throws GuacamoleException { - - UserContext userContext = retrieveUserContext(session, authProviderIdentifier); - return retrieveConnection(userContext, identifier); - - } - - /** - * Retrieves a single connection group from the given user context. If - * the given identifier the REST API root identifier, the root connection - * group will be returned. The underlying authentication provider may - * additionally use a different identifier for root. - * - * @param userContext - * The user context to retrieve the connection group from. - * - * @param identifier - * The identifier of the connection group to retrieve. - * - * @return - * The connection group having the given identifier, or the root - * connection group if the identifier the root identifier. - * - * @throws GuacamoleException - * If an error occurs while retrieving the connection group, or if the - * connection group does not exist. - */ - public ConnectionGroup retrieveConnectionGroup(UserContext userContext, - String identifier) throws GuacamoleException { - - // Use root group if identifier is the standard root identifier - if (identifier != null && identifier.equals(APIConnectionGroup.ROOT_IDENTIFIER)) - return userContext.getRootConnectionGroup(); - - // Pull specified connection group otherwise - Directory directory = userContext.getConnectionGroupDirectory(); - ConnectionGroup connectionGroup = directory.get(identifier); - - if (connectionGroup == null) - throw new GuacamoleResourceNotFoundException("No such connection group: \"" + identifier + "\""); - - return connectionGroup; - - } - - /** - * Retrieves a single connection group from the given GuacamoleSession. If - * the given identifier is the REST API root identifier, the root - * connection group will be returned. The underlying authentication - * provider may additionally use a different identifier for root. - * - * @param session - * The GuacamoleSession to retrieve the connection group from. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider that created the - * UserContext from which the connection group should be retrieved. - * Only one UserContext per User per AuthenticationProvider can exist. - * - * @param identifier - * The identifier of the connection group to retrieve. - * - * @return - * The connection group having the given identifier, or the root - * connection group if the identifier is the root identifier. - * - * @throws GuacamoleException - * If an error occurs while retrieving the connection group, or if the - * connection group does not exist. - */ - public ConnectionGroup retrieveConnectionGroup(GuacamoleSession session, - String authProviderIdentifier, String identifier) throws GuacamoleException { - - UserContext userContext = retrieveUserContext(session, authProviderIdentifier); - return retrieveConnectionGroup(userContext, identifier); - - } - -} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/RESTMethodMatcher.java b/guacamole/src/main/java/org/apache/guacamole/rest/RESTMethodMatcher.java index 3a862c88d..623932f9f 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/RESTMethodMatcher.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/RESTMethodMatcher.java @@ -23,6 +23,7 @@ import com.google.inject.matcher.AbstractMatcher; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import javax.ws.rs.HttpMethod; +import javax.ws.rs.Path; import org.apache.guacamole.GuacamoleException; /** @@ -67,7 +68,7 @@ public class RESTMethodMatcher extends AbstractMatcher { /** * Returns whether the given method is annotated as a REST method. A REST * method is annotated with an annotation which is annotated with - * @HttpMethod. + * @HttpMethod or @Path. * * @param method * The method to test. @@ -86,9 +87,32 @@ public class RESTMethodMatcher extends AbstractMatcher { if (annotationType.isAnnotationPresent(HttpMethod.class)) return true; + // A method is a REST method if it is annotated with @Path + if (Path.class.isAssignableFrom(annotationType)) + return true; + } - // The method is not an HTTP method + // A method is also REST method if it overrides a REST method within + // the superclass + Class superclass = method.getDeclaringClass().getSuperclass(); + if (superclass != null) { + + // Recheck against identical method within superclass + try { + return isRESTMethod(superclass.getMethod(method.getName(), + method.getParameterTypes())); + } + + // If there is no such method, then this method cannot possibly be + // a REST method + catch (NoSuchMethodException e) { + return false; + } + + } + + // Lacking a superclass, the search stops here - it's not a REST method return false; } diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java index ec555e30e..29782788a 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java @@ -19,26 +19,27 @@ package org.apache.guacamole.rest; +import org.apache.guacamole.rest.session.UserContextResourceFactory; +import org.apache.guacamole.rest.session.SessionRESTService; import com.google.inject.Scopes; +import com.google.inject.assistedinject.FactoryModuleBuilder; import com.google.inject.matcher.Matchers; import com.google.inject.servlet.ServletModule; import com.sun.jersey.guice.spi.container.servlet.GuiceContainer; import org.aopalliance.intercept.MethodInterceptor; +import org.apache.guacamole.rest.activeconnection.ActiveConnectionModule; import org.codehaus.jackson.jaxrs.JacksonJsonProvider; import org.apache.guacamole.rest.auth.TokenRESTService; -import org.apache.guacamole.rest.connection.ConnectionRESTService; -import org.apache.guacamole.rest.connectiongroup.ConnectionGroupRESTService; -import org.apache.guacamole.rest.activeconnection.ActiveConnectionRESTService; import org.apache.guacamole.rest.auth.AuthTokenGenerator; import org.apache.guacamole.rest.auth.AuthenticationService; import org.apache.guacamole.rest.auth.SecureRandomAuthTokenGenerator; import org.apache.guacamole.rest.auth.TokenSessionMap; -import org.apache.guacamole.rest.history.HistoryRESTService; +import org.apache.guacamole.rest.connection.ConnectionModule; +import org.apache.guacamole.rest.connectiongroup.ConnectionGroupModule; import org.apache.guacamole.rest.language.LanguageRESTService; import org.apache.guacamole.rest.patch.PatchRESTService; -import org.apache.guacamole.rest.schema.SchemaRESTService; -import org.apache.guacamole.rest.tunnel.TunnelRESTService; -import org.apache.guacamole.rest.user.UserRESTService; +import org.apache.guacamole.rest.session.SessionResourceFactory; +import org.apache.guacamole.rest.user.UserModule; /** * A Guice Module to set up the servlet mappings and authentication-specific @@ -81,20 +82,21 @@ public class RESTServiceModule extends ServletModule { requestInjection(interceptor); bindInterceptor(Matchers.any(), new RESTMethodMatcher(), interceptor); - // Bind convenience services used by the REST API - bind(ObjectRetrievalService.class); - // Set up the API endpoints - bind(ActiveConnectionRESTService.class); - bind(ConnectionGroupRESTService.class); - bind(ConnectionRESTService.class); - bind(HistoryRESTService.class); bind(LanguageRESTService.class); bind(PatchRESTService.class); - bind(SchemaRESTService.class); bind(TokenRESTService.class); - bind(TunnelRESTService.class); - bind(UserRESTService.class); + + // Root-level resources + bind(SessionRESTService.class); + install(new FactoryModuleBuilder().build(SessionResourceFactory.class)); + install(new FactoryModuleBuilder().build(UserContextResourceFactory.class)); + + // Resources below root + install(new ActiveConnectionModule()); + install(new ConnectionModule()); + install(new ConnectionGroupModule()); + install(new UserModule()); // Set up the servlet and JSON mappings bind(GuiceContainer.class); diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionModule.java new file mode 100644 index 000000000..efecdde02 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionModule.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.activeconnection; + +import com.google.inject.AbstractModule; +import org.apache.guacamole.rest.directory.DirectoryObjectResourceFactory; +import org.apache.guacamole.rest.directory.DirectoryResourceFactory; +import com.google.inject.TypeLiteral; +import com.google.inject.assistedinject.FactoryModuleBuilder; +import org.apache.guacamole.net.auth.ActiveConnection; +import org.apache.guacamole.rest.directory.DirectoryObjectResource; +import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; + +/** + * Guice Module which configures injections required for handling + * ActiveConnection resources via the REST API. + * + * @author Michael Jumper + */ +public class ActiveConnectionModule extends AbstractModule { + + @Override + protected void configure() { + + // Create the required DirectoryResourceFactory implementation + install(new FactoryModuleBuilder() + .build(new TypeLiteral>() {})); + + // Create the required DirectoryObjectResourceFactory implementation + install(new FactoryModuleBuilder() + .implement( + new TypeLiteral>() {}, + ActiveConnectionResource.class + ) + .build(new TypeLiteral>() {})); + + // Bind translator for converting between ActiveConnection and APIActiveConnection + bind(new TypeLiteral>() {}) + .to(ActiveConnectionObjectTranslator.class); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionObjectTranslator.java b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionObjectTranslator.java new file mode 100644 index 000000000..f0f9ed35a --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionObjectTranslator.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.activeconnection; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleUnsupportedException; +import org.apache.guacamole.net.auth.ActiveConnection; +import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; + +/** + * Translator which converts between ActiveConnection objects and + * APIActiveConnection objects. As ActiveConnection objects are read-only, only + * toExternalObject() is implemented here. + * + * @author Michael Jumper + */ +public class ActiveConnectionObjectTranslator + implements DirectoryObjectTranslator { + + @Override + public APIActiveConnection toExternalObject(ActiveConnection object) + throws GuacamoleException { + return new APIActiveConnection(object); + } + + @Override + public ActiveConnection toInternalObject(APIActiveConnection object) + throws GuacamoleException { + + // ActiveConnection objects are read-only + throw new GuacamoleUnsupportedException("Active connection records are read-only."); + + } + + @Override + public void applyExternalChanges(ActiveConnection existingObject, + APIActiveConnection object) throws GuacamoleException { + + // Modification not supported for ActiveConnection + throw new GuacamoleUnsupportedException("Active connection records are read-only."); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionRESTService.java deleted file mode 100644 index dd7391645..000000000 --- a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionRESTService.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.guacamole.rest.activeconnection; - -import com.google.inject.Inject; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import org.apache.guacamole.GuacamoleClientException; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.GuacamoleUnsupportedException; -import org.apache.guacamole.net.auth.ActiveConnection; -import org.apache.guacamole.net.auth.Directory; -import org.apache.guacamole.net.auth.User; -import org.apache.guacamole.net.auth.UserContext; -import org.apache.guacamole.net.auth.permission.ObjectPermission; -import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; -import org.apache.guacamole.net.auth.permission.SystemPermission; -import org.apache.guacamole.net.auth.permission.SystemPermissionSet; -import org.apache.guacamole.GuacamoleSession; -import org.apache.guacamole.rest.APIPatch; -import org.apache.guacamole.rest.ObjectRetrievalService; -import org.apache.guacamole.rest.PATCH; -import org.apache.guacamole.rest.auth.AuthenticationService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * A REST Service for retrieving and managing the tunnels of active connections. - * - * @author Michael Jumper - */ -@Path("/data/{dataSource}/activeConnections") -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -public class ActiveConnectionRESTService { - - /** - * Logger for this class. - */ - private static final Logger logger = LoggerFactory.getLogger(ActiveConnectionRESTService.class); - - /** - * A service for authenticating users from auth tokens. - */ - @Inject - private AuthenticationService authenticationService; - - /** - * Service for convenient retrieval of objects. - */ - @Inject - private ObjectRetrievalService retrievalService; - - /** - * 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 authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext containing the active connections to be retrieved. - * - * @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 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 is encountered while retrieving active connections. - */ - @GET - public Map getActiveConnections(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - @QueryParam("permission") List permissions) - throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - User self = userContext.self(); - - // Do not filter on permissions if no permissions are specified - if (permissions != null && permissions.isEmpty()) - permissions = null; - - // An admin user has access to any connection - SystemPermissionSet systemPermissions = self.getSystemPermissions(); - boolean isAdmin = systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER); - - // Get the directory - Directory activeConnectionDirectory = userContext.getActiveConnectionDirectory(); - - // Filter connections, 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 apiActiveConnections; - - } - - /** - * 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 authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext containing the active connections to be deleted. - * - * @param patches - * The active connection patches to apply for this request. - * - * @throws GuacamoleException - * If an error occurs while deleting the active connections. - */ - @PATCH - public void patchTunnels(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - List> patches) throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - - // 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 active connections."); - - // Retrieve and validate path - String path = patch.getPath(); - if (!path.startsWith("/")) - throw new GuacamoleClientException("Patch paths must start with \"/\"."); - - // Close connection - activeConnectionDirectory.remove(path.substring(1)); - - } - - } - -} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java new file mode 100644 index 000000000..d006416ba --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.activeconnection; + +import com.google.inject.assistedinject.Assisted; +import com.google.inject.assistedinject.AssistedInject; +import javax.ws.rs.Consumes; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.net.auth.ActiveConnection; +import org.apache.guacamole.net.auth.Directory; +import org.apache.guacamole.net.auth.UserContext; +import org.apache.guacamole.rest.directory.DirectoryObjectResource; +import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; + +/** + * A REST resource which abstracts the operations available on an existing + * ActiveConnection. + * + * @author Michael Jumper + */ +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class ActiveConnectionResource + extends DirectoryObjectResource { + + /** + * Creates a new ActiveConnectionResource which exposes the operations and + * subresources available for the given ActiveConnection. + * + * @param userContext + * The UserContext associated with the given Directory. + * + * @param directory + * The Directory which contains the given ActiveConnection. + * + * @param connection + * The ActiveConnection that this ActiveConnectionResource should + * represent. + * + * @param translator + * A DirectoryObjectTranslator implementation which handles + * ActiveConnections. + */ + @AssistedInject + public ActiveConnectionResource(@Assisted UserContext userContext, + @Assisted Directory directory, + @Assisted ActiveConnection connection, + DirectoryObjectTranslator translator) { + super(directory, connection, translator); + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/package-info.java b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/package-info.java new file mode 100644 index 000000000..f5483dbbc --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/package-info.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Classes related to the manipulation of active connections via the Guacamole + * REST API. + */ +package org.apache.guacamole.rest.activeconnection; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionModule.java new file mode 100644 index 000000000..9f716719f --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionModule.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.connection; + +import com.google.inject.AbstractModule; +import org.apache.guacamole.rest.directory.DirectoryObjectResourceFactory; +import org.apache.guacamole.rest.directory.DirectoryObjectResource; +import org.apache.guacamole.rest.directory.DirectoryResourceFactory; +import com.google.inject.TypeLiteral; +import com.google.inject.assistedinject.FactoryModuleBuilder; +import org.apache.guacamole.net.auth.Connection; +import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; + +/** + * Guice Module which configures injections required for handling Connection + * resources via the REST API. + * + * @author Michael Jumper + */ +public class ConnectionModule extends AbstractModule { + + @Override + protected void configure() { + + // Create the required DirectoryResourceFactory implementation + install(new FactoryModuleBuilder() + .build(new TypeLiteral>() {})); + + // Create the required DirectoryObjectResourceFactory implementation + install(new FactoryModuleBuilder() + .implement( + new TypeLiteral>() {}, + ConnectionResource.class + ) + .build(new TypeLiteral>() {})); + + // Bind translator for converting between Connection and APIConnection + bind(new TypeLiteral>() {}) + .to(ConnectionObjectTranslator.class); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionObjectTranslator.java b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionObjectTranslator.java new file mode 100644 index 000000000..de6253f13 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionObjectTranslator.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.connection; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.Connection; +import org.apache.guacamole.protocol.GuacamoleConfiguration; +import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; + +/** + * Translator which converts between Connection objects and APIConnection + * objects. + * + * @author Michael Jumper + */ +public class ConnectionObjectTranslator + implements DirectoryObjectTranslator { + + @Override + public APIConnection toExternalObject(Connection object) + throws GuacamoleException { + return new APIConnection(object); + } + + @Override + public Connection toInternalObject(APIConnection object) { + return new APIConnectionWrapper(object); + } + + @Override + public void applyExternalChanges(Connection existingObject, + APIConnection object) { + + // Build updated configuration + GuacamoleConfiguration config = new GuacamoleConfiguration(); + config.setProtocol(object.getProtocol()); + config.setParameters(object.getParameters()); + + // Update the connection + existingObject.setConfiguration(config); + existingObject.setParentIdentifier(object.getParentIdentifier()); + existingObject.setName(object.getName()); + existingObject.setAttributes(object.getAttributes()); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionRESTService.java deleted file mode 100644 index feb03240d..000000000 --- a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionRESTService.java +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.guacamole.rest.connection; - -import com.google.inject.Inject; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import org.apache.guacamole.GuacamoleClientException; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.GuacamoleSecurityException; -import org.apache.guacamole.net.auth.Connection; -import org.apache.guacamole.net.auth.ConnectionRecord; -import org.apache.guacamole.net.auth.Directory; -import org.apache.guacamole.net.auth.User; -import org.apache.guacamole.net.auth.UserContext; -import org.apache.guacamole.net.auth.permission.ObjectPermission; -import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; -import org.apache.guacamole.net.auth.permission.SystemPermission; -import org.apache.guacamole.net.auth.permission.SystemPermissionSet; -import org.apache.guacamole.GuacamoleSession; -import org.apache.guacamole.rest.ObjectRetrievalService; -import org.apache.guacamole.rest.auth.AuthenticationService; -import org.apache.guacamole.rest.history.APIConnectionRecord; -import org.apache.guacamole.protocol.GuacamoleConfiguration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * A REST Service for handling connection CRUD operations. - * - * @author James Muehlner - */ -@Path("/data/{dataSource}/connections") -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -public class ConnectionRESTService { - - /** - * Logger for this class. - */ - private static final Logger logger = LoggerFactory.getLogger(ConnectionRESTService.class); - - /** - * A service for authenticating users from auth tokens. - */ - @Inject - private AuthenticationService authenticationService; - - /** - * Service for convenient retrieval of objects. - */ - @Inject - private ObjectRetrievalService retrievalService; - - /** - * Retrieves an individual connection. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext containing the connection to be retrieved. - * - * @param connectionID - * The identifier of the connection to retrieve. - * - * @return - * The connection having the given identifier. - * - * @throws GuacamoleException - * If an error occurs while retrieving the connection. - */ - @GET - @Path("/{connectionID}") - public APIConnection getConnection(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - @PathParam("connectionID") String connectionID) - throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - - // Retrieve the requested connection - return new APIConnection(retrievalService.retrieveConnection(session, authProviderIdentifier, connectionID)); - - } - - /** - * Retrieves the parameters associated with a single connection. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext containing the connection whose parameters are to be - * retrieved. - * - * @param connectionID - * The identifier of the connection. - * - * @return - * A map of parameter name/value pairs. - * - * @throws GuacamoleException - * If an error occurs while retrieving the connection parameters. - */ - @GET - @Path("/{connectionID}/parameters") - public Map getConnectionParameters(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - @PathParam("connectionID") String connectionID) - throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - User self = userContext.self(); - - // Retrieve permission sets - SystemPermissionSet systemPermissions = self.getSystemPermissions(); - ObjectPermissionSet connectionPermissions = self.getConnectionPermissions(); - - // Deny access if adminstrative or update permission is missing - if (!systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER) - && !connectionPermissions.hasPermission(ObjectPermission.Type.UPDATE, connectionID)) - throw new GuacamoleSecurityException("Permission to read connection parameters denied."); - - // Retrieve the requested connection - Connection connection = retrievalService.retrieveConnection(userContext, connectionID); - - // Retrieve connection configuration - GuacamoleConfiguration config = connection.getConfiguration(); - - // Return parameter map - return config.getParameters(); - - } - - /** - * Retrieves the usage history of a single connection. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext containing the connection whose history is to be - * retrieved. - * - * @param connectionID - * The identifier of the connection. - * - * @return - * A list of connection records, describing the start and end times of - * various usages of this connection. - * - * @throws GuacamoleException - * If an error occurs while retrieving the connection history. - */ - @GET - @Path("/{connectionID}/history") - public List getConnectionHistory(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - @PathParam("connectionID") String connectionID) - throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - - // Retrieve the requested connection - Connection connection = retrievalService.retrieveConnection(session, authProviderIdentifier, connectionID); - - // Retrieve the requested connection's history - List apiRecords = new ArrayList(); - for (ConnectionRecord record : connection.getHistory()) - apiRecords.add(new APIConnectionRecord(record)); - - // Return the converted history - return apiRecords; - - } - - /** - * Deletes an individual connection. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext containing the connection to be deleted. - * - * @param connectionID - * The identifier of the connection to delete. - * - * @throws GuacamoleException - * If an error occurs while deleting the connection. - */ - @DELETE - @Path("/{connectionID}") - public void deleteConnection(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - @PathParam("connectionID") String connectionID) - throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - - // Get the connection directory - Directory connectionDirectory = userContext.getConnectionDirectory(); - - // Delete the specified connection - connectionDirectory.remove(connectionID); - - } - - /** - * Creates a new connection and returns the new connection, with identifier - * field populated. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext in which the connection is to be created. - * - * @param connection - * The connection to create. - * - * @return - * The new connection. - * - * @throws GuacamoleException - * If an error occurs while creating the connection. - */ - @POST - public APIConnection createConnection(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - APIConnection connection) throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - - // Validate that connection data was provided - if (connection == null) - throw new GuacamoleClientException("Connection JSON must be submitted when creating connections."); - - // Add the new connection - Directory connectionDirectory = userContext.getConnectionDirectory(); - connectionDirectory.add(new APIConnectionWrapper(connection)); - - // Return the new connection - return connection; - - } - - /** - * Updates an existing connection. If the parent identifier of the - * connection is changed, the connection will also be moved to the new - * parent group. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext containing the connection to be updated. - * - * @param connectionID - * The identifier of the connection to update. - * - * @param connection - * The connection data to update the specified connection with. - * - * @throws GuacamoleException - * If an error occurs while updating the connection. - */ - @PUT - @Path("/{connectionID}") - public void updateConnection(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - @PathParam("connectionID") String connectionID, - APIConnection connection) throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - - // Validate that connection data was provided - if (connection == null) - throw new GuacamoleClientException("Connection JSON must be submitted when updating connections."); - - // Get the connection directory - Directory connectionDirectory = userContext.getConnectionDirectory(); - - // Retrieve connection to update - Connection existingConnection = retrievalService.retrieveConnection(userContext, connectionID); - - // Build updated configuration - GuacamoleConfiguration config = new GuacamoleConfiguration(); - config.setProtocol(connection.getProtocol()); - config.setParameters(connection.getParameters()); - - // Update the connection - existingConnection.setConfiguration(config); - existingConnection.setParentIdentifier(connection.getParentIdentifier()); - existingConnection.setName(connection.getName()); - existingConnection.setAttributes(connection.getAttributes()); - connectionDirectory.update(existingConnection); - - } - -} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java new file mode 100644 index 000000000..9238af15e --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.connection; + +import com.google.inject.assistedinject.Assisted; +import com.google.inject.assistedinject.AssistedInject; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleSecurityException; +import org.apache.guacamole.net.auth.Connection; +import org.apache.guacamole.net.auth.ConnectionRecord; +import org.apache.guacamole.net.auth.Directory; +import org.apache.guacamole.net.auth.User; +import org.apache.guacamole.net.auth.UserContext; +import org.apache.guacamole.net.auth.permission.ObjectPermission; +import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; +import org.apache.guacamole.net.auth.permission.SystemPermission; +import org.apache.guacamole.net.auth.permission.SystemPermissionSet; +import org.apache.guacamole.rest.history.APIConnectionRecord; +import org.apache.guacamole.protocol.GuacamoleConfiguration; +import org.apache.guacamole.rest.directory.DirectoryObjectResource; +import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; + +/** + * A REST resource which abstracts the operations available on an existing + * Connection. + * + * @author Michael Jumper + */ +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class ConnectionResource extends DirectoryObjectResource { + + /** + * The UserContext associated with the Directory which contains the + * Connection exposed by this resource. + */ + private final UserContext userContext; + + /** + * The Connection object represented by this ConnectionResource. + */ + private final Connection connection; + + /** + * Creates a new ConnectionResource which exposes the operations and + * subresources available for the given Connection. + * + * @param userContext + * The UserContext associated with the given Directory. + * + * @param directory + * The Directory which contains the given Connection. + * + * @param connection + * The Connection that this ConnectionResource should represent. + * + * @param translator + * A DirectoryObjectTranslator implementation which handles the type of + * object given. + */ + @AssistedInject + public ConnectionResource(@Assisted UserContext userContext, + @Assisted Directory directory, + @Assisted Connection connection, + DirectoryObjectTranslator translator) { + super(directory, connection, translator); + this.userContext = userContext; + this.connection = connection; + } + + /** + * Retrieves the parameters associated with a single connection. + * + * @return + * A map of parameter name/value pairs. + * + * @throws GuacamoleException + * If an error occurs while retrieving the connection parameters. + */ + @GET + @Path("parameters") + public Map getConnectionParameters() + throws GuacamoleException { + + User self = userContext.self(); + + // Retrieve permission sets + SystemPermissionSet systemPermissions = self.getSystemPermissions(); + ObjectPermissionSet connectionPermissions = self.getConnectionPermissions(); + + // Deny access if adminstrative or update permission is missing + String identifier = connection.getIdentifier(); + if (!systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER) + && !connectionPermissions.hasPermission(ObjectPermission.Type.UPDATE, identifier)) + throw new GuacamoleSecurityException("Permission to read connection parameters denied."); + + // Retrieve connection configuration + GuacamoleConfiguration config = connection.getConfiguration(); + + // Return parameter map + return config.getParameters(); + + } + + /** + * Retrieves the usage history of a single connection. + * + * @return + * A list of connection records, describing the start and end times of + * various usages of this connection. + * + * @throws GuacamoleException + * If an error occurs while retrieving the connection history. + */ + @GET + @Path("history") + public List getConnectionHistory() + throws GuacamoleException { + + // Retrieve the requested connection's history + List apiRecords = new ArrayList(); + for (ConnectionRecord record : connection.getHistory()) + apiRecords.add(new APIConnectionRecord(record)); + + // Return the converted history + return apiRecords; + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupDirectoryResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupDirectoryResource.java new file mode 100644 index 000000000..2c32e21d9 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupDirectoryResource.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.connectiongroup; + +import com.google.inject.assistedinject.Assisted; +import com.google.inject.assistedinject.AssistedInject; +import javax.ws.rs.Consumes; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.ConnectionGroup; +import org.apache.guacamole.net.auth.Directory; +import org.apache.guacamole.net.auth.UserContext; +import org.apache.guacamole.rest.directory.DirectoryObjectResource; +import org.apache.guacamole.rest.directory.DirectoryObjectResourceFactory; +import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; +import org.apache.guacamole.rest.directory.DirectoryResource; + +/** + * A REST resource which abstracts the operations available on a Directory of + * ConnectionGroups. + * + * @author Michael Jumper + */ +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class ConnectionGroupDirectoryResource + extends DirectoryResource { + + /** + * The UserContext associated with the Directory which contains the + * ConnectionGroup exposed by this resource. + */ + private final UserContext userContext; + + /** + * The Directory exposed by this resource. + */ + private final Directory directory; + + /** + * A factory which can be used to create instances of resources representing + * ConnectionGroups. + */ + private final DirectoryObjectResourceFactory resourceFactory; + + /** + * Creates a new ConnectionGroupDirectoryResource which exposes the + * operations and subresources available for the given ConnectionGroup + * Directory. + * + * @param userContext + * The UserContext associated with the given Directory. + * + * @param directory + * The Directory being exposed. + * + * @param translator + * A DirectoryObjectTranslator implementation which handles + * ConnectionGroups. + * + * @param resourceFactory + * A factory which can be used to create instances of resources + * representing ConnectionGroups. + */ + @AssistedInject + public ConnectionGroupDirectoryResource(@Assisted UserContext userContext, + @Assisted Directory directory, + DirectoryObjectTranslator translator, + DirectoryObjectResourceFactory resourceFactory) { + super(userContext, directory, translator, resourceFactory); + this.userContext = userContext; + this.directory = directory; + this.resourceFactory = resourceFactory; + } + + @Override + public DirectoryObjectResource + getObjectResource(String identifier) throws GuacamoleException { + + // Use root group if identifier is the standard root identifier + if (identifier != null && identifier.equals(APIConnectionGroup.ROOT_IDENTIFIER)) + return resourceFactory.create(userContext, directory, + userContext.getRootConnectionGroup()); + + return super.getObjectResource(identifier); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupModule.java new file mode 100644 index 000000000..df69919ea --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupModule.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.connectiongroup; + +import com.google.inject.AbstractModule; +import org.apache.guacamole.rest.directory.DirectoryObjectResourceFactory; +import org.apache.guacamole.rest.directory.DirectoryObjectResource; +import org.apache.guacamole.rest.directory.DirectoryResourceFactory; +import com.google.inject.TypeLiteral; +import com.google.inject.assistedinject.FactoryModuleBuilder; +import org.apache.guacamole.net.auth.ConnectionGroup; +import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; +import org.apache.guacamole.rest.directory.DirectoryResource; + +/** + * Guice Module which configures injections required for handling + * ConnectionGroup resources via the REST API. + * + * @author Michael Jumper + */ +public class ConnectionGroupModule extends AbstractModule { + + @Override + protected void configure() { + + // Create the required DirectoryResourceFactory implementation + install(new FactoryModuleBuilder() + .implement( + new TypeLiteral>() {}, + ConnectionGroupDirectoryResource.class + ) + .build(new TypeLiteral>() {})); + + // Create the required DirectoryObjectResourceFactory implementation + install(new FactoryModuleBuilder() + .implement( + new TypeLiteral>() {}, + ConnectionGroupResource.class + ) + .build(new TypeLiteral>() {})); + + // Bind translator for converting between ConnectionGroup and APIConnectionGroup + bind(new TypeLiteral>() {}) + .to(ConnectionGroupObjectTranslator.class); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupObjectTranslator.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupObjectTranslator.java new file mode 100644 index 000000000..efd5cb95b --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupObjectTranslator.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.connectiongroup; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.ConnectionGroup; +import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; + +/** + * Translator which converts between ConnectionGroup objects and + * APIConnectionGroup objects. + * + * @author Michael Jumper + */ +public class ConnectionGroupObjectTranslator + implements DirectoryObjectTranslator { + + @Override + public APIConnectionGroup toExternalObject(ConnectionGroup object) + throws GuacamoleException { + return new APIConnectionGroup(object); + } + + @Override + public ConnectionGroup toInternalObject(APIConnectionGroup object) { + return new APIConnectionGroupWrapper(object); + } + + @Override + public void applyExternalChanges(ConnectionGroup existingObject, + APIConnectionGroup object) { + + // Update the connection group + existingObject.setName(object.getName()); + existingObject.setParentIdentifier(object.getParentIdentifier()); + existingObject.setType(object.getType()); + existingObject.setAttributes(object.getAttributes()); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupRESTService.java deleted file mode 100644 index bd28a1978..000000000 --- a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupRESTService.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.guacamole.rest.connectiongroup; - -import com.google.inject.Inject; -import java.util.List; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import org.apache.guacamole.GuacamoleClientException; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.net.auth.ConnectionGroup; -import org.apache.guacamole.net.auth.Directory; -import org.apache.guacamole.net.auth.UserContext; -import org.apache.guacamole.net.auth.permission.ObjectPermission; -import org.apache.guacamole.GuacamoleSession; -import org.apache.guacamole.rest.ObjectRetrievalService; -import org.apache.guacamole.rest.auth.AuthenticationService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * A REST Service for handling connection group CRUD operations. - * - * @author James Muehlner - */ -@Path("/data/{dataSource}/connectionGroups") -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -public class ConnectionGroupRESTService { - - /** - * Logger for this class. - */ - private static final Logger logger = LoggerFactory.getLogger(ConnectionGroupRESTService.class); - - /** - * A service for authenticating users from auth tokens. - */ - @Inject - private AuthenticationService authenticationService; - - /** - * Service for convenient retrieval of objects. - */ - @Inject - private ObjectRetrievalService retrievalService; - - /** - * Gets an individual connection group. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext containing the connection group to be retrieved. - * - * @param connectionGroupID - * The ID of the connection group to retrieve. - * - * @return - * The connection group, without any descendants. - * - * @throws GuacamoleException - * If a problem is encountered while retrieving the connection group. - */ - @GET - @Path("/{connectionGroupID}") - public APIConnectionGroup getConnectionGroup(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - @PathParam("connectionGroupID") String connectionGroupID) - throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - - // Retrieve the requested connection group - return new APIConnectionGroup(retrievalService.retrieveConnectionGroup(session, authProviderIdentifier, connectionGroupID)); - - } - - /** - * Gets an individual connection group and all children. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext containing the connection group to be retrieved. - * - * @param connectionGroupID - * The ID of the connection group to retrieve. - * - * @param permissions - * If specified and non-empty, limit the returned list to only those - * connections for which the current user has any of the given - * permissions. Otherwise, all visible connections are returned. - * Connection groups are unaffected by this parameter. - * - * @return - * The requested connection group, including all descendants. - * - * @throws GuacamoleException - * If a problem is encountered while retrieving the connection group or - * its descendants. - */ - @GET - @Path("/{connectionGroupID}/tree") - public APIConnectionGroup getConnectionGroupTree(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - @PathParam("connectionGroupID") String connectionGroupID, - @QueryParam("permission") List permissions) - throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - - // Retrieve the requested tree, filtering by the given permissions - ConnectionGroup treeRoot = retrievalService.retrieveConnectionGroup(userContext, connectionGroupID); - ConnectionGroupTree tree = new ConnectionGroupTree(userContext, treeRoot, permissions); - - // Return tree as a connection group - return tree.getRootAPIConnectionGroup(); - - } - - /** - * Deletes an individual connection group. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext containing the connection group to be deleted. - * - * @param connectionGroupID - * The identifier of the connection group to delete. - * - * @throws GuacamoleException - * If an error occurs while deleting the connection group. - */ - @DELETE - @Path("/{connectionGroupID}") - public void deleteConnectionGroup(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - @PathParam("connectionGroupID") String connectionGroupID) - throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - - // Get the connection group directory - Directory connectionGroupDirectory = userContext.getConnectionGroupDirectory(); - - // Delete the connection group - connectionGroupDirectory.remove(connectionGroupID); - - } - - /** - * Creates a new connection group and returns the new connection group, - * with identifier field populated. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext in which the connection group is to be created. - * - * @param connectionGroup - * The connection group to create. - * - * @return - * The new connection group. - * - * @throws GuacamoleException - * If an error occurs while creating the connection group. - */ - @POST - public APIConnectionGroup createConnectionGroup( - @QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - APIConnectionGroup connectionGroup) throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - - // Validate that connection group data was provided - if (connectionGroup == null) - throw new GuacamoleClientException("Connection group JSON must be submitted when creating connections groups."); - - // Add the new connection group - Directory connectionGroupDirectory = userContext.getConnectionGroupDirectory(); - connectionGroupDirectory.add(new APIConnectionGroupWrapper(connectionGroup)); - - // Return the new connection group - return connectionGroup; - - } - - /** - * Updates a connection group. If the parent identifier of the - * connection group is changed, the connection group will also be moved to - * the new parent group. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext containing the connection group to be updated. - * - * @param connectionGroupID - * The identifier of the existing connection group to update. - * - * @param connectionGroup - * The data to update the existing connection group with. - * - * @throws GuacamoleException - * If an error occurs while updating the connection group. - */ - @PUT - @Path("/{connectionGroupID}") - public void updateConnectionGroup(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - @PathParam("connectionGroupID") String connectionGroupID, - APIConnectionGroup connectionGroup) - throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - - // Validate that connection group data was provided - if (connectionGroup == null) - throw new GuacamoleClientException("Connection group JSON must be submitted when updating connection groups."); - - // Get the connection group directory - Directory connectionGroupDirectory = userContext.getConnectionGroupDirectory(); - - // Retrieve connection group to update - ConnectionGroup existingConnectionGroup = retrievalService.retrieveConnectionGroup(userContext, connectionGroupID); - - // Update the connection group - existingConnectionGroup.setName(connectionGroup.getName()); - existingConnectionGroup.setParentIdentifier(connectionGroup.getParentIdentifier()); - existingConnectionGroup.setType(connectionGroup.getType()); - existingConnectionGroup.setAttributes(connectionGroup.getAttributes()); - connectionGroupDirectory.update(existingConnectionGroup); - - } - -} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupResource.java new file mode 100644 index 000000000..c0b80c44b --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupResource.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.connectiongroup; + +import com.google.inject.assistedinject.Assisted; +import com.google.inject.assistedinject.AssistedInject; +import java.util.List; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.ConnectionGroup; +import org.apache.guacamole.net.auth.Directory; +import org.apache.guacamole.net.auth.UserContext; +import org.apache.guacamole.net.auth.permission.ObjectPermission; +import org.apache.guacamole.rest.directory.DirectoryObjectResource; +import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; + +/** + * A REST resource which abstracts the operations available on an existing + * ConnectionGroup. + * + * @author Michael Jumper + */ +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class ConnectionGroupResource + extends DirectoryObjectResource { + + /** + * The UserContext associated with the Directory which contains the + * ConnectionGroup exposed by this resource. + */ + private final UserContext userContext; + + /** + * The ConnectionGroup object represented by this ConnectionGroupResource. + */ + private final ConnectionGroup connectionGroup; + + /** + * Creates a new ConnectionGroupResource which exposes the operations and + * subresources available for the given ConnectionGroup. + * + * @param userContext + * The UserContext associated with the given Directory. + * + * @param directory + * The Directory which contains the given ConnectionGroup. + * + * @param connectionGroup + * The ConnectionGroup that this ConnectionGroupResource should + * represent. + * + * @param translator + * A DirectoryObjectTranslator implementation which handles the type of + * object given. + */ + @AssistedInject + public ConnectionGroupResource(@Assisted UserContext userContext, + @Assisted Directory directory, + @Assisted ConnectionGroup connectionGroup, + DirectoryObjectTranslator translator) { + super(directory, connectionGroup, translator); + this.userContext = userContext; + this.connectionGroup = connectionGroup; + } + + /** + * Returns the current connection group along with all descendants. + * + * @param permissions + * If specified and non-empty, limit the returned list to only those + * connections for which the current user has any of the given + * permissions. Otherwise, all visible connections are returned. + * ConnectionGroups are unaffected by this parameter. + * + * @return + * The current connection group, including all descendants. + * + * @throws GuacamoleException + * If a problem is encountered while retrieving the connection group or + * its descendants. + */ + @GET + @Path("tree") + public APIConnectionGroup getConnectionGroupTree( + @QueryParam("permission") List permissions) + throws GuacamoleException { + + // Retrieve the requested tree, filtering by the given permissions + ConnectionGroupTree tree = new ConnectionGroupTree(userContext, + connectionGroup, permissions); + + // Return tree as a connection group + return tree.getRootAPIConnectionGroup(); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectResource.java new file mode 100644 index 000000000..7e45b2b82 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectResource.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.directory; + +import com.google.inject.assistedinject.Assisted; +import com.google.inject.assistedinject.AssistedInject; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.GuacamoleClientException; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.Directory; +import org.apache.guacamole.net.auth.Identifiable; + +/** + * A REST resource which abstracts the operations available on an existing + * Guacamole object that is contained within a Directory, such as modification, + * deletion, or individual retrieval. + * + * @author Michael Jumper + * @param + * The type of object that this DirectoryObjectResource represents. To + * avoid coupling the REST API too tightly to the extension API, these + * objects are not directly serialized or deserialized when handling REST + * requests. + * + * @param + * The type of object used in interchange (ie: serialized/deserialized as + * JSON) between REST clients and this DirectoryObjectResource to + * represent the InternalType. + */ +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class DirectoryObjectResource { + + /** + * The Directory which contains the object represented by this + * DirectoryObjectResource. + */ + private final Directory directory; + + /** + * The object represented by this DirectoryObjectResource. + */ + private final InternalType object; + + /** + * A DirectoryObjectTranslator implementation which handles the type of + * objects represented by this DirectoryObjectResource. + */ + private final DirectoryObjectTranslator translator; + + /** + * Creates a new DirectoryObjectResource which exposes the operations + * available for the given object. + * + * @param directory + * The Directory which contains the given object. + * + * @param object + * The object that this DirectoryObjectResource should represent. + * + * @param translator + * A DirectoryObjectTranslator implementation which handles the type of + * object given. + */ + @AssistedInject + public DirectoryObjectResource(@Assisted Directory directory, + @Assisted InternalType object, + DirectoryObjectTranslator translator) { + this.directory = directory; + this.object = object; + this.translator = translator; + } + + /** + * Returns the object represented by this DirectoryObjectResource, in a + * format intended for interchange. + * + * @return + * The object that this DirectoryObjectResource represents, in a format + * intended for interchange. + * + * @throws GuacamoleException + * If an error is encountered while retrieving the object. + */ + @GET + public ExternalType getObject() throws GuacamoleException { + return translator.toExternalObject(object); + } + + /** + * Updates an existing object. The changes to be made to the corresponding + * object within the directory indicated by the provided data. + * + * @param modifiedObject + * The data to update the corresponding object with. + * + * @throws GuacamoleException + * If an error occurs while updating the object. + */ + @PUT + public void updateObject(ExternalType modifiedObject) throws GuacamoleException { + + // Validate that data was provided + if (modifiedObject == null) + throw new GuacamoleClientException("Data must be submitted when updating objects."); + + // Perform update + translator.applyExternalChanges(object, modifiedObject); + directory.update(object); + + } + + /** + * Removes this object from the containing directory. + * + * @throws GuacamoleException + * If an error occurs while removing the object. + */ + @DELETE + public void deleteObject() throws GuacamoleException { + directory.remove(object.getIdentifier()); + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectResourceFactory.java b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectResourceFactory.java new file mode 100644 index 000000000..613ac4220 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectResourceFactory.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.directory; + +import org.apache.guacamole.net.auth.Directory; +import org.apache.guacamole.net.auth.Identifiable; +import org.apache.guacamole.net.auth.UserContext; + +/** + * Factory which creates DirectoryObjectResource instances exposing objects of + * a particular type. + * + * @param + * The type of object exposed by the DirectoryObjectResource instances + * created by this DirectoryResourceFactory. + * + * @param + * The type of object used in interchange (ie: serialized or deserialized + * as JSON) between REST clients and resources when representing the + * InternalType. + */ +public interface DirectoryObjectResourceFactory { + + /** + * Creates a new DirectoryObjectResource which exposes the given object. + * + * @param userContext + * The UserContext which contains the given Directory. + * + * @param directory + * The Directory which contains the object being exposed. + * + * @param object + * The object which should be exposed by the created + * DirectoryObjectResource. + * + * @return + * A new DirectoryObjectResource which exposes the given object. + */ + DirectoryObjectResource + create(UserContext userContext, Directory directory, + InternalType object); + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectTranslator.java b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectTranslator.java new file mode 100644 index 000000000..bac47957b --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectTranslator.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.directory; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.Identifiable; + +/** + * Provides bidirectional conversion between REST-specific objects and the + * internal objects defined by the Guacamole extension API. + * + * @author Michael Jumper + * @param + * The type of object converted by this DirectoryObjectTranslator which is + * not necessarily intended for use in interchange. + * + * @param + * The type of object used in interchange (ie: serialized or + * deserialized as JSON) between REST clients and resource implementations + * when representing the InternalType. + */ +public interface DirectoryObjectTranslator { + + /** + * Converts the given object to an object which is intended to be used in + * interchange. + * + * @param object + * The object to convert for the sake of interchange. + * + * @return + * A new object containing the same data as the given internal object, + * but intended for use in interchange. + * + * @throws GuacamoleException + * If the provided object cannot be converted for any reason. + */ + ExternalType toExternalObject(InternalType object) + throws GuacamoleException; + + /** + * Converts the given object to an object which is intended to be used + * within the Guacamole extension API. + * + * @param object + * An object of the type intended for use in interchange, such as that + * produced by toExternalObject() or received from a user via REST. + * + * @return + * A new object containing the same data as the given external object, + * but intended for use within the Guacamole extension API. + * + * @throws GuacamoleException + * If the provided object cannot be converted for any reason. + */ + InternalType toInternalObject(ExternalType object) + throws GuacamoleException; + + /** + * Overlays the changes indicated by the given external object, modifying + * the given existing object from the Guacamole extension API. + * + * @param existingObject + * The existing object from the Guacamole extension API which should be + * modified. + * + * @param object + * The external object representing the modifications to the existing + * internal object. + * + * @throws GuacamoleException + * If the provided modifications cannot be applied for any reason. + */ + void applyExternalChanges(InternalType existingObject, ExternalType object) + throws GuacamoleException; + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java new file mode 100644 index 000000000..2dbf7f136 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java @@ -0,0 +1,266 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.directory; + +import com.google.inject.assistedinject.Assisted; +import com.google.inject.assistedinject.AssistedInject; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.GuacamoleClientException; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleResourceNotFoundException; +import org.apache.guacamole.GuacamoleUnsupportedException; +import org.apache.guacamole.net.auth.Directory; +import org.apache.guacamole.net.auth.Identifiable; +import org.apache.guacamole.net.auth.User; +import org.apache.guacamole.net.auth.UserContext; +import org.apache.guacamole.net.auth.permission.ObjectPermission; +import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; +import org.apache.guacamole.net.auth.permission.SystemPermission; +import org.apache.guacamole.net.auth.permission.SystemPermissionSet; +import org.apache.guacamole.rest.APIPatch; +import org.apache.guacamole.rest.PATCH; + +/** + * A REST resource which abstracts the operations available on all Guacamole + * Directory implementations, such as the creation of new objects, or listing + * of existing objects. A DirectoryResource functions as the parent of any + * number of child DirectoryObjectResources, which are created with the factory + * provided at the time of this object's construction. + * + * @author Michael Jumper + * @param + * The type of object contained within the Directory that this + * DirectoryResource exposes. To avoid coupling the REST API too tightly to + * the extension API, these objects are not directly serialized or + * deserialized when handling REST requests. + * + * @param + * The type of object used in interchange (ie: serialized/deserialized as + * JSON) between REST clients and this DirectoryResource when representing + * the InternalType. + */ +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class DirectoryResource { + + /** + * The UserContext associated with the Directory being exposed by this + * DirectoryResource. + */ + private final UserContext userContext; + + /** + * The Directory being exposed by this DirectoryResource. + */ + private final Directory directory; + + /** + * A DirectoryObjectTranslator implementation which handles the type of + * objects contained within the Directory exposed by this DirectoryResource. + */ + private final DirectoryObjectTranslator translator; + + /** + * A factory which can be used to create instances of resources representing + * individual objects contained within the Directory exposed by this + * DirectoryResource. + */ + private final DirectoryObjectResourceFactory resourceFactory; + + /** + * Creates a new DirectoryResource which exposes the operations available + * for the given Directory. + * + * @param userContext + * The UserContext associated with the given Directory. + * + * @param directory + * The Directory being exposed by this DirectoryResource. + * + * @param translator + * A DirectoryObjectTranslator implementation which handles the type of + * objects contained within the given Directory. + * + * @param resourceFactory + * A factory which can be used to create instances of resources + * representing individual objects contained within the given Directory. + */ + @AssistedInject + public DirectoryResource(@Assisted UserContext userContext, + @Assisted Directory directory, + DirectoryObjectTranslator translator, + DirectoryObjectResourceFactory resourceFactory) { + this.userContext = userContext; + this.directory = directory; + this.translator = translator; + this.resourceFactory = resourceFactory; + } + + /** + * Returns a map of all objects available within this DirectoryResource, + * filtering the returned map by the given permission, if specified. + * + * @param permissions + * The set of permissions to filter with. A user must have one or more + * of these permissions for the affected objects to appear in the + * result. If null, no filtering will be performed. + * + * @return + * A map of all visible objects. If a permission was specified, this + * map will contain only those objects for which the current user has + * that permission. + * + * @throws GuacamoleException + * If an error is encountered while retrieving the objects. + */ + @GET + public Map getObjects( + @QueryParam("permission") List permissions) + throws GuacamoleException { + + // An admin user has access to all objects + User self = userContext.self(); + SystemPermissionSet systemPermissions = self.getSystemPermissions(); + boolean isAdmin = systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER); + + // Filter objects, if requested + Collection identifiers = directory.getIdentifiers(); + if (!isAdmin && permissions != null && !permissions.isEmpty()) { + ObjectPermissionSet objectPermissions = self.getUserPermissions(); + identifiers = objectPermissions.getAccessibleObjects(permissions, identifiers); + } + + // Translate each retrieved object into the corresponding external object + Map apiObjects = new HashMap(); + for (InternalType object : directory.getAll(identifiers)) + apiObjects.put(object.getIdentifier(), translator.toExternalObject(object)); + + return apiObjects; + + } + + /** + * Applies the given object patches, updating the underlying directory + * accordingly. This operation currently only supports deletion of objects + * through the "remove" patch operation. The path of each patch operation is + * of the form "/ID" where ID is the identifier of the object being + * modified. + * + * @param patches + * The patches to apply for this request. + * + * @throws GuacamoleException + * If an error occurs while deleting the objects. + */ + @PATCH + public void patchObjects(List> patches) + throws GuacamoleException { + + // Apply each operation specified within the patch + for (APIPatch patch : patches) { + + // Only remove is supported + if (patch.getOp() != APIPatch.Operation.remove) + throw new GuacamoleUnsupportedException("Only the \"remove\" " + + "operation is supported."); + + // Retrieve and validate path + String path = patch.getPath(); + if (!path.startsWith("/")) + throw new GuacamoleClientException("Patch paths must start with \"/\"."); + + // Remove specified object + directory.remove(path.substring(1)); + + } + + } + + /** + * Creates a new object within the underlying Directory, returning the + * object that was created. The identifier of the created object will be + * populated, if applicable. + * + * @param object + * The object to create. + * + * @return + * The object that was created. + * + * @throws GuacamoleException + * If an error occurs while adding the object to the underlying + * directory. + */ + @POST + public ExternalType createObject(ExternalType object) + throws GuacamoleException { + + // Validate that data was provided + if (object == null) + throw new GuacamoleClientException("Data must be submitted when creating objects."); + + // Create the new object within the directory + directory.add(translator.toInternalObject(object)); + + return object; + + } + + /** + * Retrieves an individual object, returning a DirectoryObjectResource + * implementation which exposes operations available on that object. + * + * @param identifier + * The identifier of the object to retrieve. + * + * @return + * A DirectoryObjectResource exposing operations available on the + * object having the given identifier. + * + * @throws GuacamoleException + * If an error occurs while retrieving the object. + */ + @Path("{identifier}") + public DirectoryObjectResource + getObjectResource(@PathParam("identifier") String identifier) + throws GuacamoleException { + + // Retrieve the object having the given identifier + InternalType object = directory.get(identifier); + if (object == null) + throw new GuacamoleResourceNotFoundException("Not found: \"" + identifier + "\""); + + // Return a resource which provides access to the retrieved object + return resourceFactory.create(userContext, directory, object); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResourceFactory.java b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResourceFactory.java new file mode 100644 index 000000000..357777a6f --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResourceFactory.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.directory; + +import org.apache.guacamole.net.auth.Directory; +import org.apache.guacamole.net.auth.Identifiable; +import org.apache.guacamole.net.auth.UserContext; + +/** + * Factory which creates DirectoryResource instances exposing Directory + * objects of a particular type. + * + * @param + * The type of object contained within the Directory objects exposed by the + * DirectoryResource instances created by this DirectoryResourceFactory. + * + * @param + * The type of object used in interchange (ie: serialized or deserialized + * as JSON) between REST clients and resources when representing the + * InternalType. + */ +public interface DirectoryResourceFactory { + + /** + * Creates a new DirectoryResource which exposes the given Directory. + * + * @param userContext + * The UserContext from which the given Directory was obtained. + * + * @param directory + * The Directory which should be exposed by the created + * DirectoryResource. + * + * @return + * A new DirectoryResource which exposes the given Directory. + */ + DirectoryResource + create(UserContext userContext, Directory directory); + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/directory/package-info.java b/guacamole/src/main/java/org/apache/guacamole/rest/directory/package-info.java new file mode 100644 index 000000000..5283e8719 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/directory/package-info.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Base classes providing support for exposing and manipulating Directory + * objects and their contents via the Guacamole REST API. + */ +package org.apache.guacamole.rest.directory; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryResource.java similarity index 71% rename from guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryRESTService.java rename to guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryResource.java index fd3738ed1..d71d44555 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryRESTService.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryResource.java @@ -19,13 +19,11 @@ package org.apache.guacamole.rest.history; -import com.google.inject.Inject; import java.util.ArrayList; import java.util.List; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.Path; -import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; @@ -33,27 +31,16 @@ import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.auth.ConnectionRecord; import org.apache.guacamole.net.auth.ConnectionRecordSet; import org.apache.guacamole.net.auth.UserContext; -import org.apache.guacamole.GuacamoleSession; -import org.apache.guacamole.rest.ObjectRetrievalService; -import org.apache.guacamole.rest.auth.AuthenticationService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** - * A REST Service for retrieving and managing the history records of Guacamole + * A REST resource for retrieving and managing the history records of Guacamole * objects. * * @author Michael Jumper */ -@Path("/data/{dataSource}/history") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) -public class HistoryRESTService { - - /** - * Logger for this class. - */ - private static final Logger logger = LoggerFactory.getLogger(HistoryRESTService.class); +public class HistoryResource { /** * The maximum number of history records to return in any one response. @@ -61,30 +48,25 @@ public class HistoryRESTService { private static final int MAXIMUM_HISTORY_SIZE = 1000; /** - * A service for authenticating users from auth tokens. + * The UserContext whose associated connection history is being exposed. */ - @Inject - private AuthenticationService authenticationService; + private final UserContext userContext; /** - * Service for convenient retrieval of objects. + * Creates a new HistoryResource which exposes the connection history + * associated with the given UserContext. + * + * @param userContext + * The UserContext whose connection history should be exposed. */ - @Inject - private ObjectRetrievalService retrievalService; + public HistoryResource(UserContext userContext) { + this.userContext = userContext; + } /** * Retrieves the usage history for all connections, restricted by optional * filter parameters. * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext containing the connection whose history is to be - * retrieved. - * * @param requiredContents * The set of strings that each must occur somewhere within the * returned connection records, whether within the associated username, @@ -105,16 +87,12 @@ public class HistoryRESTService { * If an error occurs while retrieving the connection history. */ @GET - @Path("/connections") - public List getConnectionHistory(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, + @Path("connections") + public List getConnectionHistory( @QueryParam("contains") List requiredContents, @QueryParam("order") List sortPredicates) throws GuacamoleException { - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - // Retrieve overall connection history ConnectionRecordSet history = userContext.getConnectionHistory(); diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/user/PermissionSetPatch.java b/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetPatch.java similarity index 98% rename from guacamole/src/main/java/org/apache/guacamole/rest/user/PermissionSetPatch.java rename to guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetPatch.java index 653f424b4..b08d2ba3f 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/user/PermissionSetPatch.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetPatch.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.guacamole.rest.user; +package org.apache.guacamole.rest.permission; import java.util.HashSet; import java.util.Set; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java new file mode 100644 index 000000000..e75460ccc --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java @@ -0,0 +1,261 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.permission; + +import java.util.List; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.GuacamoleClientException; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.User; +import org.apache.guacamole.net.auth.permission.ObjectPermission; +import org.apache.guacamole.net.auth.permission.Permission; +import org.apache.guacamole.net.auth.permission.SystemPermission; +import org.apache.guacamole.rest.APIPatch; +import org.apache.guacamole.rest.PATCH; + +/** + * A REST resource which abstracts the operations available on the permissions + * granted to an existing User. + * + * @author Michael Jumper + */ +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class PermissionSetResource { + + /** + * The prefix of any path within an operation of a JSON patch which + * modifies the permissions of a user regarding a specific connection. + */ + private static final String CONNECTION_PERMISSION_PATCH_PATH_PREFIX = "/connectionPermissions/"; + + /** + * The prefix of any path within an operation of a JSON patch which + * modifies the permissions of a user regarding a specific connection group. + */ + private static final String CONNECTION_GROUP_PERMISSION_PATCH_PATH_PREFIX = "/connectionGroupPermissions/"; + + /** + * The prefix of any path within an operation of a JSON patch which + * modifies the permissions of a user regarding 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. + */ + private static final String USER_PERMISSION_PATCH_PATH_PREFIX = "/userPermissions/"; + + /** + * The path of any operation within a JSON patch which modifies the + * permissions of a user regarding the entire system. + */ + private static final String SYSTEM_PERMISSION_PATCH_PATH = "/systemPermissions"; + + /** + * The User object whose permissions are represented by this + * PermissionSetResource. + */ + private final User user; + + /** + * Creates a new PermissionSetResource which exposes the operations and + * subresources available for permissions of the given User. + * + * @param user + * The User whose permissions should be represented by this + * PermissionSetResource. + */ + public PermissionSetResource(User user) { + this.user = user; + } + + /** + * Gets a list of permissions for the user with the given username. + * + * @return + * A list of all permissions granted to the specified user. + * + * @throws GuacamoleException + * If an error occurs while retrieving permissions. + */ + @GET + public APIPermissionSet getPermissions() throws GuacamoleException { + return new APIPermissionSet(user); + } + + /** + * Updates the given permission set patch by queuing an add or remove + * operation for the given permission based on the given patch operation. + * + * @param + * The type of permission stored within the permission set. + * + * @param operation + * The patch operation to perform. + * + * @param permissionSetPatch + * The permission set patch being modified. + * + * @param permission + * The permission being added or removed from the set. + * + * @throws GuacamoleException + * If the requested patch operation is not supported. + */ + private void updatePermissionSet( + APIPatch.Operation operation, + PermissionSetPatch permissionSetPatch, + PermissionType permission) throws GuacamoleException { + + // Add or remove permission based on operation + switch (operation) { + + // Add permission + case add: + permissionSetPatch.addPermission(permission); + break; + + // Remove permission + case remove: + permissionSetPatch.removePermission(permission); + break; + + // Unsupported patch operation + default: + throw new GuacamoleClientException("Unsupported patch operation: \"" + operation + "\""); + + } + + } + + /** + * Applies a given list of permission patches. Each patch specifies either + * an "add" or a "remove" operation for a permission type, represented by + * a string. Valid permission types depend on the path of each patch + * operation, as the path dictates the permission being modified, such as + * "/connectionPermissions/42" or "/systemPermissions". + * + * @param patches + * The permission patches to apply for this request. + * + * @throws GuacamoleException + * If a problem is encountered while modifying permissions. + */ + @PATCH + public void patchPermissions(List> patches) + throws GuacamoleException { + + // Permission patches for all types of permissions + 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) { + + String path = patch.getPath(); + + // Create connection permission if path has connection prefix + if (path.startsWith(CONNECTION_PERMISSION_PATCH_PATH_PREFIX)) { + + // Get identifier and type from patch operation + String identifier = path.substring(CONNECTION_PERMISSION_PATCH_PATH_PREFIX.length()); + ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue()); + + // Create and update corresponding permission + ObjectPermission permission = new ObjectPermission(type, identifier); + updatePermissionSet(patch.getOp(), connectionPermissionPatch, permission); + + } + + // Create connection group permission if path has connection group prefix + else if (path.startsWith(CONNECTION_GROUP_PERMISSION_PATCH_PATH_PREFIX)) { + + // Get identifier and type from patch operation + String identifier = path.substring(CONNECTION_GROUP_PERMISSION_PATCH_PATH_PREFIX.length()); + ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue()); + + // Create and update corresponding permission + ObjectPermission permission = new ObjectPermission(type, identifier); + updatePermissionSet(patch.getOp(), connectionGroupPermissionPatch, permission); + + } + + // 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)) { + + // Get identifier and type from patch operation + String identifier = path.substring(USER_PERMISSION_PATCH_PATH_PREFIX.length()); + ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue()); + + // Create and update corresponding permission + ObjectPermission permission = new ObjectPermission(type, identifier); + updatePermissionSet(patch.getOp(), userPermissionPatch, permission); + + } + + // Create system permission if path is system path + else if (path.equals(SYSTEM_PERMISSION_PATCH_PATH)) { + + // Get identifier and type from patch operation + SystemPermission.Type type = SystemPermission.Type.valueOf(patch.getValue()); + + // Create and update corresponding permission + SystemPermission permission = new SystemPermission(type); + updatePermissionSet(patch.getOp(), systemPermissionPatch, permission); + + } + + // Otherwise, the path is not supported + else + throw new GuacamoleClientException("Unsupported patch path: \"" + path + "\""); + + } // end for each patch operation + + // 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/java/org/apache/guacamole/rest/schema/SchemaRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/schema/SchemaRESTService.java deleted file mode 100644 index a5d9d909b..000000000 --- a/guacamole/src/main/java/org/apache/guacamole/rest/schema/SchemaRESTService.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.guacamole.rest.schema; - -import com.google.inject.Inject; -import java.util.Collection; -import java.util.Map; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.environment.Environment; -import org.apache.guacamole.environment.LocalEnvironment; -import org.apache.guacamole.form.Form; -import org.apache.guacamole.net.auth.UserContext; -import org.apache.guacamole.GuacamoleSession; -import org.apache.guacamole.rest.ObjectRetrievalService; -import org.apache.guacamole.rest.auth.AuthenticationService; -import org.apache.guacamole.protocols.ProtocolInfo; - -/** - * A REST service which provides access to descriptions of the properties, - * attributes, etc. of objects used within the Guacamole REST API. - * - * @author Michael Jumper - */ -@Path("/schema/{dataSource}") -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -public class SchemaRESTService { - - /** - * A service for authenticating users from auth tokens. - */ - @Inject - private AuthenticationService authenticationService; - - /** - * Service for convenient retrieval of objects. - */ - @Inject - private ObjectRetrievalService retrievalService; - - /** - * Retrieves the possible attributes of a user object. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext dictating the available user attributes. - * - * @return - * A collection of forms which describe the possible attributes of a - * user object. - * - * @throws GuacamoleException - * If an error occurs while retrieving the possible attributes. - */ - @GET - @Path("/users/attributes") - public Collection
getUserAttributes(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier) - throws GuacamoleException { - - // Retrieve all possible user attributes - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - return userContext.getUserAttributes(); - - } - - /** - * Retrieves the possible attributes of a connection object. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext dictating the available connection attributes. - * - * @return - * A collection of forms which describe the possible attributes of a - * connection object. - * - * @throws GuacamoleException - * If an error occurs while retrieving the possible attributes. - */ - @GET - @Path("/connections/attributes") - public Collection getConnectionAttributes(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier) - throws GuacamoleException { - - // Retrieve all possible connection attributes - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - return userContext.getConnectionAttributes(); - - } - - /** - * Retrieves the possible attributes of a connection group object. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext dictating the available connection group - * attributes. - * - * @return - * A collection of forms which describe the possible attributes of a - * connection group object. - * - * @throws GuacamoleException - * If an error occurs while retrieving the possible attributes. - */ - @GET - @Path("/connectionGroups/attributes") - public Collection getConnectionGroupAttributes(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier) - throws GuacamoleException { - - // Retrieve all possible connection group attributes - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - return userContext.getConnectionGroupAttributes(); - - } - - /** - * Gets a map of protocols defined in the system - protocol name to protocol. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext dictating the protocols available. Currently, the - * UserContext actually does not dictate this, the the same set of - * protocols will be retrieved for all users, though the identifier - * given here will be validated. - * - * @return - * A map of protocol information, where each key is the unique name - * associated with that protocol. - * - * @throws GuacamoleException - * If an error occurs while retrieving the available protocols. - */ - @GET - @Path("/protocols") - public Map getProtocols(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier) - throws GuacamoleException { - - // Verify the given auth token and auth provider identifier are valid - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - retrievalService.retrieveUserContext(session, authProviderIdentifier); - - // Get and return a map of all protocols. - Environment env = new LocalEnvironment(); - return env.getProtocols(); - - } - -} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/schema/SchemaResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/schema/SchemaResource.java new file mode 100644 index 000000000..f0fcc8694 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/schema/SchemaResource.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.schema; + +import java.util.Collection; +import java.util.Map; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.environment.Environment; +import org.apache.guacamole.environment.LocalEnvironment; +import org.apache.guacamole.form.Form; +import org.apache.guacamole.net.auth.UserContext; +import org.apache.guacamole.protocols.ProtocolInfo; + +/** + * A REST resource which provides access to descriptions of the properties, + * attributes, etc. of objects within a particular UserContext. + * + * @author Michael Jumper + */ +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class SchemaResource { + + /** + * The UserContext whose schema is exposed by this SchemaResource. + */ + private final UserContext userContext; + + /** + * Creates a new SchemaResource which exposes meta information describing + * the kind of data within the given UserContext. + * + * @param userContext + * The UserContext whose schema should be exposed by this + * SchemaResource. + */ + public SchemaResource(UserContext userContext) { + this.userContext = userContext; + } + + /** + * Retrieves the possible attributes of a user object. + * + * @return + * A collection of forms which describe the possible attributes of a + * user object. + * + * @throws GuacamoleException + * If an error occurs while retrieving the possible attributes. + */ + @GET + @Path("userAttributes") + public Collection getUserAttributes() throws GuacamoleException { + + // Retrieve all possible user attributes + return userContext.getUserAttributes(); + + } + + /** + * Retrieves the possible attributes of a connection object. + * + * @return + * A collection of forms which describe the possible attributes of a + * connection object. + * + * @throws GuacamoleException + * If an error occurs while retrieving the possible attributes. + */ + @GET + @Path("connectionAttributes") + public Collection getConnectionAttributes() + throws GuacamoleException { + + // Retrieve all possible connection attributes + return userContext.getConnectionAttributes(); + + } + + /** + * Retrieves the possible attributes of a connection group object. + * + * @return + * A collection of forms which describe the possible attributes of a + * connection group object. + * + * @throws GuacamoleException + * If an error occurs while retrieving the possible attributes. + */ + @GET + @Path("connectionGroupAttributes") + public Collection getConnectionGroupAttributes() + throws GuacamoleException { + + // Retrieve all possible connection group attributes + return userContext.getConnectionGroupAttributes(); + + } + + /** + * Gets a map of protocols defined in the system - protocol name to protocol. + * + * @return + * A map of protocol information, where each key is the unique name + * associated with that protocol. + * + * @throws GuacamoleException + * If an error occurs while retrieving the available protocols. + */ + @GET + @Path("protocols") + public Map getProtocols() throws GuacamoleException { + + // Get and return a map of all protocols. + Environment env = new LocalEnvironment(); + return env.getProtocols(); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionRESTService.java new file mode 100644 index 000000000..e8c4d7509 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionRESTService.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.session; + +import com.google.inject.Inject; +import javax.ws.rs.Consumes; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleSession; +import org.apache.guacamole.rest.auth.AuthenticationService; + +/** + * A REST service which exposes all data associated with Guacamole users' + * sessions. + * + * @author Michael Jumper + */ +@Path("/session") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class SessionRESTService { + + /** + * A service for authenticating users from auth tokens. + */ + @Inject + private AuthenticationService authenticationService; + + /** + * Factory for creating SessionResources which expose a given + * GuacamoleSession. + */ + @Inject + private SessionResourceFactory sessionResourceFactory; + + /** + * Retrieves a resource representing the GuacamoleSession associated with + * the given authentication token. + * + * @param authToken + * The authentication token that is used to authenticate the user + * having the session being exposed. + * + * @return + * A resource representing the GuacamoleSession associated with the + * given authentication token. + * + * @throws GuacamoleException + * If the authentication token is invalid. + */ + @Path("/") + public SessionResource getSessionResource(@QueryParam("token") String authToken) + throws GuacamoleException { + + // Return a resource exposing the retrieved session + GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); + return sessionResourceFactory.create(session); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionResource.java new file mode 100644 index 000000000..7c7f14f80 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionResource.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.session; + +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; +import com.google.inject.assistedinject.AssistedInject; +import javax.ws.rs.Consumes; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleSession; +import org.apache.guacamole.net.auth.UserContext; +import org.apache.guacamole.rest.tunnel.TunnelCollectionResource; + +/** + * A REST resource which exposes all data associated with a Guacamole user's + * session via the underlying UserContexts. + * + * @author Michael Jumper + */ +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class SessionResource { + + /** + * The GuacamoleSession being exposed by this SessionResource. + */ + private final GuacamoleSession session; + + /** + * Factory for creating UserContextResources which expose a given + * UserContext. + */ + @Inject + private UserContextResourceFactory userContextResourceFactory; + + /** + * Creates a new SessionResource which exposes the data within the given + * GuacamoleSession. + * + * @param session + * The GuacamoleSession which should be exposed through this + * SessionResource. + */ + @AssistedInject + public SessionResource(@Assisted GuacamoleSession session) { + this.session = session; + } + + /** + * Retrieves a resource representing the UserContext associated with the + * AuthenticationProvider having the given identifier. + * + * @param authProviderIdentifier + * The unique identifier of the AuthenticationProvider associated with + * the UserContext being retrieved. + * + * @return + * A resource representing the UserContext associated with the + * AuthenticationProvider having the given identifier. + * + * @throws GuacamoleException + * If the AuthenticationProvider identifier is invalid. + */ + @Path("data/{dataSource}") + public UserContextResource getUserContextResource( + @PathParam("dataSource") String authProviderIdentifier) + throws GuacamoleException { + + // Pull UserContext defined by the given auth provider identifier + UserContext userContext = session.getUserContext(authProviderIdentifier); + + // Return a resource exposing the retrieved UserContext + return userContextResourceFactory.create(userContext); + + } + + /** + * Retrieves a resource representing all tunnels associated with session + * exposed by this SessionResource. + * + * @return + * A resource representing all tunnels associated with the + * AuthenticationProvider having the given identifier. + */ + @Path("tunnels") + public TunnelCollectionResource getTunnelCollectionResource() { + return new TunnelCollectionResource(session); + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionResourceFactory.java b/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionResourceFactory.java new file mode 100644 index 000000000..eaa839451 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/session/SessionResourceFactory.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.session; + +import org.apache.guacamole.GuacamoleSession; + +/** + * Factory which creates resources that expose the contents of a given + * GuacamoleSession. + * + * @author Michael Jumper + */ +public interface SessionResourceFactory { + + /** + * Creates a new SessionResource which exposes the contents of the + * given GuacamoleSession. + * + * @param session + * The GuacamoleSession whose contents should be exposed. + * + * @return + * A new SessionResource which exposes the contents of the given + * GuacamoleSession. + */ + SessionResource create(GuacamoleSession session); + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java new file mode 100644 index 000000000..e0c88555e --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java @@ -0,0 +1,202 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.session; + +import org.apache.guacamole.rest.directory.DirectoryResource; +import org.apache.guacamole.rest.directory.DirectoryResourceFactory; +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; +import com.google.inject.assistedinject.AssistedInject; +import javax.ws.rs.Consumes; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.ActiveConnection; +import org.apache.guacamole.net.auth.Connection; +import org.apache.guacamole.net.auth.ConnectionGroup; +import org.apache.guacamole.net.auth.User; +import org.apache.guacamole.net.auth.UserContext; +import org.apache.guacamole.rest.activeconnection.APIActiveConnection; +import org.apache.guacamole.rest.connection.APIConnection; +import org.apache.guacamole.rest.connectiongroup.APIConnectionGroup; +import org.apache.guacamole.rest.history.HistoryResource; +import org.apache.guacamole.rest.schema.SchemaResource; +import org.apache.guacamole.rest.user.APIUser; + +/** + * A REST resource which exposes the contents of a particular UserContext. + * + * @author Michael Jumper + */ +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class UserContextResource { + + /** + * The UserContext being exposed through this resource. + */ + private final UserContext userContext; + + /** + * Factory for creating DirectoryResources which expose a given + * ActiveConnection Directory. + */ + @Inject + private DirectoryResourceFactory + activeConnectionDirectoryResourceFactory; + + /** + * Factory for creating DirectoryResources which expose a given + * Connection Directory. + */ + @Inject + private DirectoryResourceFactory + connectionDirectoryResourceFactory; + + /** + * Factory for creating DirectoryResources which expose a given + * ConnectionGroup Directory. + */ + @Inject + private DirectoryResourceFactory + connectionGroupDirectoryResourceFactory; + + /** + * Factory for creating DirectoryResources which expose a given + * User Directory. + */ + @Inject + private DirectoryResourceFactory userDirectoryResourceFactory; + + /** + * Creates a new UserContextResource which exposes the data within the + * given UserContext. + * + * @param userContext + * The UserContext which should be exposed through this + * UserContextResource. + */ + @AssistedInject + public UserContextResource(@Assisted UserContext userContext) { + this.userContext = userContext; + } + + /** + * Returns a new resource which represents the ActiveConnection Directory + * contained within the UserContext exposed by this UserContextResource. + * + * @return + * A new resource which represents the ActiveConnection Directory + * contained within the UserContext exposed by this UserContextResource. + * + * @throws GuacamoleException + * If an error occurs while retrieving the ActiveConnection Directory. + */ + @Path("activeConnections") + public DirectoryResource + getActiveConnectionDirectoryResource() throws GuacamoleException { + return activeConnectionDirectoryResourceFactory.create(userContext, + userContext.getActiveConnectionDirectory()); + } + + /** + * Returns a new resource which represents the Connection Directory + * contained within the UserContext exposed by this UserContextResource. + * + * @return + * A new resource which represents the Connection Directory contained + * within the UserContext exposed by this UserContextResource. + * + * @throws GuacamoleException + * If an error occurs while retrieving the Connection Directory. + */ + @Path("connections") + public DirectoryResource getConnectionDirectoryResource() + throws GuacamoleException { + return connectionDirectoryResourceFactory.create(userContext, + userContext.getConnectionDirectory()); + } + + /** + * Returns a new resource which represents the ConnectionGroup Directory + * contained within the UserContext exposed by this UserContextResource. + * + * @return + * A new resource which represents the ConnectionGroup Directory + * contained within the UserContext exposed by this UserContextResource. + * + * @throws GuacamoleException + * If an error occurs while retrieving the ConnectionGroup Directory. + */ + @Path("connectionGroups") + public DirectoryResource getConnectionGroupDirectoryResource() + throws GuacamoleException { + return connectionGroupDirectoryResourceFactory.create(userContext, + userContext.getConnectionGroupDirectory()); + } + + /** + * Returns a new resource which represents the User Directory contained + * within the UserContext exposed by this UserContextResource. + * + * @return + * A new resource which represents the User Directory contained within + * the UserContext exposed by this UserContextResource. + * + * @throws GuacamoleException + * If an error occurs while retrieving the User Directory. + */ + @Path("users") + public DirectoryResource getUserDirectoryResource() + throws GuacamoleException { + return userDirectoryResourceFactory.create(userContext, + userContext.getUserDirectory()); + } + + /** + * Returns a new resource which represents historical data contained + * within the UserContext exposed by this UserContextResource. + * + * @return + * A new resource which represents the historical data contained within + * the UserContext exposed by this UserContextResource. + */ + @Path("history") + public HistoryResource getHistoryResource() { + return new HistoryResource(userContext); + } + + /** + * Returns a new resource which represents meta information describing the + * kind of data which within the UserContext exposed by this + * UserContextResource. + * + * @return + * A new resource which represents the meta information describing the + * kind of data within the UserContext exposed by this + * UserContextResource. + */ + @Path("schema") + public SchemaResource getSchemaResource() { + return new SchemaResource(userContext); + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResourceFactory.java b/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResourceFactory.java new file mode 100644 index 000000000..04e60eaa3 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResourceFactory.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.session; + +import org.apache.guacamole.net.auth.UserContext; + +/** + * Factory which creates resources that expose the contents of a given + * UserContext. + * + * @author Michael Jumper + */ +public interface UserContextResourceFactory { + + /** + * Creates a new UserContextResource which exposes the contents of the + * given UserContext. + * + * @param userContext + * The UserContext whose contents should be exposed. + * + * @return + * A new UserContextResource which exposes the contents of the given + * UserContext. + */ + UserContextResource create(UserContext userContext); + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/session/package-info.java b/guacamole/src/main/java/org/apache/guacamole/rest/session/package-info.java new file mode 100644 index 000000000..118ee2ed7 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/session/package-info.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Classes which expose the absolute root of the data available to a Guacamole + * user's session. + */ +package org.apache.guacamole.rest.session; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/StreamResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/StreamResource.java new file mode 100644 index 000000000..60e48ec4b --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/StreamResource.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.tunnel; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.StreamingOutput; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.tunnel.StreamInterceptingTunnel; + +/** + * A REST resource providing access to a Guacamole protocol-level stream + * within a tunnel. + * + * @author Michael Jumper + */ +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class StreamResource { + + /** + * The tunnel whose stream is exposed through this StreamResource. + */ + private final StreamInterceptingTunnel tunnel; + + /** + * The index of the stream being exposed. + */ + private final int streamIndex; + + /** + * The media type of the data within the stream being exposed. + */ + private final String mediaType; + + /** + * Creates a new StreamResource which provides access to the given + * stream. + * + * @param tunnel + * The tunnel whose stream is being exposed. + * + * @param streamIndex + * The index of the stream to expose via this StreamResource. + * + * @param mediaType + * The media type of the data within the stream. + */ + public StreamResource(StreamInterceptingTunnel tunnel, int streamIndex, + String mediaType) { + this.tunnel = tunnel; + this.streamIndex = streamIndex; + this.mediaType = mediaType; + } + + /** + * Intercepts and returns the entire contents the stream represented by + * this StreamResource. + * + * @return + * A response through which the entire contents of the intercepted + * stream will be sent. + */ + @GET + public Response getStreamContents() { + + // Intercept all output + StreamingOutput stream = new StreamingOutput() { + + @Override + public void write(OutputStream output) throws IOException { + try { + tunnel.interceptStream(streamIndex, output); + } + catch (GuacamoleException e) { + throw new IOException(e); + } + } + + }; + + return Response.ok(stream, mediaType).build(); + + } + + /** + * Intercepts the stream represented by this StreamResource, sending the + * contents of the given InputStream over that stream as "blob" + * instructions. + * + * @param data + * An InputStream containing the data to be sent over the intercepted + * stream. + * + * @throws GuacamoleException + * If the intercepted stream closes with an error. + */ + @POST + @Consumes(MediaType.WILDCARD) + public void setStreamContents(InputStream data) throws GuacamoleException { + + // Send input over stream + tunnel.interceptStream(streamIndex, data); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResource.java new file mode 100644 index 000000000..342cc763f --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelCollectionResource.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.tunnel; + +import java.util.Map; +import java.util.Set; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleResourceNotFoundException; +import org.apache.guacamole.GuacamoleSession; +import org.apache.guacamole.tunnel.StreamInterceptingTunnel; + +/** + * A REST resource which exposes the active tunnels of a Guacamole session. + * + * @author Michael Jumper + */ +@Path("/tunnels") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class TunnelCollectionResource { + + /** + * The GuacamoleSession containing the tunnels exposed by this resource. + */ + private final GuacamoleSession session; + + /** + * Creates a new TunnelCollectionResource which exposes the active tunnels + * of the given GuacamoleSession. + * + * @param session + * The GuacamoleSession whose tunnels should be exposed by this + * resource. + */ + public TunnelCollectionResource(GuacamoleSession session) { + this.session = session; + } + + /** + * Returns the UUIDs of all tunnels exposed by this + * TunnelCollectionResource. + * + * @return + * A set containing the UUIDs of all tunnels exposed by this + * TunnelCollectionResource. + */ + @GET + public Set getTunnelUUIDs() { + return session.getTunnels().keySet(); + } + + /** + * Retrieves the tunnel having the given UUID, returning a TunnelResource + * representing that tunnel. If no such tunnel exists, an exception will be + * thrown. + * + * @param tunnelUUID + * The UUID of the tunnel to return via a TunnelResource. + * + * @return + * A TunnelResource which represents the tunnel having the given UUID. + * + * @throws GuacamoleException + * If no such tunnel exists. + */ + @Path("{tunnel}") + public TunnelResource getTunnel(@PathParam("tunnel") String tunnelUUID) + throws GuacamoleException { + + Map tunnels = session.getTunnels(); + + // Pull tunnel with given UUID + final StreamInterceptingTunnel tunnel = tunnels.get(tunnelUUID); + if (tunnel == null) + throw new GuacamoleResourceNotFoundException("No such tunnel."); + + // Return corresponding tunnel resource + return new TunnelResource(tunnel); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelRESTService.java deleted file mode 100644 index 49740d1b2..000000000 --- a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelRESTService.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.guacamole.rest.tunnel; - -import com.google.inject.Inject; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Map; -import java.util.Set; -import javax.ws.rs.Consumes; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.StreamingOutput; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.GuacamoleResourceNotFoundException; -import org.apache.guacamole.GuacamoleSession; -import org.apache.guacamole.rest.auth.AuthenticationService; -import org.apache.guacamole.tunnel.StreamInterceptingTunnel; - -/** - * A REST Service for retrieving and managing the tunnels of active - * connections, including any associated objects. - * - * @author Michael Jumper - */ -@Path("/tunnels") -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -public class TunnelRESTService { - - /** - * The media type to send as the content type of stream contents if no - * other media type is specified. - */ - private static final String DEFAULT_MEDIA_TYPE = "application/octet-stream"; - - /** - * A service for authenticating users from auth tokens. - */ - @Inject - private AuthenticationService authenticationService; - - /** - * Returns the UUIDs of all currently-active tunnels associated with the - * session identified by the given auth token. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @return - * A set containing the UUIDs of all currently-active tunnels. - * - * @throws GuacamoleException - * If the session associated with the given auth token cannot be - * retrieved. - */ - @GET - public Set getTunnelUUIDs(@QueryParam("token") String authToken) - throws GuacamoleException { - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - return session.getTunnels().keySet(); - } - - /** - * Intercepts and returns the entire contents of a specific stream. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param tunnelUUID - * The UUID of the tunnel containing the stream being intercepted. - * - * @param streamIndex - * The index of the stream to intercept. - * - * @param mediaType - * The media type (mimetype) of the data within the stream. - * - * @param filename - * The filename to use for the sake of identifying the data returned. - * - * @return - * A response through which the entire contents of the intercepted - * stream will be sent. - * - * @throws GuacamoleException - * If the session associated with the given auth token cannot be - * retrieved, or if no such tunnel exists. - */ - @GET - @Path("/{tunnel}/streams/{index}/{filename}") - public Response getStreamContents(@QueryParam("token") String authToken, - @PathParam("tunnel") String tunnelUUID, - @PathParam("index") final int streamIndex, - @QueryParam("type") @DefaultValue(DEFAULT_MEDIA_TYPE) String mediaType, - @PathParam("filename") String filename) - throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - Map tunnels = session.getTunnels(); - - // Pull tunnel with given UUID - final StreamInterceptingTunnel tunnel = tunnels.get(tunnelUUID); - if (tunnel == null) - throw new GuacamoleResourceNotFoundException("No such tunnel."); - - // Intercept all output - StreamingOutput stream = new StreamingOutput() { - - @Override - public void write(OutputStream output) throws IOException { - try { - tunnel.interceptStream(streamIndex, output); - } - catch (GuacamoleException e) { - throw new IOException(e); - } - } - - }; - - return Response.ok(stream, mediaType).build(); - - } - - /** - * Intercepts a specific stream, sending the contents of the given - * InputStream over that stream as "blob" instructions. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param tunnelUUID - * The UUID of the tunnel containing the stream being intercepted. - * - * @param streamIndex - * The index of the stream to intercept. - * - * @param filename - * The filename to use for the sake of identifying the data being sent. - * - * @param data - * An InputStream containing the data to be sent over the intercepted - * stream. - * - * @throws GuacamoleException - * If the session associated with the given auth token cannot be - * retrieved, if no such tunnel exists, or if the intercepted stream - * itself closes with an error. - */ - @POST - @Consumes(MediaType.WILDCARD) - @Path("/{tunnel}/streams/{index}/{filename}") - public void setStreamContents(@QueryParam("token") String authToken, - @PathParam("tunnel") String tunnelUUID, - @PathParam("index") final int streamIndex, - @PathParam("filename") String filename, - InputStream data) - throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - Map tunnels = session.getTunnels(); - - // Pull tunnel with given UUID - final StreamInterceptingTunnel tunnel = tunnels.get(tunnelUUID); - if (tunnel == null) - throw new GuacamoleResourceNotFoundException("No such tunnel."); - - // Send input over stream - tunnel.interceptStream(streamIndex, data); - - } - -} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java new file mode 100644 index 000000000..3bfcae200 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/TunnelResource.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.tunnel; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.tunnel.StreamInterceptingTunnel; + +/** + * A REST resource which abstracts the operations available for an individual + * tunnel. + * + * @author Michael Jumper + */ +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class TunnelResource { + + /** + * The media type to send as the content type of stream contents if no + * other media type is specified. + */ + private static final String DEFAULT_MEDIA_TYPE = "application/octet-stream"; + + /** + * The tunnel that this TunnelResource represents. + */ + private final StreamInterceptingTunnel tunnel; + + /** + * Creates a new TunnelResource which exposes the operations and + * subresources available for the given tunnel. + * + * @param tunnel + * The tunnel that this TunnelResource should represent. + */ + public TunnelResource(StreamInterceptingTunnel tunnel) { + this.tunnel = tunnel; + } + + /** + * Intercepts and returns the entire contents of a specific stream. + * + * @param streamIndex + * The index of the stream to intercept. + * + * @param mediaType + * The media type (mimetype) of the data within the stream. + * + * @param filename + * The filename to use for the sake of identifying the data returned. + * + * @return + * A response through which the entire contents of the intercepted + * stream will be sent. + * + * @throws GuacamoleException + * If the session associated with the given auth token cannot be + * retrieved, or if no such tunnel exists. + */ + @Path("streams/{index}/{filename}") + public StreamResource getStream(@PathParam("index") final int streamIndex, + @QueryParam("type") @DefaultValue(DEFAULT_MEDIA_TYPE) String mediaType, + @PathParam("filename") String filename) + throws GuacamoleException { + + return new StreamResource(tunnel, streamIndex, mediaType); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/package-info.java b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/package-info.java new file mode 100644 index 000000000..d4f07a7dc --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/tunnel/package-info.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Classes related to the manipulation of Guacamole tunnels and underlying + * streams. + */ +package org.apache.guacamole.rest.tunnel; diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/user/UserDirectoryResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/user/UserDirectoryResource.java new file mode 100644 index 000000000..04218f1b9 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/user/UserDirectoryResource.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.user; + +import com.google.inject.assistedinject.Assisted; +import com.google.inject.assistedinject.AssistedInject; +import java.util.UUID; +import javax.ws.rs.Consumes; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.User; +import org.apache.guacamole.net.auth.Directory; +import org.apache.guacamole.net.auth.UserContext; +import org.apache.guacamole.rest.directory.DirectoryObjectResource; +import org.apache.guacamole.rest.directory.DirectoryObjectResourceFactory; +import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; +import org.apache.guacamole.rest.directory.DirectoryResource; + +/** + * A REST resource which abstracts the operations available on a Directory of + * Users. + * + * @author Michael Jumper + */ +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class UserDirectoryResource extends DirectoryResource { + + /** + * The UserContext associated with the Directory which contains the + * User exposed by this resource. + */ + private final UserContext userContext; + + /** + * The Directory exposed by this resource. + */ + private final Directory directory; + + /** + * A factory which can be used to create instances of resources representing + * Users. + */ + private final DirectoryObjectResourceFactory resourceFactory; + + /** + * Creates a new UserDirectoryResource which exposes the operations and + * subresources available for the given User Directory. + * + * @param userContext + * The UserContext associated with the given Directory. + * + * @param directory + * The Directory being exposed. + * + * @param translator + * A DirectoryObjectTranslator implementation which handles + * Users. + * + * @param resourceFactory + * A factory which can be used to create instances of resources + * representing Users. + */ + @AssistedInject + public UserDirectoryResource(@Assisted UserContext userContext, + @Assisted Directory directory, + DirectoryObjectTranslator translator, + DirectoryObjectResourceFactory resourceFactory) { + super(userContext, directory, translator, resourceFactory); + this.userContext = userContext; + this.directory = directory; + this.resourceFactory = resourceFactory; + } + + @Override + public APIUser createObject(APIUser object) throws GuacamoleException { + + // Randomly set the password if it wasn't provided + if (object.getPassword() == null) + object.setPassword(UUID.randomUUID().toString()); + + return super.createObject(object); + + } + + @Override + public DirectoryObjectResource + getObjectResource(String identifier) throws GuacamoleException { + + // If username is own username, just use self - might not have query permissions + if (userContext.self().getIdentifier().equals(identifier)) + return resourceFactory.create(userContext, directory, userContext.self()); + + return super.getObjectResource(identifier); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/user/UserModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/user/UserModule.java new file mode 100644 index 000000000..5fa0774a2 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/user/UserModule.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.user; + +import com.google.inject.AbstractModule; +import org.apache.guacamole.rest.directory.DirectoryObjectResourceFactory; +import org.apache.guacamole.rest.directory.DirectoryResourceFactory; +import com.google.inject.TypeLiteral; +import com.google.inject.assistedinject.FactoryModuleBuilder; +import org.apache.guacamole.net.auth.User; +import org.apache.guacamole.rest.directory.DirectoryObjectResource; +import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; +import org.apache.guacamole.rest.directory.DirectoryResource; + +/** + * Guice Module which configures injections required for handling User resources + * via the REST API. + * + * @author Michael Jumper + */ +public class UserModule extends AbstractModule { + + @Override + protected void configure() { + + // Create the required DirectoryResourceFactory implementation + install(new FactoryModuleBuilder() + .implement( + new TypeLiteral>() {}, + UserDirectoryResource.class + ) + .build(new TypeLiteral>() {})); + + // Create the required DirectoryObjectResourceFactory implementation + install(new FactoryModuleBuilder() + .implement( + new TypeLiteral>() {}, + UserResource.class + ) + .build(new TypeLiteral>() {})); + + // Bind translator for converting between User and APIUser + bind(new TypeLiteral>() {}) + .to(UserObjectTranslator.class); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/user/UserObjectTranslator.java b/guacamole/src/main/java/org/apache/guacamole/rest/user/UserObjectTranslator.java new file mode 100644 index 000000000..5098c35fc --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/user/UserObjectTranslator.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.user; + +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.User; +import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; + +/** + * Translator which converts between User objects and APIUser objects. + * + * @author Michael Jumper + */ +public class UserObjectTranslator + implements DirectoryObjectTranslator { + + @Override + public APIUser toExternalObject(User object) + throws GuacamoleException { + return new APIUser(object); + } + + @Override + public User toInternalObject(APIUser object) + throws GuacamoleException { + return new APIUserWrapper(object); + } + + @Override + public void applyExternalChanges(User existingObject, + APIUser object) throws GuacamoleException { + + // Do not update the user password if no password was provided + if (object.getPassword() != null) + existingObject.setPassword(object.getPassword()); + + // Update user attributes + existingObject.setAttributes(object.getAttributes()); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/user/UserRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/user/UserRESTService.java deleted file mode 100644 index c446ddf98..000000000 --- a/guacamole/src/main/java/org/apache/guacamole/rest/user/UserRESTService.java +++ /dev/null @@ -1,644 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.guacamole.rest.user; - -import com.google.inject.Inject; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.UUID; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import org.apache.guacamole.GuacamoleClientException; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.GuacamoleResourceNotFoundException; -import org.apache.guacamole.GuacamoleSecurityException; -import org.apache.guacamole.net.auth.AuthenticationProvider; -import org.apache.guacamole.net.auth.Credentials; -import org.apache.guacamole.net.auth.Directory; -import org.apache.guacamole.net.auth.User; -import org.apache.guacamole.net.auth.UserContext; -import org.apache.guacamole.net.auth.credentials.GuacamoleCredentialsException; -import org.apache.guacamole.net.auth.permission.ObjectPermission; -import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; -import org.apache.guacamole.net.auth.permission.Permission; -import org.apache.guacamole.net.auth.permission.SystemPermission; -import org.apache.guacamole.net.auth.permission.SystemPermissionSet; -import org.apache.guacamole.GuacamoleSession; -import org.apache.guacamole.rest.APIPatch; -import static org.apache.guacamole.rest.APIPatch.Operation.add; -import static org.apache.guacamole.rest.APIPatch.Operation.remove; -import org.apache.guacamole.rest.ObjectRetrievalService; -import org.apache.guacamole.rest.PATCH; -import org.apache.guacamole.rest.auth.AuthenticationService; -import org.apache.guacamole.rest.permission.APIPermissionSet; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * A REST Service for handling user CRUD operations. - * - * @author James Muehlner - * @author Michael Jumper - */ -@Path("/data/{dataSource}/users") -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -public class UserRESTService { - - /** - * Logger for this class. - */ - private static final Logger logger = LoggerFactory.getLogger(UserRESTService.class); - - /** - * The prefix of any path within an operation of a JSON patch which - * modifies the permissions of a user regarding a specific connection. - */ - private static final String CONNECTION_PERMISSION_PATCH_PATH_PREFIX = "/connectionPermissions/"; - - /** - * The prefix of any path within an operation of a JSON patch which - * modifies the permissions of a user regarding a specific connection group. - */ - private static final String CONNECTION_GROUP_PERMISSION_PATCH_PATH_PREFIX = "/connectionGroupPermissions/"; - - /** - * The prefix of any path within an operation of a JSON patch which - * modifies the permissions of a user regarding 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. - */ - private static final String USER_PERMISSION_PATCH_PATH_PREFIX = "/userPermissions/"; - - /** - * The path of any operation within a JSON patch which modifies the - * permissions of a user regarding the entire system. - */ - private static final String SYSTEM_PERMISSION_PATCH_PATH = "/systemPermissions"; - - /** - * A service for authenticating users from auth tokens. - */ - @Inject - private AuthenticationService authenticationService; - - /** - * Service for convenient retrieval of objects. - */ - @Inject - private ObjectRetrievalService retrievalService; - - /** - * Gets a list of users in the given data source (UserContext), filtering - * the returned list by the given permission, if specified. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext from which the users are to be retrieved. - * - * @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 list of all visible users. If a permission was specified, this - * list will contain only those users for whom the current user has - * that permission. - * - * @throws GuacamoleException - * If an error is encountered while retrieving users. - */ - @GET - public List getUsers(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - @QueryParam("permission") List permissions) - throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - - // An admin user has access to any user - User self = userContext.self(); - SystemPermissionSet systemPermissions = self.getSystemPermissions(); - boolean isAdmin = systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER); - - // Get the directory - Directory userDirectory = userContext.getUserDirectory(); - - // Filter users, if requested - Collection userIdentifiers = userDirectory.getIdentifiers(); - if (!isAdmin && permissions != null && !permissions.isEmpty()) { - ObjectPermissionSet userPermissions = self.getUserPermissions(); - userIdentifiers = userPermissions.getAccessibleObjects(permissions, userIdentifiers); - } - - // Retrieve all users, converting to API users - List apiUsers = new ArrayList(); - for (User user : userDirectory.getAll(userIdentifiers)) - apiUsers.add(new APIUser(user)); - - return apiUsers; - - } - - /** - * Retrieves an individual user. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext from which the requested user is to be retrieved. - * - * @param username - * The username of the user to retrieve. - * - * @return user - * The user having the given username. - * - * @throws GuacamoleException - * If an error occurs while retrieving the user. - */ - @GET - @Path("/{username}") - public APIUser getUser(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - @PathParam("username") String username) - throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - - // Retrieve the requested user - User user = retrievalService.retrieveUser(session, authProviderIdentifier, username); - return new APIUser(user); - - } - - /** - * Creates a new user and returns the user that was created. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext in which the requested user is to be created. - * - * @param user - * The new user to create. - * - * @throws GuacamoleException - * If a problem is encountered while creating the user. - * - * @return - * The newly created user. - */ - @POST - public APIUser createUser(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, APIUser user) - throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - - // Get the directory - Directory userDirectory = userContext.getUserDirectory(); - - // Randomly set the password if it wasn't provided - if (user.getPassword() == null) - user.setPassword(UUID.randomUUID().toString()); - - // Create the user - userDirectory.add(new APIUserWrapper(user)); - - return user; - - } - - /** - * Updates an individual existing user. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext in which the requested user is to be updated. - * - * @param username - * The username of the user to update. - * - * @param user - * The data to update the user with. - * - * @throws GuacamoleException - * If an error occurs while updating the user. - */ - @PUT - @Path("/{username}") - public void updateUser(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - @PathParam("username") String username, APIUser user) - throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - - // Get the directory - Directory userDirectory = userContext.getUserDirectory(); - - // Validate data and path are sane - if (!user.getUsername().equals(username)) - throw new GuacamoleClientException("Username in path does not match username provided JSON data."); - - // A user may not use this endpoint to modify himself - if (userContext.self().getIdentifier().equals(user.getUsername())) - throw new GuacamoleSecurityException("Permission denied."); - - // Get the user - User existingUser = retrievalService.retrieveUser(userContext, username); - - // Do not update the user password if no password was provided - if (user.getPassword() != null) - existingUser.setPassword(user.getPassword()); - - // Update user attributes - existingUser.setAttributes(user.getAttributes()); - - // Update the user - userDirectory.update(existingUser); - - } - - /** - * Updates the password for an individual existing user. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext in which the requested user is to be updated. - * - * @param username - * The username of the user to update. - * - * @param userPasswordUpdate - * The object containing the old password for the user, as well as the - * new password to set for that user. - * - * @param request - * The HttpServletRequest associated with the password update attempt. - * - * @throws GuacamoleException - * If an error occurs while updating the user's password. - */ - @PUT - @Path("/{username}/password") - public void updatePassword(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - @PathParam("username") String username, - APIUserPasswordUpdate userPasswordUpdate, - @Context HttpServletRequest request) throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - - // Build credentials - Credentials credentials = new Credentials(); - credentials.setUsername(username); - credentials.setPassword(userPasswordUpdate.getOldPassword()); - credentials.setRequest(request); - credentials.setSession(request.getSession(true)); - - // Verify that the old password was correct - try { - AuthenticationProvider authProvider = userContext.getAuthenticationProvider(); - if (authProvider.authenticateUser(credentials) == null) - throw new GuacamoleSecurityException("Permission denied."); - } - - // Pass through any credentials exceptions as simple permission denied - catch (GuacamoleCredentialsException e) { - throw new GuacamoleSecurityException("Permission denied."); - } - - // Get the user directory - Directory userDirectory = userContext.getUserDirectory(); - - // Get the user that we want to updates - User user = retrievalService.retrieveUser(userContext, username); - - // Set password to the newly provided one - user.setPassword(userPasswordUpdate.getNewPassword()); - - // Update the user - userDirectory.update(user); - - } - - /** - * Deletes an individual existing user. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext from which the requested user is to be deleted. - * - * @param username - * The username of the user to delete. - * - * @throws GuacamoleException - * If an error occurs while deleting the user. - */ - @DELETE - @Path("/{username}") - public void deleteUser(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - @PathParam("username") String username) - throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - - // Get the directory - Directory userDirectory = userContext.getUserDirectory(); - - // Get the user - User existingUser = userDirectory.get(username); - if (existingUser == null) - throw new GuacamoleResourceNotFoundException("No such user: \"" + username + "\""); - - // Delete the user - userDirectory.remove(username); - - } - - /** - * Gets a list of permissions for the user with the given username. - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext in which the requested user is to be found. - * - * @param username - * The username of the user to retrieve permissions for. - * - * @return - * A list of all permissions granted to the specified user. - * - * @throws GuacamoleException - * If an error occurs while retrieving permissions. - */ - @GET - @Path("/{username}/permissions") - public APIPermissionSet getPermissions(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - @PathParam("username") String username) - throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - - User user; - - // If username is own username, just use self - might not have query permissions - if (userContext.self().getIdentifier().equals(username)) - user = userContext.self(); - - // If not self, query corresponding user from directory - else { - user = userContext.getUserDirectory().get(username); - if (user == null) - throw new GuacamoleResourceNotFoundException("No such user: \"" + username + "\""); - } - - return new APIPermissionSet(user); - - } - - /** - * Updates the given permission set patch by queuing an add or remove - * operation for the given permission based on the given patch operation. - * - * @param - * The type of permission stored within the permission set. - * - * @param operation - * The patch operation to perform. - * - * @param permissionSetPatch - * The permission set patch being modified. - * - * @param permission - * The permission being added or removed from the set. - * - * @throws GuacamoleException - * If the requested patch operation is not supported. - */ - private void updatePermissionSet( - APIPatch.Operation operation, - PermissionSetPatch permissionSetPatch, - PermissionType permission) throws GuacamoleException { - - // Add or remove permission based on operation - switch (operation) { - - // Add permission - case add: - permissionSetPatch.addPermission(permission); - break; - - // Remove permission - case remove: - permissionSetPatch.removePermission(permission); - break; - - // Unsupported patch operation - default: - throw new GuacamoleClientException("Unsupported patch operation: \"" + operation + "\""); - - } - - } - - /** - * Applies a given list of permission patches. Each patch specifies either - * an "add" or a "remove" operation for a permission type, represented by - * a string. Valid permission types depend on the path of each patch - * operation, as the path dictates the permission being modified, such as - * "/connectionPermissions/42" or "/systemPermissions". - * - * @param authToken - * The authentication token that is used to authenticate the user - * performing the operation. - * - * @param authProviderIdentifier - * The unique identifier of the AuthenticationProvider associated with - * the UserContext in which the requested user is to be found. - * - * @param username - * The username of the user to modify the permissions of. - * - * @param patches - * The permission patches to apply for this request. - * - * @throws GuacamoleException - * If a problem is encountered while modifying permissions. - */ - @PATCH - @Path("/{username}/permissions") - public void patchPermissions(@QueryParam("token") String authToken, - @PathParam("dataSource") String authProviderIdentifier, - @PathParam("username") String username, - List> patches) throws GuacamoleException { - - GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); - - // Get the user - User user = userContext.getUserDirectory().get(username); - if (user == null) - throw new GuacamoleResourceNotFoundException("No such user: \"" + username + "\""); - - // Permission patches for all types of permissions - PermissionSetPatch connectionPermissionPatch = new PermissionSetPatch(); - PermissionSetPatch connectionGroupPermissionPatch = new PermissionSetPatch(); - PermissionSetPatch activeConnectionPermissionPatch = new PermissionSetPatch(); - PermissionSetPatch userPermissionPatch = new PermissionSetPatch(); - PermissionSetPatch systemPermissionPatch = new PermissionSetPatch(); - - // Apply all patch operations individually - for (APIPatch patch : patches) { - - String path = patch.getPath(); - - // Create connection permission if path has connection prefix - if (path.startsWith(CONNECTION_PERMISSION_PATCH_PATH_PREFIX)) { - - // Get identifier and type from patch operation - String identifier = path.substring(CONNECTION_PERMISSION_PATCH_PATH_PREFIX.length()); - ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue()); - - // Create and update corresponding permission - ObjectPermission permission = new ObjectPermission(type, identifier); - updatePermissionSet(patch.getOp(), connectionPermissionPatch, permission); - - } - - // Create connection group permission if path has connection group prefix - else if (path.startsWith(CONNECTION_GROUP_PERMISSION_PATCH_PATH_PREFIX)) { - - // Get identifier and type from patch operation - String identifier = path.substring(CONNECTION_GROUP_PERMISSION_PATCH_PATH_PREFIX.length()); - ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue()); - - // Create and update corresponding permission - ObjectPermission permission = new ObjectPermission(type, identifier); - updatePermissionSet(patch.getOp(), connectionGroupPermissionPatch, permission); - - } - - // 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)) { - - // Get identifier and type from patch operation - String identifier = path.substring(USER_PERMISSION_PATCH_PATH_PREFIX.length()); - ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue()); - - // Create and update corresponding permission - ObjectPermission permission = new ObjectPermission(type, identifier); - updatePermissionSet(patch.getOp(), userPermissionPatch, permission); - - } - - // Create system permission if path is system path - else if (path.equals(SYSTEM_PERMISSION_PATCH_PATH)) { - - // Get identifier and type from patch operation - SystemPermission.Type type = SystemPermission.Type.valueOf(patch.getValue()); - - // Create and update corresponding permission - SystemPermission permission = new SystemPermission(type); - updatePermissionSet(patch.getOp(), systemPermissionPatch, permission); - - } - - // Otherwise, the path is not supported - else - throw new GuacamoleClientException("Unsupported patch path: \"" + path + "\""); - - } // end for each patch operation - - // 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/java/org/apache/guacamole/rest/user/UserResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/user/UserResource.java new file mode 100644 index 000000000..7329a0253 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/user/UserResource.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.rest.user; + +import com.google.inject.assistedinject.Assisted; +import com.google.inject.assistedinject.AssistedInject; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleSecurityException; +import org.apache.guacamole.net.auth.AuthenticationProvider; +import org.apache.guacamole.net.auth.Credentials; +import org.apache.guacamole.net.auth.User; +import org.apache.guacamole.net.auth.Directory; +import org.apache.guacamole.net.auth.UserContext; +import org.apache.guacamole.net.auth.credentials.GuacamoleCredentialsException; +import org.apache.guacamole.rest.directory.DirectoryObjectResource; +import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; +import org.apache.guacamole.rest.permission.PermissionSetResource; + +/** + * A REST resource which abstracts the operations available on an existing + * User. + * + * @author Michael Jumper + */ +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class UserResource + extends DirectoryObjectResource { + + /** + * The UserContext associated with the Directory which contains the User + * exposed by this resource. + */ + private final UserContext userContext; + + /** + * The Directory which contains the User object represented by this + * UserResource. + */ + private final Directory directory; + + /** + * The User object represented by this UserResource. + */ + private final User user; + + /** + * Creates a new UserResource which exposes the operations and subresources + * available for the given User. + * + * @param userContext + * The UserContext associated with the given Directory. + * + * @param directory + * The Directory which contains the given User. + * + * @param user + * The User that this UserResource should represent. + * + * @param translator + * A DirectoryObjectTranslator implementation which handles Users. + */ + @AssistedInject + public UserResource(@Assisted UserContext userContext, + @Assisted Directory directory, + @Assisted User user, + DirectoryObjectTranslator translator) { + super(directory, user, translator); + this.userContext = userContext; + this.directory = directory; + this.user = user; + } + + @Override + public void updateObject(APIUser modifiedObject) throws GuacamoleException { + + // A user may not use this endpoint to modify himself + if (userContext.self().getIdentifier().equals(modifiedObject.getUsername())) + throw new GuacamoleSecurityException("Permission denied."); + + super.updateObject(modifiedObject); + + } + + /** + * Updates the password for an individual existing user. + * + * @param userPasswordUpdate + * The object containing the old password for the user, as well as the + * new password to set for that user. + * + * @param request + * The HttpServletRequest associated with the password update attempt. + * + * @throws GuacamoleException + * If an error occurs while updating the user's password. + */ + @PUT + @Path("password") + public void updatePassword(APIUserPasswordUpdate userPasswordUpdate, + @Context HttpServletRequest request) throws GuacamoleException { + + // Build credentials + Credentials credentials = new Credentials(); + credentials.setUsername(user.getIdentifier()); + credentials.setPassword(userPasswordUpdate.getOldPassword()); + credentials.setRequest(request); + credentials.setSession(request.getSession(true)); + + // Verify that the old password was correct + try { + AuthenticationProvider authProvider = userContext.getAuthenticationProvider(); + if (authProvider.authenticateUser(credentials) == null) + throw new GuacamoleSecurityException("Permission denied."); + } + + // Pass through any credentials exceptions as simple permission denied + catch (GuacamoleCredentialsException e) { + throw new GuacamoleSecurityException("Permission denied."); + } + + // Set password to the newly provided one + user.setPassword(userPasswordUpdate.getNewPassword()); + directory.update(user); + + } + + /** + * Returns a resource which abstracts operations available on the overall + * permissions granted to the User represented by this UserResource. + * + * @return + * A resource which representing the permissions granted to the User + * represented by this UserResource. + */ + @Path("permissions") + public PermissionSetResource getPermissions() { + return new PermissionSetResource(user); + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java index 80404723a..95e4bb9ac 100644 --- a/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java +++ b/guacamole/src/main/java/org/apache/guacamole/tunnel/TunnelRequestService.java @@ -31,7 +31,6 @@ import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.ConnectionGroup; import org.apache.guacamole.net.auth.Directory; import org.apache.guacamole.net.auth.UserContext; -import org.apache.guacamole.rest.ObjectRetrievalService; import org.apache.guacamole.rest.auth.AuthenticationService; import org.apache.guacamole.protocol.GuacamoleClientInformation; import org.slf4j.Logger; @@ -61,12 +60,6 @@ public class TunnelRequestService { @Inject private AuthenticationService authenticationService; - /** - * Service for convenient retrieval of objects. - */ - @Inject - private ObjectRetrievalService retrievalService; - /** * Reads and returns the client information provided within the given * request. @@ -327,7 +320,7 @@ public class TunnelRequestService { GuacamoleClientInformation info = getClientInformation(request); GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); - UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); + UserContext userContext = session.getUserContext(authProviderIdentifier); try { diff --git a/guacamole/src/main/webapp/app/rest/services/activeConnectionService.js b/guacamole/src/main/webapp/app/rest/services/activeConnectionService.js index 4ebb131f4..9c967be32 100644 --- a/guacamole/src/main/webapp/app/rest/services/activeConnectionService.js +++ b/guacamole/src/main/webapp/app/rest/services/activeConnectionService.js @@ -60,7 +60,7 @@ angular.module('rest').factory('activeConnectionService', ['$injector', // Retrieve tunnels return $http({ method : 'GET', - url : 'api/data/' + encodeURIComponent(dataSource) + '/activeConnections', + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/activeConnections', params : httpParameters }); @@ -159,7 +159,7 @@ angular.module('rest').factory('activeConnectionService', ['$injector', // Perform active connection deletion via PATCH return $http({ method : 'PATCH', - url : 'api/data/' + encodeURIComponent(dataSource) + '/activeConnections', + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/activeConnections', params : httpParameters, data : activeConnectionPatch }); diff --git a/guacamole/src/main/webapp/app/rest/services/connectionGroupService.js b/guacamole/src/main/webapp/app/rest/services/connectionGroupService.js index 3283102d2..0662e4d6e 100644 --- a/guacamole/src/main/webapp/app/rest/services/connectionGroupService.js +++ b/guacamole/src/main/webapp/app/rest/services/connectionGroupService.js @@ -73,7 +73,7 @@ angular.module('rest').factory('connectionGroupService', ['$injector', return $http({ cache : cacheService.connections, method : 'GET', - url : 'api/data/' + encodeURIComponent(dataSource) + '/connectionGroups/' + encodeURIComponent(connectionGroupID) + '/tree', + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connectionGroups/' + encodeURIComponent(connectionGroupID) + '/tree', params : httpParameters }); @@ -106,7 +106,7 @@ angular.module('rest').factory('connectionGroupService', ['$injector', return $http({ cache : cacheService.connections, method : 'GET', - url : 'api/data/' + encodeURIComponent(dataSource) + '/connectionGroups/' + encodeURIComponent(connectionGroupID), + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connectionGroups/' + encodeURIComponent(connectionGroupID), params : httpParameters }); @@ -136,7 +136,7 @@ angular.module('rest').factory('connectionGroupService', ['$injector', if (!connectionGroup.identifier) { return $http({ method : 'POST', - url : 'api/data/' + encodeURIComponent(dataSource) + '/connectionGroups', + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connectionGroups', params : httpParameters, data : connectionGroup }) @@ -152,7 +152,7 @@ angular.module('rest').factory('connectionGroupService', ['$injector', else { return $http({ method : 'PUT', - url : 'api/data/' + encodeURIComponent(dataSource) + '/connectionGroups/' + encodeURIComponent(connectionGroup.identifier), + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connectionGroups/' + encodeURIComponent(connectionGroup.identifier), params : httpParameters, data : connectionGroup }) @@ -185,7 +185,7 @@ angular.module('rest').factory('connectionGroupService', ['$injector', // Delete connection group return $http({ method : 'DELETE', - url : 'api/data/' + encodeURIComponent(dataSource) + '/connectionGroups/' + encodeURIComponent(connectionGroup.identifier), + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connectionGroups/' + encodeURIComponent(connectionGroup.identifier), params : httpParameters }) diff --git a/guacamole/src/main/webapp/app/rest/services/connectionService.js b/guacamole/src/main/webapp/app/rest/services/connectionService.js index 7629cd141..7b63a7e21 100644 --- a/guacamole/src/main/webapp/app/rest/services/connectionService.js +++ b/guacamole/src/main/webapp/app/rest/services/connectionService.js @@ -56,7 +56,7 @@ angular.module('rest').factory('connectionService', ['$injector', return $http({ cache : cacheService.connections, method : 'GET', - url : 'api/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(id), + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(id), params : httpParameters }); @@ -84,7 +84,7 @@ angular.module('rest').factory('connectionService', ['$injector', // Retrieve connection history return $http({ method : 'GET', - url : 'api/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(id) + '/history', + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(id) + '/history', params : httpParameters }); @@ -113,7 +113,7 @@ angular.module('rest').factory('connectionService', ['$injector', return $http({ cache : cacheService.connections, method : 'GET', - url : 'api/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(id) + '/parameters', + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(id) + '/parameters', params : httpParameters }); @@ -143,7 +143,7 @@ angular.module('rest').factory('connectionService', ['$injector', if (!connection.identifier) { return $http({ method : 'POST', - url : 'api/data/' + encodeURIComponent(dataSource) + '/connections', + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connections', params : httpParameters, data : connection }) @@ -159,7 +159,7 @@ angular.module('rest').factory('connectionService', ['$injector', else { return $http({ method : 'PUT', - url : 'api/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(connection.identifier), + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(connection.identifier), params : httpParameters, data : connection }) @@ -192,7 +192,7 @@ angular.module('rest').factory('connectionService', ['$injector', // Delete connection return $http({ method : 'DELETE', - url : 'api/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(connection.identifier), + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(connection.identifier), params : httpParameters }) diff --git a/guacamole/src/main/webapp/app/rest/services/historyService.js b/guacamole/src/main/webapp/app/rest/services/historyService.js index 717389ddb..c77c4ab80 100644 --- a/guacamole/src/main/webapp/app/rest/services/historyService.js +++ b/guacamole/src/main/webapp/app/rest/services/historyService.js @@ -76,7 +76,7 @@ angular.module('rest').factory('historyService', ['$injector', // Retrieve connection history return $http({ method : 'GET', - url : 'api/data/' + encodeURIComponent(dataSource) + '/history/connections', + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/history/connections', params : httpParameters }); diff --git a/guacamole/src/main/webapp/app/rest/services/permissionService.js b/guacamole/src/main/webapp/app/rest/services/permissionService.js index d398447e7..8a4184526 100644 --- a/guacamole/src/main/webapp/app/rest/services/permissionService.js +++ b/guacamole/src/main/webapp/app/rest/services/permissionService.js @@ -62,7 +62,7 @@ angular.module('rest').factory('permissionService', ['$injector', return $http({ cache : cacheService.users, method : 'GET', - url : 'api/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(userID) + '/permissions', + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(userID) + '/permissions', params : httpParameters }); @@ -235,7 +235,7 @@ angular.module('rest').factory('permissionService', ['$injector', // Patch user permissions return $http({ method : 'PATCH', - url : 'api/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(userID) + '/permissions', + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(userID) + '/permissions', params : httpParameters, data : permissionPatch }) diff --git a/guacamole/src/main/webapp/app/rest/services/schemaService.js b/guacamole/src/main/webapp/app/rest/services/schemaService.js index 39d89e10a..f0db23fe0 100644 --- a/guacamole/src/main/webapp/app/rest/services/schemaService.js +++ b/guacamole/src/main/webapp/app/rest/services/schemaService.js @@ -58,7 +58,7 @@ angular.module('rest').factory('schemaService', ['$injector', return $http({ cache : cacheService.schema, method : 'GET', - url : 'api/schema/' + encodeURIComponent(dataSource) + '/users/attributes', + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/schema/userAttributes', params : httpParameters }); @@ -92,7 +92,7 @@ angular.module('rest').factory('schemaService', ['$injector', return $http({ cache : cacheService.schema, method : 'GET', - url : 'api/schema/' + encodeURIComponent(dataSource) + '/connections/attributes', + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/schema/connectionAttributes', params : httpParameters }); @@ -126,7 +126,7 @@ angular.module('rest').factory('schemaService', ['$injector', return $http({ cache : cacheService.schema, method : 'GET', - url : 'api/schema/' + encodeURIComponent(dataSource) + '/connectionGroups/attributes', + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/schema/connectionGroupAttributes', params : httpParameters }); @@ -157,7 +157,7 @@ angular.module('rest').factory('schemaService', ['$injector', return $http({ cache : cacheService.schema, method : 'GET', - url : 'api/schema/' + encodeURIComponent(dataSource) + '/protocols', + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/schema/protocols', params : httpParameters }); diff --git a/guacamole/src/main/webapp/app/rest/services/tunnelService.js b/guacamole/src/main/webapp/app/rest/services/tunnelService.js index 35c42a896..e93c50fb9 100644 --- a/guacamole/src/main/webapp/app/rest/services/tunnelService.js +++ b/guacamole/src/main/webapp/app/rest/services/tunnelService.js @@ -62,7 +62,7 @@ angular.module('rest').factory('tunnelService', ['$injector', // Retrieve tunnels return $http({ method : 'GET', - url : 'api/tunnels', + url : 'api/session/tunnels', params : httpParameters }); @@ -100,7 +100,7 @@ angular.module('rest').factory('tunnelService', ['$injector', // Build download URL var url = $window.location.origin + $window.location.pathname - + 'api/tunnels/' + encodeURIComponent(tunnel) + + 'api/session/tunnels/' + encodeURIComponent(tunnel) + '/streams/' + encodeURIComponent(stream.index) + '/' + encodeURIComponent(filename) + '?token=' + encodeURIComponent(authenticationService.getCurrentToken()); @@ -163,7 +163,7 @@ angular.module('rest').factory('tunnelService', ['$injector', // Build upload URL var url = $window.location.origin + $window.location.pathname - + 'api/tunnels/' + encodeURIComponent(tunnel) + + 'api/session/tunnels/' + encodeURIComponent(tunnel) + '/streams/' + encodeURIComponent(stream.index) + '/' + encodeURIComponent(file.name) + '?token=' + encodeURIComponent(authenticationService.getCurrentToken()); diff --git a/guacamole/src/main/webapp/app/rest/services/userService.js b/guacamole/src/main/webapp/app/rest/services/userService.js index bd124622b..659aed7b6 100644 --- a/guacamole/src/main/webapp/app/rest/services/userService.js +++ b/guacamole/src/main/webapp/app/rest/services/userService.js @@ -50,9 +50,10 @@ angular.module('rest').factory('userService', ['$injector', * If null, no filtering will be performed. Valid values are listed * within PermissionSet.ObjectType. * - * @returns {Promise.} - * A promise which will resolve with an array of @link{User} objects - * upon success. + * @returns {Promise.>} + * A promise which will resolve with a map of @link{User} objects + * where each key is the identifier (username) of the corresponding + * user. */ service.getUsers = function getUsers(dataSource, permissionTypes) { @@ -69,7 +70,7 @@ angular.module('rest').factory('userService', ['$injector', return $http({ cache : cacheService.users, method : 'GET', - url : 'api/data/' + encodeURIComponent(dataSource) + '/users', + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/users', params : httpParameters }); @@ -102,7 +103,7 @@ angular.module('rest').factory('userService', ['$injector', return $http({ cache : cacheService.users, method : 'GET', - url : 'api/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(username), + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(username), params : httpParameters }); @@ -134,7 +135,7 @@ angular.module('rest').factory('userService', ['$injector', // Delete user return $http({ method : 'DELETE', - url : 'api/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(user.username), + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(user.username), params : httpParameters }) @@ -172,7 +173,7 @@ angular.module('rest').factory('userService', ['$injector', // Create user return $http({ method : 'POST', - url : 'api/data/' + encodeURIComponent(dataSource) + '/users', + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/users', params : httpParameters, data : user }) @@ -210,7 +211,7 @@ angular.module('rest').factory('userService', ['$injector', // Update user return $http({ method : 'PUT', - url : 'api/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(user.username), + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(user.username), params : httpParameters, data : user }) @@ -255,7 +256,7 @@ angular.module('rest').factory('userService', ['$injector', // Update user password return $http({ method : 'PUT', - url : 'api/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(username) + '/password', + url : 'api/session/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(username) + '/password', params : httpParameters, data : new UserPasswordUpdate({ oldPassword : oldPassword, diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js index fe15b1425..cc134fa94 100644 --- a/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js +++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js @@ -227,14 +227,14 @@ angular.module('settings').directive('guacSettingsUsers', [function guacSettings PermissionSet.ObjectPermissionType.DELETE ]); - userPromise.then(function usersReceived(userArrays) { + userPromise.then(function usersReceived(allUsers) { var addedUsers = {}; $scope.manageableUsers = []; // For each user in each data source angular.forEach(dataSources, function addUserList(dataSource) { - angular.forEach(userArrays[dataSource], function addUser(user) { + angular.forEach(allUsers[dataSource], function addUser(user) { // Do not add the same user twice if (addedUsers[user.username])