From 4df635e4074f1fb8b8e4c5b37025dabf11f380db Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Mon, 2 Sep 2013 19:20:30 -0700 Subject: [PATCH 01/33] Ticket #362: Super basic REST endpoint exposed with Guice and Jersey. --- guacamole/pom.xml | 28 ++++++++++ .../rest/RESTServletContextListener.java | 52 +++++++++++++++++++ .../rest/connection/ConnectionService.java | 24 +++++++++ guacamole/src/main/webapp/WEB-INF/web.xml | 14 +++++ 4 files changed, 118 insertions(+) create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletContextListener.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java diff --git a/guacamole/pom.xml b/guacamole/pom.xml index df7115cbd..1fba0a374 100644 --- a/guacamole/pom.xml +++ b/guacamole/pom.xml @@ -110,6 +110,34 @@ 1.6.1 runtime + + + + com.google.inject + guice + 3.0 + + + + + com.google.inject.extensions + guice-servlet + 3.0 + + + + + com.sun.jersey + jersey-server + 1.7 + + + + + com.sun.jersey.contribs + jersey-guice + 1.7 + diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletContextListener.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletContextListener.java new file mode 100644 index 000000000..ea59621ca --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletContextListener.java @@ -0,0 +1,52 @@ +package org.glyptodon.guacamole.net.basic.rest; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import com.google.inject.Guice; +import com.google.inject.servlet.ServletModule; +import com.sun.jersey.guice.spi.container.servlet.GuiceContainer; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionService; + +/** + * A ServletContextListenr to listen for initialization of the servlet context + * in order to set up the REST services. + * + * @author James Muehlner + */ +public class RESTServletContextListener implements ServletContextListener { + + @Override + public void contextInitialized(ServletContextEvent sce) { + Guice.createInjector(new ServletModule() { + @Override + protected void configureServlets() { + + bind(ConnectionService.class); + + serve("*").with(GuiceContainer.class); + } + }); + } + + @Override + public void contextDestroyed(ServletContextEvent sce) {} + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java new file mode 100644 index 000000000..f784113d0 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java @@ -0,0 +1,24 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.glyptodon.guacamole.net.basic.rest.connection; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +/** + * A REST Service for handling connection CRUD operations. + * + * @author James Muehlner + */ +@Path("/api/connection") +public class ConnectionService { + + @Path("/") + @GET + public String getConnections() { + return "goo"; + } + +} diff --git a/guacamole/src/main/webapp/WEB-INF/web.xml b/guacamole/src/main/webapp/WEB-INF/web.xml index 728d5e699..b02dc535d 100644 --- a/guacamole/src/main/webapp/WEB-INF/web.xml +++ b/guacamole/src/main/webapp/WEB-INF/web.xml @@ -241,6 +241,20 @@ Tunnel /tunnel + + + guiceFilter + com.google.inject.servlet.GuiceFilter + + + + guiceFilter + /api/* + + + + org.glyptodon.guacamole.net.basic.rest.RESTServletContextListener + mp3 From 064802a9fbc4d3e8e024ed362fe306d691cde99b Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Fri, 6 Sep 2013 21:25:03 -0700 Subject: [PATCH 02/33] Ticket #362: Authentication working. --- guacamole/pom.xml | 14 +++ .../guacamole/net/basic/rest/RESTModule.java | 69 +++++++++++ .../rest/RESTServletContextListener.java | 16 +-- .../net/basic/rest/RESTServletModule.java | 42 +++++++ .../basic/rest/auth/AuthTokenGenerator.java | 34 ++++++ .../rest/auth/BasicTokenUserContextMap.java | 30 +++++ .../net/basic/rest/auth/LoginService.java | 107 ++++++++++++++++++ .../auth/SecureRandomAuthTokenGenerator.java | 30 +++++ .../basic/rest/auth/TokenUserContextMap.java | 30 +++++ .../rest/connection/ConnectionService.java | 53 ++++++++- 10 files changed, 407 insertions(+), 18 deletions(-) create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthTokenGenerator.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginService.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/SecureRandomAuthTokenGenerator.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenUserContextMap.java diff --git a/guacamole/pom.xml b/guacamole/pom.xml index 1fba0a374..b76f8b088 100644 --- a/guacamole/pom.xml +++ b/guacamole/pom.xml @@ -138,6 +138,20 @@ jersey-guice 1.7 + + + + javax.annotation + jsr250-api + 1.0 + + + + + commons-codec + commons-codec + 1.4 + diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java new file mode 100644 index 000000000..1ac01a6e7 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java @@ -0,0 +1,69 @@ +package org.glyptodon.guacamole.net.basic.rest; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import com.google.inject.AbstractModule; +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.BasicTokenUserContextMap; +import org.glyptodon.guacamole.net.basic.rest.auth.SecureRandomAuthTokenGenerator; +import org.glyptodon.guacamole.net.basic.rest.auth.TokenUserContextMap; +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("Error getting authentication provider from properties.", e); + throw new RuntimeException(e); + } + + bind(AuthenticationProvider.class).toInstance(authProvider); + bind(TokenUserContextMap.class).toInstance(new BasicTokenUserContextMap()); + + bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class); + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletContextListener.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletContextListener.java index ea59621ca..50e1783f8 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletContextListener.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletContextListener.java @@ -19,11 +19,8 @@ package org.glyptodon.guacamole.net.basic.rest; */ import com.google.inject.Guice; -import com.google.inject.servlet.ServletModule; -import com.sun.jersey.guice.spi.container.servlet.GuiceContainer; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; -import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionService; /** * A ServletContextListenr to listen for initialization of the servlet context @@ -35,15 +32,10 @@ public class RESTServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { - Guice.createInjector(new ServletModule() { - @Override - protected void configureServlets() { - - bind(ConnectionService.class); - - serve("*").with(GuiceContainer.class); - } - }); + Guice.createInjector( + new RESTServletModule(), + new RESTModule() + ); } @Override diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java new file mode 100644 index 000000000..00627e59d --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java @@ -0,0 +1,42 @@ +package org.glyptodon.guacamole.net.basic.rest; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import com.google.inject.servlet.ServletModule; +import com.sun.jersey.guice.spi.container.servlet.GuiceContainer; +import org.glyptodon.guacamole.net.basic.rest.auth.LoginService; +import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionService; + +/** + * 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() { + + bind(ConnectionService.class); + bind(LoginService.class); + + serve("*").with(GuiceContainer.class); + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthTokenGenerator.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthTokenGenerator.java new file mode 100644 index 000000000..2842fe673 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthTokenGenerator.java @@ -0,0 +1,34 @@ +package org.glyptodon.guacamole.net.basic.rest.auth; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * Generates an auth token for an authenticated user. + * + * @author James Muehlner + */ +public interface AuthTokenGenerator { + + /** + * Get a new auth token. + * + * @return A new auth token. + */ + public String getToken(); +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java new file mode 100644 index 000000000..b6ba99882 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java @@ -0,0 +1,30 @@ +package org.glyptodon.guacamole.net.basic.rest.auth; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import java.util.HashMap; +import org.glyptodon.guacamole.net.auth.UserContext; + +/** + * A Basic, HashMap-based implementation of the TokenUserContextMap. + * + * @author James Muehlner + */ +public class BasicTokenUserContextMap extends HashMap + implements TokenUserContextMap {} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginService.java new file mode 100644 index 000000000..19e97392e --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginService.java @@ -0,0 +1,107 @@ +package org.glyptodon.guacamole.net.basic.rest.auth; + +import com.google.inject.Inject; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +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.rest.RESTModule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * 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("/api/login") +public class LoginService { + + /** + * The authentication provider used to authenticate this user. + */ + @Inject + private AuthenticationProvider authProvider; + + /** + * The map of auth tokens to users for the REST endpoints. + */ + @Inject + private TokenUserContextMap tokenUserMap; + + /** + * A generator for creating new auth tokens. + */ + @Inject + private AuthTokenGenerator authTokenGenerator; + + /** + * Logger for this class. + */ + private static final Logger logger = LoggerFactory.getLogger(LoginService.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. + * @return The auth token for the newly logged-in user. + */ + @POST + @Path("/") + public String login(@QueryParam("username") String username, + @QueryParam("password") String password) { + + Credentials credentials = new Credentials(); + credentials.setUsername(username); + credentials.setPassword(password); + + UserContext userContext; + + try { + userContext = authProvider.getUserContext(credentials); + } catch(GuacamoleException e) { + logger.error("Exception caught while authenticating user.", e); + throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR); + } + + // authentication failed. + if(userContext == null) + throw new WebApplicationException(Response.Status.UNAUTHORIZED); + + String authToken = authTokenGenerator.getToken(); + + tokenUserMap.put(authToken, userContext); + + return authToken; + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/SecureRandomAuthTokenGenerator.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/SecureRandomAuthTokenGenerator.java new file mode 100644 index 000000000..89fc01b4f --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/SecureRandomAuthTokenGenerator.java @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +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 SecureRandom secureRandom = new SecureRandom(); + + @Override + public String getToken() { + byte[] bytes = new byte[32]; + secureRandom.nextBytes(bytes); + + return Hex.encodeHexString(bytes); + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenUserContextMap.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenUserContextMap.java new file mode 100644 index 000000000..0f106ecfc --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenUserContextMap.java @@ -0,0 +1,30 @@ +package org.glyptodon.guacamole.net.basic.rest.auth; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import java.util.Map; +import org.glyptodon.guacamole.net.auth.UserContext; + +/** + * Represents a mapping of auth token to user context for the REST + * authentication system. + * + * @author James Muehlner + */ +public interface TokenUserContextMap extends Map {} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java index f784113d0..3f4125787 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java @@ -1,11 +1,33 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ package org.glyptodon.guacamole.net.basic.rest.connection; +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import com.google.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +import org.glyptodon.guacamole.net.auth.UserContext; +import org.glyptodon.guacamole.net.basic.rest.auth.TokenUserContextMap; /** * A REST Service for handling connection CRUD operations. @@ -15,10 +37,29 @@ import javax.ws.rs.Path; @Path("/api/connection") public class ConnectionService { + /** + * The map of auth tokens to users for the REST endpoints. + */ + @Inject + private TokenUserContextMap tokenUserMap; + @Path("/") @GET - public String getConnections() { - return "goo"; + public String getConnections(@QueryParam("token") String authToken) { + UserContext userContext = tokenUserMap.get(authToken); + + // authentication failed. + if(userContext == null) + throw new WebApplicationException(Response.Status.UNAUTHORIZED); + + try { + //TODO: Make this work for realzies + return userContext.getRootConnectionGroup().getConnectionDirectory().getIdentifiers().toString(); + } catch(GuacamoleSecurityException e) { + throw new WebApplicationException(e, Response.Status.UNAUTHORIZED); + } catch(GuacamoleException e) { + throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR); + } } } From b15777a70f9bf8e5125dfac9d117fe2ebf43a45c Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Mon, 16 Sep 2013 21:17:21 -0700 Subject: [PATCH 03/33] Ticket #362: Got JSON mapping working. --- guacamole/pom.xml | 11 +- .../guacamole/net/basic/rest/APIError.java | 47 ++++++++ .../guacamole/net/basic/rest/RESTModule.java | 2 + .../net/basic/rest/RESTServletModule.java | 20 ++-- ...oginService.java => LoginRESTService.java} | 5 +- .../net/basic/rest/connection/Connection.java | 104 +++++++++++++++++ .../connection/ConnectionRESTService.java | 108 ++++++++++++++++++ .../rest/connection/ConnectionService.java | 55 ++++----- guacamole/src/main/webapp/WEB-INF/web.xml | 10 ++ 9 files changed, 316 insertions(+), 46 deletions(-) create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIError.java rename guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/{LoginService.java => LoginRESTService.java} (98%) create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/Connection.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java diff --git a/guacamole/pom.xml b/guacamole/pom.xml index b76f8b088..0de815902 100644 --- a/guacamole/pom.xml +++ b/guacamole/pom.xml @@ -129,14 +129,14 @@ com.sun.jersey jersey-server - 1.7 + 1.17.1 com.sun.jersey.contribs jersey-guice - 1.7 + 1.17.1 @@ -152,6 +152,13 @@ commons-codec 1.4 + + + + com.sun.jersey + jersey-json + 1.17.1 + diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIError.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIError.java new file mode 100644 index 000000000..be68456f8 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/APIError.java @@ -0,0 +1,47 @@ +package org.glyptodon.guacamole.net.basic.rest; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * A simple object to represent an error to be sent from the REST API. + * @author James Muehlner + */ +public class APIError { + + /** + * The error message. + */ + private String message; + + /** + * Get the error message. + * @return The error message. + */ + public String getMessage() { + return message; + } + + /** + * Create a new APIError with the specified error message. + * @param message The error message. + */ + public APIError(String message) { + this.message = message; + } +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java index 1ac01a6e7..c1e6bf856 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java @@ -26,6 +26,7 @@ import org.glyptodon.guacamole.net.basic.rest.auth.AuthTokenGenerator; import org.glyptodon.guacamole.net.basic.rest.auth.BasicTokenUserContextMap; import org.glyptodon.guacamole.net.basic.rest.auth.SecureRandomAuthTokenGenerator; import org.glyptodon.guacamole.net.basic.rest.auth.TokenUserContextMap; +import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionService; import org.glyptodon.guacamole.properties.GuacamoleProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,6 +63,7 @@ public class RESTModule extends AbstractModule { bind(AuthenticationProvider.class).toInstance(authProvider); bind(TokenUserContextMap.class).toInstance(new BasicTokenUserContextMap()); + bind(ConnectionService.class); bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class); } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java index 00627e59d..d11992563 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java @@ -18,10 +18,12 @@ package org.glyptodon.guacamole.net.basic.rest; * along with this program. If not, see . */ +import com.google.inject.Scopes; import com.google.inject.servlet.ServletModule; import com.sun.jersey.guice.spi.container.servlet.GuiceContainer; -import org.glyptodon.guacamole.net.basic.rest.auth.LoginService; -import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionService; +import org.codehaus.jackson.jaxrs.JacksonJsonProvider; +import org.glyptodon.guacamole.net.basic.rest.auth.LoginRESTService; +import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionRESTService; /** * A Guice Module to set up the servlet mappings for the Guacamole REST API. @@ -32,11 +34,15 @@ public class RESTServletModule extends ServletModule { @Override protected void configureServlets() { - - bind(ConnectionService.class); - bind(LoginService.class); - - serve("*").with(GuiceContainer.class); + + // Set up the API endpoints + bind(ConnectionRESTService.class); + bind(LoginRESTService.class); + + // Set up the servlet and JSON mappings + bind(GuiceContainer.class); + bind(JacksonJsonProvider.class).in(Scopes.SINGLETON); + serve("/*").with(GuiceContainer.class); } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java similarity index 98% rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginService.java rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java index 19e97392e..f0ae5a9a7 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java @@ -42,7 +42,7 @@ import org.slf4j.LoggerFactory; @Path("/api/login") -public class LoginService { +public class LoginRESTService { /** * The authentication provider used to authenticate this user. @@ -65,7 +65,7 @@ public class LoginService { /** * Logger for this class. */ - private static final Logger logger = LoggerFactory.getLogger(LoginService.class); + private static final Logger logger = LoggerFactory.getLogger(LoginRESTService.class); /** * Authenticates a user, generates an auth token, associates that auth token @@ -76,7 +76,6 @@ public class LoginService { * @return The auth token for the newly logged-in user. */ @POST - @Path("/") public String login(@QueryParam("username") String username, @QueryParam("password") String password) { diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/Connection.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/Connection.java new file mode 100644 index 000000000..9e4406f3d --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/Connection.java @@ -0,0 +1,104 @@ +package org.glyptodon.guacamole.net.basic.rest.connection; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import java.util.List; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.auth.ConnectionRecord; +import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; + +/** + * A simple connection to expose through the REST endpoints. + * + * @author James Muehlner + */ +public class Connection { + + /** + * The name of this connection. + */ + private String name; + + /** + * The identifier of this connection. + */ + private String identifier; + + /** + * The configuration associated with this connection. + */ + private GuacamoleConfiguration configuration; + + /** + * The history records associated with this connection. + */ + private List history; + + /** + * Create an empty Connection. + */ + public Connection() {} + + /** + * Create a Connection from a org.glyptodon.guacamole.net.auth.Connection. + * @param connection The connection to create this Connection from. + * @throws GuacamoleException If a problem is encountered while + * instantiating this new Connection. + */ + public Connection(org.glyptodon.guacamole.net.auth.Connection connection) + throws GuacamoleException { + this.name = connection.getName(); + this.identifier = connection.getIdentifier(); + this.configuration = connection.getConfiguration(); + this.history = connection.getHistory(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getIdentifier() { + return identifier; + } + + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + public GuacamoleConfiguration getConfiguration() { + return configuration; + } + + public void setConfiguration(GuacamoleConfiguration configuration) { + this.configuration = configuration; + } + + public List getHistory() { + return history; + } + + public void setHistory(List history) { + this.history = history; + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java new file mode 100644 index 000000000..27961ade7 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java @@ -0,0 +1,108 @@ +package org.glyptodon.guacamole.net.basic.rest.connection; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import com.google.inject.Inject; +import com.google.inject.servlet.RequestScoped; +import java.util.ArrayList; +import java.util.List; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +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.APIError; +import org.glyptodon.guacamole.net.basic.rest.auth.TokenUserContextMap; + +/** + * A REST Service for handling connection CRUD operations. + * + * @author James Muehlner + */ +@Path("/api/connection") +@RequestScoped +public class ConnectionRESTService { + + /** + * The map of auth tokens to users for the REST endpoints. + */ + @Inject + private TokenUserContextMap tokenUserMap; + + /** + * A service for managing the REST endpoint Connection objects. + */ + @Inject + private ConnectionService connectionService; + + @GET + @Produces(MediaType.APPLICATION_JSON) + public List getConnections(@QueryParam("token") String authToken, @QueryParam("parentID") String parentID) { + UserContext userContext = tokenUserMap.get(authToken); + + // authentication failed. + if(userContext == null) + throw new WebApplicationException(Response.Status.UNAUTHORIZED); + + + try { + // If the parent connection group is passed in, try to find it. + ConnectionGroup parentConnectionGroup; + if(parentID == null) + parentConnectionGroup = userContext.getRootConnectionGroup(); + else { + ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); + Directory connectionGroupDirectory = rootGroup.getConnectionGroupDirectory(); + parentConnectionGroup = connectionGroupDirectory.get(parentID); + } + + if(parentConnectionGroup == null) + throw new WebApplicationException( + Response.status(Response.Status.BAD_REQUEST) + .entity(new APIError("No ConnectionGroup found with the provided parentID.")) + .build()); + + Directory connectionDirectory = + parentConnectionGroup.getConnectionDirectory(); + + // Get the list of connection names + List connections + = new ArrayList(); + Iterable identifiers = connectionDirectory.getIdentifiers(); + + // Get the connection for each name + for(String identifier : identifiers) + connections.add(connectionDirectory.get(identifier)); + + return connectionService.convertConnectionList(connections); + } catch(GuacamoleSecurityException e) { + throw new WebApplicationException(e, Response.Status.UNAUTHORIZED); + } catch(GuacamoleException e) { + throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR); + } + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java index 3f4125787..182261035 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java @@ -1,5 +1,9 @@ package org.glyptodon.guacamole.net.basic.rest.connection; +import java.util.ArrayList; +import java.util.List; +import org.glyptodon.guacamole.GuacamoleException; + /* * Guacamole - Clientless Remote Desktop * Copyright (C) 2010 Michael Jumper @@ -18,48 +22,31 @@ package org.glyptodon.guacamole.net.basic.rest.connection; * along with this program. If not, see . */ -import com.google.inject.Inject; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; -import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.GuacamoleSecurityException; -import org.glyptodon.guacamole.net.auth.UserContext; -import org.glyptodon.guacamole.net.basic.rest.auth.TokenUserContextMap; - /** - * A REST Service for handling connection CRUD operations. + * A service for performing useful manipulations on REST Connections. * * @author James Muehlner */ -@Path("/api/connection") public class ConnectionService { /** - * The map of auth tokens to users for the REST endpoints. + * Converts a list of org.glyptodon.guacamole.net.auth.Connection to + * Connection objects for exposure with the REST endpoints. + * + * @param connections The org.glyptodon.guacamole.net.auth.Connection to + * convert for REST endpoint use. + * @return A List of Connection objects for use with the REST endpoint. + * @throws GuacamoleException If an error occurs while converting the + * connections. */ - @Inject - private TokenUserContextMap tokenUserMap; - - @Path("/") - @GET - public String getConnections(@QueryParam("token") String authToken) { - UserContext userContext = tokenUserMap.get(authToken); - - // authentication failed. - if(userContext == null) - throw new WebApplicationException(Response.Status.UNAUTHORIZED); + public List convertConnectionList(List connections) + throws GuacamoleException { + List restConnections = new ArrayList(); - try { - //TODO: Make this work for realzies - return userContext.getRootConnectionGroup().getConnectionDirectory().getIdentifiers().toString(); - } catch(GuacamoleSecurityException e) { - throw new WebApplicationException(e, Response.Status.UNAUTHORIZED); - } catch(GuacamoleException e) { - throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR); + for(org.glyptodon.guacamole.net.auth.Connection connection : connections) { + restConnections.add(new Connection(connection)); } - } - + + return restConnections; + } } diff --git a/guacamole/src/main/webapp/WEB-INF/web.xml b/guacamole/src/main/webapp/WEB-INF/web.xml index b02dc535d..452f763a5 100644 --- a/guacamole/src/main/webapp/WEB-INF/web.xml +++ b/guacamole/src/main/webapp/WEB-INF/web.xml @@ -255,6 +255,16 @@ org.glyptodon.guacamole.net.basic.rest.RESTServletContextListener + + + com.sun.jersey.api.json.POJOMappingFeature + true + + + + com.sun.jersey.config.property.packages + org.codehaus.jackson.jaxrs + mp3 From 6f15c20e50cc21249a3ad77786d051d49067cb9f Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Mon, 16 Sep 2013 21:37:24 -0700 Subject: [PATCH 04/33] Ticket #362: Cleaned up a little bit. --- .../net/basic/rest/auth/APIAuthToken.java | 49 +++++++++++++++++++ .../net/basic/rest/auth/LoginRESTService.java | 18 +++++-- .../net/basic/rest/connection/Connection.java | 36 +++++++++++++- .../connection/ConnectionRESTService.java | 16 +++--- 4 files changed, 106 insertions(+), 13 deletions(-) create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/APIAuthToken.java diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/APIAuthToken.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/APIAuthToken.java new file mode 100644 index 000000000..47bdffcc4 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/APIAuthToken.java @@ -0,0 +1,49 @@ +package org.glyptodon.guacamole.net.basic.rest.auth; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * A simple object to represent an auth token in the API. + * + * @author James Muehlner + */ +public class APIAuthToken { + + /** + * The auth token. + */ + private String authToken; + + /** + * Get the auth token. + * @return The auth token. + */ + public String getAuthToken() { + return authToken; + } + + /** + * Create a new APIAuthToken Object with the given auth token. + * + * @param authToken The auth token to create the new APIAuthToken with. + */ + public APIAuthToken(String authToken) { + this.authToken = authToken; + } +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java index f0ae5a9a7..7d79b4f65 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java @@ -1,16 +1,20 @@ package org.glyptodon.guacamole.net.basic.rest.auth; import com.google.inject.Inject; +import java.util.HashMap; +import java.util.Map; import javax.ws.rs.POST; import javax.ws.rs.Path; +import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; 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.rest.RESTModule; +import org.glyptodon.guacamole.net.basic.rest.APIError; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,6 +46,7 @@ import org.slf4j.LoggerFactory; @Path("/api/login") +@Produces(MediaType.APPLICATION_JSON) public class LoginRESTService { /** @@ -76,7 +81,7 @@ public class LoginRESTService { * @return The auth token for the newly logged-in user. */ @POST - public String login(@QueryParam("username") String username, + public APIAuthToken login(@QueryParam("username") String username, @QueryParam("password") String password) { Credentials credentials = new Credentials(); @@ -89,18 +94,21 @@ public class LoginRESTService { userContext = authProvider.getUserContext(credentials); } catch(GuacamoleException e) { logger.error("Exception caught while authenticating user.", e); - throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR); + throw new WebApplicationException( + Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e).build()); } // authentication failed. if(userContext == null) - throw new WebApplicationException(Response.Status.UNAUTHORIZED); + throw new WebApplicationException( + Response.status(Response.Status.UNAUTHORIZED) + .entity(new APIError("Permission Denied.")).build()); String authToken = authTokenGenerator.getToken(); tokenUserMap.put(authToken, userContext); - return authToken; + return new APIAuthToken(authToken); } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/Connection.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/Connection.java index 9e4406f3d..53012d400 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/Connection.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/Connection.java @@ -69,34 +69,66 @@ public class Connection { this.history = connection.getHistory(); } + /** + * Get 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; } + /** + * Get the identifier of this connection. + * @return The identifier of this connection. + */ public String getIdentifier() { return identifier; } - + + /** + * Set the identifier of this connection. + * @param identifier The identifier of this connection. + */ public void setIdentifier(String identifier) { this.identifier = identifier; } + /** + * Get the configuration for this connection. + * @return The configuration for this connection. + */ public GuacamoleConfiguration getConfiguration() { return configuration; } - + + /** + * Set the configuration for this connection. + * @param configuration The configuration for this connection. + */ public void setConfiguration(GuacamoleConfiguration configuration) { this.configuration = configuration; } + /** + * Get the history records for this connection. + * @return The history records for this connection. + */ public List getHistory() { return history; } + /** + * Set the history records for this connection. + * @param history The history records for this connection. + */ public void setHistory(List history) { this.history = history; } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java index 27961ade7..1bc98328e 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java @@ -43,7 +43,7 @@ import org.glyptodon.guacamole.net.basic.rest.auth.TokenUserContextMap; * @author James Muehlner */ @Path("/api/connection") -@RequestScoped +@Produces(MediaType.APPLICATION_JSON) public class ConnectionRESTService { /** @@ -59,14 +59,14 @@ public class ConnectionRESTService { private ConnectionService connectionService; @GET - @Produces(MediaType.APPLICATION_JSON) public List getConnections(@QueryParam("token") String authToken, @QueryParam("parentID") String parentID) { UserContext userContext = tokenUserMap.get(authToken); // authentication failed. if(userContext == null) - throw new WebApplicationException(Response.Status.UNAUTHORIZED); - + throw new WebApplicationException( + Response.status(Response.Status.UNAUTHORIZED) + .entity(new APIError("Permission Denied.")).build()); try { // If the parent connection group is passed in, try to find it. @@ -99,9 +99,13 @@ public class ConnectionRESTService { return connectionService.convertConnectionList(connections); } catch(GuacamoleSecurityException e) { - throw new WebApplicationException(e, Response.Status.UNAUTHORIZED); + throw new WebApplicationException( + Response.status(Response.Status.UNAUTHORIZED) + .entity(new APIError("Permission Denied.")).build()); } catch(GuacamoleException e) { - throw new WebApplicationException(e, Response.Status.INTERNAL_SERVER_ERROR); + throw new WebApplicationException( + Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(e).build()); } } From a624182d76373980bf81de80c3af1c27ed2c4187 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Mon, 16 Sep 2013 21:53:17 -0700 Subject: [PATCH 05/33] Ticket #362: Streamlined authentication. --- .../guacamole/net/basic/rest/RESTModule.java | 2 + .../rest/auth/AuthenticationService.java | 64 +++++++++++++++++++ .../connection/ConnectionRESTService.java | 19 ++---- 3 files changed, 72 insertions(+), 13 deletions(-) create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthenticationService.java diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java index c1e6bf856..94deb77e4 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java @@ -23,6 +23,7 @@ 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.BasicTokenUserContextMap; import org.glyptodon.guacamole.net.basic.rest.auth.SecureRandomAuthTokenGenerator; import org.glyptodon.guacamole.net.basic.rest.auth.TokenUserContextMap; @@ -64,6 +65,7 @@ public class RESTModule extends AbstractModule { bind(AuthenticationProvider.class).toInstance(authProvider); bind(TokenUserContextMap.class).toInstance(new BasicTokenUserContextMap()); bind(ConnectionService.class); + bind(AuthenticationService.class); bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class); } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthenticationService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthenticationService.java new file mode 100644 index 000000000..ccf2a518f --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthenticationService.java @@ -0,0 +1,64 @@ +package org.glyptodon.guacamole.net.basic.rest.auth; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import com.google.inject.Inject; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import org.glyptodon.guacamole.net.auth.UserContext; +import org.glyptodon.guacamole.net.basic.rest.APIError; + +/** + * A service for performing authentication checks in REST endpoints. + * + * @author James Muehlner + */ +public class AuthenticationService { + + /** + * The map of auth tokens to users for the REST endpoints. + */ + @Inject + private TokenUserContextMap tokenUserMap; + + /** + * 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 userContext that corresponds to the provided auth token. + * @throws WebApplicationException If the auth token does not correspond to + * any logged in user. + */ + public UserContext getUserContextFromAuthToken(String authToken) + throws WebApplicationException { + + // Try to get the userContext from the map of logged in users. + UserContext userContext = tokenUserMap.get(authToken); + + // Authentication failed. + if(userContext == null) + throw new WebApplicationException( + Response.status(Response.Status.UNAUTHORIZED) + .entity(new APIError("Permission Denied.")).build()); + + return userContext; + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java index 1bc98328e..92a46e2b0 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java @@ -19,7 +19,6 @@ package org.glyptodon.guacamole.net.basic.rest.connection; */ import com.google.inject.Inject; -import com.google.inject.servlet.RequestScoped; import java.util.ArrayList; import java.util.List; import javax.ws.rs.GET; @@ -35,7 +34,7 @@ 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.APIError; -import org.glyptodon.guacamole.net.basic.rest.auth.TokenUserContextMap; +import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; /** * A REST Service for handling connection CRUD operations. @@ -47,10 +46,10 @@ import org.glyptodon.guacamole.net.basic.rest.auth.TokenUserContextMap; public class ConnectionRESTService { /** - * The map of auth tokens to users for the REST endpoints. + * A service for authenticating users from auth tokens. */ @Inject - private TokenUserContextMap tokenUserMap; + private AuthenticationService authenticationService; /** * A service for managing the REST endpoint Connection objects. @@ -60,13 +59,7 @@ public class ConnectionRESTService { @GET public List getConnections(@QueryParam("token") String authToken, @QueryParam("parentID") String parentID) { - UserContext userContext = tokenUserMap.get(authToken); - - // authentication failed. - if(userContext == null) - throw new WebApplicationException( - Response.status(Response.Status.UNAUTHORIZED) - .entity(new APIError("Permission Denied.")).build()); + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); try { // If the parent connection group is passed in, try to find it. @@ -107,6 +100,6 @@ public class ConnectionRESTService { Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity(e).build()); } - } - + } + } From 131aea1173d1182b06b9608ddf94dc5fb5828bf3 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Mon, 16 Sep 2013 22:12:45 -0700 Subject: [PATCH 06/33] Ticket #362: Added getConnection() with path-params. --- .../{Connection.java => APIConnection.java} | 15 ++--- .../connection/ConnectionRESTService.java | 62 +++++++++++++++++-- .../rest/connection/ConnectionService.java | 14 ++--- 3 files changed, 72 insertions(+), 19 deletions(-) rename guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/{Connection.java => APIConnection.java} (89%) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/Connection.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java similarity index 89% rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/Connection.java rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java index 53012d400..797f045da 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/Connection.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java @@ -20,6 +20,7 @@ package org.glyptodon.guacamole.net.basic.rest.connection; import java.util.List; import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionRecord; import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; @@ -28,7 +29,7 @@ import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; * * @author James Muehlner */ -public class Connection { +public class APIConnection { /** * The name of this connection. @@ -51,17 +52,17 @@ public class Connection { private List history; /** - * Create an empty Connection. + * Create an empty APIConnection. */ - public Connection() {} + public APIConnection() {} /** - * Create a Connection from a org.glyptodon.guacamole.net.auth.Connection. - * @param connection The connection to create this Connection from. + * 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 Connection. + * instantiating this new APIConnection. */ - public Connection(org.glyptodon.guacamole.net.auth.Connection connection) + public APIConnection(Connection connection) throws GuacamoleException { this.name = connection.getName(); this.identifier = connection.getIdentifier(); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java index 92a46e2b0..b0f99c35e 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.List; import javax.ws.rs.GET; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; @@ -30,6 +31,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleSecurityException; +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; @@ -52,13 +54,23 @@ public class ConnectionRESTService { private AuthenticationService authenticationService; /** - * A service for managing the REST endpoint Connection objects. + * 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. + */ @GET - public List getConnections(@QueryParam("token") String authToken, @QueryParam("parentID") String parentID) { + public List getConnections(@QueryParam("token") String authToken, @QueryParam("parentID") String parentID) { UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); try { @@ -78,12 +90,11 @@ public class ConnectionRESTService { .entity(new APIError("No ConnectionGroup found with the provided parentID.")) .build()); - Directory connectionDirectory = + Directory connectionDirectory = parentConnectionGroup.getConnectionDirectory(); // Get the list of connection names - List connections - = new ArrayList(); + List connections = new ArrayList(); Iterable identifiers = connectionDirectory.getIdentifiers(); // Get the connection for each name @@ -101,5 +112,46 @@ public class ConnectionRESTService { .entity(e).build()); } } + + /** + * 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 APIConnection.. + * @return The connection. + */ + @GET + @Path("/{connectionID}") + public APIConnection getConnection(@QueryParam("token") String authToken, @PathParam("connectionID") String connectionID) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + try { + + // Get the connection directory + ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); + Directory connectionDirectory = + rootGroup.getConnectionDirectory(); + + // Get the connection + Connection connection = connectionDirectory.get(connectionID); + + if(connection == null) + throw new WebApplicationException( + Response.status(Response.Status.BAD_REQUEST) + .entity(new APIError("No Connection found with the provided ID.")) + .build()); + + return new APIConnection(connection); + } catch(GuacamoleSecurityException e) { + throw new WebApplicationException( + Response.status(Response.Status.UNAUTHORIZED) + .entity(new APIError("Permission Denied.")).build()); + } catch(GuacamoleException e) { + throw new WebApplicationException( + Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(e).build()); + } + } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java index 182261035..51513b494 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java @@ -30,21 +30,21 @@ import org.glyptodon.guacamole.GuacamoleException; public class ConnectionService { /** - * Converts a list of org.glyptodon.guacamole.net.auth.Connection to - * Connection objects for exposure with the REST endpoints. + * Converts a list of org.glyptodon.guacamole.net.auth.APIConnection to + * APIConnection objects for exposure with the REST endpoints. * - * @param connections The org.glyptodon.guacamole.net.auth.Connection to + * @param connections The org.glyptodon.guacamole.net.auth.APIConnection to * convert for REST endpoint use. - * @return A List of Connection objects for use with the REST endpoint. + * @return A List of APIConnection objects for use with the REST endpoint. * @throws GuacamoleException If an error occurs while converting the * connections. */ - public List convertConnectionList(List connections) + public List convertConnectionList(List connections) throws GuacamoleException { - List restConnections = new ArrayList(); + List restConnections = new ArrayList(); for(org.glyptodon.guacamole.net.auth.Connection connection : connections) { - restConnections.add(new Connection(connection)); + restConnections.add(new APIConnection(connection)); } return restConnections; From 614e6395fbed0e9c2d166179d5afe006f6673549 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Thu, 19 Sep 2013 19:17:11 -0700 Subject: [PATCH 07/33] Ticket #362: Cleaned up a bit and converted error throwing to new HTTPException. --- .../net/basic/rest/HTTPException.java | 40 +++++++++++++++++++ .../rest/auth/AuthenticationService.java | 8 ++-- .../net/basic/rest/auth/LoginRESTService.java | 12 +++--- .../connection/ConnectionRESTService.java | 33 ++++++--------- .../rest/connection/ConnectionService.java | 12 +++--- 5 files changed, 66 insertions(+), 39 deletions(-) create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java new file mode 100644 index 000000000..11a4fd70d --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java @@ -0,0 +1,40 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +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 entity The entity to wrap in an APIError as the body of the response. + */ + public HTTPException(Status status, String message) { + super(Response.status(status).entity(new APIError(message)).build()); + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthenticationService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthenticationService.java index ccf2a518f..c4c93cb11 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthenticationService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/AuthenticationService.java @@ -20,9 +20,9 @@ package org.glyptodon.guacamole.net.basic.rest.auth; import com.google.inject.Inject; import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; import org.glyptodon.guacamole.net.auth.UserContext; -import org.glyptodon.guacamole.net.basic.rest.APIError; +import org.glyptodon.guacamole.net.basic.rest.HTTPException; /** * A service for performing authentication checks in REST endpoints. @@ -54,9 +54,7 @@ public class AuthenticationService { // Authentication failed. if(userContext == null) - throw new WebApplicationException( - Response.status(Response.Status.UNAUTHORIZED) - .entity(new APIError("Permission Denied.")).build()); + throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied."); return userContext; } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java index 7d79b4f65..720ef88ad 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java @@ -1,8 +1,6 @@ package org.glyptodon.guacamole.net.basic.rest.auth; import com.google.inject.Inject; -import java.util.HashMap; -import java.util.Map; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; @@ -10,11 +8,13 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +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.rest.APIError; +import org.glyptodon.guacamole.net.basic.rest.HTTPException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -94,15 +94,13 @@ public class LoginRESTService { userContext = authProvider.getUserContext(credentials); } catch(GuacamoleException e) { logger.error("Exception caught while authenticating user.", e); - throw new WebApplicationException( - Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e).build()); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, + "Unexpected server error."); } // authentication failed. if(userContext == null) - throw new WebApplicationException( - Response.status(Response.Status.UNAUTHORIZED) - .entity(new APIError("Permission Denied.")).build()); + throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied."); String authToken = authTokenGenerator.getToken(); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java index b0f99c35e..c52dc7359 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java @@ -29,6 +29,7 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleSecurityException; import org.glyptodon.guacamole.net.auth.Connection; @@ -36,6 +37,7 @@ 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.APIError; +import org.glyptodon.guacamole.net.basic.rest.HTTPException; import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; /** @@ -85,10 +87,8 @@ public class ConnectionRESTService { } if(parentConnectionGroup == null) - throw new WebApplicationException( - Response.status(Response.Status.BAD_REQUEST) - .entity(new APIError("No ConnectionGroup found with the provided parentID.")) - .build()); + throw new HTTPException(Status.BAD_REQUEST, + "No ConnectionGroup found with the provided parentID."); Directory connectionDirectory = parentConnectionGroup.getConnectionDirectory(); @@ -103,13 +103,10 @@ public class ConnectionRESTService { return connectionService.convertConnectionList(connections); } catch(GuacamoleSecurityException e) { - throw new WebApplicationException( - Response.status(Response.Status.UNAUTHORIZED) - .entity(new APIError("Permission Denied.")).build()); + throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied."); } catch(GuacamoleException e) { - throw new WebApplicationException( - Response.status(Response.Status.INTERNAL_SERVER_ERROR) - .entity(e).build()); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, + "Unexpected server error."); } } @@ -127,7 +124,6 @@ public class ConnectionRESTService { UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); try { - // Get the connection directory ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); Directory connectionDirectory = @@ -137,20 +133,15 @@ public class ConnectionRESTService { Connection connection = connectionDirectory.get(connectionID); if(connection == null) - throw new WebApplicationException( - Response.status(Response.Status.BAD_REQUEST) - .entity(new APIError("No Connection found with the provided ID.")) - .build()); + throw new HTTPException(Status.BAD_REQUEST, + "No Connection found with the provided ID."); return new APIConnection(connection); } catch(GuacamoleSecurityException e) { - throw new WebApplicationException( - Response.status(Response.Status.UNAUTHORIZED) - .entity(new APIError("Permission Denied.")).build()); + throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied."); } catch(GuacamoleException e) { - throw new WebApplicationException( - Response.status(Response.Status.INTERNAL_SERVER_ERROR) - .entity(e).build()); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, + "Unexpected server error."); } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java index 51513b494..710d23d8f 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java @@ -3,6 +3,7 @@ package org.glyptodon.guacamole.net.basic.rest.connection; import java.util.ArrayList; import java.util.List; import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.auth.Connection; /* * Guacamole - Clientless Remote Desktop @@ -30,20 +31,19 @@ import org.glyptodon.guacamole.GuacamoleException; public class ConnectionService { /** - * Converts a list of org.glyptodon.guacamole.net.auth.APIConnection to - * APIConnection objects for exposure with the REST endpoints. + * Converts a list of Connection to a list of APIConnection objects for + * exposing with the REST endpoints. * - * @param connections The org.glyptodon.guacamole.net.auth.APIConnection to - * convert for REST endpoint use. + * @param connections The Connection 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 * connections. */ - public List convertConnectionList(List connections) + public List convertConnectionList(List connections) throws GuacamoleException { List restConnections = new ArrayList(); - for(org.glyptodon.guacamole.net.auth.Connection connection : connections) { + for(Connection connection : connections) { restConnections.add(new APIConnection(connection)); } From c4ad38e74a75aa0d5ecb194b85b445aa7b880c60 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Thu, 19 Sep 2013 19:52:40 -0700 Subject: [PATCH 08/33] Ticket #362: Improved error handling a bit, and added a DELETE function for connections. --- .../connection/ConnectionRESTService.java | 55 ++++++++++++++++--- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java index c52dc7359..961cccb13 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java @@ -21,14 +21,13 @@ package org.glyptodon.guacamole.net.basic.rest.connection; import com.google.inject.Inject; import java.util.ArrayList; import java.util.List; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleSecurityException; @@ -36,9 +35,10 @@ 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.APIError; 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. @@ -48,6 +48,11 @@ import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; @Path("/api/connection") @Produces(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. @@ -105,8 +110,9 @@ public class ConnectionRESTService { } catch(GuacamoleSecurityException e) { throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied."); } catch(GuacamoleException e) { + logger.error("Unexpected GuacamoleException caught while listing connections.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, - "Unexpected server error."); + "Unexpected server error. " + e.getMessage()); } } @@ -115,7 +121,7 @@ public class ConnectionRESTService { * * @param authToken The authentication token that is used to authenticate * the user performing the operation. - * @param connectionID The ID of the APIConnection.. + * @param connectionID The ID of the Connection.. * @return The connection. */ @GET @@ -140,8 +146,43 @@ public class ConnectionRESTService { } catch(GuacamoleSecurityException e) { throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied."); } catch(GuacamoleException e) { - throw new HTTPException(Status.INTERNAL_SERVER_ERROR, - "Unexpected server error."); + logger.error("Unexpected GuacamoleException caught while getting a connection.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, + "Unexpected server error. " + e.getMessage()); + } + } + + /** + * 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. + */ + @DELETE + @Path("/{connectionID}") + public void deleteConnection(@QueryParam("token") String authToken, @PathParam("connectionID") String connectionID) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + try { + // Get the connection directory + ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); + Directory connectionDirectory = + rootGroup.getConnectionDirectory(); + + // Make sure the connection is there before trying to delete + if(connectionDirectory.get(connectionID) == null) + throw new HTTPException(Status.BAD_REQUEST, + "No Connection found with the provided ID."); + + // Delete the connection + connectionDirectory.remove(connectionID); + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied."); + } catch(GuacamoleException e) { + logger.error("Unexpected GuacamoleException caught while deleting a connection.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, + "Unexpected server error. " + e.getMessage()); } } From 1c36eab1c7d81a8b3adbf28d3930f8c07152bb81 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Thu, 19 Sep 2013 20:58:40 -0700 Subject: [PATCH 09/33] Ticket #362: Added session timeout. --- .../properties/LongGuacamoleProperty.java | 67 +++++++++++ .../properties/BasicGuacamoleProperties.java | 11 ++ .../rest/auth/BasicTokenUserContextMap.java | 106 +++++++++++++++++- .../basic/rest/auth/TokenUserContextMap.java | 23 +++- 4 files changed, 202 insertions(+), 5 deletions(-) create mode 100644 guacamole-ext/src/main/java/org/glyptodon/guacamole/properties/LongGuacamoleProperty.java diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/properties/LongGuacamoleProperty.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/properties/LongGuacamoleProperty.java new file mode 100644 index 000000000..7f6c86aac --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/properties/LongGuacamoleProperty.java @@ -0,0 +1,67 @@ + +package org.glyptodon.guacamole.properties; + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is guacamole-ext. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleServerException; + +/** + * A GuacamoleProperty whose value is an long. + * + * @author James Muehlner + */ +public abstract class LongGuacamoleProperty implements GuacamoleProperty { + + @Override + public Long parseValue(String value) throws GuacamoleException { + + // If no property provided, return null. + if (value == null) + return null; + + try { + Long longValue = new Long(value); + return longValue; + } + catch (NumberFormatException e) { + throw new GuacamoleServerException("Property \"" + getName() + "\" must be an long.", e); + } + + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/BasicGuacamoleProperties.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/BasicGuacamoleProperties.java index 91e1d384e..a6054399c 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/BasicGuacamoleProperties.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/BasicGuacamoleProperties.java @@ -20,6 +20,7 @@ package org.glyptodon.guacamole.net.basic.properties; */ import org.glyptodon.guacamole.properties.FileGuacamoleProperty; +import org.glyptodon.guacamole.properties.LongGuacamoleProperty; /** * Properties used by the default Guacamole web application. @@ -64,4 +65,14 @@ public class BasicGuacamoleProperties { }; + /** + * The session timeout for the API, in milliseconds. + */ + public static final LongGuacamoleProperty API_SESSION_TIMEOUT = new LongGuacamoleProperty() { + + @Override + public String getName() { return "api-session-timeout"; } + + }; + } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java index b6ba99882..204abf46c 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java @@ -18,13 +18,113 @@ package org.glyptodon.guacamole.net.basic.rest.auth; * along with this program. If not, see . */ +import java.util.Date; import java.util.HashMap; +import java.util.Map; +import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.net.auth.UserContext; +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 TokenUserContextMap. + * A basic, HashMap-based implementation of the TokenUserContextMap with support + * for session timeouts. * * @author James Muehlner */ -public class BasicTokenUserContextMap extends HashMap - implements TokenUserContextMap {} +public class BasicTokenUserContextMap implements TokenUserContextMap { + + /** + * Logger for this class. + */ + private static Logger logger = LoggerFactory.getLogger(BasicTokenUserContextMap.class); + + /** + * The last time a user with a specific auth token accessed the API. + */ + private Map lastAccessTimeMap = new HashMap(); + + /** + * Keeps track of the authToken to UserContext mapping. + */ + private Map userContextMap = new HashMap(); + + /** + * The session timeout configuration for an API session. + */ + private final long SESSION_TIMEOUT; + + /** + * Create a new BasicTokenUserContextMap and initialize the session timeout value. + */ + public BasicTokenUserContextMap() { + + // Set up the authToken => userContext hashmap + super(); + + // Set up the SESSION_TIMEOUT value, with a one hour default. + long sessionTimeoutValue = 3600000l; + try { + sessionTimeoutValue = GuacamoleProperties.getProperty(BasicGuacamoleProperties.API_SESSION_TIMEOUT, 3600000l); + } catch (GuacamoleException e) { + logger.error("Unexpected GuacamoleException caught while reading API_SESSION_TIMEOUT property.", e); + } + + 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) { + userContextMap.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()); + } + + 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 UserContext 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 UserContext + logAccessTime(authToken); + return userContextMap.get(authToken); + } + + @Override + public void put(String authToken, UserContext userContext) { + + // Update the last access time, and create the token/UserContext mapping + logAccessTime(authToken); + userContextMap.put(authToken, userContext); + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenUserContextMap.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenUserContextMap.java index 0f106ecfc..55ad45a8d 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenUserContextMap.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenUserContextMap.java @@ -18,7 +18,6 @@ package org.glyptodon.guacamole.net.basic.rest.auth; * along with this program. If not, see . */ -import java.util.Map; import org.glyptodon.guacamole.net.auth.UserContext; /** @@ -27,4 +26,24 @@ import org.glyptodon.guacamole.net.auth.UserContext; * * @author James Muehlner */ -public interface TokenUserContextMap extends Map {} +public interface TokenUserContextMap { + + /** + * Registers that a user has just logged in with the specified authToken and + * UserContext. + * + * @param authToken The authentication token for the logged in user. + * @param userContext The UserContext for the logged in user. + */ + public void put(String authToken, UserContext userContext); + + /** + * Get the UserContext 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 UserContext for the given auth token, if the auth token + * represents a currently logged in user, null otherwise. + */ + public UserContext get(String authToken); +} From 873fd57e25ee22fa38742fc2ee91bbe2665fa1dd Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Thu, 19 Sep 2013 21:10:26 -0700 Subject: [PATCH 10/33] Ticket #362: Removed pointless super() call. --- .../net/basic/rest/auth/BasicTokenUserContextMap.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java index 204abf46c..997343bbf 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java @@ -61,9 +61,6 @@ public class BasicTokenUserContextMap implements TokenUserContextMap { */ public BasicTokenUserContextMap() { - // Set up the authToken => userContext hashmap - super(); - // Set up the SESSION_TIMEOUT value, with a one hour default. long sessionTimeoutValue = 3600000l; try { From d111d9a9a67f9ee4d9be65bf52001d326438d30e Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Thu, 19 Sep 2013 22:58:20 -0700 Subject: [PATCH 11/33] Ticket #362: Changes to make mjumper happy. --- .../net/basic/rest/auth/BasicTokenUserContextMap.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java index 997343bbf..0629efdde 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java @@ -52,7 +52,7 @@ public class BasicTokenUserContextMap implements TokenUserContextMap { private Map userContextMap = new HashMap(); /** - * The session timeout configuration for an API session. + * The session timeout configuration for an API session, in milliseconds. */ private final long SESSION_TIMEOUT; @@ -62,14 +62,17 @@ public class BasicTokenUserContextMap implements TokenUserContextMap { public BasicTokenUserContextMap() { // Set up the SESSION_TIMEOUT value, with a one hour default. - long sessionTimeoutValue = 3600000l; + 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.", e); + } + catch (GuacamoleException e) { + logger.error("Unexpected GuacamoleException caught while reading API_SESSION_TIMEOUT property. Defaulting to 1 hour.", e); + sessionTimeoutValue = 3600000l; } SESSION_TIMEOUT = sessionTimeoutValue; + } /** From feb03b87c589373ce18400680fc74daa1fc35baf Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Tue, 24 Sep 2013 19:58:22 -0700 Subject: [PATCH 12/33] Ticket #362: All basic CRUD operations for connection. --- .../basic/rest/connection/APIConnection.java | 45 ++---- .../connection/ConnectionRESTService.java | 146 +++++++++++++++++- 2 files changed, 159 insertions(+), 32 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java index 797f045da..0e78f3fcc 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java @@ -20,8 +20,10 @@ package org.glyptodon.guacamole.net.basic.rest.connection; import java.util.List; 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; /** @@ -29,7 +31,7 @@ import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; * * @author James Muehlner */ -public class APIConnection { +public class APIConnection implements Connection { /** * The name of this connection. @@ -70,58 +72,36 @@ public class APIConnection { this.history = connection.getHistory(); } - /** - * Get the name of this connection. - * @return The name of this connection. - */ + @Override public String getName() { return name; } - /** - * Set the name of this connection. - * @param name The name of this connection. - */ + @Override public void setName(String name) { this.name = name; } - - /** - * Get the identifier of this connection. - * @return The identifier of this connection. - */ + @Override public String getIdentifier() { return identifier; } - /** - * Set the identifier of this connection. - * @param identifier The identifier of this connection. - */ + @Override public void setIdentifier(String identifier) { this.identifier = identifier; } - /** - * Get the configuration for this connection. - * @return The configuration for this connection. - */ + @Override public GuacamoleConfiguration getConfiguration() { return configuration; } - /** - * Set the configuration for this connection. - * @param configuration The configuration for this connection. - */ + @Override public void setConfiguration(GuacamoleConfiguration configuration) { this.configuration = configuration; } - /** - * Get the history records for this connection. - * @return The history records for this connection. - */ + @Override public List getHistory() { return history; } @@ -133,5 +113,10 @@ public class APIConnection { public void setHistory(List history) { this.history = history; } + + @Override + public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException { + throw new UnsupportedOperationException("Not supported."); + } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java index 961cccb13..692bff933 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java @@ -23,6 +23,8 @@ import java.util.ArrayList; import java.util.List; 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; @@ -126,7 +128,8 @@ public class ConnectionRESTService { */ @GET @Path("/{connectionID}") - public APIConnection getConnection(@QueryParam("token") String authToken, @PathParam("connectionID") String connectionID) { + public APIConnection getConnection(@QueryParam("token") String authToken, + @PathParam("connectionID") String connectionID) { UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); try { @@ -157,7 +160,7 @@ public class ConnectionRESTService { * * @param authToken The authentication token that is used to authenticate * the user performing the operation. - * @param connectionID The ID of the Connection. + * @param connectionID The ID of the Connection to delete. */ @DELETE @Path("/{connectionID}") @@ -185,5 +188,144 @@ public class ConnectionRESTService { "Unexpected server error. " + e.getMessage()); } } + + /** + * 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. + */ + @POST + public String createConnection(@QueryParam("token") String authToken, + @QueryParam("parentID") String parentID, APIConnection connection) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + if(connection == null) + throw new HTTPException(Status.BAD_REQUEST, + "A connection is required for this request."); + + try { + // If the parent connection group is passed in, try to find it. + ConnectionGroup parentConnectionGroup; + if(parentID == null) + parentConnectionGroup = userContext.getRootConnectionGroup(); + else { + ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); + Directory connectionGroupDirectory = rootGroup.getConnectionGroupDirectory(); + parentConnectionGroup = connectionGroupDirectory.get(parentID); + } + + if(parentConnectionGroup == null) + throw new HTTPException(Status.BAD_REQUEST, + "No ConnectionGroup found with the provided parentID."); + + Directory connectionDirectory = + parentConnectionGroup.getConnectionDirectory(); + + // Create the connection + connectionDirectory.add(connection); + + // Return the new connection identifier + return connection.getIdentifier(); + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied."); + } catch(GuacamoleException e) { + logger.error("Unexpected GuacamoleException caught while listing connections.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, + "Unexpected server error. " + e.getMessage()); + } + } + + /** + * 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 delete. + * @param connection The connection to update. + */ + @POST + @Path("/{connectionID}") + public void updateConnection(@QueryParam("token") String authToken, + @PathParam("connectionID") String connectionID, APIConnection connection) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + if(connection == null) + throw new HTTPException(Status.BAD_REQUEST, + "A connection is required for this request."); + + try { + // Get the connection directory + ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); + Directory connectionDirectory = + rootGroup.getConnectionDirectory(); + + // Make sure the connection is there before trying to update + if(connectionDirectory.get(connectionID) == null) + throw new HTTPException(Status.BAD_REQUEST, + "No Connection with the provided ID."); + + // Update the connection + connectionDirectory.update(connection); + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied."); + } catch(GuacamoleException e) { + logger.error("Unexpected GuacamoleException caught while listing connections.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, + "Unexpected server error. " + e.getMessage()); + } + } + + /** + * 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 delete. + * @param parentID The ID of the ConnectionGroup the connections + * belong to. If null, the root connection group will be used. + */ + @PUT + @Path("/{connectionID}") + public void moveConnection(@QueryParam("token") String authToken, + @PathParam("connectionID") String connectionID, @QueryParam("parentID") String parentID) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + try { + // Get the connection directory + ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); + Directory connectionDirectory = + rootGroup.getConnectionDirectory(); + + // Find the new parent connection group + Directory connectionGroupDirectory = rootGroup.getConnectionGroupDirectory(); + ConnectionGroup parentConnectionGroup = connectionGroupDirectory.get(parentID); + + if(parentConnectionGroup == null) + throw new HTTPException(Status.BAD_REQUEST, + "No ConnectionGroup found with the provided parentID."); + + // Make sure the connection is there before trying to delete + if(connectionDirectory.get(connectionID) == null) + throw new HTTPException(Status.BAD_REQUEST, + "No Connection found with the provided ID."); + + // Move the connection + connectionDirectory.move(connectionID, connectionDirectory); + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied."); + } catch(GuacamoleException e) { + logger.error("Unexpected GuacamoleException caught while deleting a connection.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, + "Unexpected server error. " + e.getMessage()); + } + } } From 8b2acc99b60da318ac6744690f74fe9b7cb525de Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Tue, 24 Sep 2013 21:47:03 -0700 Subject: [PATCH 13/33] Ticket #362. Got connection create working. --- .../guacamole/net/auth/mysql/ConnectionDirectory.java | 3 +++ .../guacamole/net/auth/mysql/ConnectionGroupDirectory.java | 3 +++ .../net/basic/rest/connection/ConnectionRESTService.java | 2 ++ 3 files changed, 8 insertions(+) diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionDirectory.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionDirectory.java index 232cdd169..be313877e 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionDirectory.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionDirectory.java @@ -180,6 +180,9 @@ public class ConnectionDirectory implements Directory{ // Create connection MySQLConnection connection = connectionService.createConnection( name, object.getConfiguration().getProtocol(), user_id, parentID); + + // Set the connection ID + object.setIdentifier(connection.getIdentifier()); // Add connection parameters createConfigurationValues(connection.getConnectionID(), diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionGroupDirectory.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionGroupDirectory.java index fdcb862aa..0dd2899c3 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionGroupDirectory.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/ConnectionGroupDirectory.java @@ -168,6 +168,9 @@ public class ConnectionGroupDirectory implements Directory Date: Tue, 24 Sep 2013 22:50:17 -0700 Subject: [PATCH 14/33] Ticket #362. Improved error handling. --- .../net/basic/rest/auth/LoginRESTService.java | 3 - .../connection/ConnectionRESTService.java | 89 +++++++++---------- 2 files changed, 43 insertions(+), 49 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java index 720ef88ad..9971bf359 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/LoginRESTService.java @@ -5,15 +5,12 @@ import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; 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.rest.APIError; import org.glyptodon.guacamole.net.basic.rest.HTTPException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java index 5cd3afb38..9a081650f 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java @@ -32,6 +32,7 @@ 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.GuacamoleSecurityException; import org.glyptodon.guacamole.net.auth.Connection; @@ -96,8 +97,7 @@ public class ConnectionRESTService { } if(parentConnectionGroup == null) - throw new HTTPException(Status.BAD_REQUEST, - "No ConnectionGroup found with the provided parentID."); + throw new GuacamoleClientException("No ConnectionGroup found with the provided parentID."); Directory connectionDirectory = parentConnectionGroup.getConnectionDirectory(); @@ -112,11 +112,12 @@ public class ConnectionRESTService { return connectionService.convertConnectionList(connections); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + } catch(GuacamoleClientException e) { + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught while listing connections.", e); - throw new HTTPException(Status.INTERNAL_SERVER_ERROR, - "Unexpected server error. " + e.getMessage()); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); } } @@ -144,16 +145,16 @@ public class ConnectionRESTService { Connection connection = connectionDirectory.get(connectionID); if(connection == null) - throw new HTTPException(Status.BAD_REQUEST, - "No Connection found with the provided ID."); + throw new GuacamoleClientException("No Connection found with the provided ID."); return new APIConnection(connection); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + } catch(GuacamoleClientException e) { + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { - logger.error("Unexpected GuacamoleException caught while getting a connection.", e); - throw new HTTPException(Status.INTERNAL_SERVER_ERROR, - "Unexpected server error. " + e.getMessage()); + logger.error("Unexpected GuacamoleException caught while getting connection.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); } } @@ -177,17 +178,17 @@ public class ConnectionRESTService { // Make sure the connection is there before trying to delete if(connectionDirectory.get(connectionID) == null) - throw new HTTPException(Status.BAD_REQUEST, - "No Connection found with the provided ID."); + throw new GuacamoleClientException("No Connection found with the provided ID."); // Delete the connection connectionDirectory.remove(connectionID); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + } catch(GuacamoleClientException e) { + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { - logger.error("Unexpected GuacamoleException caught while deleting a connection.", e); - throw new HTTPException(Status.INTERNAL_SERVER_ERROR, - "Unexpected server error. " + e.getMessage()); + logger.error("Unexpected GuacamoleException caught while deleting connection.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); } } @@ -209,11 +210,10 @@ public class ConnectionRESTService { @QueryParam("parentID") String parentID, APIConnection connection) { UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); - if(connection == null) - throw new HTTPException(Status.BAD_REQUEST, - "A connection is required for this request."); - try { + 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) @@ -225,8 +225,7 @@ public class ConnectionRESTService { } if(parentConnectionGroup == null) - throw new HTTPException(Status.BAD_REQUEST, - "No ConnectionGroup found with the provided parentID."); + throw new GuacamoleClientException("No ConnectionGroup found with the provided parentID."); Directory connectionDirectory = parentConnectionGroup.getConnectionDirectory(); @@ -237,11 +236,12 @@ public class ConnectionRESTService { // Return the new connection identifier return connection.getIdentifier(); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + } catch(GuacamoleClientException e) { + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { - logger.error("Unexpected GuacamoleException caught while listing connections.", e); - throw new HTTPException(Status.INTERNAL_SERVER_ERROR, - "Unexpected server error. " + e.getMessage()); + logger.error("Unexpected GuacamoleException caught while creating connection.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); } } @@ -259,11 +259,10 @@ public class ConnectionRESTService { @PathParam("connectionID") String connectionID, APIConnection connection) { UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); - if(connection == null) - throw new HTTPException(Status.BAD_REQUEST, - "A connection is required for this request."); - try { + if(connection == null) + throw new GuacamoleClientException("A connection is required for this request."); + // Get the connection directory ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); Directory connectionDirectory = @@ -271,17 +270,17 @@ public class ConnectionRESTService { // Make sure the connection is there before trying to update if(connectionDirectory.get(connectionID) == null) - throw new HTTPException(Status.BAD_REQUEST, - "No Connection with the provided ID."); + throw new GuacamoleClientException("No Connection with the provided ID."); // Update the connection connectionDirectory.update(connection); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + } catch(GuacamoleClientException e) { + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { - logger.error("Unexpected GuacamoleException caught while listing connections.", e); - throw new HTTPException(Status.INTERNAL_SERVER_ERROR, - "Unexpected server error. " + e.getMessage()); + logger.error("Unexpected GuacamoleException caught updating connection.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); } } @@ -311,22 +310,20 @@ public class ConnectionRESTService { ConnectionGroup parentConnectionGroup = connectionGroupDirectory.get(parentID); if(parentConnectionGroup == null) - throw new HTTPException(Status.BAD_REQUEST, - "No ConnectionGroup found with the provided parentID."); + throw new GuacamoleClientException("No ConnectionGroup found with the provided parentID."); // Make sure the connection is there before trying to delete if(connectionDirectory.get(connectionID) == null) - throw new HTTPException(Status.BAD_REQUEST, - "No Connection found with the provided ID."); + throw new GuacamoleClientException("No Connection found with the provided ID."); // Move the connection - connectionDirectory.move(connectionID, connectionDirectory); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, "Permission Denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + } catch(GuacamoleClientException e) { + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { - logger.error("Unexpected GuacamoleException caught while deleting a connection.", e); - throw new HTTPException(Status.INTERNAL_SERVER_ERROR, - "Unexpected server error. " + e.getMessage()); + logger.error("Unexpected GuacamoleException caught moving connection.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); } } From 08934a4723c9fa99cd6dde5668f15832c8f42e93 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Wed, 25 Sep 2013 22:12:23 -0700 Subject: [PATCH 15/33] Ticket #362. Created APIConnectionWrapper and added package info to the java packages. --- .../net/basic/rest/auth/package-info.java | 6 ++ .../basic/rest/connection/APIConnection.java | 71 +++++++------ .../rest/connection/APIConnectionWrapper.java | 100 ++++++++++++++++++ .../connection/ConnectionRESTService.java | 4 +- .../basic/rest/connection/package-info.java | 6 ++ .../net/basic/rest/package-info.java | 6 ++ 6 files changed, 158 insertions(+), 35 deletions(-) create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/package-info.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionWrapper.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/package-info.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/package-info.java diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/package-info.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/package-info.java new file mode 100644 index 000000000..f40e4544e --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/package-info.java @@ -0,0 +1,6 @@ + +/** + * Classes related to the authentication aspect of the Guacamole REST API. + */ +package org.glyptodon.guacamole.net.basic.rest; + diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java index 0e78f3fcc..14a610aeb 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnection.java @@ -18,20 +18,19 @@ package org.glyptodon.guacamole.net.basic.rest.connection; * along with this program. If not, see . */ +import java.util.HashMap; 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 simple connection to expose through the REST endpoints. * * @author James Muehlner */ -public class APIConnection implements Connection { +public class APIConnection { /** * The name of this connection. @@ -43,15 +42,15 @@ public class APIConnection implements Connection { */ private String identifier; - /** - * The configuration associated with this connection. - */ - private GuacamoleConfiguration configuration; - /** * The history records associated with this connection. */ private List history; + + /** + * Map of all associated parameter values, indexed by parameter name. + */ + private Map parameters = new HashMap(); /** * Create an empty APIConnection. @@ -68,55 +67,61 @@ public class APIConnection implements Connection { throws GuacamoleException { this.name = connection.getName(); this.identifier = connection.getIdentifier(); - this.configuration = connection.getConfiguration(); this.history = connection.getHistory(); } - @Override + /** + * Returns the name of this connection. + * @return The name of this connection. + */ public String getName() { return name; } - @Override + /** + * Set the name of this connection. + * @param name The name of this connection. + */ public void setName(String name) { this.name = name; } - @Override + + /** + * Returns the unique identifier for this connection. + * @return The unique identifier for this connection. + */ public String getIdentifier() { return identifier; } - - @Override + /** + * Sets the unique identifier for this connection. + */ public void setIdentifier(String identifier) { this.identifier = identifier; } - @Override - public GuacamoleConfiguration getConfiguration() { - return configuration; - } - - @Override - public void setConfiguration(GuacamoleConfiguration configuration) { - this.configuration = configuration; - } - - @Override + /** + * Returns the history records associated with this connection. + * @return The history records associated with this connection. + */ public List getHistory() { return history; } /** - * Set the history records for this connection. - * @param history The history records for this connection. + * Returns the parameter map for this connection. + * @return The parameter map for this connection. */ - public void setHistory(List history) { - this.history = history; + public Map getParameters() { + return parameters; } - @Override - public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException { - throw new UnsupportedOperationException("Not supported."); + /** + * Sets the parameter map for this connection. + * @param parameters The parameter map for this connection. + */ + public void setParameters(Map parameters) { + this.parameters = parameters; } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionWrapper.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionWrapper.java new file mode 100644 index 000000000..0b9404ac6 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionWrapper.java @@ -0,0 +1,100 @@ +package org.glyptodon.guacamole.net.basic.rest.connection; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import java.util.HashMap; +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 GuacamoleConfiguration getConfiguration() { + + // Create the GuacamoleConfiguration from the parameter map + GuacamoleConfiguration configuration = new GuacamoleConfiguration(); + + Map parameters = apiConnection.getParameters(); + + for(String key : parameters.keySet()) + configuration.setParameter(key, parameters.get(key)); + + return configuration; + } + + @Override + public void setConfiguration(GuacamoleConfiguration config) { + + // Create a parameter map from the GuacamoleConfiguration + Map newParameters = new HashMap(); + for(String key : config.getParameterNames()) + newParameters.put(key, config.getParameter(key)); + + apiConnection.setParameters(newParameters); + } + + @Override + public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException { + throw new UnsupportedOperationException("Operation not supported."); + } + + @Override + public List getHistory() throws GuacamoleException { + return apiConnection.getHistory(); + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java index 9a081650f..c1ddc1e1c 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java @@ -231,7 +231,7 @@ public class ConnectionRESTService { parentConnectionGroup.getConnectionDirectory(); // Create the connection - connectionDirectory.add(connection); + connectionDirectory.add(new APIConnectionWrapper(connection)); // Return the new connection identifier return connection.getIdentifier(); @@ -273,7 +273,7 @@ public class ConnectionRESTService { throw new GuacamoleClientException("No Connection with the provided ID."); // Update the connection - connectionDirectory.update(connection); + connectionDirectory.update(new APIConnectionWrapper(connection)); } catch(GuacamoleSecurityException e) { throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/package-info.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/package-info.java new file mode 100644 index 000000000..8e4dcfde6 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/package-info.java @@ -0,0 +1,6 @@ + +/** + * Classes related to the connection manipulation aspect of the Guacamole REST API. + */ +package org.glyptodon.guacamole.net.basic.rest.connection; + diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/package-info.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/package-info.java new file mode 100644 index 000000000..3abc5554b --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/package-info.java @@ -0,0 +1,6 @@ + +/** + * Classes related to the basic Guacamole REST API. + */ +package org.glyptodon.guacamole.net.basic.rest; + From 0fefe94b4d9aa449668f477b3538e759aba14c29 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Wed, 2 Oct 2013 21:12:17 -0700 Subject: [PATCH 16/33] Ticket #362: Created packing info file. --- .../glyptodon/guacamole/net/basic/rest/auth/package-info.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/package-info.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/package-info.java index f40e4544e..cb88a3e0d 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/package-info.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/package-info.java @@ -2,5 +2,5 @@ /** * Classes related to the authentication aspect of the Guacamole REST API. */ -package org.glyptodon.guacamole.net.basic.rest; +package org.glyptodon.guacamole.net.basic.rest.auth; From 754bbb28c0e19888680532f420d9c6701827f9ad Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Sun, 13 Oct 2013 16:31:54 -0700 Subject: [PATCH 17/33] Ticket #362: Added ConnectionGroup REST endpoints. --- .../mysql/service/ConnectionGroupService.java | 2 +- guacamole/nb-configuration.xml | 18 + .../guacamole/net/basic/rest/RESTModule.java | 2 + .../net/basic/rest/RESTServletModule.java | 2 + .../connection/ConnectionRESTService.java | 9 +- .../connectiongroup/APIConnectionGroup.java | 110 ++++++ .../APIConnectionGroupWrapper.java | 95 +++++ .../ConnectionGroupRESTService.java | 326 ++++++++++++++++++ .../ConnectionGroupService.java | 50 +++ .../rest/connectiongroup/package-info.java | 7 + guacamole/src/main/webapp/WEB-INF/web.xml | 10 - 11 files changed, 614 insertions(+), 17 deletions(-) create mode 100644 guacamole/nb-configuration.xml create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroup.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroupWrapper.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/package-info.java diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionGroupService.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionGroupService.java index f4954cb32..2e96c54ac 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionGroupService.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionGroupService.java @@ -348,7 +348,7 @@ public class ConnectionGroupService { } /** - * Creates a new connection group having the given name and protocol. + * Creates a new connection group having the given name and type. * * @param name The name to assign to the new connection group. * @param userID The ID of the user who created this connection group. diff --git a/guacamole/nb-configuration.xml b/guacamole/nb-configuration.xml new file mode 100644 index 000000000..4da1f6c9b --- /dev/null +++ b/guacamole/nb-configuration.xml @@ -0,0 +1,18 @@ + + + + + + ide + + diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java index 94deb77e4..d7f31b8d7 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java @@ -28,6 +28,7 @@ import org.glyptodon.guacamole.net.basic.rest.auth.BasicTokenUserContextMap; import org.glyptodon.guacamole.net.basic.rest.auth.SecureRandomAuthTokenGenerator; import org.glyptodon.guacamole.net.basic.rest.auth.TokenUserContextMap; import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionService; +import org.glyptodon.guacamole.net.basic.rest.connectiongroup.ConnectionGroupService; import org.glyptodon.guacamole.properties.GuacamoleProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,6 +66,7 @@ public class RESTModule extends AbstractModule { bind(AuthenticationProvider.class).toInstance(authProvider); bind(TokenUserContextMap.class).toInstance(new BasicTokenUserContextMap()); bind(ConnectionService.class); + bind(ConnectionGroupService.class); bind(AuthenticationService.class); bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java index d11992563..ccb85adbf 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java @@ -24,6 +24,7 @@ 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; /** * A Guice Module to set up the servlet mappings for the Guacamole REST API. @@ -37,6 +38,7 @@ public class RESTServletModule extends ServletModule { // Set up the API endpoints bind(ConnectionRESTService.class); + bind(ConnectionGroupRESTService.class); bind(LoginRESTService.class); // Set up the servlet and JSON mappings diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java index c1ddc1e1c..acfca2b1a 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java @@ -250,7 +250,7 @@ public class ConnectionRESTService { * * @param authToken The authentication token that is used to authenticate * the user performing the operation. - * @param connectionID The ID of the Connection to delete. + * @param connectionID The ID of the Connection to move. * @param connection The connection to update. */ @POST @@ -289,7 +289,7 @@ public class ConnectionRESTService { * * @param authToken The authentication token that is used to authenticate * the user performing the operation. - * @param connectionID The ID of the Connection to delete. + * @param connectionID The ID of the Connection to move. * @param parentID The ID of the ConnectionGroup the connections * belong to. If null, the root connection group will be used. */ @@ -312,11 +312,8 @@ public class ConnectionRESTService { if(parentConnectionGroup == null) throw new GuacamoleClientException("No ConnectionGroup found with the provided parentID."); - // Make sure the connection is there before trying to delete - if(connectionDirectory.get(connectionID) == null) - throw new GuacamoleClientException("No Connection found with the provided ID."); - // Move the connection + connectionDirectory.move(connectionID, parentConnectionGroup.getConnectionDirectory()); } catch(GuacamoleSecurityException e) { throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroup.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroup.java new file mode 100644 index 000000000..0dfbc177f --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroup.java @@ -0,0 +1,110 @@ +package org.glyptodon.guacamole.net.basic.rest.connectiongroup; + +import org.glyptodon.guacamole.net.auth.ConnectionGroup; +import org.glyptodon.guacamole.net.auth.ConnectionGroup.Type; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * A simple connection group to expose through the REST endpoints. + * + * @author James Muehlner + */ +public class APIConnectionGroup { + + /** + * The name of this connection group. + */ + private String name; + + /** + * The identifier of this connection group. + */ + private String identifier; + + /** + * 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.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 type of this connection group. + * @return The type of this connection group. + */ + public Type getType() { + return type; + } + + /** + * Set the type of this connection group. + * @param type The Type of this connection group. + */ + public void setType(Type type) { + this.type = type; + } +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroupWrapper.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroupWrapper.java new file mode 100644 index 000000000..c61d3812e --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/APIConnectionGroupWrapper.java @@ -0,0 +1,95 @@ +package org.glyptodon.guacamole.net.basic.rest.connectiongroup; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +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 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 void setType(Type type) { + apiConnectionGroup.setType(type); + } + + @Override + public Type getType() { + return apiConnectionGroup.getType(); + } + + @Override + public Directory getConnectionDirectory() throws GuacamoleException { + throw new UnsupportedOperationException("Operation not supported."); + } + + @Override + public Directory getConnectionGroupDirectory() throws GuacamoleException { + throw new UnsupportedOperationException("Operation not supported."); + } + + @Override + public GuacamoleSocket connect(GuacamoleClientInformation info) throws GuacamoleException { + throw new UnsupportedOperationException("Operation not supported."); + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java new file mode 100644 index 000000000..825fdd290 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java @@ -0,0 +1,326 @@ +package org.glyptodon.guacamole.net.basic.rest.connectiongroup; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import com.google.inject.Inject; +import java.util.ArrayList; +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.GuacamoleSecurityException; +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.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("/api/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; + + /** + * 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. + */ + @GET + public List getConnectionGroups(@QueryParam("token") String authToken, @QueryParam("parentID") String parentID) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + try { + // If the parent connection group is passed in, try to find it. + ConnectionGroup parentConnectionGroup; + if(parentID == null) + parentConnectionGroup = userContext.getRootConnectionGroup(); + else { + ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); + Directory connectionGroupDirectory = rootGroup.getConnectionGroupDirectory(); + parentConnectionGroup = connectionGroupDirectory.get(parentID); + } + + if(parentConnectionGroup == null) + throw new GuacamoleClientException("No ConnectionGroup found with the provided parentID."); + + Directory connectionGroupDirectory = + parentConnectionGroup.getConnectionGroupDirectory(); + + // Get the list of connection group names + List connectionGroups = new ArrayList(); + Iterable identifiers = connectionGroupDirectory.getIdentifiers(); + + // Get the connection group for each name + for(String identifier : identifiers) + connectionGroups.add(connectionGroupDirectory.get(identifier)); + + return connectionGroupService.convertConnectionGroupList(connectionGroups); + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + } catch(GuacamoleClientException e) { + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + } catch(GuacamoleException e) { + logger.error("Unexpected GuacamoleException caught while listing connection groups.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); + } + } + + /** + * 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. + */ + @GET + @Path("/{connectionGroupID}") + public APIConnectionGroup getConnectionGroup(@QueryParam("token") String authToken, + @PathParam("connectionGroupID") String connectionGroupID) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + try { + // Get the connection group directory + ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); + Directory connectionGroupDirectory = + rootGroup.getConnectionGroupDirectory(); + + // Get the connection group + ConnectionGroup connectionGroup = connectionGroupDirectory.get(connectionGroupID); + + if(connectionGroup == null) + throw new GuacamoleClientException("No ConnectionGroup found with the provided ID."); + + return new APIConnectionGroup(connectionGroup); + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + } catch(GuacamoleClientException e) { + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + } catch(GuacamoleException e) { + logger.error("Unexpected GuacamoleException caught while getting connection group.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); + } + } + + /** + * 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. + */ + @DELETE + @Path("/{connectionGroupID}") + public void deleteConnectionGroup(@QueryParam("token") String authToken, @PathParam("connectionGroupID") String connectionGroupID) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + try { + // Get the connection group directory + ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); + Directory connectionGroupDirectory = + rootGroup.getConnectionGroupDirectory(); + + // Make sure the connection is there before trying to delete + if(connectionGroupDirectory.get(connectionGroupID) == null) + throw new GuacamoleClientException("No Connection found with the provided ID."); + + // Delete the connection group + connectionGroupDirectory.remove(connectionGroupID); + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + } catch(GuacamoleClientException e) { + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + } catch(GuacamoleException e) { + logger.error("Unexpected GuacamoleException caught while deleting connection group.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); + } + } + + /** + * 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 connection The connection group to create. + * @return The identifier of the new connection group. + */ + @POST + public String createConnectionGroup(@QueryParam("token") String authToken, + @QueryParam("parentID") String parentID, APIConnectionGroup connectionGroup) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + try { + if(connectionGroup == null) + throw new GuacamoleClientException("A connection group is required for this request."); + + // If the parent connection group is passed in, try to find it. + ConnectionGroup parentConnectionGroup; + if(parentID == null) + parentConnectionGroup = userContext.getRootConnectionGroup(); + else { + ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); + Directory connectionGroupDirectory = rootGroup.getConnectionGroupDirectory(); + parentConnectionGroup = connectionGroupDirectory.get(parentID); + } + + if(parentConnectionGroup == null) + throw new GuacamoleClientException("No ConnectionGroup found with the provided parentID."); + + Directory connectionGroupDirectory = + parentConnectionGroup.getConnectionGroupDirectory(); + + // Create the connection group + connectionGroupDirectory.add(new APIConnectionGroupWrapper(connectionGroup)); + + // Return the new connection group identifier + return connectionGroup.getIdentifier(); + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + } catch(GuacamoleClientException e) { + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + } catch(GuacamoleException e) { + logger.error("Unexpected GuacamoleException caught while creating connection.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); + } + } + + /** + * Updates a connection group. + * + * @param authToken The authentication token that is used to authenticate + * the user performing the operation. + * @param connectionID The ID of the ConnectionGroup to update. + * @param connection The connection group to update. + */ + @POST + @Path("/{connectionGroupID}") + public void updateConnectionGroup(@QueryParam("token") String authToken, + @PathParam("connectionGroupID") String connectionGroupID, APIConnectionGroup connectionGroup) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + try { + if(connectionGroup == null) + throw new GuacamoleClientException("A connection is required for this request."); + + // Get the connection directory + ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); + Directory connectionGroupDirectory = + rootGroup.getConnectionGroupDirectory(); + + // Make sure the connection group is there before trying to update + if(connectionGroupDirectory.get(connectionGroupID) == null) + throw new GuacamoleClientException("No ConnectionGroup with the provided ID."); + + // Update the connection group + connectionGroupDirectory.update(new APIConnectionGroupWrapper(connectionGroup)); + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + } catch(GuacamoleClientException e) { + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + } catch(GuacamoleException e) { + logger.error("Unexpected GuacamoleException caught updating connection.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); + } + } + + /** + * 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 connectionID The ID of the ConnectionGroup to move. + * @param parentID The ID of the ConnectionGroup the connection groups + * belong to. If null, the root connection group will be used. + */ + @PUT + @Path("/{connectionGroupID}") + public void moveConnectionGroup(@QueryParam("token") String authToken, + @PathParam("connectionGroupID") String connectionGroupID, @QueryParam("parentID") String parentID) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + try { + // Get the connection group directory + ConnectionGroup rootGroup = userContext.getRootConnectionGroup(); + Directory connectionGroupDirectory = + rootGroup.getConnectionGroupDirectory(); + + // Find the new parent connection group + Directory newConnectionGroupDirectory = rootGroup.getConnectionGroupDirectory(); + ConnectionGroup parentConnectionGroup = newConnectionGroupDirectory.get(parentID); + + if(newConnectionGroupDirectory == null) + throw new GuacamoleClientException("No ConnectionGroup found with the provided parentID."); + + // Move the connection + connectionGroupDirectory.move(connectionGroupID, parentConnectionGroup.getConnectionGroupDirectory()); + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + } catch(GuacamoleClientException e) { + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + } catch(GuacamoleException e) { + logger.error("Unexpected GuacamoleException caught moving connection.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); + } + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java new file mode 100644 index 000000000..f16241f6c --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java @@ -0,0 +1,50 @@ +package org.glyptodon.guacamole.net.basic.rest.connectiongroup; + +import java.util.ArrayList; +import java.util.List; +import org.glyptodon.guacamole.net.auth.Connection; +import org.glyptodon.guacamole.net.auth.ConnectionGroup; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * A service for performing useful manipulations on REST ConnectionGroups. + * + * @author James Muehlner + */ +public class ConnectionGroupService { + + /** + * Converts a list of ConnectionGroup to a list of APIConnectionGroup + * objects for exposing with the REST endpoints. + * + * @param connectionGroups The ConnectionGroup to convert for REST endpoint use. + * @return A List of APIConnectionGroup objects for use with the REST endpoint. + */ + public List convertConnectionGroupList( + List connectionGroups) { + List restConnectionGroups = new ArrayList(); + + for(ConnectionGroup connectionGroup : connectionGroups) { + restConnectionGroups.add(new APIConnectionGroup(connectionGroup)); + } + + return restConnectionGroups; + } +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/package-info.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/package-info.java new file mode 100644 index 000000000..9b0a3cf17 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/package-info.java @@ -0,0 +1,7 @@ + +/** + * Classes related to the connection group manipulation aspect + * of the Guacamole REST API. + */ +package org.glyptodon.guacamole.net.basic.rest.connectiongroup; + diff --git a/guacamole/src/main/webapp/WEB-INF/web.xml b/guacamole/src/main/webapp/WEB-INF/web.xml index 452f763a5..950848b6f 100644 --- a/guacamole/src/main/webapp/WEB-INF/web.xml +++ b/guacamole/src/main/webapp/WEB-INF/web.xml @@ -256,16 +256,6 @@ org.glyptodon.guacamole.net.basic.rest.RESTServletContextListener - - com.sun.jersey.api.json.POJOMappingFeature - true - - - - com.sun.jersey.config.property.packages - org.codehaus.jackson.jaxrs - - mp3 audio/mpeg From da5b8ffba07dca76c8364242252da372c69d3878 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Sun, 13 Oct 2013 16:33:22 -0700 Subject: [PATCH 18/33] Ticket #362: Remove nb-configuration.xml from commited files. --- guacamole/nb-configuration.xml | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 guacamole/nb-configuration.xml diff --git a/guacamole/nb-configuration.xml b/guacamole/nb-configuration.xml deleted file mode 100644 index 4da1f6c9b..000000000 --- a/guacamole/nb-configuration.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - ide - - From 3d33b5fc6e782eccdba40abe95d2a89d2e021efd Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Wed, 4 Dec 2013 21:28:19 -0800 Subject: [PATCH 19/33] Ticket #362: Added initial permission REST services. --- .../guacamole/net/basic/rest/RESTModule.java | 2 + .../net/basic/rest/RESTServletModule.java | 2 + .../rest/connection/ConnectionService.java | 2 +- .../ConnectionGroupService.java | 3 +- .../basic/rest/permission/APIPermission.java | 195 ++++++++++++++++++ .../permission/PermissionRESTService.java | 158 ++++++++++++++ .../rest/permission/PermissionService.java | 69 +++++++ .../basic/rest/permission/package-info.java | 6 + 8 files changed, 434 insertions(+), 3 deletions(-) create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermission.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/package-info.java diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java index d7f31b8d7..d7ea84bbb 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java @@ -29,6 +29,7 @@ import org.glyptodon.guacamole.net.basic.rest.auth.SecureRandomAuthTokenGenerato import org.glyptodon.guacamole.net.basic.rest.auth.TokenUserContextMap; 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.properties.GuacamoleProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -67,6 +68,7 @@ public class RESTModule extends AbstractModule { bind(TokenUserContextMap.class).toInstance(new BasicTokenUserContextMap()); bind(ConnectionService.class); bind(ConnectionGroupService.class); + bind(PermissionService.class); bind(AuthenticationService.class); bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java index ccb85adbf..a2f960dfc 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java @@ -25,6 +25,7 @@ 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; /** * A Guice Module to set up the servlet mappings for the Guacamole REST API. @@ -39,6 +40,7 @@ public class RESTServletModule extends ServletModule { // Set up the API endpoints bind(ConnectionRESTService.class); bind(ConnectionGroupRESTService.class); + bind(PermissionRESTService.class); bind(LoginRESTService.class); // Set up the servlet and JSON mappings diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java index 710d23d8f..7e26414f1 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java @@ -39,7 +39,7 @@ public class ConnectionService { * @throws GuacamoleException If an error occurs while converting the * connections. */ - public List convertConnectionList(List connections) + public List convertConnectionList(Iterable connections) throws GuacamoleException { List restConnections = new ArrayList(); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java index f16241f6c..402647a84 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java @@ -2,7 +2,6 @@ package org.glyptodon.guacamole.net.basic.rest.connectiongroup; import java.util.ArrayList; import java.util.List; -import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionGroup; /* @@ -38,7 +37,7 @@ public class ConnectionGroupService { * @return A List of APIConnectionGroup objects for use with the REST endpoint. */ public List convertConnectionGroupList( - List connectionGroups) { + Iterable connectionGroups) { List restConnectionGroups = new ArrayList(); for(ConnectionGroup connectionGroup : connectionGroups) { diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermission.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermission.java new file mode 100644 index 000000000..d7fb5eb1f --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermission.java @@ -0,0 +1,195 @@ +package org.glyptodon.guacamole.net.basic.rest.permission; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +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 + */ +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 { + CONNECTION, + CONNECTION_GROUP, + USER, + 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) { + if(permission instanceof ConnectionPermission) { + this.objectType = ObjectType.CONNECTION; + + this.objectPermissionType = ((ConnectionPermission) permission).getType(); + } else if(permission instanceof ConnectionGroupPermission) { + this.objectType = ObjectType.CONNECTION_GROUP; + + this.objectPermissionType = ((ConnectionGroupPermission) permission).getType(); + } else if(permission instanceof UserPermission) { + this.objectType = ObjectType.USER; + + this.objectPermissionType = ((UserPermission) permission).getType(); + } 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. + * If this.objectType is CONNECTION, CONNECTION_GROUP, or USER, this will be + * the string representation of the objectPermissionType. + * If this.objectType is SYSTEM, this will be the string representation of + * the systemPermissionType. + * + * @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 getPermission() { + switch(this.objectType) { + case CONNECTION: + return new ConnectionPermission + (this.objectPermissionType, this.objectIdentifier); + case CONNECTION_GROUP: + return new ConnectionGroupPermission + (this.objectPermissionType, this.objectIdentifier); + case USER: + return new UserPermission + (this.objectPermissionType, this.objectIdentifier); + case SYSTEM: + return new SystemPermission(this.systemPermissionType); + default: + return null; + } + } +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java new file mode 100644 index 000000000..7815dee8c --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java @@ -0,0 +1,158 @@ +package org.glyptodon.guacamole.net.basic.rest.permission; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import com.google.inject.Inject; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +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.GuacamoleClientException; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +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.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("/api/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. + */ + @GET + @Path("/{userID}") + public List getPermissions(@QueryParam("token") String authToken, @PathParam("userID") String userID) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + try { + // 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()); + + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + } catch(GuacamoleClientException e) { + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + } catch(GuacamoleException e) { + logger.error("Unexpected GuacamoleException caught while listing permissions.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); + } + } + + /** + * Sets the 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 retrieve permissions for. + * @param permissions The permissions to set for the user with the given userID. + */ + @POST + @Path("/{userID}") + public void setPermissions(@QueryParam("token") String authToken, + @PathParam("userID") String userID, List permissions) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + try { + // 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."); + + // All the permissions the user should have after this operation + Set newPermissions = permissionService.convertAPIPermissionList(permissions); + + // Get the original permissions the user had + Set originalPermissions = user.getPermissions(); + + Set permissionsToRemove = new HashSet(originalPermissions); + + // Find all permissions in the original set, but not the new one + permissionsToRemove.removeAll(newPermissions); + + // Remove all permissions that are no longer wanted + for(Permission permissionToRemove : permissionsToRemove) { + user.removePermission(permissionToRemove); + } + + // Get only those permissions that need to be added + newPermissions.removeAll(originalPermissions); + + // Add all new permissions + for(Permission newPermission : newPermissions) { + user.addPermission(newPermission); + } + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + } catch(GuacamoleClientException e) { + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + } catch(GuacamoleException e) { + logger.error("Unexpected GuacamoleException caught setting permissions.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); + } + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java new file mode 100644 index 000000000..2478f3e03 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java @@ -0,0 +1,69 @@ +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.GuacamoleException; +import org.glyptodon.guacamole.net.auth.permission.Permission; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * A service for performing useful manipulations on REST Permissions. + * + * @author James Muehlner + */ +public class PermissionService { + + /** + * Converts a list of Permission to a list of APIPermission objects for + * exposing with the REST endpoints. + * + * @param permissions The Connections to convert for REST endpoint use. + * @return A List of APIPermission objects for use with the REST endpoint. + */ + public List convertPermissionList(Iterable permissions) + throws GuacamoleException { + List restPermissions = new ArrayList(); + + for(Permission permission : permissions) { + restPermissions.add(new APIPermission(permission)); + } + + return restPermissions; + } + + /** + * Converts a list of APIPermission to a set of Permission objects for internal + * Guacamole use. + * + * @param restPermissions The APIPermission objects from the REST endpoints. + * @return a List of Permission objects for internal Guacamole use. + */ + public Set convertAPIPermissionList(Iterable restPermissions) { + Set permissions = new HashSet(); + + for(APIPermission restPermission : restPermissions) { + permissions.add(restPermission.getPermission()); + } + + return permissions; + } +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/package-info.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/package-info.java new file mode 100644 index 000000000..df354267c --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/package-info.java @@ -0,0 +1,6 @@ + +/** + * Classes related to the permission manipulation aspect of the Guacamole REST API. + */ +package org.glyptodon.guacamole.net.basic.rest.permission; + From 67ea6406eb59eba206a34593f0f8d36d345b0f9d Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Wed, 4 Dec 2013 21:33:01 -0800 Subject: [PATCH 20/33] Ticket #362: Re-ordered lines. --- .../net/basic/rest/permission/PermissionRESTService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java index 7815dee8c..fd5e72a11 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java @@ -128,9 +128,8 @@ public class PermissionRESTService { // Get the original permissions the user had Set originalPermissions = user.getPermissions(); - Set permissionsToRemove = new HashSet(originalPermissions); - // Find all permissions in the original set, but not the new one + Set permissionsToRemove = new HashSet(originalPermissions); permissionsToRemove.removeAll(newPermissions); // Remove all permissions that are no longer wanted From 6146aeba93ee25cdc61f62ddfebd6c5c6ceb3a4f Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Wed, 4 Dec 2013 22:42:23 -0800 Subject: [PATCH 21/33] Ticket #362: Cleaned up permission list code a bit. --- .../guacamole/net/basic/rest/permission/APIPermission.java | 7 ++++++- .../net/basic/rest/permission/PermissionService.java | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermission.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermission.java index d7fb5eb1f..63e842e41 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermission.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermission.java @@ -18,6 +18,7 @@ package org.glyptodon.guacamole.net.basic.rest.permission; * along with this program. If not, see . */ +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; @@ -30,6 +31,7 @@ import org.glyptodon.guacamole.net.auth.permission.UserPermission; * * @author James Muehlner */ +@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) public class APIPermission { /** @@ -79,14 +81,17 @@ public class APIPermission { this.objectType = ObjectType.CONNECTION; this.objectPermissionType = ((ConnectionPermission) permission).getType(); + this.objectIdentifier = ((ConnectionPermission) permission).getObjectIdentifier(); } else if(permission instanceof ConnectionGroupPermission) { this.objectType = ObjectType.CONNECTION_GROUP; this.objectPermissionType = ((ConnectionGroupPermission) permission).getType(); + this.objectIdentifier = ((ConnectionGroupPermission) permission).getObjectIdentifier(); } else if(permission instanceof UserPermission) { this.objectType = ObjectType.USER; this.objectPermissionType = ((UserPermission) permission).getType(); + this.objectIdentifier = ((UserPermission) permission).getObjectIdentifier(); } else if(permission instanceof SystemPermission) { this.objectType = ObjectType.SYSTEM; @@ -175,7 +180,7 @@ public class APIPermission { * @return An org.glyptodon.guacamole.net.auth.permission.Permission * representation of this APIPermission. */ - public Permission getPermission() { + public Permission toPermission() { switch(this.objectType) { case CONNECTION: return new ConnectionPermission diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java index 2478f3e03..3b9626bbf 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java @@ -61,7 +61,7 @@ public class PermissionService { Set permissions = new HashSet(); for(APIPermission restPermission : restPermissions) { - permissions.add(restPermission.getPermission()); + permissions.add(restPermission.toPermission()); } return permissions; From 9aa29fe4b2aafc4e8294de5475aab5663f43569a Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Wed, 4 Dec 2013 22:47:27 -0800 Subject: [PATCH 22/33] Ticket #362: Fixed bug in user directory. --- .../sourceforge/guacamole/net/auth/mysql/UserDirectory.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/UserDirectory.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/UserDirectory.java index a37742e3b..3c1c41629 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/UserDirectory.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/UserDirectory.java @@ -150,6 +150,9 @@ public class UserDirectory implements Directory { // Get user MySQLUser user = userService.retrieveUser(identifier); + + if(user == null) + return null; // Verify access is granted permissionCheckService.verifyUserAccess(this.user_id, @@ -157,7 +160,7 @@ public class UserDirectory implements Directory { MySQLConstants.USER_READ); // Return user - return userService.retrieveUser(identifier); + return user; } From 64e56dc9ab5be4b5206308486775792662ac66c8 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Thu, 5 Dec 2013 20:45:38 -0800 Subject: [PATCH 23/33] Ticket #362: Changed permission operations to be atomic. --- .../permission/PermissionRESTService.java | 69 +++++++++++-------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java index fd5e72a11..a4253285e 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java @@ -102,17 +102,17 @@ public class PermissionRESTService { } /** - * Sets the permissions for a user with the given userID. + * 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 retrieve permissions for. - * @param permissions The permissions to set for the user with the given userID. + * @param userID The user ID to add the permission for. + * @param permission The permission to add for the user with the given userID. */ @POST @Path("/{userID}") - public void setPermissions(@QueryParam("token") String authToken, - @PathParam("userID") String userID, List permissions) { + public void addPermission(@QueryParam("token") String authToken, + @PathParam("userID") String userID, APIPermission permission) { UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); try { @@ -122,34 +122,47 @@ public class PermissionRESTService { if(user == null) throw new HTTPException(Status.NOT_FOUND, "User not found with the provided userID."); - // All the permissions the user should have after this operation - Set newPermissions = permissionService.convertAPIPermissionList(permissions); - - // Get the original permissions the user had - Set originalPermissions = user.getPermissions(); - - // Find all permissions in the original set, but not the new one - Set permissionsToRemove = new HashSet(originalPermissions); - permissionsToRemove.removeAll(newPermissions); - - // Remove all permissions that are no longer wanted - for(Permission permissionToRemove : permissionsToRemove) { - user.removePermission(permissionToRemove); - } - - // Get only those permissions that need to be added - newPermissions.removeAll(originalPermissions); - - // Add all new permissions - for(Permission newPermission : newPermissions) { - user.addPermission(newPermission); - } + // Add the new permission + user.addPermission(permission.toPermission()); } catch(GuacamoleSecurityException e) { throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { - logger.error("Unexpected GuacamoleException caught setting permissions.", e); + logger.error("Unexpected GuacamoleException caught adding permission.", e); + throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); + } + } + + /** + * 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. + */ + @POST + @Path("/{userID}/remove") + public void removePermission(@QueryParam("token") String authToken, + @PathParam("userID") String userID, APIPermission permission) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + try { + // 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()); + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + } catch(GuacamoleClientException e) { + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + } catch(GuacamoleException e) { + logger.error("Unexpected GuacamoleException caught adding permission.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); } } From 82413b2103fd88c34b9a52eadc2fc9beeb0ed5b6 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Fri, 6 Dec 2013 10:07:22 -0800 Subject: [PATCH 24/33] Ticket #362: Changed url to be a bit more semantic. --- .../net/basic/rest/permission/PermissionRESTService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java index a4253285e..f73f05177 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java @@ -143,7 +143,7 @@ public class PermissionRESTService { * @param permission The permission to remove for the user with the given userID. */ @POST - @Path("/{userID}/remove") + @Path("/remove{userID}/") public void removePermission(@QueryParam("token") String authToken, @PathParam("userID") String userID, APIPermission permission) { UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); From 756ffa7637080a846df1a27b01876e7eba803c57 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Mon, 9 Dec 2013 20:26:06 -0800 Subject: [PATCH 25/33] Ticket #362: Created user CRUD. --- .../guacamole/net/basic/rest/RESTModule.java | 2 + .../net/basic/rest/RESTServletModule.java | 2 + .../permission/PermissionRESTService.java | 3 - .../rest/permission/PermissionService.java | 3 +- .../net/basic/rest/user/APIUser.java | 82 +++++++ .../net/basic/rest/user/APIUserWrapper.java | 103 ++++++++ .../net/basic/rest/user/UserRESTService.java | 229 ++++++++++++++++++ .../net/basic/rest/user/UserService.java | 54 +++++ .../net/basic/rest/user/package-info.java | 6 + 9 files changed, 479 insertions(+), 5 deletions(-) create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUser.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUserWrapper.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserService.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/package-info.java diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java index d7ea84bbb..35d8e9aea 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTModule.java @@ -30,6 +30,7 @@ import org.glyptodon.guacamole.net.basic.rest.auth.TokenUserContextMap; 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.user.UserService; import org.glyptodon.guacamole.properties.GuacamoleProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -69,6 +70,7 @@ public class RESTModule extends AbstractModule { bind(ConnectionService.class); bind(ConnectionGroupService.class); bind(PermissionService.class); + bind(UserService.class); bind(AuthenticationService.class); bind(AuthTokenGenerator.class).to(SecureRandomAuthTokenGenerator.class); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java index a2f960dfc..c12a8b144 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java @@ -26,6 +26,7 @@ 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.user.UserRESTService; /** * A Guice Module to set up the servlet mappings for the Guacamole REST API. @@ -41,6 +42,7 @@ public class RESTServletModule extends ServletModule { bind(ConnectionRESTService.class); bind(ConnectionGroupRESTService.class); bind(PermissionRESTService.class); + bind(UserRESTService.class); bind(LoginRESTService.class); // Set up the servlet and JSON mappings diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java index f73f05177..1c9043c4b 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java @@ -19,9 +19,7 @@ package org.glyptodon.guacamole.net.basic.rest.permission; */ import com.google.inject.Inject; -import java.util.HashSet; import java.util.List; -import java.util.Set; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; @@ -36,7 +34,6 @@ import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.GuacamoleSecurityException; 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.HTTPException; import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; import org.slf4j.Logger; diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java index 3b9626bbf..32314b84d 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java @@ -39,8 +39,7 @@ public class PermissionService { * @param permissions The Connections to convert for REST endpoint use. * @return A List of APIPermission objects for use with the REST endpoint. */ - public List convertPermissionList(Iterable permissions) - throws GuacamoleException { + public List convertPermissionList(Iterable permissions) { List restPermissions = new ArrayList(); for(Permission permission : permissions) { diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUser.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUser.java new file mode 100644 index 000000000..66b9ec3d5 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUser.java @@ -0,0 +1,82 @@ +package org.glyptodon.guacamole.net.basic.rest.user; + +import org.codehaus.jackson.map.annotate.JsonSerialize; +import org.glyptodon.guacamole.net.auth.User; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * A simple User to expose through the REST endpoints. + * + * @author James Muehlner + */ +@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 APIUser from the provided User. + * @param user The User to construct the APIUser from. + */ + public APIUser(User user) { + this.username = user.getUsername(); + this.password = user.getPassword(); + } + + /** + * Returns the username for this user. + * @return The username for this user. + */ + public String getUsername() { + return username; + } + + /** + * Set the username for this user. + * @param username The username for this user. + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * Returns the password for this user. + * @return The password for this user. + */ + public String getPassword() { + return password; + } + + /** + * Set the password for this user. + * @param password The password for this user. + */ + public void setPassword(String password) { + this.password = password; + } +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUserWrapper.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUserWrapper.java new file mode 100644 index 000000000..db347457c --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUserWrapper.java @@ -0,0 +1,103 @@ +package org.glyptodon.guacamole.net.basic.rest.user; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +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 APIUser apiUser; + + /** + * The set of permissions for this user. + * NOTE: Not exposed by the REST endpoints. + */ + private Set permissionSet = Collections.EMPTY_SET; + + /** + * Wrap a given APIUser to expose as a User. + * @param apiUser The APIUser to wrap. + */ + public APIUserWrapper(APIUser apiUser) { + this.apiUser = apiUser; + } + + /** + * Wrap a given APIUser to expose as a User, with the given permission set. + * @param apiUser The APIUser to wrap. + * @param permissionSet The set of permissions for the wrapped user. + */ + public APIUserWrapper(APIUser apiUser, Set permissionSet) { + this.apiUser = apiUser; + this.permissionSet = permissionSet; + } + + @Override + public String getUsername() { + return apiUser.getUsername(); + } + + @Override + public void setUsername(String username) { + apiUser.setUsername(username); + } + + @Override + public String getPassword() { + return apiUser.getPassword(); + } + + @Override + public void setPassword(String password) { + apiUser.setPassword(password); + } + + @Override + public Set getPermissions() throws GuacamoleException { + return permissionSet; + } + + @Override + public boolean hasPermission(Permission permission) throws GuacamoleException { + return permissionSet.contains(permission); + } + + @Override + public void addPermission(Permission permission) throws GuacamoleException { + throw new UnsupportedOperationException("Operation not supported."); + } + + @Override + public void removePermission(Permission permission) throws GuacamoleException { + throw new UnsupportedOperationException("Operation not supported."); + } +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java new file mode 100644 index 000000000..44c5c603f --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java @@ -0,0 +1,229 @@ +package org.glyptodon.guacamole.net.basic.rest.user; + +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.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.GuacamoleClientException; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleSecurityException; +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.HTTPException; +import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +/** + * A REST Service for handling user CRUD operations. + * + * @author James Muehlner + */ +@Path("/api/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; + + /** + * Get 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. + */ + @GET + public List getUsers(@QueryParam("token") String authToken) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + try { + // Get the directory + Directory userDirectory = userContext.getUserDirectory(); + + // Convert and return the user directory listing + return userService.convertUserList(userDirectory); + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Response.Status.UNAUTHORIZED, 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 listing permissions.", e); + throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); + } + } + + /** + * Get a user. + * @param authToken The authentication token that is used to authenticate + * the user performing the operation. + * @return user The user. + */ + @GET + @Path("/{userID}") + public APIUser getUser(@QueryParam("token") String authToken, @PathParam("userID") String userID) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + try { + // Get the directory + Directory userDirectory = userContext.getUserDirectory(); + + // Get the user + User user = userDirectory.get(userID); + + if(user == null) + throw new HTTPException(Response.Status.NOT_FOUND, "User not found with the provided userID."); + + // Return the user + return new APIUser(user); + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Response.Status.UNAUTHORIZED, 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 listing permissions.", e); + throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); + } + } + + /** + * Create a new user. + * @param authToken The authentication token that is used to authenticate + * the user performing the operation. + * @param user The new user to create. + */ + @POST + public void createUser(@QueryParam("token") String authToken, APIUser user) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + try { + // Get the directory + Directory userDirectory = userContext.getUserDirectory(); + + // Create the user + userDirectory.add(new APIUserWrapper(user)); + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Response.Status.UNAUTHORIZED, 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 listing permissions.", e); + throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); + } + } + + /** + * Update an 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. + */ + @POST + @Path("/{userID}") + public void updateUser(@QueryParam("token") String authToken, @PathParam("userID") String userID, APIUser user) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + try { + // Get the directory + Directory userDirectory = userContext.getUserDirectory(); + + if(!user.getUsername().equals(userID)) + throw new HTTPException(Response.Status.BAD_REQUEST, "Username does not match provided userID."); + + // Get the user + User existingUser = userDirectory.get(userID); + + if(existingUser == null) + throw new HTTPException(Response.Status.NOT_FOUND, "User not found with the provided userID."); + + /* + * Update the user with the permission set from the existing user + * since the user REST endpoints do not expose permissions + */ + userDirectory.update(new APIUserWrapper(user, existingUser.getPermissions())); + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Response.Status.UNAUTHORIZED, 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 listing permissions.", e); + throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); + } + } + + /** + * Delete an 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. + */ + @DELETE + @Path("/{userID}") + public void deleteUser(@QueryParam("token") String authToken, @PathParam("userID") String userID) { + UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); + + try { + // Get the directory + Directory userDirectory = userContext.getUserDirectory(); + + // Get the user + User existingUser = userDirectory.get(userID); + + if(existingUser == null) + throw new HTTPException(Response.Status.NOT_FOUND, "User not found with the provided userID."); + + // Delete the user + userDirectory.remove(userID); + } catch(GuacamoleSecurityException e) { + throw new HTTPException(Response.Status.UNAUTHORIZED, 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 listing permissions.", e); + throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); + } + } +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserService.java new file mode 100644 index 000000000..5c6de105f --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserService.java @@ -0,0 +1,54 @@ +package org.glyptodon.guacamole.net.basic.rest.user; + +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; + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * A service for performing useful manipulations on REST Users. + * + * @author James Muehlner + */ +public class UserService { + + /** + * Converts a user directory to a list of APIUser objects for + * exposing with the REST endpoints. + * + * @param userDirectory The user directory to convert for REST endpoint use. + * @return A List of APIUser objects for use with the REST endpoint. + * @throws GuacamoleException If an error occurs while converting the + * user directory. + */ + public List convertUserList(Directory userDirectory) + throws GuacamoleException { + List restUsers = new ArrayList(); + + for(String username : userDirectory.getIdentifiers()) { + restUsers.add(new APIUser(userDirectory.get(username))); + } + + return restUsers; + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/package-info.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/package-info.java new file mode 100644 index 000000000..aa411d037 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/package-info.java @@ -0,0 +1,6 @@ + +/** + * Classes related to the user manipulation aspect of the Guacamole REST API. + */ +package org.glyptodon.guacamole.net.basic.rest.user; + From d2aec9457fafb4ed7f337870cf3eb747d281299a Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Mon, 9 Dec 2013 20:35:29 -0800 Subject: [PATCH 26/33] Ticket #362: Cleaned up a bit. --- .../basic/rest/auth/BasicTokenUserContextMap.java | 5 +++++ .../net/basic/rest/user/UserRESTService.java | 14 ++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java index 0629efdde..5ebb58e46 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java @@ -95,6 +95,11 @@ public class BasicTokenUserContextMap implements TokenUserContextMap { 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; diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java index 44c5c603f..d5b9397f6 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java @@ -70,7 +70,7 @@ public class UserRESTService { private UserService userService; /** - * Get a list of users in the system. + * 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. @@ -96,7 +96,7 @@ public class UserRESTService { } /** - * Get a user. + * Gets an individual user. * @param authToken The authentication token that is used to authenticate * the user performing the operation. * @return user The user. @@ -129,13 +129,13 @@ public class UserRESTService { } /** - * Create a new 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. */ @POST - public void createUser(@QueryParam("token") String authToken, APIUser user) { + public String createUser(@QueryParam("token") String authToken, APIUser user) { UserContext userContext = authenticationService.getUserContextFromAuthToken(authToken); try { @@ -144,6 +144,8 @@ public class UserRESTService { // Create the user userDirectory.add(new APIUserWrapper(user)); + + return user.getUsername(); } catch(GuacamoleSecurityException e) { throw new HTTPException(Response.Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { @@ -155,7 +157,7 @@ public class UserRESTService { } /** - * Update an existing user. + * 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. @@ -195,7 +197,7 @@ public class UserRESTService { } /** - * Delete an existing user. + * 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. From 10e8fcabd21032d519f03386ae0095a83e52fd18 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Mon, 9 Dec 2013 20:43:08 -0800 Subject: [PATCH 27/33] Ticket #362: Cleaned up a bit more. --- .../connection/ConnectionRESTService.java | 36 +++++++++--------- .../ConnectionGroupRESTService.java | 38 +++++++++---------- .../permission/PermissionRESTService.java | 12 +++--- .../net/basic/rest/user/UserRESTService.java | 20 +++++----- 4 files changed, 53 insertions(+), 53 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java index acfca2b1a..05228b178 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java @@ -97,7 +97,7 @@ public class ConnectionRESTService { } if(parentConnectionGroup == null) - throw new GuacamoleClientException("No ConnectionGroup found with the provided parentID."); + throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided parentID."); Directory connectionDirectory = parentConnectionGroup.getConnectionDirectory(); @@ -112,9 +112,9 @@ public class ConnectionRESTService { return connectionService.convertConnectionList(connections); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught while listing connections.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); @@ -145,13 +145,13 @@ public class ConnectionRESTService { Connection connection = connectionDirectory.get(connectionID); if(connection == null) - throw new GuacamoleClientException("No Connection found with the provided ID."); + throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided parentID."); return new APIConnection(connection); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught while getting connection.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); @@ -178,14 +178,14 @@ public class ConnectionRESTService { // Make sure the connection is there before trying to delete if(connectionDirectory.get(connectionID) == null) - throw new GuacamoleClientException("No Connection found with the provided ID."); + throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided ID."); // Delete the connection connectionDirectory.remove(connectionID); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught while deleting connection.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); @@ -225,7 +225,7 @@ public class ConnectionRESTService { } if(parentConnectionGroup == null) - throw new GuacamoleClientException("No ConnectionGroup found with the provided parentID."); + throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided parentID."); Directory connectionDirectory = parentConnectionGroup.getConnectionDirectory(); @@ -236,9 +236,9 @@ public class ConnectionRESTService { // Return the new connection identifier return connection.getIdentifier(); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught while creating connection.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); @@ -270,14 +270,14 @@ public class ConnectionRESTService { // Make sure the connection is there before trying to update if(connectionDirectory.get(connectionID) == null) - throw new GuacamoleClientException("No Connection with the provided ID."); + throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided ID."); // Update the connection connectionDirectory.update(new APIConnectionWrapper(connection)); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught updating connection.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); @@ -310,14 +310,14 @@ public class ConnectionRESTService { ConnectionGroup parentConnectionGroup = connectionGroupDirectory.get(parentID); if(parentConnectionGroup == null) - throw new GuacamoleClientException("No ConnectionGroup found with the provided parentID."); + throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided parentID."); // Move the connection connectionDirectory.move(connectionID, parentConnectionGroup.getConnectionDirectory()); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught moving connection.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java index 825fdd290..8b71b58fa 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java @@ -96,7 +96,7 @@ public class ConnectionGroupRESTService { } if(parentConnectionGroup == null) - throw new GuacamoleClientException("No ConnectionGroup found with the provided parentID."); + throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID."); Directory connectionGroupDirectory = parentConnectionGroup.getConnectionGroupDirectory(); @@ -111,9 +111,9 @@ public class ConnectionGroupRESTService { return connectionGroupService.convertConnectionGroupList(connectionGroups); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught while listing connection groups.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); @@ -144,13 +144,13 @@ public class ConnectionGroupRESTService { ConnectionGroup connectionGroup = connectionGroupDirectory.get(connectionGroupID); if(connectionGroup == null) - throw new GuacamoleClientException("No ConnectionGroup found with the provided ID."); + throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided ID."); return new APIConnectionGroup(connectionGroup); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught while getting connection group.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); @@ -177,14 +177,14 @@ public class ConnectionGroupRESTService { // Make sure the connection is there before trying to delete if(connectionGroupDirectory.get(connectionGroupID) == null) - throw new GuacamoleClientException("No Connection found with the provided ID."); + throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided ID."); // Delete the connection group connectionGroupDirectory.remove(connectionGroupID); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught while deleting connection group.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); @@ -224,7 +224,7 @@ public class ConnectionGroupRESTService { } if(parentConnectionGroup == null) - throw new GuacamoleClientException("No ConnectionGroup found with the provided parentID."); + throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID."); Directory connectionGroupDirectory = parentConnectionGroup.getConnectionGroupDirectory(); @@ -235,9 +235,9 @@ public class ConnectionGroupRESTService { // Return the new connection group identifier return connectionGroup.getIdentifier(); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught while creating connection.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); @@ -269,14 +269,14 @@ public class ConnectionGroupRESTService { // Make sure the connection group is there before trying to update if(connectionGroupDirectory.get(connectionGroupID) == null) - throw new GuacamoleClientException("No ConnectionGroup with the provided ID."); + throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided ID."); // Update the connection group connectionGroupDirectory.update(new APIConnectionGroupWrapper(connectionGroup)); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught updating connection.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); @@ -308,15 +308,15 @@ public class ConnectionGroupRESTService { Directory newConnectionGroupDirectory = rootGroup.getConnectionGroupDirectory(); ConnectionGroup parentConnectionGroup = newConnectionGroupDirectory.get(parentID); - if(newConnectionGroupDirectory == null) - throw new GuacamoleClientException("No ConnectionGroup found with the provided parentID."); + if(parentConnectionGroup == null) + throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID."); // Move the connection connectionGroupDirectory.move(connectionGroupID, parentConnectionGroup.getConnectionGroupDirectory()); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught moving connection.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java index 1c9043c4b..10fccb85d 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java @@ -89,9 +89,9 @@ public class PermissionRESTService { return permissionService.convertPermissionList(user.getPermissions()); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught while listing permissions.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); @@ -122,9 +122,9 @@ public class PermissionRESTService { // Add the new permission user.addPermission(permission.toPermission()); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught adding permission.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); @@ -155,9 +155,9 @@ public class PermissionRESTService { // Remove the permission user.removePermission(permission.toPermission()); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught adding permission.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java index d5b9397f6..be3704ac3 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java @@ -86,9 +86,9 @@ public class UserRESTService { // Convert and return the user directory listing return userService.convertUserList(userDirectory); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Response.Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Response.Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Response.Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Response.Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught while listing permissions.", e); throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); @@ -119,9 +119,9 @@ public class UserRESTService { // Return the user return new APIUser(user); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Response.Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Response.Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Response.Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Response.Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught while listing permissions.", e); throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); @@ -147,9 +147,9 @@ public class UserRESTService { return user.getUsername(); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Response.Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Response.Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Response.Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Response.Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught while listing permissions.", e); throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); @@ -187,9 +187,9 @@ public class UserRESTService { */ userDirectory.update(new APIUserWrapper(user, existingUser.getPermissions())); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Response.Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Response.Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Response.Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Response.Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught while listing permissions.", e); throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); @@ -220,9 +220,9 @@ public class UserRESTService { // Delete the user userDirectory.remove(userID); } catch(GuacamoleSecurityException e) { - throw new HTTPException(Response.Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); + throw new HTTPException(Response.Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { - throw new HTTPException(Response.Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); + throw new HTTPException(Response.Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { logger.error("Unexpected GuacamoleException caught while listing permissions.", e); throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); From abdb022426b0d63b2a8ca6270f47f73b32fb9323 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Mon, 9 Dec 2013 20:46:45 -0800 Subject: [PATCH 28/33] Ticket #362: Cleaned up even more. --- .../connectiongroup/ConnectionGroupRESTService.java | 6 +++--- .../basic/rest/permission/PermissionRESTService.java | 2 +- .../guacamole/net/basic/rest/user/UserRESTService.java | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java index 8b71b58fa..6798fa332 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java @@ -239,7 +239,7 @@ public class ConnectionGroupRESTService { } catch(GuacamoleClientException e) { throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { - logger.error("Unexpected GuacamoleException caught while creating connection.", e); + logger.error("Unexpected GuacamoleException caught while creating connection group.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); } } @@ -278,7 +278,7 @@ public class ConnectionGroupRESTService { } catch(GuacamoleClientException e) { throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { - logger.error("Unexpected GuacamoleException caught updating connection.", e); + logger.error("Unexpected GuacamoleException caught updating connection group.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); } } @@ -318,7 +318,7 @@ public class ConnectionGroupRESTService { } catch(GuacamoleClientException e) { throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { - logger.error("Unexpected GuacamoleException caught moving connection.", e); + logger.error("Unexpected GuacamoleException caught moving connection group.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java index 10fccb85d..91d779dc4 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionRESTService.java @@ -159,7 +159,7 @@ public class PermissionRESTService { } catch(GuacamoleClientException e) { throw new HTTPException(Status.BAD_REQUEST, e.getMessage() != null ? e.getMessage() : "Invalid Request."); } catch(GuacamoleException e) { - logger.error("Unexpected GuacamoleException caught adding permission.", e); + logger.error("Unexpected GuacamoleException caught removing permission.", e); throw new HTTPException(Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java index be3704ac3..e9fa90b94 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java @@ -90,7 +90,7 @@ public class UserRESTService { } 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 listing permissions.", e); + logger.error("Unexpected GuacamoleException caught while listing users.", e); throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); } } @@ -123,7 +123,7 @@ public class UserRESTService { } 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 listing permissions.", e); + logger.error("Unexpected GuacamoleException caught while getting user.", e); throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); } } @@ -151,7 +151,7 @@ public class UserRESTService { } 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 listing permissions.", e); + logger.error("Unexpected GuacamoleException caught while creating user.", e); throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); } } @@ -191,7 +191,7 @@ public class UserRESTService { } 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 listing permissions.", e); + logger.error("Unexpected GuacamoleException caught while updating user.", e); throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); } } @@ -224,7 +224,7 @@ public class UserRESTService { } 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 listing permissions.", e); + logger.error("Unexpected GuacamoleException caught while deleting user.", e); throw new HTTPException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage() != null ? e.getMessage() : "Unexpected server error."); } } From 94fef7307ebcf0d2715d6c9d2273abd8aa759e71 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Mon, 9 Dec 2013 20:50:07 -0800 Subject: [PATCH 29/33] Ticket #362: Cleaned up even morely. --- .../net/basic/rest/connection/ConnectionRESTService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java index 05228b178..8930261a3 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java @@ -97,7 +97,7 @@ public class ConnectionRESTService { } if(parentConnectionGroup == null) - throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided parentID."); + throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID."); Directory connectionDirectory = parentConnectionGroup.getConnectionDirectory(); @@ -145,7 +145,7 @@ public class ConnectionRESTService { Connection connection = connectionDirectory.get(connectionID); if(connection == null) - throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided parentID."); + throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided ID."); return new APIConnection(connection); } catch(GuacamoleSecurityException e) { @@ -225,7 +225,7 @@ public class ConnectionRESTService { } if(parentConnectionGroup == null) - throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided parentID."); + throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID."); Directory connectionDirectory = parentConnectionGroup.getConnectionDirectory(); @@ -310,7 +310,7 @@ public class ConnectionRESTService { ConnectionGroup parentConnectionGroup = connectionGroupDirectory.get(parentID); if(parentConnectionGroup == null) - throw new HTTPException(Status.NOT_FOUND, "No Connection found with the provided parentID."); + throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID."); // Move the connection connectionDirectory.move(connectionID, parentConnectionGroup.getConnectionDirectory()); From 339083a3796e5361183ffc42245095f139edb5c1 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Mon, 9 Dec 2013 20:56:45 -0800 Subject: [PATCH 30/33] Ticket #362: The cleaning never ends. --- .../rest/connection/ConnectionRESTService.java | 11 ++--------- .../basic/rest/connection/ConnectionService.java | 9 +++++---- .../ConnectionGroupRESTService.java | 14 ++++---------- .../connectiongroup/ConnectionGroupService.java | 10 +++++++--- .../basic/rest/permission/PermissionService.java | 1 - 5 files changed, 18 insertions(+), 27 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java index 8930261a3..6a0e70d96 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java @@ -102,15 +102,8 @@ public class ConnectionRESTService { Directory connectionDirectory = parentConnectionGroup.getConnectionDirectory(); - // Get the list of connection names - List connections = new ArrayList(); - Iterable identifiers = connectionDirectory.getIdentifiers(); - - // Get the connection for each name - for(String identifier : identifiers) - connections.add(connectionDirectory.get(identifier)); - - return connectionService.convertConnectionList(connections); + // Return the converted connection directory + return connectionService.convertConnectionList(connectionDirectory); } catch(GuacamoleSecurityException e) { throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java index 7e26414f1..daa5c3012 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java @@ -4,6 +4,7 @@ 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; /* * Guacamole - Clientless Remote Desktop @@ -37,14 +38,14 @@ public class ConnectionService { * @param connections The Connection 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 - * connections. + * connection directory. */ - public List convertConnectionList(Iterable connections) + public List convertConnectionList(Directory connectionDirectory) throws GuacamoleException { List restConnections = new ArrayList(); - for(Connection connection : connections) { - restConnections.add(new APIConnection(connection)); + for(String connectionID : connectionDirectory.getIdentifiers()) { + restConnections.add(new APIConnection(connectionDirectory.get(connectionID))); } return restConnections; diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java index 6798fa332..5dede8ac9 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java @@ -101,15 +101,8 @@ public class ConnectionGroupRESTService { Directory connectionGroupDirectory = parentConnectionGroup.getConnectionGroupDirectory(); - // Get the list of connection group names - List connectionGroups = new ArrayList(); - Iterable identifiers = connectionGroupDirectory.getIdentifiers(); - - // Get the connection group for each name - for(String identifier : identifiers) - connectionGroups.add(connectionGroupDirectory.get(identifier)); - - return connectionGroupService.convertConnectionGroupList(connectionGroups); + // return the converted connection group list + return connectionGroupService.convertConnectionGroupList(connectionGroupDirectory); } catch(GuacamoleSecurityException e) { throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); } catch(GuacamoleClientException e) { @@ -146,6 +139,7 @@ public class ConnectionGroupRESTService { if(connectionGroup == null) throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided ID."); + // Return the connectiion group return new APIConnectionGroup(connectionGroup); } catch(GuacamoleSecurityException e) { throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); @@ -311,7 +305,7 @@ public class ConnectionGroupRESTService { if(parentConnectionGroup == null) throw new HTTPException(Status.NOT_FOUND, "No ConnectionGroup found with the provided parentID."); - // Move the connection + // Move the connection group connectionGroupDirectory.move(connectionGroupID, parentConnectionGroup.getConnectionGroupDirectory()); } catch(GuacamoleSecurityException e) { throw new HTTPException(Status.UNAUTHORIZED, e.getMessage() != null ? e.getMessage() : "Permission denied."); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java index 402647a84..53ab042a4 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java @@ -2,7 +2,9 @@ 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; /* * Guacamole - Clientless Remote Desktop @@ -35,13 +37,15 @@ public class ConnectionGroupService { * * @param connectionGroups The ConnectionGroup to convert for REST endpoint use. * @return A List of APIConnectionGroup objects for use with the REST endpoint. + * @throws GuacamoleException If an error occurs while converting the + * connection group directory. */ public List convertConnectionGroupList( - Iterable connectionGroups) { + Directory connectionGroupDirectory) throws GuacamoleException { List restConnectionGroups = new ArrayList(); - for(ConnectionGroup connectionGroup : connectionGroups) { - restConnectionGroups.add(new APIConnectionGroup(connectionGroup)); + for(String connectionGroupID : connectionGroupDirectory.getIdentifiers()) { + restConnectionGroups.add(new APIConnectionGroup(connectionGroupDirectory.get(connectionGroupID))); } return restConnectionGroups; diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java index 32314b84d..85c265054 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/PermissionService.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.net.auth.permission.Permission; /* From 21a0ecfd84a891b3e4a845c3aa93d56c794aa6b7 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Mon, 9 Dec 2013 20:58:41 -0800 Subject: [PATCH 31/33] Ticket #362: The cleaning has ended, for now. --- .../net/basic/rest/connection/ConnectionService.java | 4 ++-- .../basic/rest/connectiongroup/ConnectionGroupService.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java index daa5c3012..2a669f5ee 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionService.java @@ -32,10 +32,10 @@ import org.glyptodon.guacamole.net.auth.Directory; public class ConnectionService { /** - * Converts a list of Connection to a list of APIConnection objects for + * Converts a Connection Directory to a list of APIConnection objects for * exposing with the REST endpoints. * - * @param connections The Connection to convert for REST endpoint use. + * @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. diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java index 53ab042a4..1da2fa507 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupService.java @@ -32,10 +32,10 @@ import org.glyptodon.guacamole.net.auth.Directory; public class ConnectionGroupService { /** - * Converts a list of ConnectionGroup to a list of APIConnectionGroup + * Converts a ConnectionGroup directory to a list of APIConnectionGroup * objects for exposing with the REST endpoints. * - * @param connectionGroups The ConnectionGroup to convert for REST endpoint use. + * @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. From 2414cfcce72835a0453f19045d6bfa6adadf520e Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Mon, 16 Dec 2013 16:41:55 -0800 Subject: [PATCH 32/33] Ticket #362: Fixed comment. --- .../auth/SecureRandomAuthTokenGenerator.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/SecureRandomAuthTokenGenerator.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/SecureRandomAuthTokenGenerator.java index 89fc01b4f..54f89197a 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/SecureRandomAuthTokenGenerator.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/SecureRandomAuthTokenGenerator.java @@ -1,9 +1,23 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ package org.glyptodon.guacamole.net.basic.rest.auth; +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + import java.security.SecureRandom; import org.apache.commons.codec.binary.Hex; From f115ae934c1f3c615233235f98b707c90c62378d Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Mon, 16 Dec 2013 17:05:06 -0800 Subject: [PATCH 33/33] Ticket #362: Fixed comment. --- .../net/basic/rest/HTTPException.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java index 11a4fd70d..34776ec83 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/HTTPException.java @@ -1,9 +1,23 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ package org.glyptodon.guacamole.net.basic.rest; +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status;