mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-31 00:53:21 +00:00 
			
		
		
		
	GUAC-919: Copy REST and Guice changes over from GUAC-546 branch.
This commit is contained in:
		
				
					committed by
					
						 Michael Jumper
						Michael Jumper
					
				
			
			
				
	
			
			
			
						parent
						
							48382b8285
						
					
				
				
					commit
					6bf2ff3e2f
				
			| @@ -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 | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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."); | ||||
|  | ||||
|   | ||||
| @@ -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() | ||||
|         ); | ||||
|          | ||||
|     } | ||||
| 
 | ||||
|      | ||||
| } | ||||
| 
 | ||||
| @@ -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<Object> listeners = new ArrayList<Object>(); | ||||
|      | ||||
|     /** | ||||
|      * 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<Class> 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<Object> getListeners() { | ||||
|         return Collections.unmodifiableCollection(listeners); | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -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 | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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; | ||||
|  | ||||
| } | ||||
| @@ -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()); | ||||
|   | ||||
| @@ -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> servlet = (Class<Servlet>) | ||||
|             Class<HttpServlet> servlet = (Class<HttpServlet>) | ||||
|                     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<TunnelRequestService> 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; | ||||
|             } | ||||
| @@ -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) { | ||||
| @@ -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<String, ConnectionGroup> findConnectionGroupDirectory( | ||||
|             UserContext context, String parentID) throws GuacamoleException { | ||||
|          | ||||
|         // Find the correct connection group directory | ||||
|         ConnectionGroup rootGroup = context.getRootConnectionGroup(); | ||||
|         Directory<String, ConnectionGroup> directory; | ||||
|          | ||||
|         Directory<String, ConnectionGroup> connectionGroupDirectory =  | ||||
|             rootGroup.getConnectionGroupDirectory(); | ||||
|  | ||||
|         ConnectionGroup parentGroup = connectionGroupDirectory.get(parentID); | ||||
|  | ||||
|         if(parentGroup == null) | ||||
|             return null; | ||||
|  | ||||
|         directory = parentGroup.getConnectionGroupDirectory(); | ||||
|          | ||||
|         return directory; | ||||
|     } | ||||
| } | ||||
| @@ -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<String, ConnectionGroup> 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); | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -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<String, ConnectionGroup> directory = | ||||
|                 context.getRootConnectionGroup().getConnectionGroupDirectory(); | ||||
|  | ||||
|         // Remove connection | ||||
|         directory.remove(identifier); | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -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<String, Connection> getConnectionDirectory() throws GuacamoleException { | ||||
|         throw new UnsupportedOperationException("Connection directory unsupported in DummyConnectionGroup."); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Directory<String, ConnectionGroup> getConnectionGroupDirectory() throws GuacamoleException { | ||||
|         throw new UnsupportedOperationException("Connection group directory unsuppprted in DummyConnectionGroup."); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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<String, ConnectionGroup> directory) | ||||
|             throws GuacamoleException, XMLStreamException { | ||||
|  | ||||
|         // If no connections, write nothing | ||||
|         Set<String> 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); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -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<String, ConnectionGroup> newParentDirectory = | ||||
|                 ConnectionGroupUtility.findConnectionGroupDirectory(context, parentID); | ||||
|  | ||||
|         // Attempt to get root connection group directory | ||||
|         Directory<String, ConnectionGroup> directory = | ||||
|                 context.getRootConnectionGroup().getConnectionGroupDirectory(); | ||||
|  | ||||
|         // Move connection group | ||||
|         directory.move(identifier, newParentDirectory); | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -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<String, ConnectionGroup> 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); | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -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<String, Connection> findConnectionDirectory( | ||||
|             UserContext context, String parentID) throws GuacamoleException { | ||||
|          | ||||
|         // Find the correct connection directory | ||||
|         ConnectionGroup rootGroup = context.getRootConnectionGroup(); | ||||
|         Directory<String, Connection> directory; | ||||
|          | ||||
|         Directory<String, ConnectionGroup> connectionGroupDirectory =  | ||||
|             rootGroup.getConnectionGroupDirectory(); | ||||
|  | ||||
|         ConnectionGroup parentGroup = connectionGroupDirectory.get(parentID); | ||||
|  | ||||
|         if(parentGroup == null) | ||||
|             return null; | ||||
|  | ||||
|         directory = parentGroup.getConnectionDirectory(); | ||||
|          | ||||
|         return directory; | ||||
|     } | ||||
| } | ||||
| @@ -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<String, Connection> 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<String> 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); | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -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<ConnectionRecord> getHistory() throws GuacamoleException { | ||||
|         throw new UnsupportedOperationException("History unsupported in DummyConnection."); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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<String, ConnectionGroup> directory) | ||||
|             throws GuacamoleException, XMLStreamException { | ||||
|  | ||||
|         // If no connections, write nothing | ||||
|         Set<String> 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<String, Connection> directory) | ||||
|             throws GuacamoleException, XMLStreamException { | ||||
|  | ||||
|         // If no connections, write nothing | ||||
|         Set<String> 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); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -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<String, Connection> newParentDirectory = | ||||
|                 ConnectionUtility.findConnectionDirectory(context, parentID); | ||||
|  | ||||
|         // Attempt to get root connection directory | ||||
|         Directory<String, Connection> directory = | ||||
|                 context.getRootConnectionGroup().getConnectionDirectory(); | ||||
|  | ||||
|         // Move connection | ||||
|         directory.move(identifier, newParentDirectory); | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -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<String, Connection> directory = | ||||
|                 context.getRootConnectionGroup().getConnectionDirectory(); | ||||
|  | ||||
|         // Create config | ||||
|         GuacamoleConfiguration config = new GuacamoleConfiguration(); | ||||
|         config.setProtocol(protocol); | ||||
|  | ||||
|         // Load parameters into config | ||||
|         Enumeration<String> 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); | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -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<String, User> 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); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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<String, User> directory = | ||||
|                 context.getUserDirectory(); | ||||
|  | ||||
|         // Create user skeleton | ||||
|         User user = new DummyUser(); | ||||
|         user.setUsername(username); | ||||
|         user.setPassword(UUID.randomUUID().toString()); | ||||
|  | ||||
|         // Add user | ||||
|         directory.add(user); | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -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<Permission> permissions = new HashSet<Permission>(); | ||||
|  | ||||
|     @Override | ||||
|     public Set<Permission> 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); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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<String, User> directory = context.getUserDirectory(); | ||||
|  | ||||
|             // Get users | ||||
|             Set<String> 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); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -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<String, User> 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); | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -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(); | ||||
| @@ -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"; | ||||
|      | ||||
| } | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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 <T> The type of object being patched. | ||||
|  */ | ||||
| public class APIPatch<T> { | ||||
|      | ||||
|     /** | ||||
|      * 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; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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."); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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 {} | ||||
| @@ -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()); | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -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 {}  | ||||
| @@ -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()); | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -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); | ||||
|  | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -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; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @@ -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(); | ||||
|  | ||||
| } | ||||
| @@ -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(); | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -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<String, Long> lastAccessTimeMap = new HashMap<String, Long>(); | ||||
|      | ||||
|     /** | ||||
|      * Keeps track of the authToken to GuacamoleSession mapping. | ||||
|      */ | ||||
|     private final Map<String, GuacamoleSession> sessionMap = new HashMap<String, GuacamoleSession>(); | ||||
|      | ||||
|     /** | ||||
|      * 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); | ||||
|  | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -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); | ||||
|  | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -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); | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -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); | ||||
|  | ||||
| } | ||||
| @@ -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; | ||||
| 
 | ||||
| @@ -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(); | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| @@ -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<String, String> parameters = new HashMap<String, String>(); | ||||
|      | ||||
|     /** | ||||
|      * 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<String, String> getParameters() { | ||||
|         return parameters; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sets the parameter map for this connection. | ||||
|      * @param parameters The parameter map for this connection. | ||||
|      */ | ||||
|     public void setParameters(Map<String, String> 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; | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -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<String, String> parameters = apiConnection.getParameters(); | ||||
|          | ||||
|         for(Map.Entry<String, String> 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<String, String> 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(); | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -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<APIConnection> 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<String, ConnectionGroup> connectionGroupDirectory = rootGroup.getConnectionGroupDirectory(); | ||||
|             parentConnectionGroup = connectionGroupDirectory.get(parentID); | ||||
|         } | ||||
|  | ||||
|         if (parentConnectionGroup == null) | ||||
|             throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID."); | ||||
|  | ||||
|         Directory<String, Connection> 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<String, Connection> 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<String, Connection> 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<String, ConnectionGroup> connectionGroupDirectory = rootGroup.getConnectionGroupDirectory(); | ||||
|             parentConnectionGroup = connectionGroupDirectory.get(parentID); | ||||
|         } | ||||
|  | ||||
|         if (parentConnectionGroup == null) | ||||
|             throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID."); | ||||
|  | ||||
|         Directory<String, Connection> 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<String, Connection> 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<String, Connection> connectionDirectory = | ||||
|                 rootGroup.getConnectionDirectory(); | ||||
|  | ||||
|         // Find the new parent connection group | ||||
|         Directory<String, ConnectionGroup> 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()); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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<APIConnection> convertConnectionList(Directory<String, Connection> 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<String, Connection> directory = | ||||
|                 context.getRootConnectionGroup().getConnectionDirectory(); | ||||
| 
 | ||||
|         // Remove connection | ||||
|         directory.remove(identifier); | ||||
|         List<APIConnection> restConnections = new ArrayList<APIConnection>(); | ||||
|          | ||||
|         for (String connectionID : connectionDirectory.getIdentifiers()) | ||||
|             restConnections.add(new APIConnection(connectionDirectory.get(connectionID))); | ||||
|              | ||||
|         return restConnections; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| @@ -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; | ||||
| 
 | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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<String, Connection> getConnectionDirectory() throws GuacamoleException { | ||||
|         throw new UnsupportedOperationException("Operation not supported."); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Directory<String, ConnectionGroup> getConnectionGroupDirectory() throws GuacamoleException { | ||||
|         throw new UnsupportedOperationException("Operation not supported."); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException { | ||||
|         throw new UnsupportedOperationException("Operation not supported."); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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<APIConnectionGroup> 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<String, ConnectionGroup> 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<String, ConnectionGroup> 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<String, ConnectionGroup> 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<String, ConnectionGroup> 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<String, ConnectionGroup> connectionGroupDirectory = rootGroup.getConnectionGroupDirectory(); | ||||
|             parentConnectionGroup = connectionGroupDirectory.get(parentID); | ||||
|         } | ||||
|  | ||||
|         if (parentConnectionGroup == null) | ||||
|             throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID."); | ||||
|  | ||||
|         Directory<String, ConnectionGroup> 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<String, ConnectionGroup> 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<String, ConnectionGroup> connectionGroupDirectory = | ||||
|                 rootGroup.getConnectionGroupDirectory(); | ||||
|  | ||||
|         // Find the new parent connection group | ||||
|         Directory<String, ConnectionGroup> 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()); | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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<APIConnectionGroup> convertConnectionGroupList( | ||||
|             Directory<String, ConnectionGroup> connectionGroupDirectory) throws GuacamoleException { | ||||
|  | ||||
|         List<APIConnectionGroup> restConnectionGroups = new ArrayList<APIConnectionGroup>(); | ||||
|          | ||||
|         for (String connectionGroupID : connectionGroupDirectory.getIdentifiers()) | ||||
|             restConnectionGroups.add(new APIConnectionGroup(connectionGroupDirectory.get(connectionGroupID))); | ||||
|              | ||||
|         return restConnectionGroups; | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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; | ||||
|  | ||||
| @@ -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; | ||||
| 
 | ||||
| @@ -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; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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<APIPermission> 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<APIPatch<APIPermission>> patches) throws GuacamoleException { | ||||
|  | ||||
|         UserContext userContext = authenticationService.getUserContext(authToken); | ||||
|          | ||||
|         // Get the user directory | ||||
|         Directory<String, User> userDirectory = userContext.getUserDirectory(); | ||||
|          | ||||
|         // All users who have had permissions added or removed | ||||
|         Map<String, User> modifiedUsers = new HashMap<String, User>(); | ||||
|          | ||||
|         for (APIPatch<APIPermission> 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); | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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<APIPermission> convertPermissionList(Iterable<? extends Permission> permissions) { | ||||
|  | ||||
|         List<APIPermission> restPermissions = new ArrayList<APIPermission>(); | ||||
|          | ||||
|         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<Permission> convertAPIPermissionList(Iterable<APIPermission> restPermissions) { | ||||
|  | ||||
|         Set<Permission> permissions = new HashSet<Permission>(); | ||||
|          | ||||
|         for(APIPermission restPermission : restPermissions) | ||||
|             permissions.add(restPermission.toPermission()); | ||||
|          | ||||
|         return permissions; | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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; | ||||
|  | ||||
| @@ -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<String, ProtocolInfo> getProtocols() throws GuacamoleException { | ||||
|          | ||||
|         // Get and return a map of all protocols. | ||||
|         return protocolRetrievalservice.getProtocolMap(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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<String, ProtocolInfo> getProtocolMap() throws GuacamoleException { | ||||
|         // Map of all available protocols | ||||
|         Map<String, ProtocolInfo> protocols = new HashMap<String, ProtocolInfo>(); | ||||
| 
 | ||||
| @@ -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; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @@ -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; | ||||
| 
 | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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<Permission> 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<Permission> 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<Permission> 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."); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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<APIUser> getUsers(@QueryParam("token") String authToken) throws GuacamoleException { | ||||
|  | ||||
|         UserContext userContext = authenticationService.getUserContext(authToken); | ||||
|  | ||||
|         // Get the directory | ||||
|         Directory<String, User> 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<String, User> 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<String, User> 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<String, User> 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<String, User> 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); | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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<APIUser> convertUserList(Directory<String, User> 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<String, User> directory = context.getUserDirectory(); | ||||
| 
 | ||||
|         // Remove user | ||||
|         directory.remove(username); | ||||
|         List<APIUser> restUsers = new ArrayList<APIUser>(); | ||||
|          | ||||
|         for(String username : userDirectory.getIdentifiers()) | ||||
|             restUsers.add(new APIUser(userDirectory.get(username))); | ||||
|              | ||||
|         return restUsers; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|      | ||||
| } | ||||
| 
 | ||||
| @@ -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; | ||||
| 
 | ||||
| @@ -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<TunnelRequestService> 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<TunnelRequestService> 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); | ||||
|  | ||||
|   | ||||
| @@ -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)); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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())); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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)); | ||||
|     }; | ||||
|  | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user