diff --git a/guacamole/pom.xml b/guacamole/pom.xml
index e737bb189..c0b6b3bc3 100644
--- a/guacamole/pom.xml
+++ b/guacamole/pom.xml
@@ -290,6 +290,11 @@
guice
3.0
+
+ com.google.inject.extensions
+ guice-assistedinject
+ 3.0
+
diff --git a/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java b/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java
index 06530c2f1..41832cb34 100644
--- a/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java
+++ b/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java
@@ -26,6 +26,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.auth.AuthenticatedUser;
+import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.tunnel.StreamInterceptingTunnel;
import org.slf4j.Logger;
@@ -126,6 +127,44 @@ public class GuacamoleSession {
return Collections.unmodifiableList(userContexts);
}
+ /**
+ * Returns the UserContext associated with this session that originated
+ * from the AuthenticationProvider with the given identifier. If no such
+ * UserContext exists, an exception is thrown.
+ *
+ * @param authProviderIdentifier
+ * The unique identifier of the AuthenticationProvider that created the
+ * UserContext being retrieved.
+ *
+ * @return
+ * The UserContext that was created by the AuthenticationProvider
+ * having the given identifier.
+ *
+ * @throws GuacamoleException
+ * If no such UserContext exists.
+ */
+ public UserContext getUserContext(String authProviderIdentifier)
+ throws GuacamoleException {
+
+ // Locate and return the UserContext associated with the
+ // AuthenticationProvider having the given identifier, if any
+ for (UserContext userContext : getUserContexts()) {
+
+ // Get AuthenticationProvider associated with current UserContext
+ AuthenticationProvider authProvider = userContext.getAuthenticationProvider();
+
+ // If AuthenticationProvider identifier matches, done
+ if (authProvider.getIdentifier().equals(authProviderIdentifier))
+ return userContext;
+
+ }
+
+ throw new GuacamoleResourceNotFoundException("Session not associated "
+ + "with authentication provider \"" + authProviderIdentifier + "\".");
+
+
+ }
+
/**
* Replaces all UserContexts associated with this session with the given
* List of UserContexts.
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/APIException.java b/guacamole/src/main/java/org/apache/guacamole/rest/APIException.java
index 0a004bef6..ea4cf5d5d 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/APIException.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/APIException.java
@@ -21,6 +21,7 @@ package org.apache.guacamole.rest;
import java.util.Collection;
import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.guacamole.form.Field;
import org.apache.guacamole.protocol.GuacamoleStatus;
@@ -44,7 +45,10 @@ public class APIException extends WebApplicationException {
* The error that occurred.
*/
public APIException(APIError error) {
- super(Response.status(error.getType().getStatus()).entity(error).build());
+ super(Response.status(error.getType().getStatus())
+ .type(MediaType.APPLICATION_JSON)
+ .entity(error)
+ .build());
}
/**
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/ObjectRetrievalService.java b/guacamole/src/main/java/org/apache/guacamole/rest/ObjectRetrievalService.java
deleted file mode 100644
index d0e403ca8..000000000
--- a/guacamole/src/main/java/org/apache/guacamole/rest/ObjectRetrievalService.java
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.guacamole.rest;
-
-import java.util.List;
-import org.apache.guacamole.GuacamoleException;
-import org.apache.guacamole.GuacamoleResourceNotFoundException;
-import org.apache.guacamole.net.auth.AuthenticationProvider;
-import org.apache.guacamole.net.auth.Connection;
-import org.apache.guacamole.net.auth.ConnectionGroup;
-import org.apache.guacamole.net.auth.Directory;
-import org.apache.guacamole.net.auth.User;
-import org.apache.guacamole.net.auth.UserContext;
-import org.apache.guacamole.GuacamoleSession;
-import org.apache.guacamole.rest.connectiongroup.APIConnectionGroup;
-
-/**
- * Provides easy access and automatic error handling for retrieval of objects,
- * such as users, connections, or connection groups. REST API semantics, such
- * as the special root connection group identifier, are also handled
- * automatically.
- */
-public class ObjectRetrievalService {
-
- /**
- * Retrieves a single UserContext from the given GuacamoleSession, which
- * may contain multiple UserContexts.
- *
- * @param session
- * The GuacamoleSession to retrieve the UserContext from.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider that created the
- * UserContext being retrieved. Only one UserContext per User per
- * AuthenticationProvider can exist.
- *
- * @return
- * The UserContext that was created by the AuthenticationProvider
- * having the given identifier.
- *
- * @throws GuacamoleException
- * If an error occurs while retrieving the UserContext, or if the
- * UserContext does not exist.
- */
- public UserContext retrieveUserContext(GuacamoleSession session,
- String authProviderIdentifier) throws GuacamoleException {
-
- // Get list of UserContexts
- List userContexts = session.getUserContexts();
-
- // Locate and return the UserContext associated with the
- // AuthenticationProvider having the given identifier, if any
- for (UserContext userContext : userContexts) {
-
- // Get AuthenticationProvider associated with current UserContext
- AuthenticationProvider authProvider = userContext.getAuthenticationProvider();
-
- // If AuthenticationProvider identifier matches, done
- if (authProvider.getIdentifier().equals(authProviderIdentifier))
- return userContext;
-
- }
-
- throw new GuacamoleResourceNotFoundException("Session not associated with authentication provider \"" + authProviderIdentifier + "\".");
-
- }
-
- /**
- * Retrieves a single user from the given user context.
- *
- * @param userContext
- * The user context to retrieve the user from.
- *
- * @param identifier
- * The identifier of the user to retrieve.
- *
- * @return
- * The user having the given identifier.
- *
- * @throws GuacamoleException
- * If an error occurs while retrieving the user, or if the
- * user does not exist.
- */
- public User retrieveUser(UserContext userContext,
- String identifier) throws GuacamoleException {
-
- // Get user directory
- Directory directory = userContext.getUserDirectory();
-
- // Pull specified user
- User user = directory.get(identifier);
- if (user == null)
- throw new GuacamoleResourceNotFoundException("No such user: \"" + identifier + "\"");
-
- return user;
-
- }
-
- /**
- * Retrieves a single user from the given GuacamoleSession.
- *
- * @param session
- * The GuacamoleSession to retrieve the user from.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider that created the
- * UserContext from which the user should be retrieved. Only one
- * UserContext per User per AuthenticationProvider can exist.
- *
- * @param identifier
- * The identifier of the user to retrieve.
- *
- * @return
- * The user having the given identifier.
- *
- * @throws GuacamoleException
- * If an error occurs while retrieving the user, or if the
- * user does not exist.
- */
- public User retrieveUser(GuacamoleSession session, String authProviderIdentifier,
- String identifier) throws GuacamoleException {
-
- UserContext userContext = retrieveUserContext(session, authProviderIdentifier);
- return retrieveUser(userContext, identifier);
-
- }
-
- /**
- * Retrieves a single connection from the given user context.
- *
- * @param userContext
- * The user context to retrieve the connection from.
- *
- * @param identifier
- * The identifier of the connection to retrieve.
- *
- * @return
- * The connection having the given identifier.
- *
- * @throws GuacamoleException
- * If an error occurs while retrieving the connection, or if the
- * connection does not exist.
- */
- public Connection retrieveConnection(UserContext userContext,
- String identifier) throws GuacamoleException {
-
- // Get connection directory
- Directory directory = userContext.getConnectionDirectory();
-
- // Pull specified connection
- Connection connection = directory.get(identifier);
- if (connection == null)
- throw new GuacamoleResourceNotFoundException("No such connection: \"" + identifier + "\"");
-
- return connection;
-
- }
-
- /**
- * Retrieves a single connection from the given GuacamoleSession.
- *
- * @param session
- * The GuacamoleSession to retrieve the connection from.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider that created the
- * UserContext from which the connection should be retrieved. Only one
- * UserContext per User per AuthenticationProvider can exist.
- *
- * @param identifier
- * The identifier of the connection to retrieve.
- *
- * @return
- * The connection having the given identifier.
- *
- * @throws GuacamoleException
- * If an error occurs while retrieving the connection, or if the
- * connection does not exist.
- */
- public Connection retrieveConnection(GuacamoleSession session,
- String authProviderIdentifier, String identifier)
- throws GuacamoleException {
-
- UserContext userContext = retrieveUserContext(session, authProviderIdentifier);
- return retrieveConnection(userContext, identifier);
-
- }
-
- /**
- * Retrieves a single connection group from the given user context. If
- * the given identifier the REST API root identifier, the root connection
- * group will be returned. The underlying authentication provider may
- * additionally use a different identifier for root.
- *
- * @param userContext
- * The user context to retrieve the connection group from.
- *
- * @param identifier
- * The identifier of the connection group to retrieve.
- *
- * @return
- * The connection group having the given identifier, or the root
- * connection group if the identifier the root identifier.
- *
- * @throws GuacamoleException
- * If an error occurs while retrieving the connection group, or if the
- * connection group does not exist.
- */
- public ConnectionGroup retrieveConnectionGroup(UserContext userContext,
- String identifier) throws GuacamoleException {
-
- // Use root group if identifier is the standard root identifier
- if (identifier != null && identifier.equals(APIConnectionGroup.ROOT_IDENTIFIER))
- return userContext.getRootConnectionGroup();
-
- // Pull specified connection group otherwise
- Directory directory = userContext.getConnectionGroupDirectory();
- ConnectionGroup connectionGroup = directory.get(identifier);
-
- if (connectionGroup == null)
- throw new GuacamoleResourceNotFoundException("No such connection group: \"" + identifier + "\"");
-
- return connectionGroup;
-
- }
-
- /**
- * Retrieves a single connection group from the given GuacamoleSession. If
- * the given identifier is the REST API root identifier, the root
- * connection group will be returned. The underlying authentication
- * provider may additionally use a different identifier for root.
- *
- * @param session
- * The GuacamoleSession to retrieve the connection group from.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider that created the
- * UserContext from which the connection group should be retrieved.
- * Only one UserContext per User per AuthenticationProvider can exist.
- *
- * @param identifier
- * The identifier of the connection group to retrieve.
- *
- * @return
- * The connection group having the given identifier, or the root
- * connection group if the identifier is the root identifier.
- *
- * @throws GuacamoleException
- * If an error occurs while retrieving the connection group, or if the
- * connection group does not exist.
- */
- public ConnectionGroup retrieveConnectionGroup(GuacamoleSession session,
- String authProviderIdentifier, String identifier) throws GuacamoleException {
-
- UserContext userContext = retrieveUserContext(session, authProviderIdentifier);
- return retrieveConnectionGroup(userContext, identifier);
-
- }
-
-}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/RESTMethodMatcher.java b/guacamole/src/main/java/org/apache/guacamole/rest/RESTMethodMatcher.java
index 3a862c88d..623932f9f 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/RESTMethodMatcher.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/RESTMethodMatcher.java
@@ -23,6 +23,7 @@ import com.google.inject.matcher.AbstractMatcher;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import javax.ws.rs.HttpMethod;
+import javax.ws.rs.Path;
import org.apache.guacamole.GuacamoleException;
/**
@@ -67,7 +68,7 @@ public class RESTMethodMatcher extends AbstractMatcher {
/**
* Returns whether the given method is annotated as a REST method. A REST
* method is annotated with an annotation which is annotated with
- * @HttpMethod
.
+ * @HttpMethod
or @Path
.
*
* @param method
* The method to test.
@@ -86,9 +87,32 @@ public class RESTMethodMatcher extends AbstractMatcher {
if (annotationType.isAnnotationPresent(HttpMethod.class))
return true;
+ // A method is a REST method if it is annotated with @Path
+ if (Path.class.isAssignableFrom(annotationType))
+ return true;
+
}
- // The method is not an HTTP method
+ // A method is also REST method if it overrides a REST method within
+ // the superclass
+ Class> superclass = method.getDeclaringClass().getSuperclass();
+ if (superclass != null) {
+
+ // Recheck against identical method within superclass
+ try {
+ return isRESTMethod(superclass.getMethod(method.getName(),
+ method.getParameterTypes()));
+ }
+
+ // If there is no such method, then this method cannot possibly be
+ // a REST method
+ catch (NoSuchMethodException e) {
+ return false;
+ }
+
+ }
+
+ // Lacking a superclass, the search stops here - it's not a REST method
return false;
}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java
index ec555e30e..29782788a 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java
@@ -19,26 +19,27 @@
package org.apache.guacamole.rest;
+import org.apache.guacamole.rest.session.UserContextResourceFactory;
+import org.apache.guacamole.rest.session.SessionRESTService;
import com.google.inject.Scopes;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.google.inject.matcher.Matchers;
import com.google.inject.servlet.ServletModule;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
import org.aopalliance.intercept.MethodInterceptor;
+import org.apache.guacamole.rest.activeconnection.ActiveConnectionModule;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;
import org.apache.guacamole.rest.auth.TokenRESTService;
-import org.apache.guacamole.rest.connection.ConnectionRESTService;
-import org.apache.guacamole.rest.connectiongroup.ConnectionGroupRESTService;
-import org.apache.guacamole.rest.activeconnection.ActiveConnectionRESTService;
import org.apache.guacamole.rest.auth.AuthTokenGenerator;
import org.apache.guacamole.rest.auth.AuthenticationService;
import org.apache.guacamole.rest.auth.SecureRandomAuthTokenGenerator;
import org.apache.guacamole.rest.auth.TokenSessionMap;
-import org.apache.guacamole.rest.history.HistoryRESTService;
+import org.apache.guacamole.rest.connection.ConnectionModule;
+import org.apache.guacamole.rest.connectiongroup.ConnectionGroupModule;
import org.apache.guacamole.rest.language.LanguageRESTService;
import org.apache.guacamole.rest.patch.PatchRESTService;
-import org.apache.guacamole.rest.schema.SchemaRESTService;
-import org.apache.guacamole.rest.tunnel.TunnelRESTService;
-import org.apache.guacamole.rest.user.UserRESTService;
+import org.apache.guacamole.rest.session.SessionResourceFactory;
+import org.apache.guacamole.rest.user.UserModule;
/**
* A Guice Module to set up the servlet mappings and authentication-specific
@@ -81,20 +82,21 @@ public class RESTServiceModule extends ServletModule {
requestInjection(interceptor);
bindInterceptor(Matchers.any(), new RESTMethodMatcher(), interceptor);
- // Bind convenience services used by the REST API
- bind(ObjectRetrievalService.class);
-
// Set up the API endpoints
- bind(ActiveConnectionRESTService.class);
- bind(ConnectionGroupRESTService.class);
- bind(ConnectionRESTService.class);
- bind(HistoryRESTService.class);
bind(LanguageRESTService.class);
bind(PatchRESTService.class);
- bind(SchemaRESTService.class);
bind(TokenRESTService.class);
- bind(TunnelRESTService.class);
- bind(UserRESTService.class);
+
+ // Root-level resources
+ bind(SessionRESTService.class);
+ install(new FactoryModuleBuilder().build(SessionResourceFactory.class));
+ install(new FactoryModuleBuilder().build(UserContextResourceFactory.class));
+
+ // Resources below root
+ install(new ActiveConnectionModule());
+ install(new ConnectionModule());
+ install(new ConnectionGroupModule());
+ install(new UserModule());
// Set up the servlet and JSON mappings
bind(GuiceContainer.class);
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionModule.java
new file mode 100644
index 000000000..efecdde02
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionModule.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.rest.activeconnection;
+
+import com.google.inject.AbstractModule;
+import org.apache.guacamole.rest.directory.DirectoryObjectResourceFactory;
+import org.apache.guacamole.rest.directory.DirectoryResourceFactory;
+import com.google.inject.TypeLiteral;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
+import org.apache.guacamole.net.auth.ActiveConnection;
+import org.apache.guacamole.rest.directory.DirectoryObjectResource;
+import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
+
+/**
+ * Guice Module which configures injections required for handling
+ * ActiveConnection resources via the REST API.
+ *
+ * @author Michael Jumper
+ */
+public class ActiveConnectionModule extends AbstractModule {
+
+ @Override
+ protected void configure() {
+
+ // Create the required DirectoryResourceFactory implementation
+ install(new FactoryModuleBuilder()
+ .build(new TypeLiteral>() {}));
+
+ // Create the required DirectoryObjectResourceFactory implementation
+ install(new FactoryModuleBuilder()
+ .implement(
+ new TypeLiteral>() {},
+ ActiveConnectionResource.class
+ )
+ .build(new TypeLiteral>() {}));
+
+ // Bind translator for converting between ActiveConnection and APIActiveConnection
+ bind(new TypeLiteral>() {})
+ .to(ActiveConnectionObjectTranslator.class);
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionObjectTranslator.java b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionObjectTranslator.java
new file mode 100644
index 000000000..f0f9ed35a
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionObjectTranslator.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.rest.activeconnection;
+
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleUnsupportedException;
+import org.apache.guacamole.net.auth.ActiveConnection;
+import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
+
+/**
+ * Translator which converts between ActiveConnection objects and
+ * APIActiveConnection objects. As ActiveConnection objects are read-only, only
+ * toExternalObject() is implemented here.
+ *
+ * @author Michael Jumper
+ */
+public class ActiveConnectionObjectTranslator
+ implements DirectoryObjectTranslator {
+
+ @Override
+ public APIActiveConnection toExternalObject(ActiveConnection object)
+ throws GuacamoleException {
+ return new APIActiveConnection(object);
+ }
+
+ @Override
+ public ActiveConnection toInternalObject(APIActiveConnection object)
+ throws GuacamoleException {
+
+ // ActiveConnection objects are read-only
+ throw new GuacamoleUnsupportedException("Active connection records are read-only.");
+
+ }
+
+ @Override
+ public void applyExternalChanges(ActiveConnection existingObject,
+ APIActiveConnection object) throws GuacamoleException {
+
+ // Modification not supported for ActiveConnection
+ throw new GuacamoleUnsupportedException("Active connection records are read-only.");
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionRESTService.java
deleted file mode 100644
index dd7391645..000000000
--- a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionRESTService.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.guacamole.rest.activeconnection;
-
-import com.google.inject.Inject;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
-import org.apache.guacamole.GuacamoleClientException;
-import org.apache.guacamole.GuacamoleException;
-import org.apache.guacamole.GuacamoleUnsupportedException;
-import org.apache.guacamole.net.auth.ActiveConnection;
-import org.apache.guacamole.net.auth.Directory;
-import org.apache.guacamole.net.auth.User;
-import org.apache.guacamole.net.auth.UserContext;
-import org.apache.guacamole.net.auth.permission.ObjectPermission;
-import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
-import org.apache.guacamole.net.auth.permission.SystemPermission;
-import org.apache.guacamole.net.auth.permission.SystemPermissionSet;
-import org.apache.guacamole.GuacamoleSession;
-import org.apache.guacamole.rest.APIPatch;
-import org.apache.guacamole.rest.ObjectRetrievalService;
-import org.apache.guacamole.rest.PATCH;
-import org.apache.guacamole.rest.auth.AuthenticationService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A REST Service for retrieving and managing the tunnels of active connections.
- *
- * @author Michael Jumper
- */
-@Path("/data/{dataSource}/activeConnections")
-@Produces(MediaType.APPLICATION_JSON)
-@Consumes(MediaType.APPLICATION_JSON)
-public class ActiveConnectionRESTService {
-
- /**
- * Logger for this class.
- */
- private static final Logger logger = LoggerFactory.getLogger(ActiveConnectionRESTService.class);
-
- /**
- * A service for authenticating users from auth tokens.
- */
- @Inject
- private AuthenticationService authenticationService;
-
- /**
- * Service for convenient retrieval of objects.
- */
- @Inject
- private ObjectRetrievalService retrievalService;
-
- /**
- * Gets a list of active connections in the system, filtering the returned
- * list by the given permissions, if specified.
- *
- * @param authToken
- * The authentication token that is used to authenticate the user
- * performing the operation.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider associated with
- * the UserContext containing the active connections to be retrieved.
- *
- * @param permissions
- * The set of permissions to filter with. A user must have one or more
- * of these permissions for a user to appear in the result.
- * If null, no filtering will be performed.
- *
- * @return
- * A list of all active connections. If a permission was specified,
- * this list will contain only those active connections for which the
- * current user has that permission.
- *
- * @throws GuacamoleException
- * If an error is encountered while retrieving active connections.
- */
- @GET
- public Map getActiveConnections(@QueryParam("token") String authToken,
- @PathParam("dataSource") String authProviderIdentifier,
- @QueryParam("permission") List permissions)
- throws GuacamoleException {
-
- GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
- UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
- User self = userContext.self();
-
- // Do not filter on permissions if no permissions are specified
- if (permissions != null && permissions.isEmpty())
- permissions = null;
-
- // An admin user has access to any connection
- SystemPermissionSet systemPermissions = self.getSystemPermissions();
- boolean isAdmin = systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER);
-
- // Get the directory
- Directory activeConnectionDirectory = userContext.getActiveConnectionDirectory();
-
- // Filter connections, if requested
- Collection activeConnectionIdentifiers = activeConnectionDirectory.getIdentifiers();
- if (!isAdmin && permissions != null) {
- ObjectPermissionSet activeConnectionPermissions = self.getActiveConnectionPermissions();
- activeConnectionIdentifiers = activeConnectionPermissions.getAccessibleObjects(permissions, activeConnectionIdentifiers);
- }
-
- // Retrieve all active connections , converting to API active connections
- Map apiActiveConnections = new HashMap();
- for (ActiveConnection activeConnection : activeConnectionDirectory.getAll(activeConnectionIdentifiers))
- apiActiveConnections.put(activeConnection.getIdentifier(), new APIActiveConnection(activeConnection));
-
- return apiActiveConnections;
-
- }
-
- /**
- * Applies the given active connection patches. This operation currently
- * only supports deletion of active connections through the "remove" patch
- * operation. Deleting an active connection effectively kills the
- * connection. The path of each patch operation is of the form "/ID"
- * where ID is the identifier of the active connection being modified.
- *
- * @param authToken
- * The authentication token that is used to authenticate the user
- * performing the operation.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider associated with
- * the UserContext containing the active connections to be deleted.
- *
- * @param patches
- * The active connection patches to apply for this request.
- *
- * @throws GuacamoleException
- * If an error occurs while deleting the active connections.
- */
- @PATCH
- public void patchTunnels(@QueryParam("token") String authToken,
- @PathParam("dataSource") String authProviderIdentifier,
- List> patches) throws GuacamoleException {
-
- GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
- UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
-
- // Get the directory
- Directory activeConnectionDirectory = userContext.getActiveConnectionDirectory();
-
- // Close each connection listed for removal
- for (APIPatch patch : patches) {
-
- // Only remove is supported
- if (patch.getOp() != APIPatch.Operation.remove)
- throw new GuacamoleUnsupportedException("Only the \"remove\" operation is supported when patching active connections.");
-
- // Retrieve and validate path
- String path = patch.getPath();
- if (!path.startsWith("/"))
- throw new GuacamoleClientException("Patch paths must start with \"/\".");
-
- // Close connection
- activeConnectionDirectory.remove(path.substring(1));
-
- }
-
- }
-
-}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java
new file mode 100644
index 000000000..d006416ba
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/ActiveConnectionResource.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.rest.activeconnection;
+
+import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.AssistedInject;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import org.apache.guacamole.net.auth.ActiveConnection;
+import org.apache.guacamole.net.auth.Directory;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.rest.directory.DirectoryObjectResource;
+import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
+
+/**
+ * A REST resource which abstracts the operations available on an existing
+ * ActiveConnection.
+ *
+ * @author Michael Jumper
+ */
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class ActiveConnectionResource
+ extends DirectoryObjectResource {
+
+ /**
+ * Creates a new ActiveConnectionResource which exposes the operations and
+ * subresources available for the given ActiveConnection.
+ *
+ * @param userContext
+ * The UserContext associated with the given Directory.
+ *
+ * @param directory
+ * The Directory which contains the given ActiveConnection.
+ *
+ * @param connection
+ * The ActiveConnection that this ActiveConnectionResource should
+ * represent.
+ *
+ * @param translator
+ * A DirectoryObjectTranslator implementation which handles
+ * ActiveConnections.
+ */
+ @AssistedInject
+ public ActiveConnectionResource(@Assisted UserContext userContext,
+ @Assisted Directory directory,
+ @Assisted ActiveConnection connection,
+ DirectoryObjectTranslator translator) {
+ super(directory, connection, translator);
+ }
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/package-info.java b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/package-info.java
new file mode 100644
index 000000000..f5483dbbc
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/activeconnection/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Classes related to the manipulation of active connections via the Guacamole
+ * REST API.
+ */
+package org.apache.guacamole.rest.activeconnection;
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionModule.java
new file mode 100644
index 000000000..9f716719f
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionModule.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.rest.connection;
+
+import com.google.inject.AbstractModule;
+import org.apache.guacamole.rest.directory.DirectoryObjectResourceFactory;
+import org.apache.guacamole.rest.directory.DirectoryObjectResource;
+import org.apache.guacamole.rest.directory.DirectoryResourceFactory;
+import com.google.inject.TypeLiteral;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
+import org.apache.guacamole.net.auth.Connection;
+import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
+
+/**
+ * Guice Module which configures injections required for handling Connection
+ * resources via the REST API.
+ *
+ * @author Michael Jumper
+ */
+public class ConnectionModule extends AbstractModule {
+
+ @Override
+ protected void configure() {
+
+ // Create the required DirectoryResourceFactory implementation
+ install(new FactoryModuleBuilder()
+ .build(new TypeLiteral>() {}));
+
+ // Create the required DirectoryObjectResourceFactory implementation
+ install(new FactoryModuleBuilder()
+ .implement(
+ new TypeLiteral>() {},
+ ConnectionResource.class
+ )
+ .build(new TypeLiteral>() {}));
+
+ // Bind translator for converting between Connection and APIConnection
+ bind(new TypeLiteral>() {})
+ .to(ConnectionObjectTranslator.class);
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionObjectTranslator.java b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionObjectTranslator.java
new file mode 100644
index 000000000..de6253f13
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionObjectTranslator.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.rest.connection;
+
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.Connection;
+import org.apache.guacamole.protocol.GuacamoleConfiguration;
+import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
+
+/**
+ * Translator which converts between Connection objects and APIConnection
+ * objects.
+ *
+ * @author Michael Jumper
+ */
+public class ConnectionObjectTranslator
+ implements DirectoryObjectTranslator {
+
+ @Override
+ public APIConnection toExternalObject(Connection object)
+ throws GuacamoleException {
+ return new APIConnection(object);
+ }
+
+ @Override
+ public Connection toInternalObject(APIConnection object) {
+ return new APIConnectionWrapper(object);
+ }
+
+ @Override
+ public void applyExternalChanges(Connection existingObject,
+ APIConnection object) {
+
+ // Build updated configuration
+ GuacamoleConfiguration config = new GuacamoleConfiguration();
+ config.setProtocol(object.getProtocol());
+ config.setParameters(object.getParameters());
+
+ // Update the connection
+ existingObject.setConfiguration(config);
+ existingObject.setParentIdentifier(object.getParentIdentifier());
+ existingObject.setName(object.getName());
+ existingObject.setAttributes(object.getAttributes());
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionRESTService.java
deleted file mode 100644
index feb03240d..000000000
--- a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionRESTService.java
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.guacamole.rest.connection;
-
-import com.google.inject.Inject;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-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 org.apache.guacamole.GuacamoleClientException;
-import org.apache.guacamole.GuacamoleException;
-import org.apache.guacamole.GuacamoleSecurityException;
-import org.apache.guacamole.net.auth.Connection;
-import org.apache.guacamole.net.auth.ConnectionRecord;
-import org.apache.guacamole.net.auth.Directory;
-import org.apache.guacamole.net.auth.User;
-import org.apache.guacamole.net.auth.UserContext;
-import org.apache.guacamole.net.auth.permission.ObjectPermission;
-import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
-import org.apache.guacamole.net.auth.permission.SystemPermission;
-import org.apache.guacamole.net.auth.permission.SystemPermissionSet;
-import org.apache.guacamole.GuacamoleSession;
-import org.apache.guacamole.rest.ObjectRetrievalService;
-import org.apache.guacamole.rest.auth.AuthenticationService;
-import org.apache.guacamole.rest.history.APIConnectionRecord;
-import org.apache.guacamole.protocol.GuacamoleConfiguration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A REST Service for handling connection CRUD operations.
- *
- * @author James Muehlner
- */
-@Path("/data/{dataSource}/connections")
-@Produces(MediaType.APPLICATION_JSON)
-@Consumes(MediaType.APPLICATION_JSON)
-public class ConnectionRESTService {
-
- /**
- * Logger for this class.
- */
- private static final Logger logger = LoggerFactory.getLogger(ConnectionRESTService.class);
-
- /**
- * A service for authenticating users from auth tokens.
- */
- @Inject
- private AuthenticationService authenticationService;
-
- /**
- * Service for convenient retrieval of objects.
- */
- @Inject
- private ObjectRetrievalService retrievalService;
-
- /**
- * Retrieves an individual connection.
- *
- * @param authToken
- * The authentication token that is used to authenticate the user
- * performing the operation.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider associated with
- * the UserContext containing the connection to be retrieved.
- *
- * @param connectionID
- * The identifier of the connection to retrieve.
- *
- * @return
- * The connection having the given identifier.
- *
- * @throws GuacamoleException
- * If an error occurs while retrieving the connection.
- */
- @GET
- @Path("/{connectionID}")
- public APIConnection getConnection(@QueryParam("token") String authToken,
- @PathParam("dataSource") String authProviderIdentifier,
- @PathParam("connectionID") String connectionID)
- throws GuacamoleException {
-
- GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
-
- // Retrieve the requested connection
- return new APIConnection(retrievalService.retrieveConnection(session, authProviderIdentifier, connectionID));
-
- }
-
- /**
- * Retrieves the parameters associated with a single connection.
- *
- * @param authToken
- * The authentication token that is used to authenticate the user
- * performing the operation.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider associated with
- * the UserContext containing the connection whose parameters are to be
- * retrieved.
- *
- * @param connectionID
- * The identifier of the connection.
- *
- * @return
- * A map of parameter name/value pairs.
- *
- * @throws GuacamoleException
- * If an error occurs while retrieving the connection parameters.
- */
- @GET
- @Path("/{connectionID}/parameters")
- public Map getConnectionParameters(@QueryParam("token") String authToken,
- @PathParam("dataSource") String authProviderIdentifier,
- @PathParam("connectionID") String connectionID)
- throws GuacamoleException {
-
- GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
- UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
- User self = userContext.self();
-
- // Retrieve permission sets
- SystemPermissionSet systemPermissions = self.getSystemPermissions();
- ObjectPermissionSet connectionPermissions = self.getConnectionPermissions();
-
- // Deny access if adminstrative or update permission is missing
- if (!systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER)
- && !connectionPermissions.hasPermission(ObjectPermission.Type.UPDATE, connectionID))
- throw new GuacamoleSecurityException("Permission to read connection parameters denied.");
-
- // Retrieve the requested connection
- Connection connection = retrievalService.retrieveConnection(userContext, connectionID);
-
- // Retrieve connection configuration
- GuacamoleConfiguration config = connection.getConfiguration();
-
- // Return parameter map
- return config.getParameters();
-
- }
-
- /**
- * Retrieves the usage history of a single connection.
- *
- * @param authToken
- * The authentication token that is used to authenticate the user
- * performing the operation.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider associated with
- * the UserContext containing the connection whose history is to be
- * retrieved.
- *
- * @param connectionID
- * The identifier of the connection.
- *
- * @return
- * A list of connection records, describing the start and end times of
- * various usages of this connection.
- *
- * @throws GuacamoleException
- * If an error occurs while retrieving the connection history.
- */
- @GET
- @Path("/{connectionID}/history")
- public List getConnectionHistory(@QueryParam("token") String authToken,
- @PathParam("dataSource") String authProviderIdentifier,
- @PathParam("connectionID") String connectionID)
- throws GuacamoleException {
-
- GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
-
- // Retrieve the requested connection
- Connection connection = retrievalService.retrieveConnection(session, authProviderIdentifier, connectionID);
-
- // Retrieve the requested connection's history
- List apiRecords = new ArrayList();
- for (ConnectionRecord record : connection.getHistory())
- apiRecords.add(new APIConnectionRecord(record));
-
- // Return the converted history
- return apiRecords;
-
- }
-
- /**
- * Deletes an individual connection.
- *
- * @param authToken
- * The authentication token that is used to authenticate the user
- * performing the operation.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider associated with
- * the UserContext containing the connection to be deleted.
- *
- * @param connectionID
- * The identifier of the connection to delete.
- *
- * @throws GuacamoleException
- * If an error occurs while deleting the connection.
- */
- @DELETE
- @Path("/{connectionID}")
- public void deleteConnection(@QueryParam("token") String authToken,
- @PathParam("dataSource") String authProviderIdentifier,
- @PathParam("connectionID") String connectionID)
- throws GuacamoleException {
-
- GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
- UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
-
- // Get the connection directory
- Directory connectionDirectory = userContext.getConnectionDirectory();
-
- // Delete the specified connection
- connectionDirectory.remove(connectionID);
-
- }
-
- /**
- * Creates a new connection and returns the new connection, with identifier
- * field populated.
- *
- * @param authToken
- * The authentication token that is used to authenticate the user
- * performing the operation.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider associated with
- * the UserContext in which the connection is to be created.
- *
- * @param connection
- * The connection to create.
- *
- * @return
- * The new connection.
- *
- * @throws GuacamoleException
- * If an error occurs while creating the connection.
- */
- @POST
- public APIConnection createConnection(@QueryParam("token") String authToken,
- @PathParam("dataSource") String authProviderIdentifier,
- APIConnection connection) throws GuacamoleException {
-
- GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
- UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
-
- // Validate that connection data was provided
- if (connection == null)
- throw new GuacamoleClientException("Connection JSON must be submitted when creating connections.");
-
- // Add the new connection
- Directory connectionDirectory = userContext.getConnectionDirectory();
- connectionDirectory.add(new APIConnectionWrapper(connection));
-
- // Return the new connection
- return connection;
-
- }
-
- /**
- * Updates an existing connection. If the parent identifier of the
- * connection is changed, the connection will also be moved to the new
- * parent group.
- *
- * @param authToken
- * The authentication token that is used to authenticate the user
- * performing the operation.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider associated with
- * the UserContext containing the connection to be updated.
- *
- * @param connectionID
- * The identifier of the connection to update.
- *
- * @param connection
- * The connection data to update the specified connection with.
- *
- * @throws GuacamoleException
- * If an error occurs while updating the connection.
- */
- @PUT
- @Path("/{connectionID}")
- public void updateConnection(@QueryParam("token") String authToken,
- @PathParam("dataSource") String authProviderIdentifier,
- @PathParam("connectionID") String connectionID,
- APIConnection connection) throws GuacamoleException {
-
- GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
- UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
-
- // Validate that connection data was provided
- if (connection == null)
- throw new GuacamoleClientException("Connection JSON must be submitted when updating connections.");
-
- // Get the connection directory
- Directory connectionDirectory = userContext.getConnectionDirectory();
-
- // Retrieve connection to update
- Connection existingConnection = retrievalService.retrieveConnection(userContext, connectionID);
-
- // Build updated configuration
- GuacamoleConfiguration config = new GuacamoleConfiguration();
- config.setProtocol(connection.getProtocol());
- config.setParameters(connection.getParameters());
-
- // Update the connection
- existingConnection.setConfiguration(config);
- existingConnection.setParentIdentifier(connection.getParentIdentifier());
- existingConnection.setName(connection.getName());
- existingConnection.setAttributes(connection.getAttributes());
- connectionDirectory.update(existingConnection);
-
- }
-
-}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java
new file mode 100644
index 000000000..9238af15e
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.rest.connection;
+
+import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.AssistedInject;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleSecurityException;
+import org.apache.guacamole.net.auth.Connection;
+import org.apache.guacamole.net.auth.ConnectionRecord;
+import org.apache.guacamole.net.auth.Directory;
+import org.apache.guacamole.net.auth.User;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.net.auth.permission.ObjectPermission;
+import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
+import org.apache.guacamole.net.auth.permission.SystemPermission;
+import org.apache.guacamole.net.auth.permission.SystemPermissionSet;
+import org.apache.guacamole.rest.history.APIConnectionRecord;
+import org.apache.guacamole.protocol.GuacamoleConfiguration;
+import org.apache.guacamole.rest.directory.DirectoryObjectResource;
+import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
+
+/**
+ * A REST resource which abstracts the operations available on an existing
+ * Connection.
+ *
+ * @author Michael Jumper
+ */
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class ConnectionResource extends DirectoryObjectResource {
+
+ /**
+ * The UserContext associated with the Directory which contains the
+ * Connection exposed by this resource.
+ */
+ private final UserContext userContext;
+
+ /**
+ * The Connection object represented by this ConnectionResource.
+ */
+ private final Connection connection;
+
+ /**
+ * Creates a new ConnectionResource which exposes the operations and
+ * subresources available for the given Connection.
+ *
+ * @param userContext
+ * The UserContext associated with the given Directory.
+ *
+ * @param directory
+ * The Directory which contains the given Connection.
+ *
+ * @param connection
+ * The Connection that this ConnectionResource should represent.
+ *
+ * @param translator
+ * A DirectoryObjectTranslator implementation which handles the type of
+ * object given.
+ */
+ @AssistedInject
+ public ConnectionResource(@Assisted UserContext userContext,
+ @Assisted Directory directory,
+ @Assisted Connection connection,
+ DirectoryObjectTranslator translator) {
+ super(directory, connection, translator);
+ this.userContext = userContext;
+ this.connection = connection;
+ }
+
+ /**
+ * Retrieves the parameters associated with a single connection.
+ *
+ * @return
+ * A map of parameter name/value pairs.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the connection parameters.
+ */
+ @GET
+ @Path("parameters")
+ public Map getConnectionParameters()
+ throws GuacamoleException {
+
+ User self = userContext.self();
+
+ // Retrieve permission sets
+ SystemPermissionSet systemPermissions = self.getSystemPermissions();
+ ObjectPermissionSet connectionPermissions = self.getConnectionPermissions();
+
+ // Deny access if adminstrative or update permission is missing
+ String identifier = connection.getIdentifier();
+ if (!systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER)
+ && !connectionPermissions.hasPermission(ObjectPermission.Type.UPDATE, identifier))
+ throw new GuacamoleSecurityException("Permission to read connection parameters denied.");
+
+ // Retrieve connection configuration
+ GuacamoleConfiguration config = connection.getConfiguration();
+
+ // Return parameter map
+ return config.getParameters();
+
+ }
+
+ /**
+ * Retrieves the usage history of a single connection.
+ *
+ * @return
+ * A list of connection records, describing the start and end times of
+ * various usages of this connection.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the connection history.
+ */
+ @GET
+ @Path("history")
+ public List getConnectionHistory()
+ throws GuacamoleException {
+
+ // Retrieve the requested connection's history
+ List apiRecords = new ArrayList();
+ for (ConnectionRecord record : connection.getHistory())
+ apiRecords.add(new APIConnectionRecord(record));
+
+ // Return the converted history
+ return apiRecords;
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupDirectoryResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupDirectoryResource.java
new file mode 100644
index 000000000..2c32e21d9
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupDirectoryResource.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.rest.connectiongroup;
+
+import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.AssistedInject;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.ConnectionGroup;
+import org.apache.guacamole.net.auth.Directory;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.rest.directory.DirectoryObjectResource;
+import org.apache.guacamole.rest.directory.DirectoryObjectResourceFactory;
+import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
+import org.apache.guacamole.rest.directory.DirectoryResource;
+
+/**
+ * A REST resource which abstracts the operations available on a Directory of
+ * ConnectionGroups.
+ *
+ * @author Michael Jumper
+ */
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class ConnectionGroupDirectoryResource
+ extends DirectoryResource {
+
+ /**
+ * The UserContext associated with the Directory which contains the
+ * ConnectionGroup exposed by this resource.
+ */
+ private final UserContext userContext;
+
+ /**
+ * The Directory exposed by this resource.
+ */
+ private final Directory directory;
+
+ /**
+ * A factory which can be used to create instances of resources representing
+ * ConnectionGroups.
+ */
+ private final DirectoryObjectResourceFactory resourceFactory;
+
+ /**
+ * Creates a new ConnectionGroupDirectoryResource which exposes the
+ * operations and subresources available for the given ConnectionGroup
+ * Directory.
+ *
+ * @param userContext
+ * The UserContext associated with the given Directory.
+ *
+ * @param directory
+ * The Directory being exposed.
+ *
+ * @param translator
+ * A DirectoryObjectTranslator implementation which handles
+ * ConnectionGroups.
+ *
+ * @param resourceFactory
+ * A factory which can be used to create instances of resources
+ * representing ConnectionGroups.
+ */
+ @AssistedInject
+ public ConnectionGroupDirectoryResource(@Assisted UserContext userContext,
+ @Assisted Directory directory,
+ DirectoryObjectTranslator translator,
+ DirectoryObjectResourceFactory resourceFactory) {
+ super(userContext, directory, translator, resourceFactory);
+ this.userContext = userContext;
+ this.directory = directory;
+ this.resourceFactory = resourceFactory;
+ }
+
+ @Override
+ public DirectoryObjectResource
+ getObjectResource(String identifier) throws GuacamoleException {
+
+ // Use root group if identifier is the standard root identifier
+ if (identifier != null && identifier.equals(APIConnectionGroup.ROOT_IDENTIFIER))
+ return resourceFactory.create(userContext, directory,
+ userContext.getRootConnectionGroup());
+
+ return super.getObjectResource(identifier);
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupModule.java
new file mode 100644
index 000000000..df69919ea
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupModule.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.rest.connectiongroup;
+
+import com.google.inject.AbstractModule;
+import org.apache.guacamole.rest.directory.DirectoryObjectResourceFactory;
+import org.apache.guacamole.rest.directory.DirectoryObjectResource;
+import org.apache.guacamole.rest.directory.DirectoryResourceFactory;
+import com.google.inject.TypeLiteral;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
+import org.apache.guacamole.net.auth.ConnectionGroup;
+import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
+import org.apache.guacamole.rest.directory.DirectoryResource;
+
+/**
+ * Guice Module which configures injections required for handling
+ * ConnectionGroup resources via the REST API.
+ *
+ * @author Michael Jumper
+ */
+public class ConnectionGroupModule extends AbstractModule {
+
+ @Override
+ protected void configure() {
+
+ // Create the required DirectoryResourceFactory implementation
+ install(new FactoryModuleBuilder()
+ .implement(
+ new TypeLiteral>() {},
+ ConnectionGroupDirectoryResource.class
+ )
+ .build(new TypeLiteral>() {}));
+
+ // Create the required DirectoryObjectResourceFactory implementation
+ install(new FactoryModuleBuilder()
+ .implement(
+ new TypeLiteral>() {},
+ ConnectionGroupResource.class
+ )
+ .build(new TypeLiteral>() {}));
+
+ // Bind translator for converting between ConnectionGroup and APIConnectionGroup
+ bind(new TypeLiteral>() {})
+ .to(ConnectionGroupObjectTranslator.class);
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupObjectTranslator.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupObjectTranslator.java
new file mode 100644
index 000000000..efd5cb95b
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupObjectTranslator.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.rest.connectiongroup;
+
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.ConnectionGroup;
+import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
+
+/**
+ * Translator which converts between ConnectionGroup objects and
+ * APIConnectionGroup objects.
+ *
+ * @author Michael Jumper
+ */
+public class ConnectionGroupObjectTranslator
+ implements DirectoryObjectTranslator {
+
+ @Override
+ public APIConnectionGroup toExternalObject(ConnectionGroup object)
+ throws GuacamoleException {
+ return new APIConnectionGroup(object);
+ }
+
+ @Override
+ public ConnectionGroup toInternalObject(APIConnectionGroup object) {
+ return new APIConnectionGroupWrapper(object);
+ }
+
+ @Override
+ public void applyExternalChanges(ConnectionGroup existingObject,
+ APIConnectionGroup object) {
+
+ // Update the connection group
+ existingObject.setName(object.getName());
+ existingObject.setParentIdentifier(object.getParentIdentifier());
+ existingObject.setType(object.getType());
+ existingObject.setAttributes(object.getAttributes());
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupRESTService.java
deleted file mode 100644
index bd28a1978..000000000
--- a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupRESTService.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.guacamole.rest.connectiongroup;
-
-import com.google.inject.Inject;
-import java.util.List;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
-import org.apache.guacamole.GuacamoleClientException;
-import org.apache.guacamole.GuacamoleException;
-import org.apache.guacamole.net.auth.ConnectionGroup;
-import org.apache.guacamole.net.auth.Directory;
-import org.apache.guacamole.net.auth.UserContext;
-import org.apache.guacamole.net.auth.permission.ObjectPermission;
-import org.apache.guacamole.GuacamoleSession;
-import org.apache.guacamole.rest.ObjectRetrievalService;
-import org.apache.guacamole.rest.auth.AuthenticationService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A REST Service for handling connection group CRUD operations.
- *
- * @author James Muehlner
- */
-@Path("/data/{dataSource}/connectionGroups")
-@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;
-
- /**
- * Service for convenient retrieval of objects.
- */
- @Inject
- private ObjectRetrievalService retrievalService;
-
- /**
- * Gets an individual connection group.
- *
- * @param authToken
- * The authentication token that is used to authenticate the user
- * performing the operation.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider associated with
- * the UserContext containing the connection group to be retrieved.
- *
- * @param connectionGroupID
- * The ID of the connection group to retrieve.
- *
- * @return
- * The connection group, without any descendants.
- *
- * @throws GuacamoleException
- * If a problem is encountered while retrieving the connection group.
- */
- @GET
- @Path("/{connectionGroupID}")
- public APIConnectionGroup getConnectionGroup(@QueryParam("token") String authToken,
- @PathParam("dataSource") String authProviderIdentifier,
- @PathParam("connectionGroupID") String connectionGroupID)
- throws GuacamoleException {
-
- GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
-
- // Retrieve the requested connection group
- return new APIConnectionGroup(retrievalService.retrieveConnectionGroup(session, authProviderIdentifier, connectionGroupID));
-
- }
-
- /**
- * Gets an individual connection group and all children.
- *
- * @param authToken
- * The authentication token that is used to authenticate the user
- * performing the operation.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider associated with
- * the UserContext containing the connection group to be retrieved.
- *
- * @param connectionGroupID
- * The ID of the connection group to retrieve.
- *
- * @param permissions
- * If specified and non-empty, limit the returned list to only those
- * connections for which the current user has any of the given
- * permissions. Otherwise, all visible connections are returned.
- * Connection groups are unaffected by this parameter.
- *
- * @return
- * The requested connection group, including all descendants.
- *
- * @throws GuacamoleException
- * If a problem is encountered while retrieving the connection group or
- * its descendants.
- */
- @GET
- @Path("/{connectionGroupID}/tree")
- public APIConnectionGroup getConnectionGroupTree(@QueryParam("token") String authToken,
- @PathParam("dataSource") String authProviderIdentifier,
- @PathParam("connectionGroupID") String connectionGroupID,
- @QueryParam("permission") List permissions)
- throws GuacamoleException {
-
- GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
- UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
-
- // Retrieve the requested tree, filtering by the given permissions
- ConnectionGroup treeRoot = retrievalService.retrieveConnectionGroup(userContext, connectionGroupID);
- ConnectionGroupTree tree = new ConnectionGroupTree(userContext, treeRoot, permissions);
-
- // Return tree as a connection group
- return tree.getRootAPIConnectionGroup();
-
- }
-
- /**
- * Deletes an individual connection group.
- *
- * @param authToken
- * The authentication token that is used to authenticate the user
- * performing the operation.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider associated with
- * the UserContext containing the connection group to be deleted.
- *
- * @param connectionGroupID
- * The identifier of the connection group to delete.
- *
- * @throws GuacamoleException
- * If an error occurs while deleting the connection group.
- */
- @DELETE
- @Path("/{connectionGroupID}")
- public void deleteConnectionGroup(@QueryParam("token") String authToken,
- @PathParam("dataSource") String authProviderIdentifier,
- @PathParam("connectionGroupID") String connectionGroupID)
- throws GuacamoleException {
-
- GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
- UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
-
- // Get the connection group directory
- Directory connectionGroupDirectory = userContext.getConnectionGroupDirectory();
-
- // Delete the connection group
- connectionGroupDirectory.remove(connectionGroupID);
-
- }
-
- /**
- * Creates a new connection group and returns the new connection group,
- * with identifier field populated.
- *
- * @param authToken
- * The authentication token that is used to authenticate the user
- * performing the operation.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider associated with
- * the UserContext in which the connection group is to be created.
- *
- * @param connectionGroup
- * The connection group to create.
- *
- * @return
- * The new connection group.
- *
- * @throws GuacamoleException
- * If an error occurs while creating the connection group.
- */
- @POST
- public APIConnectionGroup createConnectionGroup(
- @QueryParam("token") String authToken,
- @PathParam("dataSource") String authProviderIdentifier,
- APIConnectionGroup connectionGroup) throws GuacamoleException {
-
- GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
- UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
-
- // Validate that connection group data was provided
- if (connectionGroup == null)
- throw new GuacamoleClientException("Connection group JSON must be submitted when creating connections groups.");
-
- // Add the new connection group
- Directory connectionGroupDirectory = userContext.getConnectionGroupDirectory();
- connectionGroupDirectory.add(new APIConnectionGroupWrapper(connectionGroup));
-
- // Return the new connection group
- return connectionGroup;
-
- }
-
- /**
- * Updates a connection group. If the parent identifier of the
- * connection group is changed, the connection group will also be moved to
- * the new parent group.
- *
- * @param authToken
- * The authentication token that is used to authenticate the user
- * performing the operation.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider associated with
- * the UserContext containing the connection group to be updated.
- *
- * @param connectionGroupID
- * The identifier of the existing connection group to update.
- *
- * @param connectionGroup
- * The data to update the existing connection group with.
- *
- * @throws GuacamoleException
- * If an error occurs while updating the connection group.
- */
- @PUT
- @Path("/{connectionGroupID}")
- public void updateConnectionGroup(@QueryParam("token") String authToken,
- @PathParam("dataSource") String authProviderIdentifier,
- @PathParam("connectionGroupID") String connectionGroupID,
- APIConnectionGroup connectionGroup)
- throws GuacamoleException {
-
- GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
- UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
-
- // Validate that connection group data was provided
- if (connectionGroup == null)
- throw new GuacamoleClientException("Connection group JSON must be submitted when updating connection groups.");
-
- // Get the connection group directory
- Directory connectionGroupDirectory = userContext.getConnectionGroupDirectory();
-
- // Retrieve connection group to update
- ConnectionGroup existingConnectionGroup = retrievalService.retrieveConnectionGroup(userContext, connectionGroupID);
-
- // Update the connection group
- existingConnectionGroup.setName(connectionGroup.getName());
- existingConnectionGroup.setParentIdentifier(connectionGroup.getParentIdentifier());
- existingConnectionGroup.setType(connectionGroup.getType());
- existingConnectionGroup.setAttributes(connectionGroup.getAttributes());
- connectionGroupDirectory.update(existingConnectionGroup);
-
- }
-
-}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupResource.java
new file mode 100644
index 000000000..c0b80c44b
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupResource.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.rest.connectiongroup;
+
+import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.AssistedInject;
+import java.util.List;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.ConnectionGroup;
+import org.apache.guacamole.net.auth.Directory;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.net.auth.permission.ObjectPermission;
+import org.apache.guacamole.rest.directory.DirectoryObjectResource;
+import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
+
+/**
+ * A REST resource which abstracts the operations available on an existing
+ * ConnectionGroup.
+ *
+ * @author Michael Jumper
+ */
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class ConnectionGroupResource
+ extends DirectoryObjectResource {
+
+ /**
+ * The UserContext associated with the Directory which contains the
+ * ConnectionGroup exposed by this resource.
+ */
+ private final UserContext userContext;
+
+ /**
+ * The ConnectionGroup object represented by this ConnectionGroupResource.
+ */
+ private final ConnectionGroup connectionGroup;
+
+ /**
+ * Creates a new ConnectionGroupResource which exposes the operations and
+ * subresources available for the given ConnectionGroup.
+ *
+ * @param userContext
+ * The UserContext associated with the given Directory.
+ *
+ * @param directory
+ * The Directory which contains the given ConnectionGroup.
+ *
+ * @param connectionGroup
+ * The ConnectionGroup that this ConnectionGroupResource should
+ * represent.
+ *
+ * @param translator
+ * A DirectoryObjectTranslator implementation which handles the type of
+ * object given.
+ */
+ @AssistedInject
+ public ConnectionGroupResource(@Assisted UserContext userContext,
+ @Assisted Directory directory,
+ @Assisted ConnectionGroup connectionGroup,
+ DirectoryObjectTranslator translator) {
+ super(directory, connectionGroup, translator);
+ this.userContext = userContext;
+ this.connectionGroup = connectionGroup;
+ }
+
+ /**
+ * Returns the current connection group along with all descendants.
+ *
+ * @param permissions
+ * If specified and non-empty, limit the returned list to only those
+ * connections for which the current user has any of the given
+ * permissions. Otherwise, all visible connections are returned.
+ * ConnectionGroups are unaffected by this parameter.
+ *
+ * @return
+ * The current connection group, including all descendants.
+ *
+ * @throws GuacamoleException
+ * If a problem is encountered while retrieving the connection group or
+ * its descendants.
+ */
+ @GET
+ @Path("tree")
+ public APIConnectionGroup getConnectionGroupTree(
+ @QueryParam("permission") List permissions)
+ throws GuacamoleException {
+
+ // Retrieve the requested tree, filtering by the given permissions
+ ConnectionGroupTree tree = new ConnectionGroupTree(userContext,
+ connectionGroup, permissions);
+
+ // Return tree as a connection group
+ return tree.getRootAPIConnectionGroup();
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectResource.java
new file mode 100644
index 000000000..7e45b2b82
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectResource.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.rest.directory;
+
+import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.AssistedInject;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import org.apache.guacamole.GuacamoleClientException;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.Directory;
+import org.apache.guacamole.net.auth.Identifiable;
+
+/**
+ * A REST resource which abstracts the operations available on an existing
+ * Guacamole object that is contained within a Directory, such as modification,
+ * deletion, or individual retrieval.
+ *
+ * @author Michael Jumper
+ * @param
+ * The type of object that this DirectoryObjectResource represents. To
+ * avoid coupling the REST API too tightly to the extension API, these
+ * objects are not directly serialized or deserialized when handling REST
+ * requests.
+ *
+ * @param
+ * The type of object used in interchange (ie: serialized/deserialized as
+ * JSON) between REST clients and this DirectoryObjectResource to
+ * represent the InternalType.
+ */
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class DirectoryObjectResource {
+
+ /**
+ * The Directory which contains the object represented by this
+ * DirectoryObjectResource.
+ */
+ private final Directory directory;
+
+ /**
+ * The object represented by this DirectoryObjectResource.
+ */
+ private final InternalType object;
+
+ /**
+ * A DirectoryObjectTranslator implementation which handles the type of
+ * objects represented by this DirectoryObjectResource.
+ */
+ private final DirectoryObjectTranslator translator;
+
+ /**
+ * Creates a new DirectoryObjectResource which exposes the operations
+ * available for the given object.
+ *
+ * @param directory
+ * The Directory which contains the given object.
+ *
+ * @param object
+ * The object that this DirectoryObjectResource should represent.
+ *
+ * @param translator
+ * A DirectoryObjectTranslator implementation which handles the type of
+ * object given.
+ */
+ @AssistedInject
+ public DirectoryObjectResource(@Assisted Directory directory,
+ @Assisted InternalType object,
+ DirectoryObjectTranslator translator) {
+ this.directory = directory;
+ this.object = object;
+ this.translator = translator;
+ }
+
+ /**
+ * Returns the object represented by this DirectoryObjectResource, in a
+ * format intended for interchange.
+ *
+ * @return
+ * The object that this DirectoryObjectResource represents, in a format
+ * intended for interchange.
+ *
+ * @throws GuacamoleException
+ * If an error is encountered while retrieving the object.
+ */
+ @GET
+ public ExternalType getObject() throws GuacamoleException {
+ return translator.toExternalObject(object);
+ }
+
+ /**
+ * Updates an existing object. The changes to be made to the corresponding
+ * object within the directory indicated by the provided data.
+ *
+ * @param modifiedObject
+ * The data to update the corresponding object with.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while updating the object.
+ */
+ @PUT
+ public void updateObject(ExternalType modifiedObject) throws GuacamoleException {
+
+ // Validate that data was provided
+ if (modifiedObject == null)
+ throw new GuacamoleClientException("Data must be submitted when updating objects.");
+
+ // Perform update
+ translator.applyExternalChanges(object, modifiedObject);
+ directory.update(object);
+
+ }
+
+ /**
+ * Removes this object from the containing directory.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while removing the object.
+ */
+ @DELETE
+ public void deleteObject() throws GuacamoleException {
+ directory.remove(object.getIdentifier());
+ }
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectResourceFactory.java b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectResourceFactory.java
new file mode 100644
index 000000000..613ac4220
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectResourceFactory.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.rest.directory;
+
+import org.apache.guacamole.net.auth.Directory;
+import org.apache.guacamole.net.auth.Identifiable;
+import org.apache.guacamole.net.auth.UserContext;
+
+/**
+ * Factory which creates DirectoryObjectResource instances exposing objects of
+ * a particular type.
+ *
+ * @param
+ * The type of object exposed by the DirectoryObjectResource instances
+ * created by this DirectoryResourceFactory.
+ *
+ * @param
+ * The type of object used in interchange (ie: serialized or deserialized
+ * as JSON) between REST clients and resources when representing the
+ * InternalType.
+ */
+public interface DirectoryObjectResourceFactory {
+
+ /**
+ * Creates a new DirectoryObjectResource which exposes the given object.
+ *
+ * @param userContext
+ * The UserContext which contains the given Directory.
+ *
+ * @param directory
+ * The Directory which contains the object being exposed.
+ *
+ * @param object
+ * The object which should be exposed by the created
+ * DirectoryObjectResource.
+ *
+ * @return
+ * A new DirectoryObjectResource which exposes the given object.
+ */
+ DirectoryObjectResource
+ create(UserContext userContext, Directory directory,
+ InternalType object);
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectTranslator.java b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectTranslator.java
new file mode 100644
index 000000000..bac47957b
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryObjectTranslator.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.rest.directory;
+
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.Identifiable;
+
+/**
+ * Provides bidirectional conversion between REST-specific objects and the
+ * internal objects defined by the Guacamole extension API.
+ *
+ * @author Michael Jumper
+ * @param
+ * The type of object converted by this DirectoryObjectTranslator which is
+ * not necessarily intended for use in interchange.
+ *
+ * @param
+ * The type of object used in interchange (ie: serialized or
+ * deserialized as JSON) between REST clients and resource implementations
+ * when representing the InternalType.
+ */
+public interface DirectoryObjectTranslator {
+
+ /**
+ * Converts the given object to an object which is intended to be used in
+ * interchange.
+ *
+ * @param object
+ * The object to convert for the sake of interchange.
+ *
+ * @return
+ * A new object containing the same data as the given internal object,
+ * but intended for use in interchange.
+ *
+ * @throws GuacamoleException
+ * If the provided object cannot be converted for any reason.
+ */
+ ExternalType toExternalObject(InternalType object)
+ throws GuacamoleException;
+
+ /**
+ * Converts the given object to an object which is intended to be used
+ * within the Guacamole extension API.
+ *
+ * @param object
+ * An object of the type intended for use in interchange, such as that
+ * produced by toExternalObject() or received from a user via REST.
+ *
+ * @return
+ * A new object containing the same data as the given external object,
+ * but intended for use within the Guacamole extension API.
+ *
+ * @throws GuacamoleException
+ * If the provided object cannot be converted for any reason.
+ */
+ InternalType toInternalObject(ExternalType object)
+ throws GuacamoleException;
+
+ /**
+ * Overlays the changes indicated by the given external object, modifying
+ * the given existing object from the Guacamole extension API.
+ *
+ * @param existingObject
+ * The existing object from the Guacamole extension API which should be
+ * modified.
+ *
+ * @param object
+ * The external object representing the modifications to the existing
+ * internal object.
+ *
+ * @throws GuacamoleException
+ * If the provided modifications cannot be applied for any reason.
+ */
+ void applyExternalChanges(InternalType existingObject, ExternalType object)
+ throws GuacamoleException;
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java
new file mode 100644
index 000000000..2dbf7f136
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java
@@ -0,0 +1,266 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.rest.directory;
+
+import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.AssistedInject;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import org.apache.guacamole.GuacamoleClientException;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleResourceNotFoundException;
+import org.apache.guacamole.GuacamoleUnsupportedException;
+import org.apache.guacamole.net.auth.Directory;
+import org.apache.guacamole.net.auth.Identifiable;
+import org.apache.guacamole.net.auth.User;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.net.auth.permission.ObjectPermission;
+import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
+import org.apache.guacamole.net.auth.permission.SystemPermission;
+import org.apache.guacamole.net.auth.permission.SystemPermissionSet;
+import org.apache.guacamole.rest.APIPatch;
+import org.apache.guacamole.rest.PATCH;
+
+/**
+ * A REST resource which abstracts the operations available on all Guacamole
+ * Directory implementations, such as the creation of new objects, or listing
+ * of existing objects. A DirectoryResource functions as the parent of any
+ * number of child DirectoryObjectResources, which are created with the factory
+ * provided at the time of this object's construction.
+ *
+ * @author Michael Jumper
+ * @param
+ * The type of object contained within the Directory that this
+ * DirectoryResource exposes. To avoid coupling the REST API too tightly to
+ * the extension API, these objects are not directly serialized or
+ * deserialized when handling REST requests.
+ *
+ * @param
+ * The type of object used in interchange (ie: serialized/deserialized as
+ * JSON) between REST clients and this DirectoryResource when representing
+ * the InternalType.
+ */
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class DirectoryResource {
+
+ /**
+ * The UserContext associated with the Directory being exposed by this
+ * DirectoryResource.
+ */
+ private final UserContext userContext;
+
+ /**
+ * The Directory being exposed by this DirectoryResource.
+ */
+ private final Directory directory;
+
+ /**
+ * A DirectoryObjectTranslator implementation which handles the type of
+ * objects contained within the Directory exposed by this DirectoryResource.
+ */
+ private final DirectoryObjectTranslator translator;
+
+ /**
+ * A factory which can be used to create instances of resources representing
+ * individual objects contained within the Directory exposed by this
+ * DirectoryResource.
+ */
+ private final DirectoryObjectResourceFactory resourceFactory;
+
+ /**
+ * Creates a new DirectoryResource which exposes the operations available
+ * for the given Directory.
+ *
+ * @param userContext
+ * The UserContext associated with the given Directory.
+ *
+ * @param directory
+ * The Directory being exposed by this DirectoryResource.
+ *
+ * @param translator
+ * A DirectoryObjectTranslator implementation which handles the type of
+ * objects contained within the given Directory.
+ *
+ * @param resourceFactory
+ * A factory which can be used to create instances of resources
+ * representing individual objects contained within the given Directory.
+ */
+ @AssistedInject
+ public DirectoryResource(@Assisted UserContext userContext,
+ @Assisted Directory directory,
+ DirectoryObjectTranslator translator,
+ DirectoryObjectResourceFactory resourceFactory) {
+ this.userContext = userContext;
+ this.directory = directory;
+ this.translator = translator;
+ this.resourceFactory = resourceFactory;
+ }
+
+ /**
+ * Returns a map of all objects available within this DirectoryResource,
+ * filtering the returned map by the given permission, if specified.
+ *
+ * @param permissions
+ * The set of permissions to filter with. A user must have one or more
+ * of these permissions for the affected objects to appear in the
+ * result. If null, no filtering will be performed.
+ *
+ * @return
+ * A map of all visible objects. If a permission was specified, this
+ * map will contain only those objects for which the current user has
+ * that permission.
+ *
+ * @throws GuacamoleException
+ * If an error is encountered while retrieving the objects.
+ */
+ @GET
+ public Map getObjects(
+ @QueryParam("permission") List permissions)
+ throws GuacamoleException {
+
+ // An admin user has access to all objects
+ User self = userContext.self();
+ SystemPermissionSet systemPermissions = self.getSystemPermissions();
+ boolean isAdmin = systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER);
+
+ // Filter objects, if requested
+ Collection identifiers = directory.getIdentifiers();
+ if (!isAdmin && permissions != null && !permissions.isEmpty()) {
+ ObjectPermissionSet objectPermissions = self.getUserPermissions();
+ identifiers = objectPermissions.getAccessibleObjects(permissions, identifiers);
+ }
+
+ // Translate each retrieved object into the corresponding external object
+ Map apiObjects = new HashMap();
+ for (InternalType object : directory.getAll(identifiers))
+ apiObjects.put(object.getIdentifier(), translator.toExternalObject(object));
+
+ return apiObjects;
+
+ }
+
+ /**
+ * Applies the given object patches, updating the underlying directory
+ * accordingly. This operation currently only supports deletion of objects
+ * through the "remove" patch operation. The path of each patch operation is
+ * of the form "/ID" where ID is the identifier of the object being
+ * modified.
+ *
+ * @param patches
+ * The patches to apply for this request.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while deleting the objects.
+ */
+ @PATCH
+ public void patchObjects(List> patches)
+ throws GuacamoleException {
+
+ // Apply each operation specified within the patch
+ for (APIPatch patch : patches) {
+
+ // Only remove is supported
+ if (patch.getOp() != APIPatch.Operation.remove)
+ throw new GuacamoleUnsupportedException("Only the \"remove\" "
+ + "operation is supported.");
+
+ // Retrieve and validate path
+ String path = patch.getPath();
+ if (!path.startsWith("/"))
+ throw new GuacamoleClientException("Patch paths must start with \"/\".");
+
+ // Remove specified object
+ directory.remove(path.substring(1));
+
+ }
+
+ }
+
+ /**
+ * Creates a new object within the underlying Directory, returning the
+ * object that was created. The identifier of the created object will be
+ * populated, if applicable.
+ *
+ * @param object
+ * The object to create.
+ *
+ * @return
+ * The object that was created.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while adding the object to the underlying
+ * directory.
+ */
+ @POST
+ public ExternalType createObject(ExternalType object)
+ throws GuacamoleException {
+
+ // Validate that data was provided
+ if (object == null)
+ throw new GuacamoleClientException("Data must be submitted when creating objects.");
+
+ // Create the new object within the directory
+ directory.add(translator.toInternalObject(object));
+
+ return object;
+
+ }
+
+ /**
+ * Retrieves an individual object, returning a DirectoryObjectResource
+ * implementation which exposes operations available on that object.
+ *
+ * @param identifier
+ * The identifier of the object to retrieve.
+ *
+ * @return
+ * A DirectoryObjectResource exposing operations available on the
+ * object having the given identifier.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the object.
+ */
+ @Path("{identifier}")
+ public DirectoryObjectResource
+ getObjectResource(@PathParam("identifier") String identifier)
+ throws GuacamoleException {
+
+ // Retrieve the object having the given identifier
+ InternalType object = directory.get(identifier);
+ if (object == null)
+ throw new GuacamoleResourceNotFoundException("Not found: \"" + identifier + "\"");
+
+ // Return a resource which provides access to the retrieved object
+ return resourceFactory.create(userContext, directory, object);
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResourceFactory.java b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResourceFactory.java
new file mode 100644
index 000000000..357777a6f
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResourceFactory.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.rest.directory;
+
+import org.apache.guacamole.net.auth.Directory;
+import org.apache.guacamole.net.auth.Identifiable;
+import org.apache.guacamole.net.auth.UserContext;
+
+/**
+ * Factory which creates DirectoryResource instances exposing Directory
+ * objects of a particular type.
+ *
+ * @param
+ * The type of object contained within the Directory objects exposed by the
+ * DirectoryResource instances created by this DirectoryResourceFactory.
+ *
+ * @param
+ * The type of object used in interchange (ie: serialized or deserialized
+ * as JSON) between REST clients and resources when representing the
+ * InternalType.
+ */
+public interface DirectoryResourceFactory {
+
+ /**
+ * Creates a new DirectoryResource which exposes the given Directory.
+ *
+ * @param userContext
+ * The UserContext from which the given Directory was obtained.
+ *
+ * @param directory
+ * The Directory which should be exposed by the created
+ * DirectoryResource.
+ *
+ * @return
+ * A new DirectoryResource which exposes the given Directory.
+ */
+ DirectoryResource
+ create(UserContext userContext, Directory directory);
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/directory/package-info.java b/guacamole/src/main/java/org/apache/guacamole/rest/directory/package-info.java
new file mode 100644
index 000000000..5283e8719
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/directory/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Base classes providing support for exposing and manipulating Directory
+ * objects and their contents via the Guacamole REST API.
+ */
+package org.apache.guacamole.rest.directory;
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryResource.java
similarity index 71%
rename from guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryRESTService.java
rename to guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryResource.java
index fd3738ed1..d71d44555 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryRESTService.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryResource.java
@@ -19,13 +19,11 @@
package org.apache.guacamole.rest.history;
-import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.Consumes;
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.core.MediaType;
@@ -33,27 +31,16 @@ import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.ConnectionRecord;
import org.apache.guacamole.net.auth.ConnectionRecordSet;
import org.apache.guacamole.net.auth.UserContext;
-import org.apache.guacamole.GuacamoleSession;
-import org.apache.guacamole.rest.ObjectRetrievalService;
-import org.apache.guacamole.rest.auth.AuthenticationService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
- * A REST Service for retrieving and managing the history records of Guacamole
+ * A REST resource for retrieving and managing the history records of Guacamole
* objects.
*
* @author Michael Jumper
*/
-@Path("/data/{dataSource}/history")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
-public class HistoryRESTService {
-
- /**
- * Logger for this class.
- */
- private static final Logger logger = LoggerFactory.getLogger(HistoryRESTService.class);
+public class HistoryResource {
/**
* The maximum number of history records to return in any one response.
@@ -61,30 +48,25 @@ public class HistoryRESTService {
private static final int MAXIMUM_HISTORY_SIZE = 1000;
/**
- * A service for authenticating users from auth tokens.
+ * The UserContext whose associated connection history is being exposed.
*/
- @Inject
- private AuthenticationService authenticationService;
+ private final UserContext userContext;
/**
- * Service for convenient retrieval of objects.
+ * Creates a new HistoryResource which exposes the connection history
+ * associated with the given UserContext.
+ *
+ * @param userContext
+ * The UserContext whose connection history should be exposed.
*/
- @Inject
- private ObjectRetrievalService retrievalService;
+ public HistoryResource(UserContext userContext) {
+ this.userContext = userContext;
+ }
/**
* Retrieves the usage history for all connections, restricted by optional
* filter parameters.
*
- * @param authToken
- * The authentication token that is used to authenticate the user
- * performing the operation.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider associated with
- * the UserContext containing the connection whose history is to be
- * retrieved.
- *
* @param requiredContents
* The set of strings that each must occur somewhere within the
* returned connection records, whether within the associated username,
@@ -105,16 +87,12 @@ public class HistoryRESTService {
* If an error occurs while retrieving the connection history.
*/
@GET
- @Path("/connections")
- public List getConnectionHistory(@QueryParam("token") String authToken,
- @PathParam("dataSource") String authProviderIdentifier,
+ @Path("connections")
+ public List getConnectionHistory(
@QueryParam("contains") List requiredContents,
@QueryParam("order") List sortPredicates)
throws GuacamoleException {
- GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
- UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
-
// Retrieve overall connection history
ConnectionRecordSet history = userContext.getConnectionHistory();
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/user/PermissionSetPatch.java b/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetPatch.java
similarity index 98%
rename from guacamole/src/main/java/org/apache/guacamole/rest/user/PermissionSetPatch.java
rename to guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetPatch.java
index 653f424b4..b08d2ba3f 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/user/PermissionSetPatch.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetPatch.java
@@ -17,7 +17,7 @@
* under the License.
*/
-package org.apache.guacamole.rest.user;
+package org.apache.guacamole.rest.permission;
import java.util.HashSet;
import java.util.Set;
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java
new file mode 100644
index 000000000..e75460ccc
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java
@@ -0,0 +1,261 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.rest.permission;
+
+import java.util.List;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import org.apache.guacamole.GuacamoleClientException;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.User;
+import org.apache.guacamole.net.auth.permission.ObjectPermission;
+import org.apache.guacamole.net.auth.permission.Permission;
+import org.apache.guacamole.net.auth.permission.SystemPermission;
+import org.apache.guacamole.rest.APIPatch;
+import org.apache.guacamole.rest.PATCH;
+
+/**
+ * A REST resource which abstracts the operations available on the permissions
+ * granted to an existing User.
+ *
+ * @author Michael Jumper
+ */
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class PermissionSetResource {
+
+ /**
+ * The prefix of any path within an operation of a JSON patch which
+ * modifies the permissions of a user regarding a specific connection.
+ */
+ private static final String CONNECTION_PERMISSION_PATCH_PATH_PREFIX = "/connectionPermissions/";
+
+ /**
+ * The prefix of any path within an operation of a JSON patch which
+ * modifies the permissions of a user regarding a specific connection group.
+ */
+ private static final String CONNECTION_GROUP_PERMISSION_PATCH_PATH_PREFIX = "/connectionGroupPermissions/";
+
+ /**
+ * The prefix of any path within an operation of a JSON patch which
+ * modifies the permissions of a user regarding a specific active connection.
+ */
+ private static final String ACTIVE_CONNECTION_PERMISSION_PATCH_PATH_PREFIX = "/activeConnectionPermissions/";
+
+ /**
+ * The prefix of any path within an operation of a JSON patch which
+ * modifies the permissions of a user regarding another, specific user.
+ */
+ private static final String USER_PERMISSION_PATCH_PATH_PREFIX = "/userPermissions/";
+
+ /**
+ * The path of any operation within a JSON patch which modifies the
+ * permissions of a user regarding the entire system.
+ */
+ private static final String SYSTEM_PERMISSION_PATCH_PATH = "/systemPermissions";
+
+ /**
+ * The User object whose permissions are represented by this
+ * PermissionSetResource.
+ */
+ private final User user;
+
+ /**
+ * Creates a new PermissionSetResource which exposes the operations and
+ * subresources available for permissions of the given User.
+ *
+ * @param user
+ * The User whose permissions should be represented by this
+ * PermissionSetResource.
+ */
+ public PermissionSetResource(User user) {
+ this.user = user;
+ }
+
+ /**
+ * Gets a list of permissions for the user with the given username.
+ *
+ * @return
+ * A list of all permissions granted to the specified user.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving permissions.
+ */
+ @GET
+ public APIPermissionSet getPermissions() throws GuacamoleException {
+ return new APIPermissionSet(user);
+ }
+
+ /**
+ * Updates the given permission set patch by queuing an add or remove
+ * operation for the given permission based on the given patch operation.
+ *
+ * @param
+ * The type of permission stored within the permission set.
+ *
+ * @param operation
+ * The patch operation to perform.
+ *
+ * @param permissionSetPatch
+ * The permission set patch being modified.
+ *
+ * @param permission
+ * The permission being added or removed from the set.
+ *
+ * @throws GuacamoleException
+ * If the requested patch operation is not supported.
+ */
+ private void updatePermissionSet(
+ APIPatch.Operation operation,
+ PermissionSetPatch permissionSetPatch,
+ PermissionType permission) throws GuacamoleException {
+
+ // Add or remove permission based on operation
+ switch (operation) {
+
+ // Add permission
+ case add:
+ permissionSetPatch.addPermission(permission);
+ break;
+
+ // Remove permission
+ case remove:
+ permissionSetPatch.removePermission(permission);
+ break;
+
+ // Unsupported patch operation
+ default:
+ throw new GuacamoleClientException("Unsupported patch operation: \"" + operation + "\"");
+
+ }
+
+ }
+
+ /**
+ * Applies a given list of permission patches. Each patch specifies either
+ * an "add" or a "remove" operation for a permission type, represented by
+ * a string. Valid permission types depend on the path of each patch
+ * operation, as the path dictates the permission being modified, such as
+ * "/connectionPermissions/42" or "/systemPermissions".
+ *
+ * @param patches
+ * The permission patches to apply for this request.
+ *
+ * @throws GuacamoleException
+ * If a problem is encountered while modifying permissions.
+ */
+ @PATCH
+ public void patchPermissions(List> patches)
+ throws GuacamoleException {
+
+ // Permission patches for all types of permissions
+ PermissionSetPatch connectionPermissionPatch = new PermissionSetPatch();
+ PermissionSetPatch connectionGroupPermissionPatch = new PermissionSetPatch();
+ PermissionSetPatch activeConnectionPermissionPatch = new PermissionSetPatch();
+ PermissionSetPatch userPermissionPatch = new PermissionSetPatch();
+ PermissionSetPatch systemPermissionPatch = new PermissionSetPatch();
+
+ // Apply all patch operations individually
+ for (APIPatch patch : patches) {
+
+ String path = patch.getPath();
+
+ // Create connection permission if path has connection prefix
+ if (path.startsWith(CONNECTION_PERMISSION_PATCH_PATH_PREFIX)) {
+
+ // Get identifier and type from patch operation
+ String identifier = path.substring(CONNECTION_PERMISSION_PATCH_PATH_PREFIX.length());
+ ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue());
+
+ // Create and update corresponding permission
+ ObjectPermission permission = new ObjectPermission(type, identifier);
+ updatePermissionSet(patch.getOp(), connectionPermissionPatch, permission);
+
+ }
+
+ // Create connection group permission if path has connection group prefix
+ else if (path.startsWith(CONNECTION_GROUP_PERMISSION_PATCH_PATH_PREFIX)) {
+
+ // Get identifier and type from patch operation
+ String identifier = path.substring(CONNECTION_GROUP_PERMISSION_PATCH_PATH_PREFIX.length());
+ ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue());
+
+ // Create and update corresponding permission
+ ObjectPermission permission = new ObjectPermission(type, identifier);
+ updatePermissionSet(patch.getOp(), connectionGroupPermissionPatch, permission);
+
+ }
+
+ // Create active connection permission if path has active connection prefix
+ else if (path.startsWith(ACTIVE_CONNECTION_PERMISSION_PATCH_PATH_PREFIX)) {
+
+ // Get identifier and type from patch operation
+ String identifier = path.substring(ACTIVE_CONNECTION_PERMISSION_PATCH_PATH_PREFIX.length());
+ ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue());
+
+ // Create and update corresponding permission
+ ObjectPermission permission = new ObjectPermission(type, identifier);
+ updatePermissionSet(patch.getOp(), activeConnectionPermissionPatch, permission);
+
+ }
+
+ // Create user permission if path has user prefix
+ else if (path.startsWith(USER_PERMISSION_PATCH_PATH_PREFIX)) {
+
+ // Get identifier and type from patch operation
+ String identifier = path.substring(USER_PERMISSION_PATCH_PATH_PREFIX.length());
+ ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue());
+
+ // Create and update corresponding permission
+ ObjectPermission permission = new ObjectPermission(type, identifier);
+ updatePermissionSet(patch.getOp(), userPermissionPatch, permission);
+
+ }
+
+ // Create system permission if path is system path
+ else if (path.equals(SYSTEM_PERMISSION_PATCH_PATH)) {
+
+ // Get identifier and type from patch operation
+ SystemPermission.Type type = SystemPermission.Type.valueOf(patch.getValue());
+
+ // Create and update corresponding permission
+ SystemPermission permission = new SystemPermission(type);
+ updatePermissionSet(patch.getOp(), systemPermissionPatch, permission);
+
+ }
+
+ // Otherwise, the path is not supported
+ else
+ throw new GuacamoleClientException("Unsupported patch path: \"" + path + "\"");
+
+ } // end for each patch operation
+
+ // Save the permission changes
+ connectionPermissionPatch.apply(user.getConnectionPermissions());
+ connectionGroupPermissionPatch.apply(user.getConnectionGroupPermissions());
+ activeConnectionPermissionPatch.apply(user.getActiveConnectionPermissions());
+ userPermissionPatch.apply(user.getUserPermissions());
+ systemPermissionPatch.apply(user.getSystemPermissions());
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/schema/SchemaRESTService.java b/guacamole/src/main/java/org/apache/guacamole/rest/schema/SchemaRESTService.java
deleted file mode 100644
index a5d9d909b..000000000
--- a/guacamole/src/main/java/org/apache/guacamole/rest/schema/SchemaRESTService.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.guacamole.rest.schema;
-
-import com.google.inject.Inject;
-import java.util.Collection;
-import java.util.Map;
-import javax.ws.rs.Consumes;
-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.core.MediaType;
-import org.apache.guacamole.GuacamoleException;
-import org.apache.guacamole.environment.Environment;
-import org.apache.guacamole.environment.LocalEnvironment;
-import org.apache.guacamole.form.Form;
-import org.apache.guacamole.net.auth.UserContext;
-import org.apache.guacamole.GuacamoleSession;
-import org.apache.guacamole.rest.ObjectRetrievalService;
-import org.apache.guacamole.rest.auth.AuthenticationService;
-import org.apache.guacamole.protocols.ProtocolInfo;
-
-/**
- * A REST service which provides access to descriptions of the properties,
- * attributes, etc. of objects used within the Guacamole REST API.
- *
- * @author Michael Jumper
- */
-@Path("/schema/{dataSource}")
-@Produces(MediaType.APPLICATION_JSON)
-@Consumes(MediaType.APPLICATION_JSON)
-public class SchemaRESTService {
-
- /**
- * A service for authenticating users from auth tokens.
- */
- @Inject
- private AuthenticationService authenticationService;
-
- /**
- * Service for convenient retrieval of objects.
- */
- @Inject
- private ObjectRetrievalService retrievalService;
-
- /**
- * Retrieves the possible attributes of a user object.
- *
- * @param authToken
- * The authentication token that is used to authenticate the user
- * performing the operation.
- *
- * @param authProviderIdentifier
- * The unique identifier of the AuthenticationProvider associated with
- * the UserContext dictating the available user attributes.
- *
- * @return
- * A collection of forms which describe the possible attributes of a
- * user object.
- *
- * @throws GuacamoleException
- * If an error occurs while retrieving the possible attributes.
- */
- @GET
- @Path("/users/attributes")
- public Collection