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 c40224ea9..d0c9bd6f3 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java @@ -27,9 +27,9 @@ 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.activeconnection.ActiveConnectionRESTService; import org.apache.guacamole.rest.auth.AuthTokenGenerator; import org.apache.guacamole.rest.auth.AuthenticationService; import org.apache.guacamole.rest.auth.SecureRandomAuthTokenGenerator; @@ -88,7 +88,6 @@ public class RESTServiceModule extends ServletModule { bind(ObjectRetrievalService.class); // Set up the API endpoints - bind(ActiveConnectionRESTService.class); bind(HistoryRESTService.class); bind(LanguageRESTService.class); bind(PatchRESTService.class); @@ -102,6 +101,7 @@ public class RESTServiceModule extends ServletModule { install(new FactoryModuleBuilder().build(UserContextResourceFactory.class)); // Resources below root + install(new ActiveConnectionModule()); install(new ConnectionModule()); install(new ConnectionGroupModule()); 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/session/UserContextResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java index e5d5cb59b..9091e99cd 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java @@ -29,9 +29,11 @@ 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.UserContext; +import org.apache.guacamole.rest.activeconnection.APIActiveConnection; import org.apache.guacamole.rest.connection.APIConnection; import org.apache.guacamole.rest.connectiongroup.APIConnectionGroup; @@ -49,6 +51,14 @@ public class UserContextResource { */ 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. @@ -78,6 +88,24 @@ public class UserContextResource { 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.