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