GUACAMOLE-38: Implement extension-specific REST endpoint and utilities for parsing connection.

This commit is contained in:
Nick Couchman
2017-10-11 15:55:14 -04:00
parent 498400ef0d
commit c0a6bacad5
5 changed files with 353 additions and 2 deletions

View File

@@ -185,6 +185,13 @@
<scope>provided</scope>
</dependency>
<!-- Jersey - JAX-RS Implementation -->
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.17.1</version>
</dependency>
<!-- Guice -->
<dependency>
<groupId>com.google.inject</groupId>

View File

@@ -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);

View File

@@ -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

View File

@@ -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));
}
}

View File

@@ -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<String> 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;
}
}