From c0a6bacad5e43833d4b78d60215bbd72210ac0a4 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Wed, 11 Oct 2017 15:55:14 -0400 Subject: [PATCH] GUACAMOLE-38: Implement extension-specific REST endpoint and utilities for parsing connection. --- .../guacamole-auth-quickconnect/pom.xml | 7 + .../quickconnect/QuickConnectDirectory.java | 71 ++++++++ .../quickconnect/QuickConnectUserContext.java | 5 +- .../quickconnect/rest/QuickConnectREST.java | 103 +++++++++++ .../auth/quickconnect/utility/QCParser.java | 169 ++++++++++++++++++ 5 files changed, 353 insertions(+), 2 deletions(-) create mode 100644 extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/rest/QuickConnectREST.java create mode 100644 extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/utility/QCParser.java diff --git a/extensions/guacamole-auth-quickconnect/pom.xml b/extensions/guacamole-auth-quickconnect/pom.xml index 9d2bcfb17..fdf04c75a 100644 --- a/extensions/guacamole-auth-quickconnect/pom.xml +++ b/extensions/guacamole-auth-quickconnect/pom.xml @@ -185,6 +185,13 @@ provided + + + com.sun.jersey + jersey-server + 1.17.1 + + com.google.inject diff --git a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectDirectory.java b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectDirectory.java index c2ca355e4..d56df95ad 100644 --- a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectDirectory.java +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectDirectory.java @@ -21,9 +21,11 @@ package org.apache.guacamole.auth.quickconnect; import java.util.Collection; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.quickconnect.utility.QCParser; import org.apache.guacamole.net.auth.ConnectionGroup; import org.apache.guacamole.net.auth.simple.SimpleConnectionDirectory; import org.apache.guacamole.net.auth.Connection; +import org.apache.guacamole.protocol.GuacamoleConfiguration; /** * Implementation of the Connection Directory, stored @@ -89,6 +91,75 @@ public class QuickConnectDirectory extends SimpleConnectionDirectory { this.rootGroup.addConnectionIdentifier(connectionId); } + /** + * Create a connection object on the tree using an existing + * QuickConnection connection, after setting the identifier + * and parent object. + * + * @param object + * The QuickConnection object to add to the tree. + * + * @returns + * The connectionId of the object added to the directory. + * + * @throws GuacamoleException + * If an error is encountered adding the object to the + * directory. + */ + public String put(QuickConnection object) throws GuacamoleException { + + // Get the next connection ID. + String connectionId = getNextConnectionID().toString(); + + // Set up identifier and parent on object. + object.setIdentifier(connectionId); + object.setParentIdentifier(ROOT_IDENTIFIER); + + // Add connection to the directory + putConnection(object); + + // Add connection to the tree + this.rootGroup.addConnectionIdentifier(connectionId); + + return connectionId; + + } + + /** + * Create a QuickConnection object from a GuacamoleConfiguration + * and get an ID and place it on the tree. + * + * @param config + * The GuacamoleConfiguration to use to create the + * QuickConnection object. + * + * @returns + * The connectionId of the object creation in the directory. + * + * @throws GuacamoleException + * If an error occurs adding the object to the tree. + */ + public String create(GuacamoleConfiguration config) throws GuacamoleException { + + // Get the next connection ID + String connectionId = getNextConnectionID().toString(); + + // Generate a name for the configuration + String name = QCParser.getName(config); + + // Create a new connection and set parent identifier. + Connection connection = new QuickConnection(name, connectionId, config); + connection.setParentIdentifier(ROOT_IDENTIFIER); + + // Place the object in directory + putConnection(connection); + + // Add connection to the tree. + this.rootGroup.addConnectionIdentifier(connectionId); + + return connectionId; + } + @Override public void update(Connection object) throws GuacamoleException { putConnection(object); diff --git a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectUserContext.java b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectUserContext.java index bc9754cad..b1a2752ca 100644 --- a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectUserContext.java +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/QuickConnectUserContext.java @@ -21,6 +21,7 @@ package org.apache.guacamole.auth.quickconnect; import java.util.Collection; import java.util.Collections; +import org.apache.guacamole.auth.quickconnect.rest.QuickConnectREST; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.form.Form; import org.apache.guacamole.net.auth.ActiveConnection; @@ -50,7 +51,7 @@ public class QuickConnectUserContext implements UserContext { /** * The unique identifier of the root connection group. */ - private static final String ROOT_IDENTIFIER = "ROOT"; + public static final String ROOT_IDENTIFIER = "ROOT"; /** * The AuthenticationProvider that created this UserContext. @@ -129,7 +130,7 @@ public class QuickConnectUserContext implements UserContext { @Override public Object getResource() throws GuacamoleException { - return null; + return new QuickConnectREST(this); } @Override diff --git a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/rest/QuickConnectREST.java b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/rest/QuickConnectREST.java new file mode 100644 index 000000000..d494eb883 --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/rest/QuickConnectREST.java @@ -0,0 +1,103 @@ +/* + * 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.auth.quickconnect.rest; + +import com.google.inject.Inject; +import java.util.List; +import javax.ws.rs.Consumes; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.POST; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleServerException; +import org.apache.guacamole.protocol.GuacamoleConfiguration; +import org.apache.guacamole.auth.quickconnect.QuickConnection; +import org.apache.guacamole.auth.quickconnect.QuickConnectDirectory; +import org.apache.guacamole.auth.quickconnect.QuickConnectUserContext; +import org.apache.guacamole.auth.quickconnect.utility.QCParser; +import org.apache.guacamole.net.auth.Connection; +import org.apache.guacamole.net.auth.Directory; + +/** + * A class to create and manage REST endpoints for the + * QuickConnect extension. + */ +@Produces(MediaType.APPLICATION_JSON) +public class QuickConnectREST { + + /** + * The connection directory for this REST endpoint. + */ + private QuickConnectDirectory directory; + + /** + * The UserContext object for this REST endpoint. + */ + private QuickConnectUserContext userContext; + + /** + * Construct a new QuickConnectREST class, taking in the UserContext + * object that calls this constructor. + * + * @param userContext + * The UserContext object associated with this REST endpoint + * + * @throws GuacamoleException + * If the UserContext is unavailable or the directory object + * cannot be retrieved. + */ + public QuickConnectREST(QuickConnectUserContext userContext) + throws GuacamoleException { + this.userContext = userContext; + this.directory = (QuickConnectDirectory)this.userContext.getConnectionDirectory(); + } + + /** + * Parse the URI read from the POST input, add the connection + * to the directory, and return the ID of the newly-created + * connection. + * + * @param uri + * The URI to parse into a connection. + * + * @returns + * The ID of the connection in the directory. + * + * @throws + * Throws a GuacamoleException if an error is encountered + * parsing the URI. + */ + @POST + @Path("create") + public String create(@FormParam("uri") String uri) + throws GuacamoleException { + + if (directory == null) + throw new GuacamoleServerException("No connection directory available."); + + return directory.create(QCParser.getConfiguration(uri)); + + } + + +} diff --git a/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/utility/QCParser.java b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/utility/QCParser.java new file mode 100644 index 000000000..afe272adb --- /dev/null +++ b/extensions/guacamole-auth-quickconnect/src/main/java/org/apache/guacamole/auth/quickconnect/utility/QCParser.java @@ -0,0 +1,169 @@ +/* + * 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.auth.quickconnect.utility; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.List; +import org.apache.guacamole.GuacamoleClientException; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.protocol.GuacamoleConfiguration; + +public class QCParser { + + /** + * The default protocol to parse to if one is undefined. + */ + public static final String DEFAULT_URI_PROTOCOL = "ssh"; + + /** + * The default host to use if one is not defined. + */ + public static final String DEFAULT_URI_HOST = "localhost"; + + /** + * The default port to use if one is not defined. + */ + public static final Integer DEFAULT_URI_PORT = 22; + + /** + * Parse out a URI string and get a connection from that string, + * or an exception if the parsing fails. + * + * @param uri + * The string form of the URI to be parsed. + * + * @returns + * A GuacamoleConfiguration using a combination of the parsed + * URI values and default values when not specified in the + * URI. + * + * @throws GuacamoleException + * When an error occurs parsing the URI. + */ + public static GuacamoleConfiguration getConfiguration(String uri) + throws GuacamoleException { + + URI qcUri; + try { + qcUri = new URI(uri); + } + catch (URISyntaxException e) { + throw new GuacamoleClientException("Invalid URI Syntax", e); + } + String protocol = qcUri.getScheme(); + String host = qcUri.getHost(); + Integer port = qcUri.getPort(); + String userInfo = qcUri.getUserInfo(); + String query = qcUri.getQuery(); + String username = null; + String password = null; + List paramList = null; + + if (protocol == null || protocol.equals("")) + protocol = DEFAULT_URI_PROTOCOL; + + if (host == null || host.equals("")) + host = DEFAULT_URI_HOST; + + if (port == -1 || port < 1) + port = DEFAULT_URI_PORT; + + if (query != null && !query.equals("")) + paramList = Arrays.asList(query.split("&")); + + if (userInfo != null && !userInfo.equals("")) { + String[] authenticators = userInfo.split(":"); + if (authenticators[0] != null) + username = authenticators[0]; + if (authenticators[1] != null) + password = authenticators[1]; + } + + GuacamoleConfiguration qcConfig = new GuacamoleConfiguration(); + qcConfig.setProtocol(protocol); + qcConfig.setParameter("hostname",host); + qcConfig.setParameter("port", port.toString()); + + if (username != null) + qcConfig.setParameter("username", username); + + if (password != null) + qcConfig.setParameter("password", password); + + if (paramList != null) { + for (String parameter : paramList) { + String[] paramArray = parameter.split("="); + qcConfig.setParameter(paramArray[0],paramArray[1]); + } + } + + return qcConfig; + + } + + /** + * Given a GuacamoleConfiguration object, generate a name + * for the configuration based on the protocol, host, user + * and port in the configuration, and return the string value. + * + * @param config + * The GuacamoleConfiguration object to use to generate + * the name. + * + * @return + * The String value of the name that is generated, or + * null if no config is provided. + * + * @throws GuacamoleException + * If an error occurs getting items in the configuration. + */ + public static String getName(GuacamoleConfiguration config) + throws GuacamoleException { + + if (config == null) + return null; + + String protocol = config.getProtocol(); + String host = config.getParameter("hostname"); + String port = config.getParameter("port"); + String user = config.getParameter("username"); + + String name = ""; + + if (protocol != null && !protocol.equals("")) + name += protocol + "://"; + + if (user != null && !user.equals("")) + name += user + "@"; + + if (host != null && !host.equals("")) + name += host; + + if (port != null && !port.equals("")) + name += ":" + port; + + name += "/"; + + return name; + } + +}