diff --git a/.gitignore b/.gitignore
index bcc52fede..d26be9c0e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
*~
target/
+nb-configuration.xml
diff --git a/guacamole/lib/unknown/binary/javax.ws.rs-api-2.0/SNAPSHOT/javax.ws.rs-api-2.0-SNAPSHOT.jar b/guacamole/lib/unknown/binary/javax.ws.rs-api-2.0/SNAPSHOT/javax.ws.rs-api-2.0-SNAPSHOT.jar
new file mode 100644
index 000000000..b9de41659
Binary files /dev/null and b/guacamole/lib/unknown/binary/javax.ws.rs-api-2.0/SNAPSHOT/javax.ws.rs-api-2.0-SNAPSHOT.jar differ
diff --git a/guacamole/pom.xml b/guacamole/pom.xml
index 33fe0fd6b..723f43941 100644
--- a/guacamole/pom.xml
+++ b/guacamole/pom.xml
@@ -185,6 +185,55 @@
provided
+
+
+ com.google.inject
+ guice
+ 3.0
+
+
+
+
+ com.google.inject.extensions
+ guice-servlet
+ 3.0
+
+
+
+
+ com.sun.jersey
+ jersey-server
+ 1.17.1
+
+
+
+
+ com.sun.jersey.contribs
+ jersey-guice
+ 1.17.1
+
+
+
+
+ javax.annotation
+ jsr250-api
+ 1.0
+
+
+
+
+ commons-codec
+ commons-codec
+ 1.4
+
+
+
+
+ com.sun.jersey
+ jersey-json
+ 1.17.1
+
+
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/AuthenticatingFilter.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/AuthenticatingFilter.java
deleted file mode 100644
index 600dbfd41..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/AuthenticatingFilter.java
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.util.Collection;
-import java.util.regex.Pattern;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-import javax.xml.bind.DatatypeConverter;
-import org.glyptodon.guacamole.GuacamoleClientException;
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
-import org.glyptodon.guacamole.net.auth.Credentials;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.net.basic.event.SessionListenerCollection;
-import org.glyptodon.guacamole.net.basic.properties.BasicGuacamoleProperties;
-import org.glyptodon.guacamole.net.event.AuthenticationFailureEvent;
-import org.glyptodon.guacamole.net.event.AuthenticationSuccessEvent;
-import org.glyptodon.guacamole.net.event.listener.AuthenticationFailureListener;
-import org.glyptodon.guacamole.net.event.listener.AuthenticationSuccessListener;
-import org.glyptodon.guacamole.properties.GuacamoleProperties;
-import org.glyptodon.guacamole.protocol.GuacamoleStatus;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Filter which provides watches requests for credentials, authenticating the
- * user against the configured AuthenticationProvider if credentials are
- * present. Note that if authentication fails, the request is still allowed. To
- * restrict access based on the result of authentication, use
- * RestrictedHttpServlet or RestrictedFilter.
- *
- * The user context is retrieved using the authentication provider defined in
- * guacamole.properties. The authentication provider has access to the request
- * and session, in addition to any submitted username and password, in order
- * to authenticate the user.
- *
- * The user context will be stored in the current HttpSession.
- *
- * Success and failure are logged.
- *
- * @author Michael Jumper
- */
-public class AuthenticatingFilter implements Filter {
-
- /**
- * Logger for this class.
- */
- private final Logger logger = LoggerFactory.getLogger(AuthenticatingFilter.class);
-
- /**
- * The session attribute holding the current UserContext.
- */
- public static final String CONTEXT_ATTRIBUTE = "GUAC_CONTEXT";
-
- /**
- * The session attribute holding the credentials authorizing this session.
- */
- public static final String CREDENTIALS_ATTRIBUTE = "GUAC_CREDS";
-
- /**
- * The session attribute holding the session-scoped clipboard storage.
- */
- public static final String CLIPBOARD_ATTRIBUTE = "GUAC_CLIP";
-
- /**
- * The AuthenticationProvider to use to authenticate all requests.
- */
- private AuthenticationProvider authProvider;
-
- /**
- * Whether HTTP authentication should be used (the "Authorization" header).
- */
- private boolean useHttpAuthentication;
-
- @Override
- public void init(FilterConfig config) throws ServletException {
-
- // Parse Guacamole configuration
- try {
-
- // Get auth provider instance
- authProvider = GuacamoleProperties.getRequiredProperty(BasicGuacamoleProperties.AUTH_PROVIDER);
-
- // Enable HTTP auth, if requested
- useHttpAuthentication = GuacamoleProperties.getProperty(BasicGuacamoleProperties.ENABLE_HTTP_AUTH, false);
-
- }
- catch (GuacamoleException e) {
- logger.error("Unable to read guacamole.properties: {}", e.getMessage());
- logger.debug("Error reading guacamole.properties.", e);
- throw new ServletException(e);
- }
-
- }
-
- /**
- * Notifies all listeners in the given collection that authentication has
- * failed.
- *
- * @param listeners A collection of all listeners that should be notified.
- * @param credentials The credentials associated with the authentication
- * request that failed.
- */
- private void notifyFailed(Collection listeners, Credentials credentials) {
-
- // Build event for auth failure
- AuthenticationFailureEvent event = new AuthenticationFailureEvent(credentials);
-
- // Notify all listeners
- for (Object listener : listeners) {
- try {
- if (listener instanceof AuthenticationFailureListener)
- ((AuthenticationFailureListener) listener).authenticationFailed(event);
- }
- catch (GuacamoleException e) {
- logger.debug("Error notifying AuthenticationFailureListener: {}", e);
- }
- }
-
- }
-
- /**
- * Notifies all listeners in the given collection that authentication was
- * successful.
- *
- * @param listeners A collection of all listeners that should be notified.
- * @param context The UserContext created as a result of authentication
- * success.
- * @param credentials The credentials associated with the authentication
- * request that succeeded.
- * @return true if all listeners are allowing the authentication success,
- * or if there are no listeners, and false if any listener is
- * canceling the authentication success. Note that once one
- * listener cancels, no other listeners will run.
- * @throws GuacamoleException If any listener throws an error while being
- * notified. Note that if any listener throws an
- * error, the success is canceled, and no other
- * listeners will run.
- */
- private boolean notifySuccess(Collection listeners, UserContext context,
- Credentials credentials) throws GuacamoleException {
-
- // Build event for auth success
- AuthenticationSuccessEvent event =
- new AuthenticationSuccessEvent(context, credentials);
-
- // Notify all listeners
- for (Object listener : listeners) {
- if (listener instanceof AuthenticationSuccessListener) {
-
- // Cancel immediately if hook returns false
- if (!((AuthenticationSuccessListener) listener).authenticationSucceeded(event))
- return false;
-
- }
- }
-
- return true;
-
- }
-
- /**
- * Sends an error on the given HTTP response using the information within
- * the given GuacamoleStatus.
- *
- * @param response The HTTP response to use to send the error.
- * @param guac_status The status to send
- * @param message A human-readable message that can be presented to the
- * user.
- * @throws ServletException If an error prevents sending of the error
- * code.
- */
- public static void sendError(HttpServletResponse response,
- GuacamoleStatus guac_status, String message)
- throws ServletException {
-
- try {
-
- // If response not committed, send error code and message
- if (!response.isCommitted()) {
- response.addHeader("Guacamole-Status-Code", Integer.toString(guac_status.getGuacamoleStatusCode()));
- response.addHeader("Guacamole-Error-Message", message);
- response.sendError(guac_status.getHttpStatusCode());
- }
-
- }
- catch (IOException ioe) {
-
- // If unable to send error at all due to I/O problems,
- // rethrow as servlet exception
- throw new ServletException(ioe);
-
- }
-
- }
-
- /**
- * Returns the credentials associated with the given session.
- *
- * @param session The session to retrieve credentials from.
- * @return The credentials associated with the given session.
- */
- public static Credentials getCredentials(HttpSession session) {
- return (Credentials) session.getAttribute(CREDENTIALS_ATTRIBUTE);
- }
-
- /**
- * Returns the UserContext associated with the given session.
- *
- * @param session The session to retrieve UserContext from.
- * @return The UserContext associated with the given session.
- */
- public static UserContext getUserContext(HttpSession session) {
- return (UserContext) session.getAttribute(CONTEXT_ATTRIBUTE);
- }
-
- /**
- * Returns the ClipboardState associated with the given session. If none
- * exists yet, one is created.
- *
- * @param session The session to retrieve the ClipboardState from.
- * @return The ClipboardState associated with the given session.
- */
- public static ClipboardState getClipboardState(HttpSession session) {
-
- ClipboardState clipboard = (ClipboardState) session.getAttribute(CLIPBOARD_ATTRIBUTE);
- if (clipboard == null) {
- clipboard = new ClipboardState();
- session.setAttribute(CLIPBOARD_ATTRIBUTE, clipboard);
- }
-
- return clipboard;
-
- }
-
- /**
- * Returns whether the request given has updated credentials. If this
- * function returns false, the UserContext will not be updated.
- *
- * @param request The request to check for credentials.
- * @return true if the request contains credentials, false otherwise.
- */
- protected boolean hasNewCredentials(HttpServletRequest request) {
- return true;
- }
-
- /**
- * Regular expression which matches any IPv4 address.
- */
- private static final String IPV4_ADDRESS_REGEX = "([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})";
-
- /**
- * Regular expression which matches any IPv6 address.
- */
- private static final String IPV6_ADDRESS_REGEX = "([0-9a-fA-F]*(:[0-9a-fA-F]*){0,7})";
-
- /**
- * Regular expression which matches any IP address, regardless of version.
- */
- private static final String IP_ADDRESS_REGEX = "(" + IPV4_ADDRESS_REGEX + "|" + IPV6_ADDRESS_REGEX + ")";
-
- /**
- * Pattern which matches valid values of the de-facto standard
- * "X-Forwarded-For" header.
- */
- private static final Pattern X_FORWARDED_FOR = Pattern.compile("^" + IP_ADDRESS_REGEX + "(, " + IP_ADDRESS_REGEX + ")*$");
-
- /**
- * Returns a formatted string containing an IP address, or list of IP
- * addresses, which represent the HTTP client and any involved proxies. As
- * the headers used to determine proxies can easily be forged, this data is
- * superficially validated to ensure that it at least looks like a list of
- * IPs.
- *
- * @param request The HTTP request to format.
- * @return A formatted string containing one or more IP addresses.
- */
- private String getLoggableAddress(HttpServletRequest request) {
-
- // Log X-Forwarded-For, if present and valid
- String header = request.getHeader("X-Forwarded-For");
- if (header != null && X_FORWARDED_FOR.matcher(header).matches())
- return "[" + header + ", " + request.getRemoteAddr() + "]";
-
- // If header absent or invalid, just use source IP
- return request.getRemoteAddr();
-
- }
-
- @Override
- public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
- throws IOException, ServletException {
-
- HttpServletRequest request = (HttpServletRequest) req;
- HttpServletResponse response = (HttpServletResponse) resp;
-
- // Set character encoding to UTF-8 if it's not already set
- if(request.getCharacterEncoding() == null) {
- try {
- request.setCharacterEncoding("UTF-8");
- } catch (UnsupportedEncodingException exception) {
- throw new ServletException(exception);
- }
- }
-
- try {
-
- // Obtain context from session
- HttpSession httpSession = request.getSession(true);
- UserContext context = getUserContext(httpSession);
-
- // If new credentials present, update/create context
- if (hasNewCredentials(request)) {
-
- // Retrieve username and password from parms
- String username = request.getParameter("username");
- String password = request.getParameter("password");
-
- // If no username/password given, try Authorization header
- if (useHttpAuthentication && username == null && password == null) {
-
- String authorization = request.getHeader("Authorization");
- if (authorization != null && authorization.startsWith("Basic ")) {
-
- // Decode base64 authorization
- String basicBase64 = authorization.substring(6);
- String basicCredentials = new String(DatatypeConverter.parseBase64Binary(basicBase64), "UTF-8");
-
- // Pull username/password from auth data
- int colon = basicCredentials.indexOf(':');
- if (colon != -1) {
- username = basicCredentials.substring(0, colon);
- password = basicCredentials.substring(colon+1);
- }
-
- else
- logger.info("Invalid HTTP Basic \"Authorization\" header received.");
-
- }
-
- } // end Authorization header fallback
-
- // Build credentials object
- Credentials credentials = new Credentials();
- credentials.setSession(httpSession);
- credentials.setRequest(request);
- credentials.setUsername(username);
- credentials.setPassword(password);
-
- SessionListenerCollection listeners = new SessionListenerCollection(httpSession);
-
- // If no cached context, attempt to get new context
- if (context == null) {
-
- context = authProvider.getUserContext(credentials);
-
- // Log successful authentication
- if (context != null && logger.isInfoEnabled())
- logger.info("User \"{}\" successfully authenticated from {}.",
- context.self().getUsername(), getLoggableAddress(request));
-
- }
-
- // Otherwise, update existing context
- else
- context = authProvider.updateUserContext(context, credentials);
-
- // If auth failed, notify listeners
- if (context == null) {
-
- if (logger.isWarnEnabled()) {
-
- // Only bother logging failures involving usernames
- if (credentials.getUsername() != null)
- logger.info("Authentication attempt from {} for user \"{}\" failed.",
- getLoggableAddress(request), credentials.getUsername());
- else
- logger.debug("Authentication attempt from {} without username failed.",
- getLoggableAddress(request));
- }
-
- notifyFailed(listeners, credentials);
- }
-
- // If auth succeeded, notify and check with listeners
- else if (!notifySuccess(listeners, context, credentials))
- logger.info("Successful authentication canceled by hook.");
-
- // If auth still OK, associate context with session
- else {
- httpSession.setAttribute(CONTEXT_ATTRIBUTE, context);
- httpSession.setAttribute(CREDENTIALS_ATTRIBUTE, credentials);
- }
-
- } // end if credentials present
-
- // Allow servlet to run now that authentication has been validated
- chain.doFilter(request, response);
-
- }
-
- // Catch any thrown guacamole exception and attempt to pass within the
- // HTTP response, logging each error appropriately.
- catch (GuacamoleClientException e) {
- logger.info("HTTP request rejected: {}", e.getMessage());
- logger.debug("HTTP request rejected by AuthenticatingFilter.", e);
- sendError(response, e.getStatus(), e.getMessage());
- }
- catch (GuacamoleException e) {
- logger.error("Authentication failed internally: {}", e.getMessage());
- logger.debug("Internal server error during authentication.", e);
- sendError(response, e.getStatus(), "Internal server error.");
- }
-
- }
-
- @Override
- public void destroy() {
- // No destruction needed
- }
-
-}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicGuacamoleTunnelServlet.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicGuacamoleTunnelServlet.java
index f6da591d2..be6e328f6 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicGuacamoleTunnelServlet.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicGuacamoleTunnelServlet.java
@@ -22,6 +22,8 @@
package org.glyptodon.guacamole.net.basic;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import javax.servlet.http.HttpServletRequest;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.GuacamoleTunnel;
@@ -35,8 +37,15 @@ import org.slf4j.LoggerFactory;
*
* @author Michael Jumper
*/
+@Singleton
public class BasicGuacamoleTunnelServlet extends GuacamoleHTTPTunnelServlet {
+ /**
+ * Service for handling tunnel requests.
+ */
+ @Inject
+ private TunnelRequestService tunnelRequestService;
+
/**
* Logger for this class.
*/
@@ -46,8 +55,8 @@ public class BasicGuacamoleTunnelServlet extends GuacamoleHTTPTunnelServlet {
protected GuacamoleTunnel doConnect(HttpServletRequest request) throws GuacamoleException {
// Attempt to create HTTP tunnel
- GuacamoleTunnel tunnel = BasicTunnelRequestUtility.createTunnel(new HTTPTunnelRequest(request));
-
+ GuacamoleTunnel tunnel = tunnelRequestService.createTunnel(new HTTPTunnelRequest(request));
+
// If successful, warn of lack of WebSocket
logger.info("Using HTTP tunnel (not WebSocket). Performance may be sub-optimal.");
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicLogout.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicServletContextListener.java
similarity index 59%
rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicLogout.java
rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicServletContextListener.java
index eb40f0c84..d7ba6b499 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicLogout.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicServletContextListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Glyptodon LLC
+ * Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,33 +22,31 @@
package org.glyptodon.guacamole.net.basic;
-import java.io.IOException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.servlet.GuiceServletContextListener;
+import org.glyptodon.guacamole.net.basic.log.LogModule;
+import org.glyptodon.guacamole.net.basic.rest.RESTModule;
+import org.glyptodon.guacamole.net.basic.rest.RESTServletModule;
/**
- * Logs out the current user by invalidating the associated HttpSession and
- * redirecting the user to the login page.
- *
- * @author Michael Jumper
+ * A ServletContextListener to listen for initialization of the servlet context
+ * in order to set up dependency injection.
+ *
+ * @author James Muehlner
*/
-public class BasicLogout extends HttpServlet {
+public class BasicServletContextListener extends GuiceServletContextListener {
@Override
- protected void service(HttpServletRequest request, HttpServletResponse response)
- throws IOException {
-
- // Invalidate session, if any
- HttpSession httpSession = request.getSession(false);
- if (httpSession != null)
- httpSession.invalidate();
-
- // Redirect to index
- response.sendRedirect("index.xhtml");
+ protected Injector getInjector() {
+ return Guice.createInjector(
+ new LogModule(),
+ new RESTServletModule(),
+ new RESTModule(),
+ new TunnelModule()
+ );
+
}
-
+
}
-
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/GuacamoleSession.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/GuacamoleSession.java
new file mode 100644
index 000000000..912ad8146
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/GuacamoleSession.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import org.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.net.auth.Credentials;
+import org.glyptodon.guacamole.net.auth.UserContext;
+import org.glyptodon.guacamole.net.basic.properties.BasicGuacamoleProperties;
+import org.glyptodon.guacamole.properties.GuacamoleProperties;
+
+/**
+ * Contains Guacamole-specific user information which is tied to the current
+ * session, such as the UserContext and current clipboard state.
+ *
+ * @author Michael Jumper
+ */
+public class GuacamoleSession {
+
+ /**
+ * The credentials provided when the user logged in.
+ */
+ private final Credentials credentials;
+
+ /**
+ * The user context associated with this session.
+ */
+ private final UserContext userContext;
+
+ /**
+ * Collection of all event listeners configured in guacamole.properties.
+ */
+ private final Collection listeners = new ArrayList();
+
+ /**
+ * The current clipboard state.
+ */
+ private final ClipboardState clipboardState = new ClipboardState();
+
+ /**
+ * Creates a new Guacamole session associated with the given user context.
+ *
+ * @param credentials The credentials provided by the user during login.
+ * @param userContext The user context to associate this session with.
+ * @throws GuacamoleException If an error prevents the session from being
+ * created.
+ */
+ public GuacamoleSession(Credentials credentials, UserContext userContext) throws GuacamoleException {
+
+ this.credentials = credentials;
+ this.userContext = userContext;
+
+ // Load listeners from guacamole.properties
+ try {
+
+ // Get all listener classes from properties
+ Collection listenerClasses =
+ GuacamoleProperties.getProperty(BasicGuacamoleProperties.EVENT_LISTENERS);
+
+ // Add an instance of each class to the list
+ if (listenerClasses != null) {
+ for (Class listenerClass : listenerClasses) {
+
+ // Instantiate listener
+ Object listener = listenerClass.getConstructor().newInstance();
+
+ // Add listener to collection of listeners
+ listeners.add(listener);
+
+ }
+ }
+
+ }
+ catch (InstantiationException e) {
+ throw new GuacamoleException("Listener class is abstract.", e);
+ }
+ catch (IllegalAccessException e) {
+ throw new GuacamoleException("No access to listener constructor.", e);
+ }
+ catch (IllegalArgumentException e) {
+ // This should not happen, given there ARE no arguments
+ throw new GuacamoleException("Illegal arguments to listener constructor.", e);
+ }
+ catch (InvocationTargetException e) {
+ throw new GuacamoleException("Error while instantiating listener.", e);
+ }
+ catch (NoSuchMethodException e) {
+ throw new GuacamoleException("Listener has no default constructor.", e);
+ }
+ catch (SecurityException e) {
+ throw new GuacamoleException("Security restrictions prevent instantiation of listener.", e);
+ }
+
+ }
+
+ /**
+ * Returns the credentials used when the user associated with this session
+ * logged in.
+ *
+ * @return The credentials used when the user associated with this session
+ * logged in.
+ */
+ public Credentials getCredentials() {
+ return credentials;
+ }
+
+ /**
+ * Returns the UserContext associated with this session.
+ *
+ * @return The UserContext associated with this session.
+ */
+ public UserContext getUserContext() {
+ return userContext;
+ }
+
+ /**
+ * Returns the ClipboardState associated with this session.
+ *
+ * @return The ClipboardState associated with this session.
+ */
+ public ClipboardState getClipboardState() {
+ return clipboardState;
+ }
+
+ /**
+ * Returns a collection which iterates over instances of all listeners
+ * defined in guacamole.properties. For each listener defined in
+ * guacamole.properties, a new instance is created and stored in this
+ * collection. The contents of this collection is stored within the
+ * HttpSession, and will be reused if available. Each listener is
+ * instantiated once per session. Listeners are singleton classes within the
+ * session, but not globally.
+ *
+ * @return A collection which iterates over instances of all listeners
+ * defined in guacamole.properties.
+ */
+ public Collection getListeners() {
+ return Collections.unmodifiableCollection(listeners);
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/RestrictedFilter.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/RestrictedFilter.java
deleted file mode 100644
index 7fbb9c9e7..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/RestrictedFilter.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2014 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic;
-
-import java.io.IOException;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.protocol.GuacamoleStatus;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Filter which enforces authentication. If no user context is associated with
- * the current HTTP session, or no HTTP session exists, the request is denied.
- *
- * @author Michael Jumper
- */
-public class RestrictedFilter implements Filter {
-
- /**
- * Logger for this class.
- */
- private final Logger logger = LoggerFactory.getLogger(RestrictedFilter.class);
-
- @Override
- public void init(FilterConfig config) throws ServletException {
- // No configuration
- }
-
- @Override
- public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
- throws IOException, ServletException {
-
- HttpServletRequest request = (HttpServletRequest) req;
- HttpServletResponse response = (HttpServletResponse) resp;
-
- // Pull user context from session
- UserContext context = null;
- HttpSession session = request.getSession(false);
- if (session != null)
- context = AuthenticatingFilter.getUserContext(session);
-
- // If authenticated, proceed with rest of chain
- if (context != null)
- chain.doFilter(req, resp);
-
- // Otherwise, deny entire request
- else {
- final GuacamoleStatus status = GuacamoleStatus.CLIENT_UNAUTHORIZED;
- final String message = "Not authenticated";
-
- logger.info("HTTP request rejected: {}", message);
- response.addHeader("Guacamole-Status-Code", Integer.toString(status.getGuacamoleStatusCode()));
- response.addHeader("Guacamole-Error-Message", message);
- response.sendError(status.getHttpStatusCode());
- }
-
- }
-
- @Override
- public void destroy() {
- // No destruction needed
- }
-
-}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/RestrictedHttpServlet.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/RestrictedHttpServlet.java
deleted file mode 100644
index ea6f1dc32..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/RestrictedHttpServlet.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-import org.glyptodon.guacamole.GuacamoleClientException;
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.GuacamoleUnauthorizedException;
-import org.glyptodon.guacamole.GuacamoleUnsupportedException;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.protocol.GuacamoleStatus;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Abstract servlet which provides an restrictedService() function that is only
- * called if the current HTTP session has already been authenticated by the
- * AuthenticatingFilter.
- *
- * @author Michael Jumper
- */
-public abstract class RestrictedHttpServlet extends HttpServlet {
-
- /**
- * Logger for this class.
- */
- private final Logger logger = LoggerFactory.getLogger(RestrictedHttpServlet.class);
-
- /**
- * Sends an error on the given HTTP response using the information within
- * the given GuacamoleStatus.
- *
- * @param response The HTTP response to use to send the error.
- * @param guac_status The status to send
- * @param message A human-readable message that can be presented to the
- * user.
- * @throws ServletException If an error prevents sending of the error
- * code.
- */
- public static void sendError(HttpServletResponse response,
- GuacamoleStatus guac_status, String message)
- throws ServletException {
-
- try {
-
- // If response not committed, send error code and message
- if (!response.isCommitted()) {
- response.addHeader("Guacamole-Status-Code", Integer.toString(guac_status.getGuacamoleStatusCode()));
- response.addHeader("Guacamole-Error-Message", message);
- response.sendError(guac_status.getHttpStatusCode());
- }
-
- }
- catch (IOException ioe) {
-
- // If unable to send error at all due to I/O problems,
- // rethrow as servlet exception
- throw new ServletException(ioe);
-
- }
-
- }
-
- @Override
- protected void service(HttpServletRequest request, HttpServletResponse response)
- throws IOException, ServletException {
-
- // Set character encoding to UTF-8 if it's not already set
- if(request.getCharacterEncoding() == null) {
- try {
- request.setCharacterEncoding("UTF-8");
- } catch (UnsupportedEncodingException exception) {
- throw new ServletException(exception);
- }
- }
-
- try {
-
- // Obtain context from session
- HttpSession httpSession = request.getSession(true);
- UserContext context = AuthenticatingFilter.getUserContext(httpSession);
-
- // If no context, no authorizaton present
- if (context == null)
- throw new GuacamoleUnauthorizedException("Not authenticated");
-
- // Allow servlet to run now that authentication has been validated
- restrictedService(context, request, response);
-
- }
-
- // Catch any thrown guacamole exception and attempt to pass within the
- // HTTP response, logging each error appropriately.
- catch (GuacamoleClientException e) {
- logger.debug("HTTP request rejected by RestrictedHttpServlet.", e);
- sendError(response, e.getStatus(), e.getMessage());
- }
- catch (GuacamoleUnsupportedException e) {
- logger.debug("Unsupported operation.", e);
- sendError(response, e.getStatus(), e.getMessage());
- }
- catch (GuacamoleException e) {
- logger.error("HTTP request failed: {}", e.getMessage());
- logger.debug("Internal server error while handling HTTP request to restricted resource.", e);
- sendError(response, e.getStatus(), "Internal server error.");
- }
-
- }
-
- /**
- * Function called after the request and associated session are validated.
- * If the current session is not associated with valid credentials, this
- * function will not be called.
- *
- * @param context The current UserContext.
- * @param request The HttpServletRequest being serviced.
- * @param response An HttpServletResponse which controls the HTTP response
- * of this servlet.
- *
- * @throws GuacamoleException If an error occurs that interferes with the
- * normal operation of this servlet.
- */
- protected abstract void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
- throws GuacamoleException;
-
-}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/SessionKeepAlive.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/SessionKeepAlive.java
index 94cc94060..b065e4730 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/SessionKeepAlive.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/SessionKeepAlive.java
@@ -22,29 +22,43 @@
package org.glyptodon.guacamole.net.basic;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import com.google.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.UserContext;
+import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure;
+import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Dummy servlet which provides an endpoint for arbitrary requests intended to
- * simply keep the HTTP session from expiring.
+ * REST service which updates the last access time of the Guacamole session,
+ * preventing the session from becoming invalid.
*
* @author Michael Jumper
*/
-public class SessionKeepAlive extends RestrictedHttpServlet {
+@Path("/keep-alive")
+public class SessionKeepAlive {
+
+ /**
+ * A service for authenticating users from auth tokens.
+ */
+ @Inject
+ private AuthenticationService authenticationService;
/**
* Logger for this class.
*/
private final Logger logger = LoggerFactory.getLogger(SessionKeepAlive.class);
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response) {
+ @GET
+ @AuthProviderRESTExposure
+ public void updateSession(@QueryParam("token") String authToken) throws GuacamoleException {
+
+ // Tickle the session
+ UserContext context = authenticationService.getUserContext(authToken);
// Do nothing
logger.debug("Keep-alive signal received from user \"{}\".", context.self().getUsername());
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/WebSocketSupportLoader.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelModule.java
similarity index 58%
rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/WebSocketSupportLoader.java
rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelModule.java
index 34ca01f09..4466aecbf 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/WebSocketSupportLoader.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelModule.java
@@ -20,36 +20,31 @@
* THE SOFTWARE.
*/
-package org.glyptodon.guacamole.net.basic.websocket;
+package org.glyptodon.guacamole.net.basic;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import javax.servlet.Servlet;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletContextEvent;
-import javax.servlet.ServletContextListener;
+import com.google.inject.Provider;
+import com.google.inject.servlet.ServletModule;
+import java.util.Arrays;
+import javax.servlet.http.HttpServlet;
+import javax.websocket.DeploymentException;
+import javax.websocket.server.ServerContainer;
+import javax.websocket.server.ServerEndpointConfig;
import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.net.basic.GuacamoleClassLoader;
+import org.glyptodon.guacamole.net.basic.websocket.BasicGuacamoleWebSocketTunnelEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Simple ServletContextListener which loads a WebSocket tunnel implementation
- * if available, using the Servlet 3.0 API to dynamically load and install
- * the tunnel servlet.
- *
- * Note that because Guacamole depends on the Servlet 2.5 API, and 3.0 may
- * not be available or needed if WebSocket is not desired, the 3.0 API is
- * detected and invoked dynamically via reflection.
+ * Module which loads tunnel implementations.
*
* @author Michael Jumper
*/
-public class WebSocketSupportLoader implements ServletContextListener {
+public class TunnelModule extends ServletModule {
/**
* Logger for this class.
*/
- private final Logger logger = LoggerFactory.getLogger(WebSocketSupportLoader.class);
+ private final Logger logger = LoggerFactory.getLogger(TunnelModule.class);
/**
* Classnames of all legacy (non-JSR) WebSocket tunnel implementations.
@@ -94,52 +89,17 @@ public class WebSocketSupportLoader implements ServletContextListener {
}
- private boolean loadWebSocketTunnel(ServletContext context, String classname) {
+ private boolean loadWebSocketTunnel(String classname) {
try {
// Attempt to find WebSocket servlet
- Class servlet = (Class)
+ Class servlet = (Class)
GuacamoleClassLoader.getInstance().findClass(classname);
- // Dynamically add servlet IF SERVLET 3.0 API AVAILABLE!
- try {
-
- // Get servlet registration class
- Class regClass = Class.forName("javax.servlet.ServletRegistration");
-
- // Get and invoke addServlet()
- Method addServlet = ServletContext.class.getMethod("addServlet",
- String.class, Class.class);
- Object reg = addServlet.invoke(context, "WebSocketTunnel", servlet);
-
- // Get and invoke addMapping()
- Method addMapping = regClass.getMethod("addMapping", String[].class);
- addMapping.invoke(reg, (Object) new String[]{"/websocket-tunnel"});
-
- // If we succesfully load and register the WebSocket tunnel servlet,
- // WebSocket is supported.
- return true;
-
- }
-
- // Servlet API 3.0 unsupported
- catch (ClassNotFoundException e) {
- logger.info("Servlet API 3.0 not found.", e);
- }
- catch (NoSuchMethodException e) {
- logger.warn("Servlet API 3.0 found, but incomplete.", e);
- }
-
- // Servlet API 3.0 found, but errors during use
- catch (IllegalAccessException e) {
- logger.error("Unable to load WebSocket tunnel servlet: {}", e.getMessage());
- logger.debug("Error loading WebSocket tunnel servlet.", e);
- }
- catch (InvocationTargetException e) {
- logger.error("Unable to load WebSocket tunnel servlet: {}", e.getMessage());
- logger.debug("Error loading WebSocket tunnel servlet.", e);
- }
+ // Add WebSocket servlet
+ serve("/websocket-tunnel").with(servlet);
+ return true;
}
@@ -160,21 +120,50 @@ public class WebSocketSupportLoader implements ServletContextListener {
}
@Override
- public void contextDestroyed(ServletContextEvent sce) {
- }
+ protected void configureServlets() {
- @Override
- public void contextInitialized(ServletContextEvent sce) {
+ bind(TunnelRequestService.class);
+
+ // Set up HTTP tunnel
+ serve("/tunnel").with(BasicGuacamoleTunnelServlet.class);
// Check for JSR 356 support
if (implementsJSR_356()) {
+
logger.info("JSR-356 WebSocket support present.");
+
+ // Get container
+ ServerContainer container = (ServerContainer) getServletContext().getAttribute("javax.websocket.server.ServerContainer");
+ if (container == null) {
+ logger.warn("ServerContainer attribute required by JSR-356 is missing. Cannot load JSR-356 WebSocket support.");
+ return;
+ }
+
+ Provider tunnelRequestServiceProvider = getProvider(TunnelRequestService.class);
+
+ // Build configuration for WebSocket tunnel
+ ServerEndpointConfig config =
+ ServerEndpointConfig.Builder.create(BasicGuacamoleWebSocketTunnelEndpoint.class, "/websocket-tunnel")
+ .configurator(new BasicGuacamoleWebSocketTunnelEndpoint.Configurator(tunnelRequestServiceProvider))
+ .subprotocols(Arrays.asList(new String[]{"guacamole"}))
+ .build();
+
+ try {
+
+ // Add configuration to container
+ container.addEndpoint(config);
+
+ }
+ catch (DeploymentException e) {
+ logger.error("Unable to deploy WebSocket tunnel.", e);
+ }
+
return;
}
// Try to load each WebSocket tunnel in sequence
for (String classname : WEBSOCKET_CLASSES) {
- if (loadWebSocketTunnel(sce.getServletContext(), classname)) {
+ if (loadWebSocketTunnel(classname)) {
logger.info("Legacy (non-JSR) WebSocket support loaded: {}", classname);
return;
}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicTunnelRequestUtility.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelRequestService.java
similarity index 90%
rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicTunnelRequestUtility.java
rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelRequestService.java
index 0fb90992b..bea981608 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicTunnelRequestUtility.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/TunnelRequestService.java
@@ -22,6 +22,9 @@
package org.glyptodon.guacamole.net.basic;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import org.glyptodon.guacamole.net.basic.rest.clipboard.ClipboardRESTService;
import java.util.Collection;
import java.util.List;
import javax.servlet.http.HttpSession;
@@ -37,6 +40,7 @@ import org.glyptodon.guacamole.net.auth.Credentials;
import org.glyptodon.guacamole.net.auth.Directory;
import org.glyptodon.guacamole.net.auth.UserContext;
import org.glyptodon.guacamole.net.basic.event.SessionListenerCollection;
+import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
import org.glyptodon.guacamole.net.event.TunnelCloseEvent;
import org.glyptodon.guacamole.net.event.TunnelConnectEvent;
import org.glyptodon.guacamole.net.event.listener.TunnelCloseListener;
@@ -55,12 +59,19 @@ import org.slf4j.LoggerFactory;
*
* @author Michael Jumper
*/
-public class BasicTunnelRequestUtility {
+@Singleton
+public class TunnelRequestService {
/**
* Logger for this class.
*/
- private static final Logger logger = LoggerFactory.getLogger(BasicTunnelRequestUtility.class);
+ private final Logger logger = LoggerFactory.getLogger(TunnelRequestService.class);
+
+ /**
+ * A service for authenticating users from auth tokens.
+ */
+ @Inject
+ private AuthenticationService authenticationService;
/**
* Notifies all listeners in the given collection that a tunnel has been
@@ -79,7 +90,7 @@ public class BasicTunnelRequestUtility {
* error, the connect is canceled, and no other
* listeners will run.
*/
- private static boolean notifyConnect(Collection listeners, UserContext context,
+ private boolean notifyConnect(Collection listeners, UserContext context,
Credentials credentials, GuacamoleTunnel tunnel)
throws GuacamoleException {
@@ -119,7 +130,7 @@ public class BasicTunnelRequestUtility {
* error, the close is canceled, and no other
* listeners will run.
*/
- private static boolean notifyClose(Collection listeners, UserContext context,
+ private boolean notifyClose(Collection listeners, UserContext context,
Credentials credentials, GuacamoleTunnel tunnel)
throws GuacamoleException {
@@ -151,7 +162,7 @@ public class BasicTunnelRequestUtility {
* @return The created tunnel, or null if the tunnel could not be created.
* @throws GuacamoleException If an error occurs while creating the tunnel.
*/
- public static GuacamoleTunnel createTunnel(TunnelRequest request)
+ public GuacamoleTunnel createTunnel(TunnelRequest request)
throws GuacamoleException {
HttpSession httpSession = request.getSession();
@@ -169,6 +180,10 @@ public class BasicTunnelRequestUtility {
throw e;
}
+ // Get auth token and session
+ String authToken = request.getParameter("authToken");
+ GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
+
// Get ID of connection
String id = request.getParameter("id");
TunnelRequest.IdentifierType id_type = TunnelRequest.IdentifierType.getType(id);
@@ -181,17 +196,17 @@ public class BasicTunnelRequestUtility {
id = id.substring(id_type.PREFIX.length());
// Get credentials
- final Credentials credentials = AuthenticatingFilter.getCredentials(httpSession);
+ final Credentials credentials = session.getCredentials();
// Get context
- final UserContext context = AuthenticatingFilter.getUserContext(httpSession);
-
+ final UserContext context = session.getUserContext();
+
// If no context or no credentials, not logged in
if (context == null || credentials == null)
throw new GuacamoleSecurityException("Cannot connect - user not logged in.");
// Get clipboard
- final ClipboardState clipboard = AuthenticatingFilter.getClipboardState(httpSession);
+ final ClipboardState clipboard = session.getClipboardState();
// Get client information
GuacamoleClientInformation info = new GuacamoleClientInformation();
@@ -279,7 +294,7 @@ public class BasicTunnelRequestUtility {
// Monitor instructions which pertain to server-side events, if necessary
try {
- if (GuacamoleProperties.getProperty(CaptureClipboard.INTEGRATION_ENABLED, false))
+ if (GuacamoleProperties.getProperty(ClipboardRESTService.INTEGRATION_ENABLED, false))
return new MonitoringGuacamoleReader(clipboard, super.acquireReader());
}
catch (GuacamoleException e) {
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/ConnectionGroupUtility.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/ConnectionGroupUtility.java
deleted file mode 100644
index 52cc35f91..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/ConnectionGroupUtility.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.connectiongroups;
-
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.net.auth.ConnectionGroup;
-import org.glyptodon.guacamole.net.auth.Directory;
-import org.glyptodon.guacamole.net.auth.UserContext;
-
-/**
- * A class that provides helper methods for the ConnectionGroup CRUD servlets.
- *
- * @author James Muehlner
- */
-class ConnectionGroupUtility {
-
- // This class should not be instantiated
- private ConnectionGroupUtility() {}
-
- /**
- * Get the ConnectionGroupDirectory with the parent connection group
- * specified by parentID.
- *
- * @param context The UserContext to search for the connectionGroup directory.
- * @param parentID The ID of the parent connection group to search for.
- *
- * @return The ConnectionGroupDirectory with the parent connection group,
- * if found.
- * @throws GuacamoleException If an error is encountered while getting the
- * connection group directory.
- */
- static Directory findConnectionGroupDirectory(
- UserContext context, String parentID) throws GuacamoleException {
-
- // Find the correct connection group directory
- ConnectionGroup rootGroup = context.getRootConnectionGroup();
- Directory directory;
-
- Directory connectionGroupDirectory =
- rootGroup.getConnectionGroupDirectory();
-
- ConnectionGroup parentGroup = connectionGroupDirectory.get(parentID);
-
- if(parentGroup == null)
- return null;
-
- directory = parentGroup.getConnectionGroupDirectory();
-
- return directory;
- }
-}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/Create.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/Create.java
deleted file mode 100644
index 702cc851f..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/Create.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.connectiongroups;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.net.auth.ConnectionGroup;
-import org.glyptodon.guacamole.net.auth.Directory;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.net.basic.RestrictedHttpServlet;
-
-/**
- * Simple HttpServlet which handles connection group creation.
- *
- * @author James Muehlner
- */
-public class Create extends RestrictedHttpServlet {
-
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
- throws GuacamoleException {
-
- // Get name and type
- String name = request.getParameter("name");
- String type = request.getParameter("type");
-
- // Get the ID of the parent connection group
- String parentID = request.getParameter("parentID");
-
- // Find the correct connection group directory
- Directory directory =
- ConnectionGroupUtility.findConnectionGroupDirectory(context, parentID);
-
- if(directory == null)
- throw new GuacamoleException("Connection group directory not found.");
-
- // Create connection skeleton
- ConnectionGroup connectionGroup = new DummyConnectionGroup();
- connectionGroup.setName(name);
-
- if("balancing".equals(type))
- connectionGroup.setType(ConnectionGroup.Type.BALANCING);
- else if("organizational".equals(type))
- connectionGroup.setType(ConnectionGroup.Type.ORGANIZATIONAL);
-
- // Add connection
- directory.add(connectionGroup);
-
- }
-
-}
-
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/Delete.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/Delete.java
deleted file mode 100644
index 64de349d7..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/Delete.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.connectiongroups;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.net.auth.ConnectionGroup;
-import org.glyptodon.guacamole.net.auth.Directory;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.net.basic.RestrictedHttpServlet;
-
-/**
- * Simple HttpServlet which handles connection group deletion.
- *
- * @author Michael Jumper
- */
-public class Delete extends RestrictedHttpServlet {
-
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
- throws GuacamoleException {
-
- // Get ID
- String identifier = request.getParameter("id");
-
- // Attempt to get connection group directory
- Directory directory =
- context.getRootConnectionGroup().getConnectionGroupDirectory();
-
- // Remove connection
- directory.remove(identifier);
-
- }
-
-}
-
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/DummyConnectionGroup.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/DummyConnectionGroup.java
deleted file mode 100644
index 5bfd8eafd..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/DummyConnectionGroup.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.connectiongroups;
-
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.net.GuacamoleSocket;
-import org.glyptodon.guacamole.net.auth.AbstractConnectionGroup;
-import org.glyptodon.guacamole.net.auth.Connection;
-import org.glyptodon.guacamole.net.auth.ConnectionGroup;
-import org.glyptodon.guacamole.net.auth.Directory;
-import org.glyptodon.guacamole.protocol.GuacamoleClientInformation;
-
-/**
- * Basic ConnectionGroup skeleton, providing a means of storing Connection data
- * prior to CRUD operations. This ConnectionGroup has no functionality for actually
- * performing a connection operation, and does not promote any of the
- * semantics that would otherwise be present because of the authentication
- * provider. It is up to the authentication provider to create a new
- * ConnectionGroup based on the information contained herein.
- *
- * @author James Muehlner
- */
-public class DummyConnectionGroup extends AbstractConnectionGroup {
-
- @Override
- public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException {
- throw new UnsupportedOperationException("Connection unsupported in DummyConnectionGroup.");
- }
-
- @Override
- public Directory getConnectionDirectory() throws GuacamoleException {
- throw new UnsupportedOperationException("Connection directory unsupported in DummyConnectionGroup.");
- }
-
- @Override
- public Directory getConnectionGroupDirectory() throws GuacamoleException {
- throw new UnsupportedOperationException("Connection group directory unsuppprted in DummyConnectionGroup.");
- }
-
-}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/List.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/List.java
deleted file mode 100644
index c91ba72e7..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/List.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.connectiongroups;
-
-import java.io.IOException;
-import java.util.Set;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.xml.stream.XMLOutputFactory;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.GuacamoleSecurityException;
-import org.glyptodon.guacamole.GuacamoleServerException;
-import org.glyptodon.guacamole.net.auth.ConnectionGroup;
-import org.glyptodon.guacamole.net.auth.Directory;
-import org.glyptodon.guacamole.net.auth.User;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.net.auth.permission.ConnectionPermission;
-import org.glyptodon.guacamole.net.auth.permission.ObjectPermission;
-import org.glyptodon.guacamole.net.auth.permission.Permission;
-import org.glyptodon.guacamole.net.auth.permission.SystemPermission;
-import org.glyptodon.guacamole.net.basic.RestrictedHttpServlet;
-
-/**
- * Simple HttpServlet which outputs XML containing a list of all authorized
- * connection groups for the current user.
- *
- * @author Michael Jumper
- */
-public class List extends RestrictedHttpServlet {
-
- /**
- * System administration permission.
- */
- private static final Permission SYSTEM_PERMISSION =
- new SystemPermission(SystemPermission.Type.ADMINISTER);
-
-
- /**
- * Checks whether the given user has permission to perform the given
- * object operation. Security exceptions are handled appropriately - only
- * non-security exceptions pass through.
- *
- * @param user The user whose permissions should be verified.
- * @param type The type of operation to check for permission for.
- * @param identifier The identifier of the connection the operation
- * would be performed upon.
- * @return true if permission is granted, false otherwise.
- *
- * @throws GuacamoleException If an error occurs while checking permissions.
- */
- private boolean hasConfigPermission(User user, ObjectPermission.Type type,
- String identifier)
- throws GuacamoleException {
-
- // Build permission
- Permission permission = new ConnectionPermission(
- type,
- identifier
- );
-
- try {
- // Return result of permission check, if possible
- return user.hasPermission(permission);
- }
- catch (GuacamoleSecurityException e) {
- // If cannot check due to security restrictions, no permission
- return false;
- }
-
- }
-
- /**
- * Writes the XML for the given connection group.
- *
- * @param self The user whose permissions dictate the availability of the
- * data written.
- * @param xml The XMLStremWriter to use when writing the data.
- * @param group The connection group whose XML representation will be
- * written.
- * @throws GuacamoleException If an error occurs while reading the
- * requested data.
- * @throws XMLStreamException If an error occurs while writing the XML.
- */
- private void writeConnectionGroup(User self, XMLStreamWriter xml,
- ConnectionGroup group) throws GuacamoleException, XMLStreamException {
-
- // Write group
- xml.writeStartElement("group");
- xml.writeAttribute("id", group.getIdentifier());
- xml.writeAttribute("name", group.getName());
-
- // Write group type
- switch (group.getType()) {
-
- case ORGANIZATIONAL:
- xml.writeAttribute("type", "organizational");
- break;
-
- case BALANCING:
- xml.writeAttribute("type", "balancing");
- break;
-
- }
-
- // Write contained connection groups
- writeConnectionGroups(self, xml, group.getConnectionGroupDirectory());
-
- // End of group
- xml.writeEndElement();
-
- }
-
- /**
- * Writes the XML for the given directory of connection groups.
- *
- * @param self The user whose permissions dictate the availability of the
- * data written.
- * @param xml The XMLStremWriter to use when writing the data.
- * @param directory The directory whose XML representation will be
- * written.
- * @throws GuacamoleException If an error occurs while reading the
- * requested data.
- * @throws XMLStreamException If an error occurs while writing the XML.
- */
- private void writeConnectionGroups(User self, XMLStreamWriter xml,
- Directory directory)
- throws GuacamoleException, XMLStreamException {
-
- // If no connections, write nothing
- Set identifiers = directory.getIdentifiers();
- if (identifiers.isEmpty())
- return;
-
- // Begin connections
- xml.writeStartElement("groups");
-
- // For each entry, write corresponding connection element
- for (String identifier : identifiers) {
-
- // Write each group
- ConnectionGroup group = directory.get(identifier);
- writeConnectionGroup(self, xml, group);
-
- }
-
- // End connections
- xml.writeEndElement();
-
- }
-
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
- throws GuacamoleException {
-
- // Do not cache
- response.setHeader("Cache-Control", "no-cache");
-
- // Write XML content type
- response.setHeader("Content-Type", "text/xml");
-
- // Set encoding
- response.setCharacterEncoding("UTF-8");
-
- // Get root group
- ConnectionGroup root = context.getRootConnectionGroup();
-
- // Write actual XML
- try {
-
- // Get self
- User self = context.self();
-
- XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
- XMLStreamWriter xml = outputFactory.createXMLStreamWriter(response.getWriter());
-
- // Write content of root group
- xml.writeStartDocument();
- writeConnectionGroup(self, xml, root);
- xml.writeEndDocument();
-
- }
- catch (XMLStreamException e) {
- throw new GuacamoleServerException(
- "Unable to write connection group list XML.", e);
- }
- catch (IOException e) {
- throw new GuacamoleServerException(
- "I/O error writing connection group list XML.", e);
- }
-
- }
-
-}
-
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/Move.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/Move.java
deleted file mode 100644
index 363f10d83..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/Move.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.connectiongroups;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.net.auth.ConnectionGroup;
-import org.glyptodon.guacamole.net.auth.Directory;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.net.basic.RestrictedHttpServlet;
-
-/**
- * Simple HttpServlet which handles moving connection groups.
- *
- * @author James Muehlner
- */
-public class Move extends RestrictedHttpServlet {
-
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
- throws GuacamoleException {
-
- // Get ID
- String identifier = request.getParameter("id");
-
- // Get the identifier of the new parent connection group
- String parentID = request.getParameter("parentID");
-
- // Attempt to get the new parent connection group directory
- Directory newParentDirectory =
- ConnectionGroupUtility.findConnectionGroupDirectory(context, parentID);
-
- // Attempt to get root connection group directory
- Directory directory =
- context.getRootConnectionGroup().getConnectionGroupDirectory();
-
- // Move connection group
- directory.move(identifier, newParentDirectory);
-
- }
-
-}
-
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/Update.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/Update.java
deleted file mode 100644
index 09931ef87..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/Update.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.connectiongroups;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.net.auth.ConnectionGroup;
-import org.glyptodon.guacamole.net.auth.Directory;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.net.basic.RestrictedHttpServlet;
-
-/**
- * Simple HttpServlet which handles connection group update.
- *
- * @author James Muehlner
- */
-public class Update extends RestrictedHttpServlet {
-
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
- throws GuacamoleException {
-
- // Get ID, name, and type
- String identifier = request.getParameter("id");
- String name = request.getParameter("name");
- String type = request.getParameter("type");
-
- // Attempt to get connection group directory
- Directory directory =
- context.getRootConnectionGroup().getConnectionGroupDirectory();
-
- // Create connection group skeleton
- ConnectionGroup connectionGroup = directory.get(identifier);
- connectionGroup.setName(name);
-
- if("balancing".equals(type))
- connectionGroup.setType(ConnectionGroup.Type.BALANCING);
- else if("organizational".equals(type))
- connectionGroup.setType(ConnectionGroup.Type.ORGANIZATIONAL);
-
- // Update connection group
- directory.update(connectionGroup);
-
- }
-
-}
-
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/ConnectionUtility.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/ConnectionUtility.java
deleted file mode 100644
index de4e23942..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/ConnectionUtility.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.connections;
-
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.net.auth.Connection;
-import org.glyptodon.guacamole.net.auth.ConnectionGroup;
-import org.glyptodon.guacamole.net.auth.Directory;
-import org.glyptodon.guacamole.net.auth.UserContext;
-
-/**
- * A class that provides helper methods for the Connection CRUD servlets.
- *
- * @author James Muehlner
- */
-class ConnectionUtility {
-
- // This class should not be instantiated
- private ConnectionUtility() {}
-
- /**
- * Get the ConnectionDirectory with the parent connection group specified by
- * parentID.
- *
- * @param context The UserContext to search for the connection directory.
- * @param parentID The ID of the parent connection group to search for.
- *
- * @return The ConnectionDirectory with the parent connection group,
- * if found.
- * @throws GuacamoleException If an error is encountered while getting the
- * connection directory.
- */
- static Directory findConnectionDirectory(
- UserContext context, String parentID) throws GuacamoleException {
-
- // Find the correct connection directory
- ConnectionGroup rootGroup = context.getRootConnectionGroup();
- Directory directory;
-
- Directory connectionGroupDirectory =
- rootGroup.getConnectionGroupDirectory();
-
- ConnectionGroup parentGroup = connectionGroupDirectory.get(parentID);
-
- if(parentGroup == null)
- return null;
-
- directory = parentGroup.getConnectionDirectory();
-
- return directory;
- }
-}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/Create.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/Create.java
deleted file mode 100644
index 5fa050b36..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/Create.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.connections;
-
-import java.util.Enumeration;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.net.auth.Connection;
-import org.glyptodon.guacamole.net.auth.Directory;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.net.basic.RestrictedHttpServlet;
-import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
-
-/**
- * Simple HttpServlet which handles connection creation.
- *
- * @author Michael Jumper
- */
-public class Create extends RestrictedHttpServlet {
-
- /**
- * Prefix given to a parameter name when that parameter is a protocol-
- * specific parameter meant for the configuration.
- */
- public static final String PARAMETER_PREFIX = "_";
-
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
- throws GuacamoleException {
-
- // Get name and protocol
- String name = request.getParameter("name");
- String protocol = request.getParameter("protocol");
-
- // Get the ID of the parent connection group
- String parentID = request.getParameter("parentID");
-
- // Find the correct connection directory
- Directory directory =
- ConnectionUtility.findConnectionDirectory(context, parentID);
-
- if(directory == null)
- throw new GuacamoleException("Connection directory not found.");
-
- // Create config
- GuacamoleConfiguration config = new GuacamoleConfiguration();
- config.setProtocol(protocol);
-
- // Load parameters into config
- Enumeration params = request.getParameterNames();
- while (params.hasMoreElements()) {
-
- // If parameter starts with prefix, load corresponding parameter
- // value into config
- String param = params.nextElement();
- if (param.startsWith(PARAMETER_PREFIX))
- config.setParameter(
- param.substring(PARAMETER_PREFIX.length()),
- request.getParameter(param));
-
- }
-
- // Create connection skeleton
- Connection connection = new DummyConnection();
- connection.setName(name);
- connection.setConfiguration(config);
-
- // Add connection
- directory.add(connection);
-
- }
-
-}
-
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/DummyConnection.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/DummyConnection.java
deleted file mode 100644
index c1d7c173f..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/DummyConnection.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.connections;
-
-import java.util.List;
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.net.GuacamoleSocket;
-import org.glyptodon.guacamole.net.auth.AbstractConnection;
-import org.glyptodon.guacamole.net.auth.ConnectionRecord;
-import org.glyptodon.guacamole.protocol.GuacamoleClientInformation;
-
-/**
- * Basic Connection skeleton, providing a means of storing Connection data
- * prior to CRUD operations. This Connection has no functionality for actually
- * performing a connection operation, and does not promote any of the
- * semantics that would otherwise be present because of the authentication
- * provider. It is up to the authentication provider to create a new
- * Connection based on the information contained herein.
- *
- * @author Michael Jumper
- */
-public class DummyConnection extends AbstractConnection {
-
- @Override
- public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException {
- throw new UnsupportedOperationException("Connection unsupported in DummyConnection.");
- }
-
- @Override
- public List getHistory() throws GuacamoleException {
- throw new UnsupportedOperationException("History unsupported in DummyConnection.");
- }
-
-}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/List.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/List.java
deleted file mode 100644
index 73d353360..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/List.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.connections;
-
-import java.io.IOException;
-import java.util.Set;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.xml.stream.XMLOutputFactory;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.GuacamoleSecurityException;
-import org.glyptodon.guacamole.GuacamoleServerException;
-import org.glyptodon.guacamole.net.auth.Connection;
-import org.glyptodon.guacamole.net.auth.ConnectionGroup;
-import org.glyptodon.guacamole.net.auth.ConnectionRecord;
-import org.glyptodon.guacamole.net.auth.Directory;
-import org.glyptodon.guacamole.net.auth.User;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.net.auth.permission.ConnectionGroupPermission;
-import org.glyptodon.guacamole.net.auth.permission.ConnectionPermission;
-import org.glyptodon.guacamole.net.auth.permission.ObjectPermission;
-import org.glyptodon.guacamole.net.auth.permission.Permission;
-import org.glyptodon.guacamole.net.auth.permission.SystemPermission;
-import org.glyptodon.guacamole.net.basic.RestrictedHttpServlet;
-import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
-
-/**
- * Simple HttpServlet which outputs XML containing a list of all authorized
- * configurations for the current user.
- *
- * @author Michael Jumper
- */
-public class List extends RestrictedHttpServlet {
-
- /**
- * System administration permission.
- */
- private static final Permission SYSTEM_PERMISSION =
- new SystemPermission(SystemPermission.Type.ADMINISTER);
-
-
- /**
- * Checks whether the given user has permission to perform the given
- * object operation. Security exceptions are handled appropriately - only
- * non-security exceptions pass through.
- *
- * @param user The user whose permissions should be verified.
- * @param type The type of operation to check for permission for.
- * @param identifier The identifier of the connection the operation
- * would be performed upon.
- * @return true if permission is granted, false otherwise.
- *
- * @throws GuacamoleException If an error occurs while checking permissions.
- */
- private boolean hasConfigPermission(User user, ObjectPermission.Type type,
- String identifier)
- throws GuacamoleException {
-
- // Build permission
- Permission permission = new ConnectionPermission(
- type,
- identifier
- );
-
- try {
- // Return result of permission check, if possible
- return user.hasPermission(permission);
- }
- catch (GuacamoleSecurityException e) {
- // If cannot check due to security restrictions, no permission
- return false;
- }
-
- }
-
- /**
- * Writes the XML for the given connection group.
- *
- * @param self The user whose permissions dictate the availability of the
- * data written.
- * @param xml The XMLStremWriter to use when writing the data.
- * @param group The connection group whose XML representation will be
- * written.
- * @throws GuacamoleException If an error occurs while reading the
- * requested data.
- * @throws XMLStreamException If an error occurs while writing the XML.
- */
- private void writeConnectionGroup(User self, XMLStreamWriter xml,
- ConnectionGroup group) throws GuacamoleException, XMLStreamException {
-
- // Write group
- xml.writeStartElement("group");
- xml.writeAttribute("id", group.getIdentifier());
- xml.writeAttribute("name", group.getName());
-
- // Write group type
- switch (group.getType()) {
-
- case ORGANIZATIONAL:
- xml.writeAttribute("type", "organizational");
- break;
-
- case BALANCING:
- xml.writeAttribute("type", "balancing");
- break;
-
- }
-
- Permission group_admin_permission = new ConnectionGroupPermission(
- ObjectPermission.Type.ADMINISTER, group.getIdentifier());
-
- // Attempt to list contained groups and connections ONLY if the group
- // is organizational or we have admin rights to it
- if (group.getType() == ConnectionGroup.Type.ORGANIZATIONAL
- || self.hasPermission(SYSTEM_PERMISSION)
- || self.hasPermission(group_admin_permission)) {
- writeConnections(self, xml, group.getConnectionDirectory());
- writeConnectionGroups(self, xml, group.getConnectionGroupDirectory());
- }
-
- // End of group
- xml.writeEndElement();
-
- }
-
- /**
- * Writes the XML for the given connection.
- *
- * @param self The user whose permissions dictate the availability of the
- * data written.
- * @param xml The XMLStremWriter to use when writing the data.
- * @param connection The connection whose XML representation will be
- * written.
- * @throws GuacamoleException If an error occurs while reading the
- * requested data.
- * @throws XMLStreamException If an error occurs while writing the XML.
- */
- private void writeConnection(User self, XMLStreamWriter xml,
- Connection connection) throws GuacamoleException, XMLStreamException {
-
- // Write connection
- xml.writeStartElement("connection");
- xml.writeAttribute("id", connection.getIdentifier());
- xml.writeAttribute("name", connection.getName());
- xml.writeAttribute("protocol",
- connection.getConfiguration().getProtocol());
-
- // If update permission available, include parameters
- if (self.hasPermission(SYSTEM_PERMISSION) ||
- hasConfigPermission(self, ObjectPermission.Type.UPDATE,
- connection.getIdentifier())) {
-
- // As update permission is present, also list parameters
- GuacamoleConfiguration config = connection.getConfiguration();
- for (String name : config.getParameterNames()) {
-
- String value = connection.getConfiguration().getParameter(name);
- xml.writeStartElement("param");
- xml.writeAttribute("name", name);
-
- if (value != null)
- xml.writeCharacters(value);
-
- xml.writeEndElement();
- }
-
- }
-
- // Write history
- xml.writeStartElement("history");
- for (ConnectionRecord record : connection.getHistory()) {
- xml.writeStartElement("record");
-
- // Start date
- xml.writeAttribute("start",
- Long.toString(record.getStartDate().getTime()));
-
- // End date
- if (record.getEndDate() != null)
- xml.writeAttribute("end",
- Long.toString(record.getEndDate().getTime()));
-
- // Whether connection currently active
- if (record.isActive())
- xml.writeAttribute("active", "yes");
-
- // User involved
- xml.writeCharacters(record.getUsername());
-
- xml.writeEndElement();
- }
- xml.writeEndElement();
-
- // End connection
- xml.writeEndElement();
-
- }
-
- /**
- * Writes the XML for the given directory of connection groups.
- *
- * @param self The user whose permissions dictate the availability of the
- * data written.
- * @param xml The XMLStremWriter to use when writing the data.
- * @param directory The directory whose XML representation will be
- * written.
- * @throws GuacamoleException If an error occurs while reading the
- * requested data.
- * @throws XMLStreamException If an error occurs while writing the XML.
- */
- private void writeConnectionGroups(User self, XMLStreamWriter xml,
- Directory directory)
- throws GuacamoleException, XMLStreamException {
-
- // If no connections, write nothing
- Set identifiers = directory.getIdentifiers();
- if (identifiers.isEmpty())
- return;
-
- // Begin connections
- xml.writeStartElement("groups");
-
- // For each entry, write corresponding connection element
- for (String identifier : identifiers) {
-
- // Write each group
- ConnectionGroup group = directory.get(identifier);
- writeConnectionGroup(self, xml, group);
-
- }
-
- // End connections
- xml.writeEndElement();
-
- }
-
- /**
- * Writes the XML for the given directory of connections.
- *
- * @param self The user whose permissions dictate the availability of the
- * data written.
- * @param xml The XMLStremWriter to use when writing the data.
- * @param directory The directory whose XML representation will be
- * written.
- * @throws GuacamoleException If an error occurs while reading the
- * requested data.
- * @throws XMLStreamException If an error occurs while writing the XML.
- */
- private void writeConnections(User self, XMLStreamWriter xml,
- Directory directory)
- throws GuacamoleException, XMLStreamException {
-
- // If no connections, write nothing
- Set identifiers = directory.getIdentifiers();
- if (identifiers.isEmpty())
- return;
-
- // Begin connections
- xml.writeStartElement("connections");
-
- // For each entry, write corresponding connection element
- for (String identifier : identifiers) {
-
- // Write each connection
- Connection connection = directory.get(identifier);
- writeConnection(self, xml, connection);
-
- }
-
- // End connections
- xml.writeEndElement();
-
- }
-
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
- throws GuacamoleException {
-
- // Do not cache
- response.setHeader("Cache-Control", "no-cache");
-
- // Write XML content type
- response.setHeader("Content-Type", "text/xml");
-
- // Set encoding
- response.setCharacterEncoding("UTF-8");
-
- // Get root group
- ConnectionGroup root = context.getRootConnectionGroup();
-
- // Write actual XML
- try {
-
- // Get self
- User self = context.self();
-
- XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
- XMLStreamWriter xml = outputFactory.createXMLStreamWriter(response.getWriter());
-
- // Write content of root group
- xml.writeStartDocument();
- writeConnectionGroup(self, xml, root);
- xml.writeEndDocument();
-
- }
- catch (XMLStreamException e) {
- throw new GuacamoleServerException(
- "Unable to write configuration list XML.", e);
- }
- catch (IOException e) {
- throw new GuacamoleServerException(
- "I/O error writing configuration list XML.", e);
- }
-
- }
-
-}
-
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/Move.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/Move.java
deleted file mode 100644
index e2fa7c555..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/Move.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.connections;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.net.auth.Connection;
-import org.glyptodon.guacamole.net.auth.Directory;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.net.basic.RestrictedHttpServlet;
-
-/**
- * Simple HttpServlet which handles moving connections.
- *
- * @author Michael Jumper
- */
-public class Move extends RestrictedHttpServlet {
-
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
- throws GuacamoleException {
-
- // Get ID
- String identifier = request.getParameter("id");
-
- // Get the identifier of the new parent connection group
- String parentID = request.getParameter("parentID");
-
- // Attempt to get the new parent connection directory
- Directory newParentDirectory =
- ConnectionUtility.findConnectionDirectory(context, parentID);
-
- // Attempt to get root connection directory
- Directory directory =
- context.getRootConnectionGroup().getConnectionDirectory();
-
- // Move connection
- directory.move(identifier, newParentDirectory);
-
- }
-
-}
-
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/Update.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/Update.java
deleted file mode 100644
index 6b4c90d99..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/Update.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.connections;
-
-import java.util.Enumeration;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.net.auth.Connection;
-import org.glyptodon.guacamole.net.auth.Directory;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.net.basic.RestrictedHttpServlet;
-import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
-
-/**
- * Simple HttpServlet which handles connection update.
- *
- * @author Michael Jumper
- */
-public class Update extends RestrictedHttpServlet {
-
- /**
- * Prefix given to a parameter name when that parameter is a protocol-
- * specific parameter meant for the configuration.
- */
- public static final String PARAMETER_PREFIX = "_";
-
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
- throws GuacamoleException {
-
- // Get ID, name, and protocol
- String identifier = request.getParameter("id");
- String name = request.getParameter("name");
- String protocol = request.getParameter("protocol");
-
- // Attempt to get connection directory
- Directory directory =
- context.getRootConnectionGroup().getConnectionDirectory();
-
- // Create config
- GuacamoleConfiguration config = new GuacamoleConfiguration();
- config.setProtocol(protocol);
-
- // Load parameters into config
- Enumeration params = request.getParameterNames();
- while (params.hasMoreElements()) {
-
- // If parameter starts with prefix, load corresponding parameter
- // value into config
- String param = params.nextElement();
- if (param.startsWith(PARAMETER_PREFIX))
- config.setParameter(
- param.substring(PARAMETER_PREFIX.length()),
- request.getParameter(param));
-
- }
-
- // Create connection skeleton
- Connection connection = directory.get(identifier);
- connection.setName(name);
- connection.setConfiguration(config);
-
- // Update connection
- directory.update(connection);
-
- }
-
-}
-
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/permissions/List.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/permissions/List.java
deleted file mode 100644
index df0c197fb..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/permissions/List.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.permissions;
-
-import java.io.IOException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.xml.stream.XMLOutputFactory;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
-import org.glyptodon.guacamole.GuacamoleClientException;
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.GuacamoleSecurityException;
-import org.glyptodon.guacamole.GuacamoleServerException;
-import org.glyptodon.guacamole.net.auth.Directory;
-import org.glyptodon.guacamole.net.auth.User;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.net.auth.permission.ConnectionGroupPermission;
-import org.glyptodon.guacamole.net.auth.permission.ConnectionPermission;
-import org.glyptodon.guacamole.net.auth.permission.ObjectPermission;
-import org.glyptodon.guacamole.net.auth.permission.Permission;
-import org.glyptodon.guacamole.net.auth.permission.SystemPermission;
-import org.glyptodon.guacamole.net.auth.permission.UserPermission;
-import org.glyptodon.guacamole.net.basic.RestrictedHttpServlet;
-
-/**
- * Simple HttpServlet which outputs XML containing a list of all visible
- * permissions of a given user.
- *
- * @author Michael Jumper
- */
-public class List extends RestrictedHttpServlet {
-
- /**
- * Returns the XML attribute value representation of the given
- * SystemPermission.Type.
- *
- * @param type The SystemPermission.Type to translate into a String.
- * @return The XML attribute value representation of the given
- * SystemPermission.Type.
- *
- * @throws GuacamoleException If the type given is not implemented.
- */
- private String toString(SystemPermission.Type type)
- throws GuacamoleException {
-
- switch (type) {
- case CREATE_USER: return "create-user";
- case CREATE_CONNECTION: return "create-connection";
- case CREATE_CONNECTION_GROUP: return "create-connection-group";
- case ADMINISTER: return "admin";
- }
-
- throw new GuacamoleException("Unknown permission type: " + type);
-
- }
-
- /**
- * Returns the XML attribute value representation of the given
- * ObjectPermission.Type.
- *
- * @param type The ObjectPermission.Type to translate into a String.
- * @return The XML attribute value representation of the given
- * ObjectPermission.Type.
- *
- * @throws GuacamoleException If the type given is not implemented.
- */
- private String toString(ObjectPermission.Type type)
- throws GuacamoleException {
-
- switch (type) {
- case READ: return "read";
- case UPDATE: return "update";
- case DELETE: return "delete";
- case ADMINISTER: return "admin";
- }
-
- throw new GuacamoleException("Unknown permission type: " + type);
-
- }
-
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
- throws GuacamoleException {
-
- // Do not cache
- response.setHeader("Cache-Control", "no-cache");
-
- // Set encoding
- response.setCharacterEncoding("UTF-8");
-
- // Write actual XML
- try {
-
- User user;
-
- // Get username
- String username = request.getParameter("user");
- if (username != null) {
-
- // Get user directory
- Directory users = context.getUserDirectory();
-
- // Get specific user
- user = users.get(username);
- }
- else
- user = context.self();
-
- if (user == null)
- throw new GuacamoleSecurityException("No such user.");
-
- // Write XML content type
- response.setHeader("Content-Type", "text/xml");
-
- XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
- XMLStreamWriter xml = outputFactory.createXMLStreamWriter(response.getWriter());
-
- // Begin document
- xml.writeStartDocument();
- xml.writeStartElement("permissions");
- xml.writeAttribute("user", user.getUsername());
-
- // For each entry, write corresponding user element
- for (Permission permission : user.getPermissions()) {
-
- // System permission
- if (permission instanceof SystemPermission) {
-
- // Get permission
- SystemPermission sp = (SystemPermission) permission;
-
- // Write permission
- xml.writeEmptyElement("system");
- xml.writeAttribute("type", toString(sp.getType()));
-
- }
-
- // Config permission
- else if (permission instanceof ConnectionPermission) {
-
- // Get permission
- ConnectionPermission cp =
- (ConnectionPermission) permission;
-
- // Write permission
- xml.writeEmptyElement("connection");
- xml.writeAttribute("type", toString(cp.getType()));
- xml.writeAttribute("name", cp.getObjectIdentifier());
-
- }
-
- // Connection group permission
- else if (permission instanceof ConnectionGroupPermission) {
-
- // Get permission
- ConnectionGroupPermission cgp =
- (ConnectionGroupPermission) permission;
-
- // Write permission
- xml.writeEmptyElement("connection-group");
- xml.writeAttribute("type", toString(cgp.getType()));
- xml.writeAttribute("name", cgp.getObjectIdentifier());
-
- }
-
- // User permission
- else if (permission instanceof UserPermission) {
-
- // Get permission
- UserPermission up = (UserPermission) permission;
-
- // Write permission
- xml.writeEmptyElement("user");
- xml.writeAttribute("type", toString(up.getType()));
- xml.writeAttribute("name", up.getObjectIdentifier());
-
- }
-
- else
- throw new GuacamoleClientException(
- "Unsupported permission type.");
-
- }
-
- // End document
- xml.writeEndElement();
- xml.writeEndDocument();
-
- }
- catch (XMLStreamException e) {
- throw new GuacamoleServerException(
- "Unable to write permission list XML.", e);
- }
- catch (IOException e) {
- throw new GuacamoleServerException(
- "I/O error writing permission list XML.", e);
- }
-
- }
-
-}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/Create.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/Create.java
deleted file mode 100644
index 06bebc693..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/Create.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.users;
-
-import java.util.UUID;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.net.auth.Directory;
-import org.glyptodon.guacamole.net.auth.User;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.net.basic.RestrictedHttpServlet;
-
-/**
- * Simple HttpServlet which handles user creation.
- *
- * @author Michael Jumper
- */
-public class Create extends RestrictedHttpServlet {
-
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
- throws GuacamoleException {
-
- // Create user as specified
- String username = request.getParameter("name");
-
- // Attempt to get user directory
- Directory directory =
- context.getUserDirectory();
-
- // Create user skeleton
- User user = new DummyUser();
- user.setUsername(username);
- user.setPassword(UUID.randomUUID().toString());
-
- // Add user
- directory.add(user);
-
- }
-
-}
-
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/DummyUser.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/DummyUser.java
deleted file mode 100644
index 15cd9c356..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/DummyUser.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.users;
-
-import java.util.HashSet;
-import java.util.Set;
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.net.auth.AbstractUser;
-import org.glyptodon.guacamole.net.auth.permission.Permission;
-
-/**
- * Basic User skeleton, providing a means of storing User data prior to CRUD
- * operations. This User does not promote any of the semantics that would
- * otherwise be present because of the authentication provider. It is up to the
- * authentication provider to create a new User based on the information
- * contained herein.
- *
- * @author Michael Jumper
- */
-public class DummyUser extends AbstractUser {
-
- /**
- * Set of all available permissions.
- */
- private Set permissions = new HashSet();
-
- @Override
- public Set getPermissions() throws GuacamoleException {
- return permissions;
- }
-
- @Override
- public boolean hasPermission(Permission permission) throws GuacamoleException {
- return permissions.contains(permission);
- }
-
- @Override
- public void addPermission(Permission permission) throws GuacamoleException {
- permissions.add(permission);
- }
-
- @Override
- public void removePermission(Permission permission) throws GuacamoleException {
- permissions.remove(permission);
- }
-
-}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/List.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/List.java
deleted file mode 100644
index a62f8b6b1..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/List.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.users;
-
-import java.io.IOException;
-import java.util.Set;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.xml.stream.XMLOutputFactory;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.GuacamoleServerException;
-import org.glyptodon.guacamole.net.auth.Directory;
-import org.glyptodon.guacamole.net.auth.User;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.net.basic.RestrictedHttpServlet;
-
-/**
- * Simple HttpServlet which outputs XML containing a list of all visible users.
- *
- * @author Michael Jumper
- */
-public class List extends RestrictedHttpServlet {
-
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
- throws GuacamoleException {
-
- // Do not cache
- response.setHeader("Cache-Control", "no-cache");
-
- // Write XML content type
- response.setHeader("Content-Type", "text/xml");
-
- // Set encoding
- response.setCharacterEncoding("UTF-8");
-
- // Write actual XML
- try {
-
- // Get user directory
- Directory directory = context.getUserDirectory();
-
- // Get users
- Set users = directory.getIdentifiers();
-
- XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
- XMLStreamWriter xml = outputFactory.createXMLStreamWriter(response.getWriter());
-
- // Begin document
- xml.writeStartDocument();
- xml.writeStartElement("users");
-
- // For each entry, write corresponding user element
- for (String username : users) {
-
- // Get user
- User user = directory.get(username);
-
- // Write user
- xml.writeEmptyElement("user");
- xml.writeAttribute("name", user.getUsername());
-
- }
-
- // End document
- xml.writeEndElement();
- xml.writeEndDocument();
-
- }
- catch (XMLStreamException e) {
- throw new GuacamoleServerException(
- "Unable to write configuration list XML.", e);
- }
- catch (IOException e) {
- throw new GuacamoleServerException(
- "I/O error writing configuration list XML.", e);
- }
-
- }
-
-}
-
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/Update.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/Update.java
deleted file mode 100644
index 7487357ec..000000000
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/Update.java
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * Copyright (C) 2013 Glyptodon LLC
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package org.glyptodon.guacamole.net.basic.crud.users;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.glyptodon.guacamole.GuacamoleClientException;
-import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.net.auth.Directory;
-import org.glyptodon.guacamole.net.auth.User;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.net.auth.permission.ConnectionGroupPermission;
-import org.glyptodon.guacamole.net.auth.permission.ConnectionPermission;
-import org.glyptodon.guacamole.net.auth.permission.ObjectPermission;
-import org.glyptodon.guacamole.net.auth.permission.Permission;
-import org.glyptodon.guacamole.net.auth.permission.SystemPermission;
-import org.glyptodon.guacamole.net.auth.permission.UserPermission;
-import org.glyptodon.guacamole.net.basic.RestrictedHttpServlet;
-
-/**
- * Simple HttpServlet which handles user update.
- *
- * @author Michael Jumper
- */
-public class Update extends RestrictedHttpServlet {
-
- /**
- * String given for user creation permission.
- */
- private static final String CREATE_USER_PERMISSION = "create-user";
-
- /**
- * String given for connection creation permission.
- */
- private static final String CREATE_CONNECTION_PERMISSION = "create-connection";
-
- /**
- * String given for connection group creation permission.
- */
- private static final String CREATE_CONNECTION_GROUP_PERMISSION = "create-connection-group";
-
- /**
- * String given for system administration permission.
- */
- private static final String ADMIN_PERMISSION = "admin";
-
- /**
- * Prefix given before an object identifier for read permission.
- */
- private static final String READ_PREFIX = "read:";
-
- /**
- * Prefix given before an object identifier for delete permission.
- */
- private static final String DELETE_PREFIX = "delete:";
-
- /**
- * Prefix given before an object identifier for update permission.
- */
- private static final String UPDATE_PREFIX = "update:";
-
- /**
- * Prefix given before an object identifier for administration permission.
- */
- private static final String ADMIN_PREFIX = "admin:";
-
- /**
- * Given a permission string, returns the corresponding system permission.
- *
- * @param str The permission string to parse.
- * @return The parsed system permission.
- * @throws GuacamoleException If the given string could not be parsed.
- */
- private Permission parseSystemPermission(String str)
- throws GuacamoleException {
-
- // Create user
- if (str.equals(CREATE_USER_PERMISSION))
- return new SystemPermission(SystemPermission.Type.CREATE_USER);
-
- // Create connection
- if (str.equals(CREATE_CONNECTION_PERMISSION))
- return new SystemPermission(SystemPermission.Type.CREATE_CONNECTION);
-
- // Create connection group
- if (str.equals(CREATE_CONNECTION_GROUP_PERMISSION))
- return new SystemPermission(SystemPermission.Type.CREATE_CONNECTION_GROUP);
-
- // Administration
- if (str.equals(ADMIN_PERMISSION))
- return new SystemPermission(SystemPermission.Type.ADMINISTER);
-
- throw new GuacamoleException("Invalid permission string.");
-
- }
-
- /**
- * Given a permission string, returns the corresponding user permission.
- *
- * @param str The permission string to parse.
- * @return The parsed user permission.
- * @throws GuacamoleException If the given string could not be parsed.
- */
- private Permission parseUserPermission(String str)
- throws GuacamoleException {
-
- // Read
- if (str.startsWith(READ_PREFIX))
- return new UserPermission(ObjectPermission.Type.READ,
- str.substring(READ_PREFIX.length()));
-
- // Update
- if (str.startsWith(UPDATE_PREFIX))
- return new UserPermission(ObjectPermission.Type.UPDATE,
- str.substring(UPDATE_PREFIX.length()));
-
- // Delete
- if (str.startsWith(DELETE_PREFIX))
- return new UserPermission(ObjectPermission.Type.DELETE,
- str.substring(DELETE_PREFIX.length()));
-
- // Administration
- if (str.startsWith(ADMIN_PREFIX))
- return new UserPermission(ObjectPermission.Type.ADMINISTER,
- str.substring(ADMIN_PREFIX.length()));
-
- throw new GuacamoleException("Invalid permission string.");
-
- }
-
- /**
- * Given a permission string, returns the corresponding connection
- * permission.
- *
- * @param str The permission string to parse.
- * @return The parsed connection permission.
- * @throws GuacamoleException If the given string could not be parsed.
- */
- private Permission parseConnectionPermission(String str)
- throws GuacamoleException {
-
- // Read
- if (str.startsWith(READ_PREFIX))
- return new ConnectionPermission(ObjectPermission.Type.READ,
- str.substring(READ_PREFIX.length()));
-
- // Update
- if (str.startsWith(UPDATE_PREFIX))
- return new ConnectionPermission(ObjectPermission.Type.UPDATE,
- str.substring(UPDATE_PREFIX.length()));
-
- // Delete
- if (str.startsWith(DELETE_PREFIX))
- return new ConnectionPermission(ObjectPermission.Type.DELETE,
- str.substring(DELETE_PREFIX.length()));
-
- // Administration
- if (str.startsWith(ADMIN_PREFIX))
- return new ConnectionPermission(ObjectPermission.Type.ADMINISTER,
- str.substring(ADMIN_PREFIX.length()));
-
- throw new GuacamoleClientException("Invalid permission string.");
-
- }
-
- /**
- * Given a permission string, returns the corresponding connection group
- * permission.
- *
- * @param str The permission string to parse.
- * @return The parsed connection group permission.
- * @throws GuacamoleException If the given string could not be parsed.
- */
- private Permission parseConnectionGroupPermission(String str)
- throws GuacamoleException {
-
- // Read
- if (str.startsWith(READ_PREFIX))
- return new ConnectionGroupPermission(ObjectPermission.Type.READ,
- str.substring(READ_PREFIX.length()));
-
- // Update
- if (str.startsWith(UPDATE_PREFIX))
- return new ConnectionGroupPermission(ObjectPermission.Type.UPDATE,
- str.substring(UPDATE_PREFIX.length()));
-
- // Delete
- if (str.startsWith(DELETE_PREFIX))
- return new ConnectionGroupPermission(ObjectPermission.Type.DELETE,
- str.substring(DELETE_PREFIX.length()));
-
- // Administration
- if (str.startsWith(ADMIN_PREFIX))
- return new ConnectionGroupPermission(ObjectPermission.Type.ADMINISTER,
- str.substring(ADMIN_PREFIX.length()));
-
- throw new GuacamoleClientException("Invalid permission string.");
-
- }
-
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
- throws GuacamoleException {
-
- // Create user as specified
- String username = request.getParameter("name");
- String password = request.getParameter("password");
-
- // Attempt to get user directory
- Directory directory =
- context.getUserDirectory();
-
- // Get user data, setting password if given
- User user = directory.get(username);
- user.setUsername(username);
- if (password != null)
- user.setPassword(password);
-
- /*
- * NEW PERMISSIONS
- */
-
- // Set added system permissions
- String[] add_sys_permission = request.getParameterValues("+sys");
- if (add_sys_permission != null) {
- for (String str : add_sys_permission)
- user.addPermission(parseSystemPermission(str));
- }
-
- // Set added user permissions
- String[] add_user_permission = request.getParameterValues("+user");
- if (add_user_permission != null) {
- for (String str : add_user_permission)
- user.addPermission(parseUserPermission(str));
- }
-
- // Set added connection permissions
- String[] add_connection_permission = request.getParameterValues("+connection");
- if (add_connection_permission != null) {
- for (String str : add_connection_permission)
- user.addPermission(parseConnectionPermission(str));
- }
-
- // Set added connection group permissions
- String[] add_connection_group_permission = request.getParameterValues("+connection-group");
- if (add_connection_group_permission != null) {
- for (String str : add_connection_group_permission)
- user.addPermission(parseConnectionGroupPermission(str));
- }
-
- /*
- * REMOVED PERMISSIONS
- */
-
- // Unset removed system permissions
- String[] remove_sys_permission = request.getParameterValues("-sys");
- if (remove_sys_permission != null) {
- for (String str : remove_sys_permission)
- user.removePermission(parseSystemPermission(str));
- }
-
- // Unset removed user permissions
- String[] remove_user_permission = request.getParameterValues("-user");
- if (remove_user_permission != null) {
- for (String str : remove_user_permission)
- user.removePermission(parseUserPermission(str));
- }
-
- // Unset removed connection permissions
- String[] remove_connection_permission = request.getParameterValues("-connection");
- if (remove_connection_permission != null) {
- for (String str : remove_connection_permission)
- user.removePermission(parseConnectionPermission(str));
- }
-
- // Unset removed connection group permissions
- String[] remove_connection_group_permission = request.getParameterValues("-connection-group");
- if (remove_connection_group_permission != null) {
- for (String str : remove_connection_group_permission)
- user.removePermission(parseConnectionGroupPermission(str));
- }
-
- // Update user
- directory.update(user);
-
- }
-
-}
-
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/log/LogbackInitializer.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/log/LogModule.java
similarity index 85%
rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/log/LogbackInitializer.java
rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/log/LogModule.java
index 3ea4ed6d8..a5ba1a039 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/log/LogbackInitializer.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/log/LogModule.java
@@ -26,32 +26,26 @@ import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;
+import com.google.inject.AbstractModule;
import java.io.File;
-import javax.servlet.ServletContextEvent;
-import javax.servlet.ServletContextListener;
import org.glyptodon.guacamole.properties.GuacamoleHome;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Initializes the logback logging subsystem, used behind SLF4J within
- * Guacamole for all logging.
+ * Initializes the logging subsystem.
*
* @author Michael Jumper
*/
-public class LogbackInitializer implements ServletContextListener {
+public class LogModule extends AbstractModule {
/**
* Logger for this class.
*/
- private final Logger logger = LoggerFactory.getLogger(LogbackInitializer.class);
+ private final Logger logger = LoggerFactory.getLogger(LogModule.class);
@Override
- public void contextDestroyed(ServletContextEvent sce) {
- }
-
- @Override
- public void contextInitialized(ServletContextEvent sce) {
+ protected void configure() {
// Only load logback configuration if GUACAMOLE_HOME exists
File guacamoleHome = GuacamoleHome.getDirectory();
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIConstants.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIConstants.java
new file mode 100644
index 000000000..f6277eeaa
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIConstants.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest;
+
+/**
+ * Useful constants for the REST API.
+ *
+ * @author James Muehlner
+ */
+public class APIConstants {
+
+ /**
+ * The identifier of the ROOT connection group.
+ */
+ public static final String ROOT_CONNECTION_GROUP_IDENTIFIER = "ROOT";
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIError.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIError.java
new file mode 100644
index 000000000..f08c2e402
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIError.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest;
+
+/**
+ * A simple object to represent an error to be sent from the REST API.
+ * @author James Muehlner
+ */
+public class APIError {
+
+ /**
+ * The error message.
+ */
+ private final String message;
+
+ /**
+ * Get the error message.
+ * @return The error message.
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * Create a new APIError with the specified error message.
+ * @param message The error message.
+ */
+ public APIError(String message) {
+ this.message = message;
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIPatch.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIPatch.java
new file mode 100644
index 000000000..c5c2dec8e
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIPatch.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest;
+
+/**
+ * An object for representing the body of a HTTP PATCH method.
+ * See https://tools.ietf.org/html/rfc6902
+ *
+ * @author James Muehlner
+ * @param The type of object being patched.
+ */
+public class APIPatch {
+
+ /**
+ * The possible operations for a PATCH request.
+ */
+ public enum Operation {
+ add, remove, test, copy, replace, move
+ }
+
+ /**
+ * The operation to perform for this patch.
+ */
+ private Operation op;
+
+ /**
+ * The value for this patch.
+ */
+ private T value;
+
+ /**
+ * The path for this patch.
+ */
+ private String path;
+
+ /**
+ * Returns the operation for this patch.
+ * @return the operation for this patch.
+ */
+ public Operation getOp() {
+ return op;
+ }
+
+ /**
+ * Set the operation for this patch.
+ * @param op The operation for this patch.
+ */
+ public void setOp(Operation op) {
+ this.op = op;
+ }
+
+ /**
+ * Returns the value of this patch.
+ * @return The value of this patch.
+ */
+ public T getValue() {
+ return value;
+ }
+
+ /**
+ * Sets the value of this patch.
+ * @param value The value of this patch.
+ */
+ public void setValue(T value) {
+ this.value = value;
+ }
+
+ /**
+ * Returns the path for this patch.
+ * @return The path for this patch.
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * Set the path for this patch.
+ * @param path The path for this patch.
+ */
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExceptionWrapper.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExceptionWrapper.java
new file mode 100644
index 000000000..38ec68b5d
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExceptionWrapper.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest;
+
+import javax.ws.rs.core.Response;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.glyptodon.guacamole.GuacamoleClientException;
+import org.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.GuacamoleSecurityException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A method interceptor to wrap some custom exception handling around methods
+ * that expose AuthenticationProvider functionality through the REST interface.
+ * Translates various types of GuacamoleExceptions into appropriate HTTP responses.
+ *
+ * @author James Muehlner
+ */
+public class AuthProviderRESTExceptionWrapper implements MethodInterceptor {
+
+ @Override
+ public Object invoke(MethodInvocation invocation) throws Throwable {
+
+ // Get the logger for the intercepted class
+ Logger logger = LoggerFactory.getLogger(invocation.getMethod().getDeclaringClass());
+
+ try {
+ return invocation.proceed();
+ }
+ catch(GuacamoleSecurityException e) {
+ throw new HTTPException(Response.Status.FORBIDDEN, e.getMessage() != null ? e.getMessage() : "Permission denied.");
+ }
+ catch(GuacamoleClientException e) {
+ throw new HTTPException(Response.Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request.");
+ }
+ catch(GuacamoleException e) {
+ logger.error("Unexpected GuacamoleException caught while executing " + invocation.getMethod().getName() + ".", e);
+ throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error.");
+ }
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExposure.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExposure.java
new file mode 100644
index 000000000..2a450c2c3
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/AuthProviderRESTExposure.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks that a method exposes functionality from the Guacamole AuthenticationProvider
+ * using a REST interface.
+ *
+ * @author James Muehlner
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface AuthProviderRESTExposure {}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java
new file mode 100644
index 000000000..b4d00d540
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+/**
+ * An exception that will result in the given HTTP Status and message or entity
+ * being returned from the API layer.
+ *
+ * @author James Muehlner
+ */
+public class HTTPException extends WebApplicationException {
+
+ /**
+ * Construct a new HTTPException with the given HTTP status and entity.
+ *
+ * @param status The HTTP Status to use for the response.
+ * @param entity The entity to use as the body of the response.
+ */
+ public HTTPException(Status status, Object entity) {
+ super(Response.status(status).entity(entity).build());
+ }
+
+ /**
+ * Construct a new HTTPException with the given HTTP status and message. The
+ * message will be wrapped in an APIError container.
+ *
+ * @param status The HTTP Status to use for the response.
+ * @param message The message to build the response entity with.
+ */
+ public HTTPException(Status status, String message) {
+ super(Response.status(status).entity(new APIError(message)).build());
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/PATCH.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/PATCH.java
new file mode 100644
index 000000000..bca1b3389
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/PATCH.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+package org.glyptodon.guacamole.net.basic.rest;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.ws.rs.HttpMethod;
+
+/**
+ * An annotation for using the HTTP PATCH method in the REST endpoints.
+ *
+ * @author James Muehlner
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@HttpMethod("PATCH")
+public @interface PATCH {}
\ No newline at end of file
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java
new file mode 100644
index 000000000..049f37a7e
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.matcher.Matchers;
+import org.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
+import org.glyptodon.guacamole.net.basic.properties.BasicGuacamoleProperties;
+import org.glyptodon.guacamole.net.basic.rest.auth.AuthTokenGenerator;
+import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
+import org.glyptodon.guacamole.net.basic.rest.auth.BasicTokenSessionMap;
+import org.glyptodon.guacamole.net.basic.rest.auth.SecureRandomAuthTokenGenerator;
+import org.glyptodon.guacamole.net.basic.rest.auth.TokenSessionMap;
+import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionService;
+import org.glyptodon.guacamole.net.basic.rest.connectiongroup.ConnectionGroupService;
+import org.glyptodon.guacamole.net.basic.rest.permission.PermissionService;
+import org.glyptodon.guacamole.net.basic.rest.protocol.ProtocolRetrievalService;
+import org.glyptodon.guacamole.net.basic.rest.user.UserService;
+import org.glyptodon.guacamole.properties.GuacamoleProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A Guice Module for setting up dependency injection for the
+ * Guacamole REST API.
+ *
+ * @author James Muehlner
+ */
+public class RESTModule extends AbstractModule {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(RESTModule.class);
+
+ /**
+ * The AuthenticationProvider to use to authenticate all requests.
+ */
+ private AuthenticationProvider authProvider;
+
+ @Override
+ protected void configure() {
+
+ // Get auth provider instance
+ try {
+ authProvider = GuacamoleProperties.getRequiredProperty(BasicGuacamoleProperties.AUTH_PROVIDER);
+ }
+ catch (GuacamoleException e) {
+ logger.error("Unable to read authentication provider from guacamole.properties: {}", e.getMessage());
+ logger.debug("Error reading authentication provider from guacamole.properties.", e);
+ throw new RuntimeException(e);
+ }
+
+ bind(AuthenticationProvider.class).toInstance(authProvider);
+ bind(TokenSessionMap.class).toInstance(new BasicTokenSessionMap());
+ bind(ConnectionService.class);
+ bind(ConnectionGroupService.class);
+ bind(PermissionService.class);
+ bind(UserService.class);
+ bind(AuthenticationService.class);
+ bind(ProtocolRetrievalService.class);
+
+ bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class);
+
+ bindInterceptor(Matchers.any(), Matchers.annotatedWith(AuthProviderRESTExposure.class), new AuthProviderRESTExceptionWrapper());
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java
new file mode 100644
index 000000000..399136fae
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest;
+
+import com.google.inject.Scopes;
+import com.google.inject.servlet.ServletModule;
+import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
+import org.codehaus.jackson.jaxrs.JacksonJsonProvider;
+import org.glyptodon.guacamole.net.basic.rest.auth.LoginRESTService;
+import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionRESTService;
+import org.glyptodon.guacamole.net.basic.rest.connectiongroup.ConnectionGroupRESTService;
+import org.glyptodon.guacamole.net.basic.rest.permission.PermissionRESTService;
+import org.glyptodon.guacamole.net.basic.rest.protocol.ProtocolRESTService;
+import org.glyptodon.guacamole.net.basic.rest.user.UserRESTService;
+
+/**
+ * A Guice Module to set up the servlet mappings for the Guacamole REST API.
+ *
+ * @author James Muehlner
+ */
+public class RESTServletModule extends ServletModule {
+
+ @Override
+ protected void configureServlets() {
+
+ // Set up the API endpoints
+ bind(ConnectionRESTService.class);
+ bind(ConnectionGroupRESTService.class);
+ bind(PermissionRESTService.class);
+ bind(ProtocolRESTService.class);
+ bind(UserRESTService.class);
+ bind(LoginRESTService.class);
+
+ // Set up the servlet and JSON mappings
+ bind(GuiceContainer.class);
+ bind(JacksonJsonProvider.class).in(Scopes.SINGLETON);
+ serve("/api/*").with(GuiceContainer.class);
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicLogin.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/APIAuthToken.java
similarity index 52%
rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicLogin.java
rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/APIAuthToken.java
index a462d5e4a..5d71c9edb 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/BasicLogin.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/APIAuthToken.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Glyptodon LLC
+ * Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,32 +20,50 @@
* THE SOFTWARE.
*/
-package org.glyptodon.guacamole.net.basic;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+package org.glyptodon.guacamole.net.basic.rest.auth;
/**
- * Simple dummy AuthenticatingHttpServlet which provides an endpoint for arbitrary
- * authentication requests that do not expect a response.
- *
- * @author Michael Jumper
+ * A simple object to represent an auth token/userID pair in the API.
+ *
+ * @author James Muehlner
*/
-public class BasicLogin extends RestrictedHttpServlet {
+public class APIAuthToken {
+
+ /**
+ * The auth token.
+ */
+ private final String authToken;
+
+
+ /**
+ * The user ID.
+ */
+ private final String userID;
/**
- * Logger for this class.
+ * Get the auth token.
+ * @return The auth token.
*/
- private final Logger logger = LoggerFactory.getLogger(BasicLogin.class);
-
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response) {
- logger.debug("Login was successful for user \"{}\".", context.self().getUsername());
+ public String getAuthToken() {
+ return authToken;
+ }
+
+ /**
+ * Get the user ID.
+ * @return The user ID.
+ */
+ public String getUserID() {
+ return userID;
+ }
+
+ /**
+ * Create a new APIAuthToken Object with the given auth token.
+ *
+ * @param authToken The auth token to create the new APIAuthToken with.
+ * @param userID The ID of the user owning the given token.
+ */
+ public APIAuthToken(String authToken, String userID) {
+ this.authToken = authToken;
+ this.userID = userID;
}
-
}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthTokenGenerator.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthTokenGenerator.java
new file mode 100644
index 000000000..1c35d580e
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthTokenGenerator.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.auth;
+
+/**
+ * Generates an auth token for an authenticated user.
+ *
+ * @author James Muehlner
+ */
+public interface AuthTokenGenerator {
+
+ /**
+ * Get a new auth token.
+ *
+ * @return A new auth token.
+ */
+ public String getToken();
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthenticationService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthenticationService.java
new file mode 100644
index 000000000..46eb082c4
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthenticationService.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.auth;
+
+import com.google.inject.Inject;
+import org.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.GuacamoleUnauthorizedException;
+import org.glyptodon.guacamole.net.auth.UserContext;
+import org.glyptodon.guacamole.net.basic.GuacamoleSession;
+
+/**
+ * A service for performing authentication checks in REST endpoints.
+ *
+ * @author James Muehlner
+ */
+public class AuthenticationService {
+
+ /**
+ * The map of auth tokens to sessions for the REST endpoints.
+ */
+ @Inject
+ private TokenSessionMap tokenSessionMap;
+
+ /**
+ * Finds the Guacamole session for a given auth token, if the auth token
+ * represents a currently logged in user. Throws an unauthorized error
+ * otherwise.
+ *
+ * @param authToken The auth token to check against the map of logged in users.
+ * @return The session that corresponds to the provided auth token.
+ * @throws GuacamoleException If the auth token does not correspond to any
+ * logged in user.
+ */
+ public GuacamoleSession getGuacamoleSession(String authToken)
+ throws GuacamoleException {
+
+ // Try to get the session from the map of logged in users.
+ GuacamoleSession session = tokenSessionMap.get(authToken);
+
+ // Authentication failed.
+ if (session == null)
+ throw new GuacamoleUnauthorizedException("Permission Denied.");
+
+ return session;
+
+ }
+
+ /**
+ * Finds the UserContext for a given auth token, if the auth token represents
+ * a currently logged in user. Throws an unauthorized error otherwise.
+ *
+ * @param authToken The auth token to check against the map of logged in users.
+ * @return The user context that corresponds to the provided auth token.
+ * @throws GuacamoleException If the auth token does not correspond to any
+ * logged in user.
+ */
+ public UserContext getUserContext(String authToken) throws GuacamoleException {
+ return getGuacamoleSession(authToken).getUserContext();
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenSessionMap.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenSessionMap.java
new file mode 100644
index 000000000..7b5a3e608
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenSessionMap.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.auth;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import org.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.net.basic.GuacamoleSession;
+import org.glyptodon.guacamole.net.basic.properties.BasicGuacamoleProperties;
+import org.glyptodon.guacamole.properties.GuacamoleProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A basic, HashMap-based implementation of the TokenSessionMap with support
+ * for session timeouts.
+ *
+ * @author James Muehlner
+ */
+public class BasicTokenSessionMap implements TokenSessionMap {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(BasicTokenSessionMap.class);
+
+ /**
+ * The last time a user with a specific auth token accessed the API.
+ */
+ private final Map lastAccessTimeMap = new HashMap();
+
+ /**
+ * Keeps track of the authToken to GuacamoleSession mapping.
+ */
+ private final Map sessionMap = new HashMap();
+
+ /**
+ * The session timeout configuration for an API session, in milliseconds.
+ */
+ private final long SESSION_TIMEOUT;
+
+ /**
+ * Create a new BasicTokenGuacamoleSessionMap and initialize the session timeout value.
+ */
+ public BasicTokenSessionMap() {
+
+ // Set up the SESSION_TIMEOUT value, with a one hour default.
+ long sessionTimeoutValue;
+ try {
+ sessionTimeoutValue = GuacamoleProperties.getProperty(BasicGuacamoleProperties.API_SESSION_TIMEOUT, 3600000l);
+ }
+ catch (GuacamoleException e) {
+ logger.error("Unexpected GuacamoleException caught while reading API_SESSION_TIMEOUT property. Defaulting to 1 hour.", e);
+ sessionTimeoutValue = 3600000l;
+ }
+
+ SESSION_TIMEOUT = sessionTimeoutValue;
+
+ }
+
+ /**
+ * Evict an authentication token from the map of logged in users and last
+ * access times.
+ *
+ * @param authToken The authentication token to evict.
+ */
+ private void evict(String authToken) {
+ sessionMap.remove(authToken);
+ lastAccessTimeMap.remove(authToken);
+ }
+
+ /**
+ * Log that the user represented by this auth token has just used the API.
+ *
+ * @param authToken The authentication token to record access time for.
+ */
+ private void logAccessTime(String authToken) {
+ lastAccessTimeMap.put(authToken, new Date().getTime());
+ }
+
+ /**
+ * Check if a session has timed out.
+ * @param authToken The auth token for the session.
+ * @return True if the session has timed out, false otherwise.
+ */
+ private boolean sessionHasTimedOut(String authToken) {
+
+ if (!lastAccessTimeMap.containsKey(authToken))
+ return true;
+
+ long lastAccessTime = lastAccessTimeMap.get(authToken);
+ long currentTime = new Date().getTime();
+
+ return currentTime - lastAccessTime > SESSION_TIMEOUT;
+
+ }
+
+ @Override
+ public GuacamoleSession get(String authToken) {
+
+ // If the session has timed out, evict the token and force the user to log in again
+ if (sessionHasTimedOut(authToken)) {
+ evict(authToken);
+ return null;
+ }
+
+ // Update the last access time and return the GuacamoleSession
+ logAccessTime(authToken);
+ return sessionMap.get(authToken);
+
+ }
+
+ @Override
+ public void put(String authToken, GuacamoleSession session) {
+
+ // Update the last access time, and create the token/GuacamoleSession mapping
+ logAccessTime(authToken);
+ sessionMap.put(authToken, session);
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java
new file mode 100644
index 000000000..b4a85adf8
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.auth;
+
+import com.google.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response.Status;
+import org.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.net.auth.AuthenticationProvider;
+import org.glyptodon.guacamole.net.auth.Credentials;
+import org.glyptodon.guacamole.net.auth.UserContext;
+import org.glyptodon.guacamole.net.basic.GuacamoleSession;
+import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure;
+import org.glyptodon.guacamole.net.basic.rest.HTTPException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A service for authenticating to the Guacamole REST API. Given valid
+ * credentials, the service will return an auth token. Invalid credentials will
+ * result in a permission error.
+ *
+ * @author James Muehlner
+ */
+
+@Path("/login")
+@Produces(MediaType.APPLICATION_JSON)
+public class LoginRESTService {
+
+ /**
+ * The authentication provider used to authenticate this user.
+ */
+ @Inject
+ private AuthenticationProvider authProvider;
+
+ /**
+ * The map of auth tokens to sessions for the REST endpoints.
+ */
+ @Inject
+ private TokenSessionMap tokenSessionMap;
+
+ /**
+ * A generator for creating new auth tokens.
+ */
+ @Inject
+ private AuthTokenGenerator authTokenGenerator;
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(LoginRESTService.class);
+
+ /**
+ * Authenticates a user, generates an auth token, associates that auth token
+ * with the user's UserContext for use by further requests.
+ *
+ * @param username The username of the user who is to be authenticated.
+ * @param password The password of the user who is to be authenticated.
+ * @param request The HttpServletRequest associated with the login attempt.
+ * @return The auth token for the newly logged-in user.
+ * @throws GuacamoleException If an error prevents successful login.
+ */
+ @POST
+ @AuthProviderRESTExposure
+ public APIAuthToken login(@QueryParam("username") String username,
+ @QueryParam("password") String password,
+ @Context HttpServletRequest request) throws GuacamoleException {
+
+ Credentials credentials = new Credentials();
+ credentials.setUsername(username);
+ credentials.setPassword(password);
+ credentials.setRequest(request);
+ credentials.setSession(request.getSession(true));
+
+ UserContext userContext;
+ try {
+ userContext = authProvider.getUserContext(credentials);
+ }
+ catch(GuacamoleException e) {
+ logger.error("Exception caught while authenticating user.", e);
+ throw new HTTPException(Status.INTERNAL_SERVER_ERROR,
+ "Unexpected server error.");
+ }
+
+ // Authentication failed.
+ if (userContext == null)
+ throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied.");
+
+ String authToken = authTokenGenerator.getToken();
+
+ tokenSessionMap.put(authToken, new GuacamoleSession(credentials, userContext));
+
+ logger.debug("Login was successful for user \"{}\".", userContext.self().getUsername());
+ return new APIAuthToken(authToken, username);
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/SecureRandomAuthTokenGenerator.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/SecureRandomAuthTokenGenerator.java
new file mode 100644
index 000000000..7fadd1014
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/SecureRandomAuthTokenGenerator.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.auth;
+
+import java.security.SecureRandom;
+import org.apache.commons.codec.binary.Hex;
+
+/**
+ * An implementation of the AuthTokenGenerator based around SecureRandom.
+ *
+ * @author James Muehlner
+ */
+public class SecureRandomAuthTokenGenerator implements AuthTokenGenerator {
+
+ /**
+ * Instance of SecureRandom for generating the auth token.
+ */
+ private final SecureRandom secureRandom = new SecureRandom();
+
+ @Override
+ public String getToken() {
+ byte[] bytes = new byte[32];
+ secureRandom.nextBytes(bytes);
+
+ return Hex.encodeHexString(bytes);
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenSessionMap.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenSessionMap.java
new file mode 100644
index 000000000..9991aab52
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenSessionMap.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.auth;
+
+import org.glyptodon.guacamole.net.basic.GuacamoleSession;
+
+/**
+ * Represents a mapping of auth token to Guacamole session for the REST
+ * authentication system.
+ *
+ * @author James Muehlner
+ */
+public interface TokenSessionMap {
+
+ /**
+ * Registers that a user has just logged in with the specified authToken and
+ * GuacamoleSession.
+ *
+ * @param authToken The authentication token for the logged in user.
+ * @param session The GuacamoleSession for the logged in user.
+ */
+ public void put(String authToken, GuacamoleSession session);
+
+ /**
+ * Get the GuacamoleSession for a logged in user. If the auth token does not
+ * represent a user who is currently logged in, returns null.
+ *
+ * @param authToken The authentication token for the logged in user.
+ * @return The GuacamoleSession for the given auth token, if the auth token
+ * represents a currently logged in user, null otherwise.
+ */
+ public GuacamoleSession get(String authToken);
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/package-info.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/package-info.java
similarity index 86%
rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/package-info.java
rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/package-info.java
index 73acc5540..ece55a39c 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/package-info.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Glyptodon LLC
+ * Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,7 +21,7 @@
*/
/**
- * Servlets dedicated to CRUD operations related to Connections.
+ * Classes related to the authentication aspect of the Guacamole REST API.
*/
-package org.glyptodon.guacamole.net.basic.crud.connections;
+package org.glyptodon.guacamole.net.basic.rest.auth;
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/CaptureClipboard.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/clipboard/ClipboardRESTService.java
similarity index 64%
rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/CaptureClipboard.java
rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/clipboard/ClipboardRESTService.java
index 4b1fc1cd4..972895cc4 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/CaptureClipboard.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/clipboard/ClipboardRESTService.java
@@ -20,26 +20,36 @@
* THE SOFTWARE.
*/
-package org.glyptodon.guacamole.net.basic;
+package org.glyptodon.guacamole.net.basic.rest.clipboard;
-import java.io.IOException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
+import com.google.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.GuacamoleServerException;
import org.glyptodon.guacamole.GuacamoleUnsupportedException;
-import org.glyptodon.guacamole.net.auth.UserContext;
+import org.glyptodon.guacamole.net.basic.ClipboardState;
+import org.glyptodon.guacamole.net.basic.GuacamoleSession;
+import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure;
+import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
import org.glyptodon.guacamole.properties.BooleanGuacamoleProperty;
import org.glyptodon.guacamole.properties.GuacamoleProperties;
/**
- * Servlet which dumps the current contents of the clipboard.
+ * A REST service for reading the current contents of the clipboard.
*
* @author Michael Jumper
*/
-public class CaptureClipboard extends RestrictedHttpServlet {
+@Path("/clipboard")
+public class ClipboardRESTService {
+ /**
+ * A service for authenticating users from auth tokens.
+ */
+ @Inject
+ private AuthenticationService authenticationService;
+
/**
* The amount of time to wait for clipboard changes, in milliseconds.
*/
@@ -55,30 +65,23 @@ public class CaptureClipboard extends RestrictedHttpServlet {
};
-
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
+ @GET
+ @AuthProviderRESTExposure
+ public Response getClipboard(@QueryParam("token") String authToken)
throws GuacamoleException {
// Only bother if actually enabled
if (GuacamoleProperties.getProperty(INTEGRATION_ENABLED, false)) {
// Get clipboard
- final HttpSession session = request.getSession(true);
- final ClipboardState clipboard = AuthenticatingFilter.getClipboardState(session);
+ GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
+ final ClipboardState clipboard = session.getClipboardState();
// Send clipboard contents
- try {
- synchronized (clipboard) {
- clipboard.waitForContents(CLIPBOARD_TIMEOUT);
- response.setContentType(clipboard.getMimetype());
- response.getOutputStream().write(clipboard.getContents());
- }
- }
- catch (IOException e) {
- throw new GuacamoleServerException("Unable to send clipboard contents", e);
+ synchronized (clipboard) {
+ clipboard.waitForContents(CLIPBOARD_TIMEOUT);
+ return Response.ok(clipboard.getContents(),
+ clipboard.getMimetype()).build();
}
}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java
new file mode 100644
index 000000000..dab0c3884
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.connection;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.net.auth.Connection;
+import org.glyptodon.guacamole.net.auth.ConnectionRecord;
+import org.glyptodon.guacamole.net.basic.rest.APIConstants;
+import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
+
+/**
+ * A simple connection to expose through the REST endpoints.
+ *
+ * @author James Muehlner
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class APIConnection {
+
+ /**
+ * The name of this connection.
+ */
+ private String name;
+
+ /**
+ * The identifier of this connection.
+ */
+ private String identifier;
+
+ /**
+ * The identifier of the parent connection group for this connection.
+ */
+ private String parentIdentifier;
+
+ /**
+ * The protocol of this connection.
+ */
+ private String protocol;
+
+ /**
+ * The history records associated with this connection.
+ */
+ private List extends ConnectionRecord> history;
+
+ /**
+ * Map of all associated parameter values, indexed by parameter name.
+ */
+ private Map parameters = new HashMap();
+
+ /**
+ * Create an empty APIConnection.
+ */
+ public APIConnection() {}
+
+ /**
+ * Create an APIConnection from a Connection record.
+ * @param connection The connection to create this APIConnection from.
+ * @throws GuacamoleException If a problem is encountered while
+ * instantiating this new APIConnection.
+ */
+ public APIConnection(Connection connection)
+ throws GuacamoleException {
+
+ this.name = connection.getName();
+ this.identifier = connection.getIdentifier();
+ this.parentIdentifier = connection.getParentIdentifier();
+ this.history = connection.getHistory();
+
+ // Use the explicit ROOT group ID
+ if (this.parentIdentifier == null)
+ this.parentIdentifier = APIConstants.ROOT_CONNECTION_GROUP_IDENTIFIER;
+
+ GuacamoleConfiguration configuration = connection.getConfiguration();
+
+ this.protocol = configuration.getProtocol();
+
+ for (String key: configuration.getParameterNames())
+ this.parameters.put(key, configuration.getParameter(key));
+
+ }
+
+ /**
+ * Returns the name of this connection.
+ * @return The name of this connection.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the name of this connection.
+ * @param name The name of this connection.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the unique identifier for this connection.
+ * @return The unique identifier for this connection.
+ */
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * Sets the unique identifier for this connection.
+ * @param identifier The unique identifier for this connection.
+ */
+ public void setIdentifier(String identifier) {
+ this.identifier = identifier;
+ }
+
+ /**
+ * Returns the unique identifier for this connection.
+ * @return The unique identifier for this connection.
+ */
+ public String getParentIdentifier() {
+ return parentIdentifier;
+ }
+
+ /**
+ * Sets the parent connection group identifier for this connection.
+ * @param parentIdentifier The parent connection group identifier
+ * for this connection.
+ */
+ public void setParentIdentifier(String parentIdentifier) {
+ this.parentIdentifier = parentIdentifier;
+ }
+
+ /**
+ * Returns the history records associated with this connection.
+ * @return The history records associated with this connection.
+ */
+ public List extends ConnectionRecord> getHistory() {
+ return history;
+ }
+
+ /**
+ * Returns the parameter map for this connection.
+ * @return The parameter map for this connection.
+ */
+ public Map getParameters() {
+ return parameters;
+ }
+
+ /**
+ * Sets the parameter map for this connection.
+ * @param parameters The parameter map for this connection.
+ */
+ public void setParameters(Map parameters) {
+ this.parameters = parameters;
+ }
+
+ /**
+ * Returns the protocol for this connection.
+ * @return The protocol for this connection.
+ */
+ public String getProtocol() {
+ return protocol;
+ }
+
+ /**
+ * Sets the protocol for this connection.
+ * @param protocol protocol for this connection.
+ */
+ public void setProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionWrapper.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionWrapper.java
new file mode 100644
index 000000000..aca765c40
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionWrapper.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.connection;
+
+import java.util.List;
+import java.util.Map;
+import org.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.net.GuacamoleSocket;
+import org.glyptodon.guacamole.net.auth.Connection;
+import org.glyptodon.guacamole.net.auth.ConnectionRecord;
+import org.glyptodon.guacamole.protocol.GuacamoleClientInformation;
+import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
+
+/**
+ * A wrapper to make an APIConnection look like a Connection. Useful where a
+ * org.glyptodon.guacamole.net.auth.Connection is required.
+ *
+ * @author James Muehlner
+ */
+public class APIConnectionWrapper implements Connection {
+
+ private final APIConnection apiConnection;
+
+ public APIConnectionWrapper(APIConnection apiConnection) {
+ this.apiConnection = apiConnection;
+ }
+
+ @Override
+ public String getName() {
+ return apiConnection.getName();
+ }
+
+ @Override
+ public void setName(String name) {
+ apiConnection.setName(name);
+ }
+
+ @Override
+ public String getIdentifier() {
+ return apiConnection.getIdentifier();
+ }
+
+ @Override
+ public void setIdentifier(String identifier) {
+ apiConnection.setIdentifier(identifier);
+ }
+
+ @Override
+ public String getParentIdentifier() {
+ return apiConnection.getParentIdentifier();
+ }
+
+ @Override
+ public void setParentIdentifier(String parentIdentifier) {
+ apiConnection.setParentIdentifier(parentIdentifier);
+ }
+
+ @Override
+ public GuacamoleConfiguration getConfiguration() {
+
+ // Create the GuacamoleConfiguration from the parameter map
+ GuacamoleConfiguration configuration = new GuacamoleConfiguration();
+
+ Map parameters = apiConnection.getParameters();
+
+ for(Map.Entry entry : parameters.entrySet())
+ configuration.setParameter(entry.getKey(), entry.getValue());
+
+ configuration.setProtocol(apiConnection.getProtocol());
+
+ return configuration;
+ }
+
+ @Override
+ public void setConfiguration(GuacamoleConfiguration config) {
+
+ // Create a parameter map from the GuacamoleConfiguration
+ Map parameters = apiConnection.getParameters();
+ for(String key : config.getParameterNames())
+ parameters.put(key, config.getParameter(key));
+
+ // Set the protocol
+ apiConnection.setProtocol(config.getProtocol());
+ }
+
+ @Override
+ public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException {
+ throw new UnsupportedOperationException("Operation not supported.");
+ }
+
+ @Override
+ public List extends ConnectionRecord> getHistory() throws GuacamoleException {
+ return apiConnection.getHistory();
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java
new file mode 100644
index 000000000..103b56643
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.connection;
+
+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 javax.ws.rs.core.Response.Status;
+import org.glyptodon.guacamole.GuacamoleClientException;
+import org.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.net.auth.Connection;
+import org.glyptodon.guacamole.net.auth.ConnectionGroup;
+import org.glyptodon.guacamole.net.auth.Directory;
+import org.glyptodon.guacamole.net.auth.UserContext;
+import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure;
+import org.glyptodon.guacamole.net.basic.rest.HTTPException;
+import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A REST Service for handling connection CRUD operations.
+ *
+ * @author James Muehlner
+ */
+@Path("/connection")
+@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;
+
+ /**
+ * A service for managing the REST endpoint APIConnection objects.
+ */
+ @Inject
+ private ConnectionService connectionService;
+
+ /**
+ * Gets a list of connections with the given ConnectionGroup parentID.
+ * If no parentID is provided, returns the connections from the root group.
+ *
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param parentID The ID of the ConnectionGroup the connections
+ * belong to. If null, the root connection group will be used.
+ * @return The connection list.
+ * @throws GuacamoleException If a problem is encountered while listing connections.
+ */
+ @GET
+ @AuthProviderRESTExposure
+ public List getConnections(@QueryParam("token") String authToken, @QueryParam("parentID") String parentID)
+ throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ // If the parent connection group is passed in, try to find it.
+ ConnectionGroup parentConnectionGroup;
+ if (parentID == null)
+ parentConnectionGroup = userContext.getRootConnectionGroup();
+
+ else {
+ ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
+ Directory connectionGroupDirectory = rootGroup.getConnectionGroupDirectory();
+ parentConnectionGroup = connectionGroupDirectory.get(parentID);
+ }
+
+ if (parentConnectionGroup == null)
+ throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID.");
+
+ Directory connectionDirectory =
+ parentConnectionGroup.getConnectionDirectory();
+
+ // Return the converted connection directory
+ return connectionService.convertConnectionList(connectionDirectory);
+
+ }
+
+ /**
+ * Gets an individual connection.
+ *
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param connectionID The ID of the Connection..
+ * @return The connection.
+ * @throws GuacamoleException If a problem is encountered while retrieving the connection.
+ */
+ @GET
+ @Path("/{connectionID}")
+ @AuthProviderRESTExposure
+ public APIConnection getConnection(@QueryParam("token") String authToken,
+ @PathParam("connectionID") String connectionID) throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ // Get the connection directory
+ ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
+ Directory connectionDirectory =
+ rootGroup.getConnectionDirectory();
+
+ // Get the connection
+ Connection connection = connectionDirectory.get(connectionID);
+ if (connection == null)
+ throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided ID.");
+
+ return new APIConnection(connection);
+
+ }
+
+ /**
+ * Deletes an individual connection.
+ *
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param connectionID The ID of the Connection to delete.
+ * @throws GuacamoleException If a problem is encountered while deleting the connection.
+ */
+ @DELETE
+ @Path("/{connectionID}")
+ @AuthProviderRESTExposure
+ public void deleteConnection(@QueryParam("token") String authToken, @PathParam("connectionID") String connectionID)
+ throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ // Get the connection directory
+ ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
+ Directory connectionDirectory =
+ rootGroup.getConnectionDirectory();
+
+ // Make sure the connection is there before trying to delete
+ if (connectionDirectory.get(connectionID) == null)
+ throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided ID.");
+
+ // Delete the connection
+ connectionDirectory.remove(connectionID);
+
+ }
+
+ /**
+ * Creates a new connection and returns the identifier of the new connection.
+ * If a parentID is provided, the connection will be created in the
+ * connection group with the parentID. Otherwise, the root connection group
+ * will be used.
+ *
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param parentID The ID of the ConnectionGroup the connections
+ * belong to. If null, the root connection group will be used.
+ * @param connection The connection to create.
+ * @return The identifier of the new connection.
+ * @throws GuacamoleException If a problem is encountered while creating the connection.
+ */
+ @POST
+ @AuthProviderRESTExposure
+ public String createConnection(@QueryParam("token") String authToken,
+ @QueryParam("parentID") String parentID, APIConnection connection) throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ if (connection == null)
+ throw new GuacamoleClientException("A connection is required for this request.");
+
+ // If the parent connection group is passed in, try to find it.
+ ConnectionGroup parentConnectionGroup;
+ if (parentID == null)
+ parentConnectionGroup = userContext.getRootConnectionGroup();
+
+ else {
+ ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
+ Directory connectionGroupDirectory = rootGroup.getConnectionGroupDirectory();
+ parentConnectionGroup = connectionGroupDirectory.get(parentID);
+ }
+
+ if (parentConnectionGroup == null)
+ throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID.");
+
+ Directory connectionDirectory =
+ parentConnectionGroup.getConnectionDirectory();
+
+ // Create the connection
+ connectionDirectory.add(new APIConnectionWrapper(connection));
+
+ // Return the new connection identifier
+ return connection.getIdentifier();
+
+ }
+
+ /**
+ * Updates a connection.
+ *
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param connectionID The ID of the Connection to move.
+ * @param connection The connection to update.
+ * @throws GuacamoleException If a problem is encountered while updating the connection.
+ */
+ @POST
+ @Path("/{connectionID}")
+ @AuthProviderRESTExposure
+ public void updateConnection(@QueryParam("token") String authToken,
+ @PathParam("connectionID") String connectionID, APIConnection connection) throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ if (connection == null)
+ throw new GuacamoleClientException("A connection is required for this request.");
+
+ // Get the connection directory
+ ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
+ Directory connectionDirectory =
+ rootGroup.getConnectionDirectory();
+
+ Connection connectionFromAuthProvider = connectionDirectory.get(connectionID);
+
+ // Make sure the connection is there before trying to update
+ if (connectionFromAuthProvider == null)
+ throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided ID.");
+
+ // Copy the information from this connection over to an object from the Auth Provider
+ APIConnectionWrapper wrappedConnection = new APIConnectionWrapper(connection);
+ connectionFromAuthProvider.setConfiguration(wrappedConnection.getConfiguration());
+ connectionFromAuthProvider.setName(wrappedConnection.getName());
+
+ // Update the connection
+ connectionDirectory.update(connectionFromAuthProvider);
+
+ }
+
+ /**
+ * Moves an individual connection to a different connection group.
+ *
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param connectionID The ID of the Connection to move.
+ * @param parentID The ID of the ConnectionGroup the connection is to be moved to.
+ * @throws GuacamoleException If a problem is encountered while moving the connection.
+ */
+ @PUT
+ @Path("/{connectionID}")
+ @AuthProviderRESTExposure
+ public void moveConnection(@QueryParam("token") String authToken,
+ @PathParam("connectionID") String connectionID, @QueryParam("parentID") String parentID)
+ throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ // Get the connection directory
+ ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
+ Directory connectionDirectory =
+ rootGroup.getConnectionDirectory();
+
+ // Find the new parent connection group
+ Directory connectionGroupDirectory = rootGroup.getConnectionGroupDirectory();
+ ConnectionGroup parentConnectionGroup = connectionGroupDirectory.get(parentID);
+
+ if (parentConnectionGroup == null)
+ throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID.");
+
+ // Move the connection
+ connectionDirectory.move(connectionID, parentConnectionGroup.getConnectionDirectory());
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/Delete.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java
similarity index 51%
rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/Delete.java
rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java
index 3c2957ce3..44449818d 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connections/Delete.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Glyptodon LLC
+ * Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,40 +20,40 @@
* THE SOFTWARE.
*/
-package org.glyptodon.guacamole.net.basic.crud.connections;
+package org.glyptodon.guacamole.net.basic.rest.connection;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import java.util.ArrayList;
+import java.util.List;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.Connection;
import org.glyptodon.guacamole.net.auth.Directory;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.net.basic.RestrictedHttpServlet;
/**
- * Simple HttpServlet which handles connection deletion.
- *
- * @author Michael Jumper
+ * A service for performing useful manipulations on REST Connections.
+ *
+ * @author James Muehlner
*/
-public class Delete extends RestrictedHttpServlet {
+public class ConnectionService {
+
+ /**
+ * Converts a Connection Directory to a list of APIConnection objects for
+ * exposing with the REST endpoints.
+ *
+ * @param connectionDirectory The Connection Directory to convert for REST endpoint use.
+ * @return A List of APIConnection objects for use with the REST endpoint.
+ * @throws GuacamoleException If an error occurs while converting the
+ * connection directory.
+ */
+ public List convertConnectionList(Directory connectionDirectory)
+ throws GuacamoleException {
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
- throws GuacamoleException {
-
- // Get ID
- String identifier = request.getParameter("id");
-
- // Attempt to get connection directory
- Directory directory =
- context.getRootConnectionGroup().getConnectionDirectory();
-
- // Remove connection
- directory.remove(identifier);
+ List restConnections = new ArrayList();
+
+ for (String connectionID : connectionDirectory.getIdentifiers())
+ restConnections.add(new APIConnection(connectionDirectory.get(connectionID)));
+
+ return restConnections;
}
}
-
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/package-info.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/package-info.java
similarity index 85%
rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/package-info.java
rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/package-info.java
index af137a42b..3d55216fd 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/connectiongroups/package-info.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Glyptodon LLC
+ * Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,7 +21,7 @@
*/
/**
- * Servlets dedicated to CRUD operations related to ConnectionGroups.
+ * Classes related to the connection manipulation aspect of the Guacamole REST API.
*/
-package org.glyptodon.guacamole.net.basic.crud.connectiongroups;
+package org.glyptodon.guacamole.net.basic.rest.connection;
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroup.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroup.java
new file mode 100644
index 000000000..4ac155d39
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroup.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.connectiongroup;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.glyptodon.guacamole.net.auth.ConnectionGroup;
+import org.glyptodon.guacamole.net.auth.ConnectionGroup.Type;
+import org.glyptodon.guacamole.net.basic.rest.APIConstants;
+
+/**
+ * A simple connection group to expose through the REST endpoints.
+ *
+ * @author James Muehlner
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class APIConnectionGroup {
+
+ /**
+ * The name of this connection group.
+ */
+ private String name;
+
+ /**
+ * The identifier of this connection group.
+ */
+ private String identifier;
+
+ /**
+ * The identifier of the parent connection group for this connection group.
+ */
+ private String parentIdentifier;
+
+ /**
+ * The type of this connection group.
+ */
+ private Type type;
+
+ /**
+ * Create an empty APIConnectionGroup.
+ */
+ public APIConnectionGroup() {}
+
+ /**
+ * Create a new APIConnectionGroup from the given ConnectionGroup record.
+ *
+ * @param connectionGroup The ConnectionGroup record to initialize this
+ * APIConnectionGroup from.
+ */
+ public APIConnectionGroup(ConnectionGroup connectionGroup) {
+
+ this.identifier = connectionGroup.getIdentifier();
+ this.parentIdentifier = connectionGroup.getParentIdentifier();
+
+ // Use the explicit ROOT group ID
+ if (this.parentIdentifier == null)
+ this.parentIdentifier = APIConstants.ROOT_CONNECTION_GROUP_IDENTIFIER;
+
+ this.name = connectionGroup.getName();
+ this.type = connectionGroup.getType();
+
+ }
+
+ /**
+ * Returns the name of this connection group.
+ * @return The name of this connection group.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the name of this connection group.
+ * @param name The name of this connection group.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Returns the identifier of this connection group.
+ * @return The identifier of this connection group.
+ */
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * Set the identifier of this connection group.
+ * @param identifier The identifier of this connection group.
+ */
+ public void setIdentifier(String identifier) {
+ this.identifier = identifier;
+ }
+
+ /**
+ * Returns the unique identifier for this connection group.
+ * @return The unique identifier for this connection group.
+ */
+ public String getParentIdentifier() {
+ return parentIdentifier;
+ }
+ /**
+ * Sets the parent connection group identifier for this connection group.
+ * @param parentIdentifier The parent connection group identifier
+ * for this connection group.
+ */
+ public void setParentIdentifier(String parentIdentifier) {
+ this.parentIdentifier = parentIdentifier;
+ }
+
+ /**
+ * Returns the type of this connection group.
+ * @return The type of this connection group.
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * Set the type of this connection group.
+ * @param type The Type of this connection group.
+ */
+ public void setType(Type type) {
+ this.type = type;
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroupWrapper.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroupWrapper.java
new file mode 100644
index 000000000..c38b22977
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroupWrapper.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.connectiongroup;
+
+import org.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.net.GuacamoleSocket;
+import org.glyptodon.guacamole.net.auth.Connection;
+import org.glyptodon.guacamole.net.auth.ConnectionGroup;
+import org.glyptodon.guacamole.net.auth.Directory;
+import org.glyptodon.guacamole.protocol.GuacamoleClientInformation;
+
+/**
+ * A wrapper to make an APIConnection look like a ConnectionGroup.
+ * Useful where a org.glyptodon.guacamole.net.auth.ConnectionGroup is required.
+ *
+ * @author James Muehlner
+ */
+public class APIConnectionGroupWrapper implements ConnectionGroup {
+
+ /**
+ * The wrapped APIConnectionGroup.
+ */
+ private final APIConnectionGroup apiConnectionGroup;
+
+ /**
+ * Create a new APIConnectionGroupWrapper to wrap the given
+ * APIConnectionGroup as a ConnectionGroup.
+ * @param apiConnectionGroup the APIConnectionGroup to wrap.
+ */
+ public APIConnectionGroupWrapper(APIConnectionGroup apiConnectionGroup) {
+ this.apiConnectionGroup = apiConnectionGroup;
+ }
+
+ @Override
+ public String getName() {
+ return apiConnectionGroup.getName();
+ }
+
+ @Override
+ public void setName(String name) {
+ apiConnectionGroup.setName(name);
+ }
+
+ @Override
+ public String getIdentifier() {
+ return apiConnectionGroup.getIdentifier();
+ }
+
+ @Override
+ public void setIdentifier(String identifier) {
+ apiConnectionGroup.setIdentifier(identifier);
+ }
+
+ @Override
+ public String getParentIdentifier() {
+ return apiConnectionGroup.getParentIdentifier();
+ }
+
+ @Override
+ public void setParentIdentifier(String parentIdentifier) {
+ apiConnectionGroup.setParentIdentifier(parentIdentifier);
+ }
+
+ @Override
+ public void setType(Type type) {
+ apiConnectionGroup.setType(type);
+ }
+
+ @Override
+ public Type getType() {
+ return apiConnectionGroup.getType();
+ }
+
+ @Override
+ public Directory getConnectionDirectory() throws GuacamoleException {
+ throw new UnsupportedOperationException("Operation not supported.");
+ }
+
+ @Override
+ public Directory getConnectionGroupDirectory() throws GuacamoleException {
+ throw new UnsupportedOperationException("Operation not supported.");
+ }
+
+ @Override
+ public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException {
+ throw new UnsupportedOperationException("Operation not supported.");
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java
new file mode 100644
index 000000000..aa04a5f60
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.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 javax.ws.rs.core.Response.Status;
+import org.glyptodon.guacamole.GuacamoleClientException;
+import org.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.net.auth.ConnectionGroup;
+import org.glyptodon.guacamole.net.auth.Directory;
+import org.glyptodon.guacamole.net.auth.UserContext;
+import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure;
+import org.glyptodon.guacamole.net.basic.rest.HTTPException;
+import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A REST Service for handling connection group CRUD operations.
+ *
+ * @author James Muehlner
+ */
+@Path("/connectionGroup")
+@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;
+
+ /**
+ * A service for managing the REST endpoint APIConnection objects.
+ */
+ @Inject
+ private ConnectionGroupService connectionGroupService;
+
+ /**
+ * The ID that will be guaranteed to refer to the root connection group.
+ */
+ private static final String ROOT_CONNECTION_GROUP_ID = "ROOT";
+
+ /**
+ * Gets a list of connection groups with the given ConnectionGroup parentID.
+ * If no parentID is provided, returns the connection groups from the root group.
+ *
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param parentID The ID of the ConnectionGroup the connection groups
+ * belong to. If null, the root connection group will be used.
+ * @return The connection list.
+ * @throws GuacamoleException If a problem is encountered while listing connection groups.
+ */
+ @GET
+ @AuthProviderRESTExposure
+ public List getConnectionGroups(@QueryParam("token") String authToken, @QueryParam("parentID") String parentID)
+ throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ // If the parent connection group is passed in, try to find it.
+ ConnectionGroup parentConnectionGroup;
+ if (parentID == null)
+ parentConnectionGroup = userContext.getRootConnectionGroup();
+
+ else {
+ ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
+ Directory connectionGroupDirectory = rootGroup.getConnectionGroupDirectory();
+ parentConnectionGroup = connectionGroupDirectory.get(parentID);
+ }
+
+ if (parentConnectionGroup == null)
+ throw new HTTPException(Status.NOT_FOUND, "No connection group found with the provided parentID.");
+
+ Directory connectionGroupDirectory =
+ parentConnectionGroup.getConnectionGroupDirectory();
+
+ // Return the converted connection group list
+ return connectionGroupService.convertConnectionGroupList(connectionGroupDirectory);
+ }
+
+ /**
+ * Gets an individual connection group.
+ *
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param connectionGroupID The ID of the ConnectionGroup.
+ * @return The connection group.
+ * @throws GuacamoleException If a problem is encountered while retrieving the connection group.
+ */
+ @GET
+ @Path("/{connectionGroupID}")
+ @AuthProviderRESTExposure
+ public APIConnectionGroup getConnectionGroup(@QueryParam("token") String authToken,
+ @PathParam("connectionGroupID") String connectionGroupID) throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ // Get the connection group directory
+ ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
+
+ // Return the root group if it was asked for
+ if (connectionGroupID != null && connectionGroupID.equals(ROOT_CONNECTION_GROUP_ID))
+ return new APIConnectionGroup(rootGroup);
+
+ Directory connectionGroupDirectory =
+ rootGroup.getConnectionGroupDirectory();
+
+ // Get the connection group
+ ConnectionGroup connectionGroup = connectionGroupDirectory.get(connectionGroupID);
+ if (connectionGroup == null)
+ throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided ID.");
+
+ // Return the connectiion group
+ return new APIConnectionGroup(connectionGroup);
+
+ }
+
+ /**
+ * Deletes an individual connection group.
+ *
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param connectionGroupID The ID of the ConnectionGroup to delete.
+ * @throws GuacamoleException If a problem is encountered while deleting the connection group.
+ */
+ @DELETE
+ @Path("/{connectionGroupID}")
+ @AuthProviderRESTExposure
+ public void deleteConnectionGroup(@QueryParam("token") String authToken,
+ @PathParam("connectionGroupID") String connectionGroupID) throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ // Get the connection group directory
+ ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
+
+ // Use the root group if it was asked for
+ if (connectionGroupID != null && connectionGroupID.equals(ROOT_CONNECTION_GROUP_ID))
+ connectionGroupID = rootGroup.getIdentifier();
+
+ Directory connectionGroupDirectory =
+ rootGroup.getConnectionGroupDirectory();
+
+ // Make sure the connection is there before trying to delete
+ if (connectionGroupDirectory.get(connectionGroupID) == null)
+ throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided ID.");
+
+ // Delete the connection group
+ connectionGroupDirectory.remove(connectionGroupID);
+
+ }
+
+ /**
+ * Creates a new connection group and returns the identifier of the new connection group.
+ * If a parentID is provided, the connection group will be created in the
+ * connection group with the parentID. Otherwise, the root connection group
+ * will be used.
+ *
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param parentID The ID of the ConnectionGroup the connection groups
+ * belong to. If null, the root connection group will be used.
+ * @param connectionGroup The connection group to create.
+ * @return The identifier of the new connection group.
+ * @throws GuacamoleException If a problem is encountered while creating the connection group.
+ */
+ @POST
+ @AuthProviderRESTExposure
+ public String createConnectionGroup(@QueryParam("token") String authToken,
+ @QueryParam("parentID") String parentID, APIConnectionGroup connectionGroup) throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ if (connectionGroup == null)
+ throw new GuacamoleClientException("A connection group is required for this request.");
+
+ // If the parent connection group is passed in, try to find it.
+ ConnectionGroup parentConnectionGroup;
+ if (parentID == null)
+ parentConnectionGroup = userContext.getRootConnectionGroup();
+
+ else {
+ ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
+ Directory connectionGroupDirectory = rootGroup.getConnectionGroupDirectory();
+ parentConnectionGroup = connectionGroupDirectory.get(parentID);
+ }
+
+ if (parentConnectionGroup == null)
+ throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID.");
+
+ Directory connectionGroupDirectory =
+ parentConnectionGroup.getConnectionGroupDirectory();
+
+ // Create the connection group
+ connectionGroupDirectory.add(new APIConnectionGroupWrapper(connectionGroup));
+
+ // Return the new connection group identifier
+ return connectionGroup.getIdentifier();
+
+ }
+
+ /**
+ * Updates a connection group.
+ *
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param connectionGroupID The ID of the ConnectionGroup to update.
+ * @param connectionGroup The connection group to update.
+ * @throws GuacamoleException If a problem is encountered while updating the connection group.
+ */
+ @POST
+ @Path("/{connectionGroupID}")
+ @AuthProviderRESTExposure
+ public void updateConnectionGroup(@QueryParam("token") String authToken,
+ @PathParam("connectionGroupID") String connectionGroupID, APIConnectionGroup connectionGroup)
+ throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ if (connectionGroup == null)
+ throw new GuacamoleClientException("A connection group is required for this request.");
+
+ // Get the connection directory
+ ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
+
+ // Use the root group if it was asked for
+ if (connectionGroupID != null && connectionGroupID.equals(ROOT_CONNECTION_GROUP_ID))
+ connectionGroupID = rootGroup.getIdentifier();
+
+ Directory connectionGroupDirectory =
+ rootGroup.getConnectionGroupDirectory();
+
+ // Make sure the connection group is there before trying to update
+ if (connectionGroupDirectory.get(connectionGroupID) == null)
+ throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided ID.");
+
+ // Update the connection group
+ connectionGroupDirectory.update(new APIConnectionGroupWrapper(connectionGroup));
+
+ }
+
+ /**
+ * Moves an individual connection group to a different connection group.
+ *
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param connectionGroupID The ID of the ConnectionGroup to move.
+ * @param parentID The ID of the ConnectionGroup the connection group is to be moved to.
+ * @throws GuacamoleException If a problem is encountered while moving the connection group.
+ */
+ @PUT
+ @Path("/{connectionGroupID}")
+ @AuthProviderRESTExposure
+ public void moveConnectionGroup(@QueryParam("token") String authToken,
+ @PathParam("connectionGroupID") String connectionGroupID,
+ @QueryParam("parentID") String parentID) throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ // Get the connection group directory
+ ConnectionGroup rootGroup = userContext.getRootConnectionGroup();
+
+ // Use the root group if it was asked for
+ if (connectionGroupID != null && connectionGroupID.equals(ROOT_CONNECTION_GROUP_ID))
+ connectionGroupID = rootGroup.getIdentifier();
+
+ Directory connectionGroupDirectory =
+ rootGroup.getConnectionGroupDirectory();
+
+ // Find the new parent connection group
+ Directory newConnectionGroupDirectory = rootGroup.getConnectionGroupDirectory();
+ ConnectionGroup parentConnectionGroup = newConnectionGroupDirectory.get(parentID);
+
+ if (parentConnectionGroup == null)
+ throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID.");
+
+ // Move the connection group
+ connectionGroupDirectory.move(connectionGroupID, parentConnectionGroup.getConnectionGroupDirectory());
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java
new file mode 100644
index 000000000..ecbe5fffd
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.connectiongroup;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.net.auth.ConnectionGroup;
+import org.glyptodon.guacamole.net.auth.Directory;
+
+/**
+ * A service for performing useful manipulations on REST ConnectionGroups.
+ *
+ * @author James Muehlner
+ */
+public class ConnectionGroupService {
+
+ /**
+ * Converts a ConnectionGroup directory to a list of APIConnectionGroup
+ * objects for exposing with the REST endpoints.
+ *
+ * @param connectionGroupDirectory The ConnectionGroup Directory to convert for REST endpoint use.
+ * @return A List of APIConnectionGroup objects for use with the REST endpoint.
+ * @throws GuacamoleException If an error occurs while converting the
+ * connection group directory.
+ */
+ public List convertConnectionGroupList(
+ Directory connectionGroupDirectory) throws GuacamoleException {
+
+ List restConnectionGroups = new ArrayList();
+
+ for (String connectionGroupID : connectionGroupDirectory.getIdentifiers())
+ restConnectionGroups.add(new APIConnectionGroup(connectionGroupDirectory.get(connectionGroupID)));
+
+ return restConnectionGroups;
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/package-info.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/package-info.java
new file mode 100644
index 000000000..dc404cbc1
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * Classes related to the connection group manipulation aspect
+ * of the Guacamole REST API.
+ */
+package org.glyptodon.guacamole.net.basic.rest.connectiongroup;
+
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/package-info.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/package-info.java
similarity index 87%
rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/package-info.java
rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/package-info.java
index bad264863..31a00e57c 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/package-info.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Glyptodon LLC
+ * Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,7 +21,7 @@
*/
/**
- * Servlets dedicated to CRUD operations related to Users.
+ * Classes related to the basic Guacamole REST API.
*/
-package org.glyptodon.guacamole.net.basic.crud.users;
+package org.glyptodon.guacamole.net.basic.rest;
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermission.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermission.java
new file mode 100644
index 000000000..ee97de0fd
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermission.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.permission;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+import org.glyptodon.guacamole.net.auth.permission.ConnectionGroupPermission;
+import org.glyptodon.guacamole.net.auth.permission.ConnectionPermission;
+import org.glyptodon.guacamole.net.auth.permission.ObjectPermission;
+import org.glyptodon.guacamole.net.auth.permission.Permission;
+import org.glyptodon.guacamole.net.auth.permission.SystemPermission;
+import org.glyptodon.guacamole.net.auth.permission.UserPermission;
+
+/**
+ * A simple user permission to expose through the REST endpoints.
+ *
+ * @author James Muehlner
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+public class APIPermission {
+
+ /**
+ * Create an empty APIPermission.
+ */
+ public APIPermission() {}
+
+ /**
+ * The type of object that this permission refers to.
+ */
+ private ObjectType objectType;
+
+ /**
+ * The type of object that a permission can refer to.
+ */
+ public enum ObjectType {
+
+ /**
+ * A normal connection.
+ */
+ CONNECTION,
+
+ /**
+ * A connection group.
+ */
+ CONNECTION_GROUP,
+
+ /**
+ * A Guacamole user.
+ */
+ USER,
+
+ /**
+ * The Guacamole system itself.
+ */
+ SYSTEM
+
+ }
+
+ /**
+ * The identifier of the object that this permission refers to.
+ */
+ private String objectIdentifier;
+
+ /**
+ * The object permission type for this APIPermission, if relevant. This is
+ * only used if this.objectType is CONNECTION, CONNECTION_GROUP, or USER.
+ */
+ private ObjectPermission.Type objectPermissionType;
+
+ /**
+ * The system permission type for this APIPermission, if relevant. This is
+ * only used if this.objectType is SYSTEM.
+ */
+ private SystemPermission.Type systemPermissionType;
+
+ /**
+ * Create an APIConnection from a Connection record.
+ *
+ * @param permission The permission to create this APIPermission from.
+ */
+ public APIPermission(Permission permission) {
+
+ // Connection permission
+ if (permission instanceof ConnectionPermission) {
+ this.objectType = ObjectType.CONNECTION;
+ this.objectPermissionType = ((ConnectionPermission) permission).getType();
+ this.objectIdentifier = ((ConnectionPermission) permission).getObjectIdentifier();
+ }
+
+ // Connection group permission
+ else if (permission instanceof ConnectionGroupPermission) {
+ this.objectType = ObjectType.CONNECTION_GROUP;
+ this.objectPermissionType = ((ConnectionGroupPermission) permission).getType();
+ this.objectIdentifier = ((ConnectionGroupPermission) permission).getObjectIdentifier();
+ }
+
+ // User permission
+ else if (permission instanceof UserPermission) {
+ this.objectType = ObjectType.USER;
+ this.objectPermissionType = ((UserPermission) permission).getType();
+ this.objectIdentifier = ((UserPermission) permission).getObjectIdentifier();
+ }
+
+ // System permission
+ else if (permission instanceof SystemPermission) {
+ this.objectType = ObjectType.SYSTEM;
+ this.systemPermissionType = ((SystemPermission) permission).getType();
+ }
+
+ }
+
+ /**
+ * Returns the type of object that this permission refers to.
+ *
+ * @return The type of object that this permission refers to.
+ */
+ public ObjectType getObjectType() {
+ return objectType;
+ }
+
+ /**
+ * Set the type of object that this permission refers to.
+ * @param objectType The type of object that this permission refers to.
+ */
+ public void setObjectType(ObjectType objectType) {
+ this.objectType = objectType;
+ }
+
+ /**
+ * Returns a string representation of the permission type.
+ *
+ * @return A string representation of the permission type.
+ */
+ public String getPermissionType() {
+ switch(this.objectType) {
+ case CONNECTION:
+ case CONNECTION_GROUP:
+ case USER:
+ return this.objectPermissionType.toString();
+ case SYSTEM:
+ return this.systemPermissionType.toString();
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Set the permission type from a string representation of that type.
+ * Since it's not clear at this point whether this is an object permission or
+ * system permission, try to set both of them.
+ *
+ * @param permissionType The string representation of the permission type.
+ */
+ public void setPermissionType(String permissionType) {
+ try {
+ this.objectPermissionType = ObjectPermission.Type.valueOf(permissionType);
+ } catch(IllegalArgumentException e) {}
+
+ try {
+ this.systemPermissionType = SystemPermission.Type.valueOf(permissionType);
+ } catch(IllegalArgumentException e) {}
+ }
+
+ /**
+ * Returns the identifier of the object that this permission refers to.
+ *
+ * @return The identifier of the object that this permission refers to.
+ */
+ public String getObjectIdentifier() {
+ return objectIdentifier;
+ }
+
+ /**
+ * Set the identifier of the object that this permission refers to.
+ *
+ * @param objectIdentifier The identifier of the object that this permission refers to.
+ */
+ public void setObjectIdentifier(String objectIdentifier) {
+ this.objectIdentifier = objectIdentifier;
+ }
+
+ /**
+ * Returns an org.glyptodon.guacamole.net.auth.permission.Permission
+ * representation of this APIPermission.
+ *
+ * @return An org.glyptodon.guacamole.net.auth.permission.Permission
+ * representation of this APIPermission.
+ */
+ public Permission toPermission() {
+ switch(this.objectType) {
+ case CONNECTION:
+ return new ConnectionPermission
+ (this.objectPermissionType, this.objectIdentifier);
+ case CONNECTION_GROUP:
+ return new ConnectionGroupPermission
+ (this.objectPermissionType, this.objectIdentifier);
+ case USER:
+ return new UserPermission
+ (this.objectPermissionType, this.objectIdentifier);
+ case SYSTEM:
+ return new SystemPermission(this.systemPermissionType);
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java
new file mode 100644
index 000000000..08e49812b
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.permission;
+
+import com.google.inject.Inject;
+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 javax.ws.rs.core.Response.Status;
+import org.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.net.auth.Directory;
+import org.glyptodon.guacamole.net.auth.User;
+import org.glyptodon.guacamole.net.auth.UserContext;
+import org.glyptodon.guacamole.net.auth.permission.Permission;
+import org.glyptodon.guacamole.net.basic.rest.APIPatch;
+import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure;
+import org.glyptodon.guacamole.net.basic.rest.HTTPException;
+import org.glyptodon.guacamole.net.basic.rest.PATCH;
+import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A REST Service for handling connection CRUD operations.
+ *
+ * @author James Muehlner
+ */
+@Path("/permission")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class PermissionRESTService {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(PermissionRESTService.class);
+
+ /**
+ * A service for authenticating users from auth tokens.
+ */
+ @Inject
+ private AuthenticationService authenticationService;
+
+ /**
+ * A service for managing the REST endpoint APIPermission objects.
+ */
+ @Inject
+ private PermissionService permissionService;
+
+ /**
+ * Gets a list of permissions for the user with the given userID.
+ *
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param userID The ID of the user to retrieve permissions for.
+ * @return The permission list.
+ * @throws GuacamoleException If a problem is encountered while listing permissions.
+ */
+ @GET
+ @Path("/{userID}")
+ @AuthProviderRESTExposure
+ public List getPermissions(@QueryParam("token") String authToken, @PathParam("userID") String userID)
+ throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ // Get the user
+ User user = userContext.getUserDirectory().get(userID);
+ if (user == null)
+ throw new HTTPException(Status.NOT_FOUND, "User not found with the provided userID.");
+
+ return permissionService.convertPermissionList(user.getPermissions());
+
+ }
+
+ /**
+ * Adds a permissions for a user with the given userID.
+ *
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param userID The user ID to add the permission for.
+ * @param permission The permission to add for the user with the given userID.
+ * @throws GuacamoleException If a problem is encountered while adding the permission.
+ */
+ @POST
+ @Path("/{userID}")
+ @AuthProviderRESTExposure
+ public void addPermission(@QueryParam("token") String authToken,
+ @PathParam("userID") String userID, APIPermission permission)
+ throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ // Get the user
+ User user = userContext.getUserDirectory().get(userID);
+ if (user == null)
+ throw new HTTPException(Status.NOT_FOUND, "User not found with the provided userID.");
+
+ // Add the new permission
+ user.addPermission(permission.toPermission());
+ userContext.getUserDirectory().update(user);
+
+ }
+
+ /**
+ * Removes a permissions for a user with the given userID.
+ *
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param userID The user ID to remove the permission for.
+ * @param permission The permission to remove for the user with the given userID.
+ * @throws GuacamoleException If a problem is encountered while removing the permission.
+ */
+ @POST
+ @Path("/remove/{userID}/")
+ @AuthProviderRESTExposure
+ public void removePermission(@QueryParam("token") String authToken,
+ @PathParam("userID") String userID, APIPermission permission)
+ throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ // Get the user
+ User user = userContext.getUserDirectory().get(userID);
+ if (user == null)
+ throw new HTTPException(Status.NOT_FOUND, "User not found with the provided userID.");
+
+ // Remove the permission
+ user.removePermission(permission.toPermission());
+ userContext.getUserDirectory().update(user);
+
+ }
+
+
+ /**
+ * Applies a given list of permission patches.
+ *
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param patches The permission patches to apply for this request.
+ * @throws GuacamoleException If a problem is encountered while removing the permission.
+ */
+ @PATCH
+ @AuthProviderRESTExposure
+ public void patchPermissions(@QueryParam("token") String authToken,
+ List> patches) throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ // Get the user directory
+ Directory userDirectory = userContext.getUserDirectory();
+
+ // All users who have had permissions added or removed
+ Map modifiedUsers = new HashMap();
+
+ for (APIPatch patch : patches) {
+
+ String userID = patch.getPath();
+ Permission permission = patch.getValue().toPermission();
+
+ // See if we've already modified this user in this request
+ User user = modifiedUsers.get(userID);
+ if (user == null)
+ user = userDirectory.get(userID);
+
+ if (user == null)
+ throw new HTTPException(Status.NOT_FOUND, "User not found with userID " + userID + ".");
+
+ // Only the add and remove operations are supported for permissions
+ switch(patch.getOp()) {
+ case add:
+ user.addPermission(permission);
+ modifiedUsers.put(userID, user);
+ break;
+ case remove:
+ user.removePermission(permission);
+ modifiedUsers.put(userID, user);
+ break;
+ }
+
+ }
+
+ // Save the permission changes for all modified users
+ for (User user : modifiedUsers.values())
+ userDirectory.update(user);
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java
new file mode 100644
index 000000000..bdff3d84b
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.permission;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.glyptodon.guacamole.net.auth.permission.Permission;
+
+/**
+ * A service for performing useful manipulations on REST Permissions.
+ *
+ * @author James Muehlner
+ */
+public class PermissionService {
+
+ /**
+ * Converts a list of Permission to a list of APIPermission objects for
+ * exposing with the REST endpoints.
+ *
+ * @param permissions The Connections to convert for REST endpoint use.
+ * @return A List of APIPermission objects for use with the REST endpoint.
+ */
+ public List convertPermissionList(Iterable extends Permission> permissions) {
+
+ List restPermissions = new ArrayList();
+
+ for(Permission permission : permissions)
+ restPermissions.add(new APIPermission(permission));
+
+ return restPermissions;
+
+ }
+
+ /**
+ * Converts a list of APIPermission to a set of Permission objects for internal
+ * Guacamole use.
+ *
+ * @param restPermissions The APIPermission objects from the REST endpoints.
+ * @return a List of Permission objects for internal Guacamole use.
+ */
+ public Set convertAPIPermissionList(Iterable restPermissions) {
+
+ Set permissions = new HashSet();
+
+ for(APIPermission restPermission : restPermissions)
+ permissions.add(restPermission.toPermission());
+
+ return permissions;
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/package-info.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/package-info.java
new file mode 100644
index 000000000..274d74b0b
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * Classes related to the permission manipulation aspect of the Guacamole REST API.
+ */
+package org.glyptodon.guacamole.net.basic.rest.permission;
+
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/protocol/ProtocolRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/protocol/ProtocolRESTService.java
new file mode 100644
index 000000000..dacdf8f56
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/protocol/ProtocolRESTService.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.protocol;
+
+import com.google.inject.Inject;
+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.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.net.basic.ProtocolInfo;
+import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A REST Service for handling the listing of protocols.
+ *
+ * @author James Muehlner
+ */
+@Path("/protocol")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class ProtocolRESTService {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(ProtocolRESTService.class);
+
+ /**
+ * Service for retrieving protocol definitions.
+ */
+ @Inject
+ private ProtocolRetrievalService protocolRetrievalservice;
+
+ /**
+ * Gets a map of protocols defined in the system - protocol name to protocol.
+ *
+ * @return The protocol map.
+ * @throws GuacamoleException If a problem is encountered while listing protocols.
+ */
+ @GET
+ @AuthProviderRESTExposure
+ public Map getProtocols() throws GuacamoleException {
+
+ // Get and return a map of all protocols.
+ return protocolRetrievalservice.getProtocolMap();
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/protocols/List.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/protocol/ProtocolRetrievalService.java
similarity index 51%
rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/protocols/List.java
rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/protocol/ProtocolRetrievalService.java
index f8eda88e9..9f8958f8d 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/protocols/List.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/protocol/ProtocolRetrievalService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Glyptodon LLC
+ * Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,7 +20,7 @@
* THE SOFTWARE.
*/
-package org.glyptodon.guacamole.net.basic.crud.protocols;
+package org.glyptodon.guacamole.net.basic.rest.protocol;
import java.io.BufferedInputStream;
import java.io.File;
@@ -30,18 +30,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.xml.stream.XMLOutputFactory;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
import org.glyptodon.guacamole.GuacamoleException;
-import org.glyptodon.guacamole.GuacamoleServerException;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.net.basic.RestrictedHttpServlet;
import org.glyptodon.guacamole.net.basic.ProtocolInfo;
-import org.glyptodon.guacamole.net.basic.ProtocolParameter;
-import org.glyptodon.guacamole.net.basic.ProtocolParameterOption;
import org.glyptodon.guacamole.net.basic.xml.DocumentHandler;
import org.glyptodon.guacamole.net.basic.xml.protocol.ProtocolTagHandler;
import org.glyptodon.guacamole.properties.GuacamoleHome;
@@ -53,18 +43,18 @@ import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
/**
- * Simple HttpServlet which outputs XML containing a list of all visible
- * protocols.
- *
- * @author Michael Jumper
+ * A service for retrieving protocol definitions from the
+ * XML files storing the information.
+ *
+ * @author James Muehlner
*/
-public class List extends RestrictedHttpServlet {
+public class ProtocolRetrievalService {
/**
* Logger for this class.
*/
- private Logger logger = LoggerFactory.getLogger(List.class);
-
+ private static final Logger logger = LoggerFactory.getLogger(ProtocolRetrievalService.class);
+
/**
* Array of all known protocol names.
*/
@@ -108,110 +98,22 @@ public class List extends RestrictedHttpServlet {
}
catch (IOException e) {
- throw new GuacamoleException("Error reading basic user mapping file.", e);
+ throw new GuacamoleException(e);
}
catch (SAXException e) {
- throw new GuacamoleException("Error parsing basic user mapping XML.", e);
+ throw new GuacamoleException(e);
}
}
/**
- * Given an XML stream and a fully-populated ProtocolInfo object, writes
- * out the corresponding protocol XML describing all available parameters.
+ * Retrieves a map of protocol name to ProtocolInfo for all protocols defined
+ * in the XML files.
*
- * @param xml The XMLStreamWriter to use to write the XML.
- * @param protocol The ProtocolInfo object to read parameters and protocol
- * information from.
- * @throws XMLStreamException If an error occurs while writing the XML.
+ * @return A map of all defined protocols.
+ * @throws GuacamoleException If an error occurs while retrieving the protocols.
*/
- private void writeProtocol(XMLStreamWriter xml, ProtocolInfo protocol)
- throws XMLStreamException {
-
- // Write protocol
- xml.writeStartElement("protocol");
- xml.writeAttribute("name", protocol.getName());
- xml.writeAttribute("title", protocol.getTitle());
-
- // Write parameters
- for (ProtocolParameter param : protocol.getParameters()) {
-
- // Write param tag
- xml.writeStartElement("param");
- xml.writeAttribute("name", param.getName());
- xml.writeAttribute("title", param.getTitle());
-
- // Write type
- switch (param.getType()) {
-
- // Text parameter
- case TEXT:
- xml.writeAttribute("type", "text");
- break;
-
- // Password parameter
- case PASSWORD:
- xml.writeAttribute("type", "password");
- break;
-
- // Numeric parameter
- case NUMERIC:
- xml.writeAttribute("type", "numeric");
- break;
-
- // Boolean parameter
- case BOOLEAN:
- xml.writeAttribute("type", "boolean");
- xml.writeAttribute("value", param.getValue());
- break;
-
- // Enumerated parameter
- case ENUM:
- xml.writeAttribute("type", "enum");
- break;
-
- // Multiline parameter
- case MULTILINE:
- xml.writeAttribute("type", "multiline");
- break;
-
- // If unknown, fail explicitly
- default:
- throw new UnsupportedOperationException(
- "Parameter type not supported: " + param.getType());
-
- }
-
- // Write options
- for (ProtocolParameterOption option : param.getOptions()) {
- xml.writeStartElement("option");
- xml.writeAttribute("value", option.getValue());
- xml.writeCharacters(option.getTitle());
- xml.writeEndElement();
- }
-
- // End parameter
- xml.writeEndElement();
-
- }
-
- // End protocol
- xml.writeEndElement();
-
- }
-
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
- throws GuacamoleException {
-
- // Do not cache
- response.setHeader("Cache-Control", "no-cache");
-
- // Set encoding
- response.setCharacterEncoding("UTF-8");
-
+ public Map getProtocolMap() throws GuacamoleException {
// Map of all available protocols
Map protocols = new HashMap();
@@ -225,20 +127,16 @@ public class List extends RestrictedHttpServlet {
// Get all XML files
File[] files = protocol_directory.listFiles(
new FilenameFilter() {
-
@Override
public boolean accept(File file, String string) {
return string.endsWith(".xml");
}
-
}
);
// Load each protocol from each file
for (File file : files) {
-
try {
-
// Parse protocol
FileInputStream stream = new FileInputStream(file);
ProtocolInfo protocol = getProtocol(stream);
@@ -252,9 +150,7 @@ public class List extends RestrictedHttpServlet {
logger.error("Unable to read connection parameter information from \"{}\": {}", file.getAbsolutePath(), e.getMessage());
logger.debug("Error reading protocol XML.", e);
}
-
}
-
}
// If known protocols are not already defined, read from classpath
@@ -262,8 +158,7 @@ public class List extends RestrictedHttpServlet {
// If protocol not defined yet, attempt to load from classpath
if (!protocols.containsKey(protocol)) {
-
- InputStream stream = List.class.getResourceAsStream(
+ InputStream stream = ProtocolRetrievalService.class.getResourceAsStream(
"/net/sourceforge/guacamole/net/protocols/"
+ protocol + ".xml");
@@ -272,39 +167,9 @@ public class List extends RestrictedHttpServlet {
protocols.put(protocol, getProtocol(stream));
}
-
}
-
- // Write actual XML
- try {
- // Write XML content type
- response.setHeader("Content-Type", "text/xml");
-
- XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
- XMLStreamWriter xml = outputFactory.createXMLStreamWriter(response.getWriter());
-
- // Begin document
- xml.writeStartDocument();
- xml.writeStartElement("protocols");
-
- // Write all protocols
- for (ProtocolInfo protocol : protocols.values())
- writeProtocol(xml, protocol);
-
- // End document
- xml.writeEndElement();
- xml.writeEndDocument();
-
- }
- catch (XMLStreamException e) {
- throw new GuacamoleServerException(
- "Unable to write protocol list XML.", e);
- }
- catch (IOException e) {
- throw new GuacamoleServerException(
- "I/O error writing protocol list XML.", e);
- }
-
+
+ return protocols;
}
}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/protocols/package-info.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/protocol/package-info.java
similarity index 86%
rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/protocols/package-info.java
rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/protocol/package-info.java
index ada38e392..445dc5cef 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/protocols/package-info.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/protocol/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Glyptodon LLC
+ * Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,7 +21,7 @@
*/
/**
- * Servlets dedicated to CRUD operations related to protocols.
+ * Classes related to the protocol retrieval aspect of the Guacamole REST API.
*/
-package org.glyptodon.guacamole.net.basic.crud.protocols;
+package org.glyptodon.guacamole.net.basic.rest.protocol;
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUser.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUser.java
new file mode 100644
index 000000000..2efcb6992
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUser.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.user;
+
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+import org.glyptodon.guacamole.net.auth.User;
+
+/**
+ * A simple User to expose through the REST endpoints.
+ *
+ * @author James Muehlner
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+public class APIUser {
+
+ /**
+ * The username of this user.
+ */
+ private String username;
+
+ /**
+ * The password of this user.
+ */
+ private String password;
+
+ /**
+ * Construct a new empty APIUser.
+ */
+ public APIUser() {}
+
+ /**
+ * Construct a new APIUser from the provided User.
+ * @param user The User to construct the APIUser from.
+ */
+ public APIUser(User user) {
+ this.username = user.getUsername();
+ this.password = user.getPassword();
+ }
+
+ /**
+ * Returns the username for this user.
+ * @return The username for this user.
+ */
+ public String getUsername() {
+ return username;
+ }
+
+ /**
+ * Set the username for this user.
+ * @param username The username for this user.
+ */
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ /**
+ * Returns the password for this user.
+ * @return The password for this user.
+ */
+ public String getPassword() {
+ return password;
+ }
+
+ /**
+ * Set the password for this user.
+ * @param password The password for this user.
+ */
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUserWrapper.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUserWrapper.java
new file mode 100644
index 000000000..dec5a9a65
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUserWrapper.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.user;
+
+import java.util.Collections;
+import java.util.Set;
+import org.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.net.auth.User;
+import org.glyptodon.guacamole.net.auth.permission.Permission;
+
+/**
+ * A wrapper to make an APIConnection look like a User. Useful where a
+ * org.glyptodon.guacamole.net.auth.User is required.
+ *
+ * @author James Muehlner
+ */
+public class APIUserWrapper implements User {
+
+ /**
+ * The wrapped APIUser.
+ */
+ private final APIUser apiUser;
+
+ /**
+ * The set of permissions for this user.
+ * NOTE: Not exposed by the REST endpoints.
+ */
+ private Set permissionSet = Collections.EMPTY_SET;
+
+ /**
+ * Wrap a given APIUser to expose as a User.
+ * @param apiUser The APIUser to wrap.
+ */
+ public APIUserWrapper(APIUser apiUser) {
+ this.apiUser = apiUser;
+ }
+
+ /**
+ * Wrap a given APIUser to expose as a User, with the given permission set.
+ * @param apiUser The APIUser to wrap.
+ * @param permissionSet The set of permissions for the wrapped user.
+ */
+ public APIUserWrapper(APIUser apiUser, Set permissionSet) {
+ this.apiUser = apiUser;
+ this.permissionSet = permissionSet;
+ }
+
+ @Override
+ public String getUsername() {
+ return apiUser.getUsername();
+ }
+
+ @Override
+ public void setUsername(String username) {
+ apiUser.setUsername(username);
+ }
+
+ @Override
+ public String getPassword() {
+ return apiUser.getPassword();
+ }
+
+ @Override
+ public void setPassword(String password) {
+ apiUser.setPassword(password);
+ }
+
+ @Override
+ public Set getPermissions() throws GuacamoleException {
+ return permissionSet;
+ }
+
+ @Override
+ public boolean hasPermission(Permission permission) throws GuacamoleException {
+ return permissionSet.contains(permission);
+ }
+
+ @Override
+ public void addPermission(Permission permission) throws GuacamoleException {
+ throw new UnsupportedOperationException("Operation not supported.");
+ }
+
+ @Override
+ public void removePermission(Permission permission) throws GuacamoleException {
+ throw new UnsupportedOperationException("Operation not supported.");
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java
new file mode 100644
index 000000000..176615b1d
--- /dev/null
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2014 Glyptodon LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package org.glyptodon.guacamole.net.basic.rest.user;
+
+import com.google.inject.Inject;
+import java.util.List;
+import java.util.UUID;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+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 org.glyptodon.guacamole.GuacamoleException;
+import org.glyptodon.guacamole.net.auth.Directory;
+import org.glyptodon.guacamole.net.auth.User;
+import org.glyptodon.guacamole.net.auth.UserContext;
+import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure;
+import org.glyptodon.guacamole.net.basic.rest.HTTPException;
+import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A REST Service for handling user CRUD operations.
+ *
+ * @author James Muehlner
+ */
+@Path("/user")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class UserRESTService {
+
+ /**
+ * Logger for this class.
+ */
+ private static final Logger logger = LoggerFactory.getLogger(UserRESTService.class);
+
+ /**
+ * A service for authenticating users from auth tokens.
+ */
+ @Inject
+ private AuthenticationService authenticationService;
+
+ /**
+ * A service for managing the REST endpoint APIPermission objects.
+ */
+ @Inject
+ private UserService userService;
+
+ /**
+ * Gets a list of users in the system.
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @return The user list.
+ * @throws GuacamoleException If a problem is encountered while listing users.
+ */
+ @GET
+ @AuthProviderRESTExposure
+ public List getUsers(@QueryParam("token") String authToken) throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ // Get the directory
+ Directory userDirectory = userContext.getUserDirectory();
+
+ // Convert and return the user directory listing
+ return userService.convertUserList(userDirectory);
+
+ }
+
+ /**
+ * Gets an individual user.
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param userID The ID of the user to retrieve.
+ * @return user The user.
+ * @throws GuacamoleException If a problem is encountered while retrieving the user.
+ */
+ @GET
+ @Path("/{userID}")
+ @AuthProviderRESTExposure
+ public APIUser getUser(@QueryParam("token") String authToken, @PathParam("userID") String userID)
+ throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ // Get the directory
+ Directory userDirectory = userContext.getUserDirectory();
+
+ // Get the user
+ User user = userDirectory.get(userID);
+ if (user == null)
+ throw new HTTPException(Response.Status.NOT_FOUND, "User not found with the provided userID.");
+
+ // Return the user
+ return new APIUser(user);
+
+ }
+
+ /**
+ * Creates a new user and returns the username.
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param user The new user to create.
+ * @throws GuacamoleException If a problem is encountered while creating the user.
+ *
+ * @return The username of the newly created user.
+ */
+ @POST
+ @AuthProviderRESTExposure
+ public String createUser(@QueryParam("token") String authToken, APIUser user)
+ throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ // 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.getUsername();
+
+ }
+
+ /**
+ * Updates an individual existing user.
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param userID The unique identifier of the user to update.
+ * @param user The updated user.
+ * @throws GuacamoleException If a problem is encountered while updating the user.
+ */
+ @POST
+ @Path("/{userID}")
+ @AuthProviderRESTExposure
+ public void updateUser(@QueryParam("token") String authToken, @PathParam("userID") String userID, APIUser user)
+ throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ // Get the directory
+ Directory userDirectory = userContext.getUserDirectory();
+
+ if (!user.getUsername().equals(userID))
+ throw new HTTPException(Response.Status.BAD_REQUEST, "Username does not match provided userID.");
+
+ // Get the user
+ User existingUser = userDirectory.get(userID);
+ if (existingUser == null)
+ throw new HTTPException(Response.Status.NOT_FOUND, "User not found with the provided userID.");
+
+ // Do not update the user password if no password was provided
+ if (user.getPassword() != null) {
+ /*
+ * Update the user with the permission set from the existing user
+ * since the user REST endpoints do not expose permissions.
+ */
+ existingUser.setPassword(user.getPassword());
+ userDirectory.update(existingUser);
+ }
+
+ }
+
+ /**
+ * Deletes an individual existing user.
+ * @param authToken The authentication token that is used to authenticate
+ * the user performing the operation.
+ * @param userID The unique identifier of the user to delete.
+ * @throws GuacamoleException If a problem is encountered while deleting the user.
+ */
+ @DELETE
+ @Path("/{userID}")
+ @AuthProviderRESTExposure
+ public void deleteUser(@QueryParam("token") String authToken, @PathParam("userID") String userID)
+ throws GuacamoleException {
+
+ UserContext userContext = authenticationService.getUserContext(authToken);
+
+ // Get the directory
+ Directory userDirectory = userContext.getUserDirectory();
+
+ // Get the user
+ User existingUser = userDirectory.get(userID);
+ if (existingUser == null)
+ throw new HTTPException(Response.Status.NOT_FOUND, "User not found with the provided userID.");
+
+ // Delete the user
+ userDirectory.remove(userID);
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/Delete.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserService.java
similarity index 54%
rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/Delete.java
rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserService.java
index 8eb5916d3..c671b5246 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/users/Delete.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Glyptodon LLC
+ * Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,39 +20,40 @@
* THE SOFTWARE.
*/
-package org.glyptodon.guacamole.net.basic.crud.users;
+package org.glyptodon.guacamole.net.basic.rest.user;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import java.util.ArrayList;
+import java.util.List;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.Directory;
import org.glyptodon.guacamole.net.auth.User;
-import org.glyptodon.guacamole.net.auth.UserContext;
-import org.glyptodon.guacamole.net.basic.RestrictedHttpServlet;
/**
- * Simple HttpServlet which handles user deletion.
- *
- * @author Michael Jumper
+ * A service for performing useful manipulations on REST Users.
+ *
+ * @author James Muehlner
*/
-public class Delete extends RestrictedHttpServlet {
+public class UserService {
+
+ /**
+ * Converts a user directory to a list of APIUser objects for
+ * exposing with the REST endpoints.
+ *
+ * @param userDirectory The user directory to convert for REST endpoint use.
+ * @return A List of APIUser objects for use with the REST endpoint.
+ * @throws GuacamoleException If an error occurs while converting the
+ * user directory.
+ */
+ public List convertUserList(Directory userDirectory)
+ throws GuacamoleException {
- @Override
- protected void restrictedService(
- UserContext context,
- HttpServletRequest request, HttpServletResponse response)
- throws GuacamoleException {
-
- // Get username
- String username = request.getParameter("name");
-
- // Attempt to get user directory
- Directory directory = context.getUserDirectory();
-
- // Remove user
- directory.remove(username);
+ List restUsers = new ArrayList();
+
+ for(String username : userDirectory.getIdentifiers())
+ restUsers.add(new APIUser(userDirectory.get(username)));
+
+ return restUsers;
}
-
+
}
-
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/permissions/package-info.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/package-info.java
similarity index 86%
rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/permissions/package-info.java
rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/package-info.java
index 9617b8020..d45b5f4f8 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/crud/permissions/package-info.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Glyptodon LLC
+ * Copyright (C) 2014 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,7 +21,7 @@
*/
/**
- * Servlets dedicated to CRUD operations related to Permissions.
+ * Classes related to the user manipulation aspect of the Guacamole REST API.
*/
-package org.glyptodon.guacamole.net.basic.crud.permissions;
+package org.glyptodon.guacamole.net.basic.rest.user;
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/BasicGuacamoleWebSocketTunnelEndpoint.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/BasicGuacamoleWebSocketTunnelEndpoint.java
index 77db63f68..0dbc11efc 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/BasicGuacamoleWebSocketTunnelEndpoint.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/BasicGuacamoleWebSocketTunnelEndpoint.java
@@ -22,25 +22,22 @@
package org.glyptodon.guacamole.net.basic.websocket;
+import com.google.inject.Provider;
import java.util.Map;
import javax.websocket.EndpointConfig;
import javax.websocket.HandshakeResponse;
import javax.websocket.Session;
import javax.websocket.server.HandshakeRequest;
-import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.GuacamoleTunnel;
-import org.glyptodon.guacamole.net.basic.BasicTunnelRequestUtility;
+import org.glyptodon.guacamole.net.basic.TunnelRequestService;
import org.glyptodon.guacamole.websocket.GuacamoleWebSocketTunnelEndpoint;
/**
* Tunnel implementation which uses WebSocket as a tunnel backend, rather than
* HTTP, properly parsing connection IDs included in the connection request.
*/
-@ServerEndpoint(value = "/websocket-tunnel",
- subprotocols = {"guacamole"},
- configurator = BasicGuacamoleWebSocketTunnelEndpoint.Configurator.class)
public class BasicGuacamoleWebSocketTunnelEndpoint extends GuacamoleWebSocketTunnelEndpoint {
/**
@@ -62,6 +59,25 @@ public class BasicGuacamoleWebSocketTunnelEndpoint extends GuacamoleWebSocketTun
*/
public static class Configurator extends ServerEndpointConfig.Configurator {
+ /**
+ * Provider which provides instances of a service for handling
+ * tunnel requests.
+ */
+ private final Provider tunnelRequestServiceProvider;
+
+ /**
+ * Creates a new Configurator which uses the given tunnel request
+ * service provider to retrieve the necessary service to handle new
+ * connections requests.
+ *
+ * @param tunnelRequestServiceProvider The tunnel request service
+ * provider to use for all new
+ * connections.
+ */
+ public Configurator(Provider tunnelRequestServiceProvider) {
+ this.tunnelRequestServiceProvider = tunnelRequestServiceProvider;
+ }
+
@Override
public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
@@ -72,8 +88,11 @@ public class BasicGuacamoleWebSocketTunnelEndpoint extends GuacamoleWebSocketTun
userProperties.clear();
try {
+ // Get tunnel request service
+ TunnelRequestService tunnelRequestService = tunnelRequestServiceProvider.get();
+
// Store new tunnel within user properties
- GuacamoleTunnel tunnel = BasicTunnelRequestUtility.createTunnel(new WebSocketTunnelRequest(request));
+ GuacamoleTunnel tunnel = tunnelRequestService.createTunnel(new WebSocketTunnelRequest(request));
if (tunnel != null)
userProperties.put(TUNNEL_USER_PROPERTY, tunnel);
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/jetty8/BasicGuacamoleWebSocketTunnelServlet.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/jetty8/BasicGuacamoleWebSocketTunnelServlet.java
index f9e6106a3..035e8d3c1 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/jetty8/BasicGuacamoleWebSocketTunnelServlet.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/jetty8/BasicGuacamoleWebSocketTunnelServlet.java
@@ -22,10 +22,12 @@
package org.glyptodon.guacamole.net.basic.websocket.jetty8;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import javax.servlet.http.HttpServletRequest;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.GuacamoleTunnel;
-import org.glyptodon.guacamole.net.basic.BasicTunnelRequestUtility;
+import org.glyptodon.guacamole.net.basic.TunnelRequestService;
import org.glyptodon.guacamole.net.basic.HTTPTunnelRequest;
/**
@@ -33,12 +35,19 @@ import org.glyptodon.guacamole.net.basic.HTTPTunnelRequest;
* rather than HTTP, properly parsing connection IDs included in the connection
* request.
*/
+@Singleton
public class BasicGuacamoleWebSocketTunnelServlet extends GuacamoleWebSocketTunnelServlet {
+ /**
+ * Service for handling tunnel requests.
+ */
+ @Inject
+ private TunnelRequestService tunnelRequestService;
+
@Override
protected GuacamoleTunnel doConnect(HttpServletRequest request)
throws GuacamoleException {
- return BasicTunnelRequestUtility.createTunnel(new HTTPTunnelRequest(request));
+ return tunnelRequestService.createTunnel(new HTTPTunnelRequest(request));
}
}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/jetty9/BasicGuacamoleWebSocketTunnelListener.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/jetty9/BasicGuacamoleWebSocketTunnelListener.java
index 093bcbfaa..12c4660f2 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/jetty9/BasicGuacamoleWebSocketTunnelListener.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/jetty9/BasicGuacamoleWebSocketTunnelListener.java
@@ -22,10 +22,12 @@
package org.glyptodon.guacamole.net.basic.websocket.jetty9;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import org.eclipse.jetty.websocket.api.Session;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.GuacamoleTunnel;
-import org.glyptodon.guacamole.net.basic.BasicTunnelRequestUtility;
+import org.glyptodon.guacamole.net.basic.TunnelRequestService;
/**
* WebSocket listener implementation which properly parses connection IDs
@@ -33,11 +35,18 @@ import org.glyptodon.guacamole.net.basic.BasicTunnelRequestUtility;
*
* @author Michael Jumper
*/
+@Singleton
public class BasicGuacamoleWebSocketTunnelListener extends GuacamoleWebSocketTunnelListener {
+ /**
+ * Service for handling tunnel requests.
+ */
+ @Inject
+ private TunnelRequestService tunnelRequestService;
+
@Override
protected GuacamoleTunnel createTunnel(Session session) throws GuacamoleException {
- return BasicTunnelRequestUtility.createTunnel(new WebSocketTunnelRequest(session.getUpgradeRequest()));
+ return tunnelRequestService.createTunnel(new WebSocketTunnelRequest(session.getUpgradeRequest()));
}
}
diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/tomcat/BasicGuacamoleWebSocketTunnelServlet.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/tomcat/BasicGuacamoleWebSocketTunnelServlet.java
index e54cf8bdb..d0de4cbf3 100644
--- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/tomcat/BasicGuacamoleWebSocketTunnelServlet.java
+++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/websocket/tomcat/BasicGuacamoleWebSocketTunnelServlet.java
@@ -22,10 +22,12 @@
package org.glyptodon.guacamole.net.basic.websocket.tomcat;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
import javax.servlet.http.HttpServletRequest;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.GuacamoleTunnel;
-import org.glyptodon.guacamole.net.basic.BasicTunnelRequestUtility;
+import org.glyptodon.guacamole.net.basic.TunnelRequestService;
import org.glyptodon.guacamole.net.basic.HTTPTunnelRequest;
/**
@@ -33,12 +35,19 @@ import org.glyptodon.guacamole.net.basic.HTTPTunnelRequest;
* rather than HTTP, properly parsing connection IDs included in the connection
* request.
*/
+@Singleton
public class BasicGuacamoleWebSocketTunnelServlet extends GuacamoleWebSocketTunnelServlet {
+ /**
+ * Service for handling tunnel requests.
+ */
+ @Inject
+ private TunnelRequestService tunnelRequestService;
+
@Override
protected GuacamoleTunnel doConnect(HttpServletRequest request)
throws GuacamoleException {
- return BasicTunnelRequestUtility.createTunnel(new HTTPTunnelRequest(request));
+ return tunnelRequestService.createTunnel(new HTTPTunnelRequest(request));
};
}
diff --git a/guacamole/src/main/webapp/WEB-INF/web.xml b/guacamole/src/main/webapp/WEB-INF/web.xml
index 7c9985f62..a59ddbe24 100644
--- a/guacamole/src/main/webapp/WEB-INF/web.xml
+++ b/guacamole/src/main/webapp/WEB-INF/web.xml
@@ -20,286 +20,35 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
-
+
index.xhtml
-
- 30
-
+ 30
-
-
- org.glyptodon.guacamole.net.basic.websocket.WebSocketSupportLoader
-
-
-
-
- org.glyptodon.guacamole.net.basic.log.LogbackInitializer
-
-
-
+
- AuthenticatingFilter
- org.glyptodon.guacamole.net.basic.AuthenticatingFilter
+ guiceFilter
+ com.google.inject.servlet.GuiceFilter
- AuthenticatingFilter
- *.xhtml
- /login
- /clipboard
- /connections/*
- /connectiongroups/*
- /users/*
- /permissions/*
- /protocols/*
+ guiceFilter
+ /*
-
-
- RestrictedFilter
- org.glyptodon.guacamole.net.basic.RestrictedFilter
-
-
- RestrictedFilter
- /websocket-tunnel
-
+
+ org.glyptodon.guacamole.net.basic.BasicServletContextListener
+
-
-
- Login servlet.
- Login
- org.glyptodon.guacamole.net.basic.BasicLogin
-
-
- Login
- /login
-
-
-
-
- Logout servlet.
- Logout
- org.glyptodon.guacamole.net.basic.BasicLogout
-
-
- Logout
- /logout
-
-
-
-
- Session keep-alive servlet.
- SessionKeepAlive
- org.glyptodon.guacamole.net.basic.SessionKeepAlive
-
-
- SessionKeepAlive
- /keep-alive
-
-
-
-
- Clipboard state servlet.
- Clipboard
- org.glyptodon.guacamole.net.basic.CaptureClipboard
-
-
- Clipboard
- /clipboard
-
-
-
-
- Connection creation servlet.
- ConnectionCreate
- org.glyptodon.guacamole.net.basic.crud.connections.Create
-
-
- ConnectionCreate
- /connections/create
-
-
-
-
- Connection list servlet.
- Connections
- org.glyptodon.guacamole.net.basic.crud.connections.List
-
-
- Connections
- /connections
-
-
-
-
- Connection update servlet.
- ConnectionUpdate
- org.glyptodon.guacamole.net.basic.crud.connections.Update
-
-
- ConnectionUpdate
- /connections/update
-
-
-
-
- Connection move servlet.
- ConnectionMove
- org.glyptodon.guacamole.net.basic.crud.connections.Move
-
-
- ConnectionMove
- /connections/move
-
-
-
-
- Connection deletion servlet.
- ConnectionDelete
- org.glyptodon.guacamole.net.basic.crud.connections.Delete
-
-
- ConnectionDelete
- /connections/delete
-
-
-
-
- ConnectionGroup creation servlet.
- ConnectionGroupCreate
- org.glyptodon.guacamole.net.basic.crud.connectiongroups.Create
-
-
- ConnectionGroupCreate
- /connectiongroups/create
-
-
-
-
- ConnectionGroup list servlet.
- ConnectionGroups
- org.glyptodon.guacamole.net.basic.crud.connectiongroups.List
-
-
- ConnectionGroups
- /connectiongroups
-
-
-
-
- ConnectionGroup update servlet.
- ConnectionGroupUpdate
- org.glyptodon.guacamole.net.basic.crud.connectiongroups.Update
-
-
- ConnectionGroupUpdate
- /connectiongroups/update
-
-
-
-
- ConnectionGroup move servlet.
- ConnectionGroupMove
- org.glyptodon.guacamole.net.basic.crud.connectiongroups.Move
-
-
- ConnectionGroupMove
- /connectiongroups/move
-
-
-
-
- ConnectionGroup deletion servlet.
- ConnectionGroupDelete
- org.glyptodon.guacamole.net.basic.crud.connectiongroups.Delete
-
-
- ConnectionGroupDelete
- /connectiongroups/delete
-
-
-
-
- User creation servlet.
- UserCreate
- org.glyptodon.guacamole.net.basic.crud.users.Create
-
-
- UserCreate
- /users/create
-
-
-
-
- User list servlet.
- Users
- org.glyptodon.guacamole.net.basic.crud.users.List
-
-
- Users
- /users
-
-
-
-
- User update servlet.
- UserUpdate
- org.glyptodon.guacamole.net.basic.crud.users.Update
-
-
- UserUpdate
- /users/update
-
-
-
-
- User deletion servlet.
- UserDelete
- org.glyptodon.guacamole.net.basic.crud.users.Delete
-
-
- UserDelete
- /users/delete
-
-
-
-
- Permission list servlet.
- Permissions
- org.glyptodon.guacamole.net.basic.crud.permissions.List
-
-
- Permissions
- /permissions
-
-
-
-
- Protocol list servlet.
- Protocols
- org.glyptodon.guacamole.net.basic.crud.protocols.List
-
-
- Protocols
- /protocols
-
-
-
-
- Tunnel servlet.
- Tunnel
- org.glyptodon.guacamole.net.basic.BasicGuacamoleTunnelServlet
-
-
- Tunnel
- /tunnel
-
-
+
mp3
audio/mpeg