GUACAMOLE-5: Merge REST resource refactor.

This commit is contained in:
James Muehlner
2016-07-12 22:07:06 -07:00
57 changed files with 3307 additions and 2241 deletions

View File

@@ -290,6 +290,11 @@
<artifactId>guice</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-assistedinject</artifactId>
<version>3.0</version>
</dependency>
<!-- Guice Servlet -->
<dependency>

View File

@@ -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.

View File

@@ -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());
}
/**

View File

@@ -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<UserContext> 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<User> 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<Connection> 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<ConnectionGroup> 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);
}
}

View File

@@ -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<Method> {
/**
* Returns whether the given method is annotated as a REST method. A REST
* method is annotated with an annotation which is annotated with
* <code>@HttpMethod</code>.
* <code>@HttpMethod</code> or <code>@Path</code>.
*
* @param method
* The method to test.
@@ -86,9 +87,32 @@ public class RESTMethodMatcher extends AbstractMatcher<Method> {
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;
}

View File

@@ -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);

View File

@@ -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<DirectoryResourceFactory<ActiveConnection, APIActiveConnection>>() {}));
// Create the required DirectoryObjectResourceFactory implementation
install(new FactoryModuleBuilder()
.implement(
new TypeLiteral<DirectoryObjectResource<ActiveConnection, APIActiveConnection>>() {},
ActiveConnectionResource.class
)
.build(new TypeLiteral<DirectoryObjectResourceFactory<ActiveConnection, APIActiveConnection>>() {}));
// Bind translator for converting between ActiveConnection and APIActiveConnection
bind(new TypeLiteral<DirectoryObjectTranslator<ActiveConnection, APIActiveConnection>>() {})
.to(ActiveConnectionObjectTranslator.class);
}
}

View File

@@ -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<ActiveConnection, APIActiveConnection> {
@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.");
}
}

View File

@@ -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<String, APIActiveConnection> getActiveConnections(@QueryParam("token") String authToken,
@PathParam("dataSource") String authProviderIdentifier,
@QueryParam("permission") List<ObjectPermission.Type> 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<ActiveConnection> activeConnectionDirectory = userContext.getActiveConnectionDirectory();
// Filter connections, if requested
Collection<String> 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<String, APIActiveConnection> apiActiveConnections = new HashMap<String, APIActiveConnection>();
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<APIPatch<String>> patches) throws GuacamoleException {
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
// Get the directory
Directory<ActiveConnection> activeConnectionDirectory = userContext.getActiveConnectionDirectory();
// Close each connection listed for removal
for (APIPatch<String> 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));
}
}
}

View File

@@ -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<ActiveConnection, APIActiveConnection> {
/**
* 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<ActiveConnection> directory,
@Assisted ActiveConnection connection,
DirectoryObjectTranslator<ActiveConnection, APIActiveConnection> translator) {
super(directory, connection, translator);
}
}

View File

@@ -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;

View File

@@ -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<DirectoryResourceFactory<Connection, APIConnection>>() {}));
// Create the required DirectoryObjectResourceFactory implementation
install(new FactoryModuleBuilder()
.implement(
new TypeLiteral<DirectoryObjectResource<Connection, APIConnection>>() {},
ConnectionResource.class
)
.build(new TypeLiteral<DirectoryObjectResourceFactory<Connection, APIConnection>>() {}));
// Bind translator for converting between Connection and APIConnection
bind(new TypeLiteral<DirectoryObjectTranslator<Connection, APIConnection>>() {})
.to(ConnectionObjectTranslator.class);
}
}

View File

@@ -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<Connection, APIConnection> {
@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());
}
}

View File

@@ -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<String, String> 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<APIConnectionRecord> 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<APIConnectionRecord> apiRecords = new ArrayList<APIConnectionRecord>();
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<Connection> 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<Connection> 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<Connection> 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);
}
}

View File

@@ -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<Connection, APIConnection> {
/**
* 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<Connection> directory,
@Assisted Connection connection,
DirectoryObjectTranslator<Connection, APIConnection> 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<String, String> 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<APIConnectionRecord> getConnectionHistory()
throws GuacamoleException {
// Retrieve the requested connection's history
List<APIConnectionRecord> apiRecords = new ArrayList<APIConnectionRecord>();
for (ConnectionRecord record : connection.getHistory())
apiRecords.add(new APIConnectionRecord(record));
// Return the converted history
return apiRecords;
}
}

View File

@@ -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<ConnectionGroup, APIConnectionGroup> {
/**
* 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<ConnectionGroup> directory;
/**
* A factory which can be used to create instances of resources representing
* ConnectionGroups.
*/
private final DirectoryObjectResourceFactory<ConnectionGroup, APIConnectionGroup> 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<ConnectionGroup> directory,
DirectoryObjectTranslator<ConnectionGroup, APIConnectionGroup> translator,
DirectoryObjectResourceFactory<ConnectionGroup, APIConnectionGroup> resourceFactory) {
super(userContext, directory, translator, resourceFactory);
this.userContext = userContext;
this.directory = directory;
this.resourceFactory = resourceFactory;
}
@Override
public DirectoryObjectResource<ConnectionGroup, APIConnectionGroup>
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);
}
}

View File

@@ -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<DirectoryResource<ConnectionGroup, APIConnectionGroup>>() {},
ConnectionGroupDirectoryResource.class
)
.build(new TypeLiteral<DirectoryResourceFactory<ConnectionGroup, APIConnectionGroup>>() {}));
// Create the required DirectoryObjectResourceFactory implementation
install(new FactoryModuleBuilder()
.implement(
new TypeLiteral<DirectoryObjectResource<ConnectionGroup, APIConnectionGroup>>() {},
ConnectionGroupResource.class
)
.build(new TypeLiteral<DirectoryObjectResourceFactory<ConnectionGroup, APIConnectionGroup>>() {}));
// Bind translator for converting between ConnectionGroup and APIConnectionGroup
bind(new TypeLiteral<DirectoryObjectTranslator<ConnectionGroup, APIConnectionGroup>>() {})
.to(ConnectionGroupObjectTranslator.class);
}
}

View File

@@ -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<ConnectionGroup, APIConnectionGroup> {
@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());
}
}

View File

@@ -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<ObjectPermission.Type> 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<ConnectionGroup> 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<ConnectionGroup> 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<ConnectionGroup> 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);
}
}

View File

@@ -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<ConnectionGroup, APIConnectionGroup> {
/**
* 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<ConnectionGroup> directory,
@Assisted ConnectionGroup connectionGroup,
DirectoryObjectTranslator<ConnectionGroup, APIConnectionGroup> 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<ObjectPermission.Type> 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();
}
}

View File

@@ -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 <InternalType>
* 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 <ExternalType>
* 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<InternalType extends Identifiable, ExternalType> {
/**
* The Directory which contains the object represented by this
* DirectoryObjectResource.
*/
private final Directory<InternalType> 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<InternalType, ExternalType> 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<InternalType> directory,
@Assisted InternalType object,
DirectoryObjectTranslator<InternalType, ExternalType> 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());
}
}

View File

@@ -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 <InternalType>
* The type of object exposed by the DirectoryObjectResource instances
* created by this DirectoryResourceFactory.
*
* @param <ExternalType>
* 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<InternalType extends Identifiable, ExternalType> {
/**
* 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<InternalType, ExternalType>
create(UserContext userContext, Directory<InternalType> directory,
InternalType object);
}

View File

@@ -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 <InternalType>
* The type of object converted by this DirectoryObjectTranslator which is
* not necessarily intended for use in interchange.
*
* @param <ExternalType>
* 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<InternalType extends Identifiable, ExternalType> {
/**
* 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;
}

View File

@@ -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 <InternalType>
* 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 <ExternalType>
* 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<InternalType extends Identifiable, ExternalType> {
/**
* 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<InternalType> directory;
/**
* A DirectoryObjectTranslator implementation which handles the type of
* objects contained within the Directory exposed by this DirectoryResource.
*/
private final DirectoryObjectTranslator<InternalType, ExternalType> 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<InternalType, ExternalType> 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<InternalType> directory,
DirectoryObjectTranslator<InternalType, ExternalType> translator,
DirectoryObjectResourceFactory<InternalType, ExternalType> 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<String, ExternalType> getObjects(
@QueryParam("permission") List<ObjectPermission.Type> 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<String> 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<String, ExternalType> apiObjects = new HashMap<String, ExternalType>();
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<APIPatch<String>> patches)
throws GuacamoleException {
// Apply each operation specified within the patch
for (APIPatch<String> 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<InternalType, ExternalType>
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);
}
}

View File

@@ -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 <InternalType>
* The type of object contained within the Directory objects exposed by the
* DirectoryResource instances created by this DirectoryResourceFactory.
*
* @param <ExternalType>
* 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<InternalType extends Identifiable, ExternalType> {
/**
* 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<InternalType, ExternalType>
create(UserContext userContext, Directory<InternalType> directory);
}

View File

@@ -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;

View File

@@ -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<APIConnectionRecord> getConnectionHistory(@QueryParam("token") String authToken,
@PathParam("dataSource") String authProviderIdentifier,
@Path("connections")
public List<APIConnectionRecord> getConnectionHistory(
@QueryParam("contains") List<String> requiredContents,
@QueryParam("order") List<APIConnectionRecordSortPredicate> sortPredicates)
throws GuacamoleException {
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
// Retrieve overall connection history
ConnectionRecordSet history = userContext.getConnectionHistory();

View File

@@ -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;

View File

@@ -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 <PermissionType>
* 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 <PermissionType extends Permission> void updatePermissionSet(
APIPatch.Operation operation,
PermissionSetPatch<PermissionType> 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<APIPatch<String>> patches)
throws GuacamoleException {
// Permission patches for all types of permissions
PermissionSetPatch<ObjectPermission> connectionPermissionPatch = new PermissionSetPatch<ObjectPermission>();
PermissionSetPatch<ObjectPermission> connectionGroupPermissionPatch = new PermissionSetPatch<ObjectPermission>();
PermissionSetPatch<ObjectPermission> activeConnectionPermissionPatch = new PermissionSetPatch<ObjectPermission>();
PermissionSetPatch<ObjectPermission> userPermissionPatch = new PermissionSetPatch<ObjectPermission>();
PermissionSetPatch<SystemPermission> systemPermissionPatch = new PermissionSetPatch<SystemPermission>();
// Apply all patch operations individually
for (APIPatch<String> 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());
}
}

View File

@@ -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<Form> getUserAttributes(@QueryParam("token") String authToken,
@PathParam("dataSource") String authProviderIdentifier)
throws GuacamoleException {
// Retrieve all possible user attributes
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
return userContext.getUserAttributes();
}
/**
* Retrieves the possible attributes of a connection 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 connection attributes.
*
* @return
* A collection of forms which describe the possible attributes of a
* connection object.
*
* @throws GuacamoleException
* If an error occurs while retrieving the possible attributes.
*/
@GET
@Path("/connections/attributes")
public Collection<Form> getConnectionAttributes(@QueryParam("token") String authToken,
@PathParam("dataSource") String authProviderIdentifier)
throws GuacamoleException {
// Retrieve all possible connection attributes
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
return userContext.getConnectionAttributes();
}
/**
* Retrieves the possible attributes of a connection group 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 connection group
* attributes.
*
* @return
* A collection of forms which describe the possible attributes of a
* connection group object.
*
* @throws GuacamoleException
* If an error occurs while retrieving the possible attributes.
*/
@GET
@Path("/connectionGroups/attributes")
public Collection<Form> getConnectionGroupAttributes(@QueryParam("token") String authToken,
@PathParam("dataSource") String authProviderIdentifier)
throws GuacamoleException {
// Retrieve all possible connection group attributes
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
return userContext.getConnectionGroupAttributes();
}
/**
* Gets a map of protocols defined in the system - protocol name to protocol.
*
* @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 protocols available. Currently, the
* UserContext actually does not dictate this, the the same set of
* protocols will be retrieved for all users, though the identifier
* given here will be validated.
*
* @return
* A map of protocol information, where each key is the unique name
* associated with that protocol.
*
* @throws GuacamoleException
* If an error occurs while retrieving the available protocols.
*/
@GET
@Path("/protocols")
public Map<String, ProtocolInfo> getProtocols(@QueryParam("token") String authToken,
@PathParam("dataSource") String authProviderIdentifier)
throws GuacamoleException {
// Verify the given auth token and auth provider identifier are valid
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
retrievalService.retrieveUserContext(session, authProviderIdentifier);
// Get and return a map of all protocols.
Environment env = new LocalEnvironment();
return env.getProtocols();
}
}

View File

@@ -0,0 +1,142 @@
/*
* 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 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.Produces;
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.protocols.ProtocolInfo;
/**
* A REST resource which provides access to descriptions of the properties,
* attributes, etc. of objects within a particular UserContext.
*
* @author Michael Jumper
*/
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class SchemaResource {
/**
* The UserContext whose schema is exposed by this SchemaResource.
*/
private final UserContext userContext;
/**
* Creates a new SchemaResource which exposes meta information describing
* the kind of data within the given UserContext.
*
* @param userContext
* The UserContext whose schema should be exposed by this
* SchemaResource.
*/
public SchemaResource(UserContext userContext) {
this.userContext = userContext;
}
/**
* Retrieves the possible attributes of a user object.
*
* @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("userAttributes")
public Collection<Form> getUserAttributes() throws GuacamoleException {
// Retrieve all possible user attributes
return userContext.getUserAttributes();
}
/**
* Retrieves the possible attributes of a connection object.
*
* @return
* A collection of forms which describe the possible attributes of a
* connection object.
*
* @throws GuacamoleException
* If an error occurs while retrieving the possible attributes.
*/
@GET
@Path("connectionAttributes")
public Collection<Form> getConnectionAttributes()
throws GuacamoleException {
// Retrieve all possible connection attributes
return userContext.getConnectionAttributes();
}
/**
* Retrieves the possible attributes of a connection group object.
*
* @return
* A collection of forms which describe the possible attributes of a
* connection group object.
*
* @throws GuacamoleException
* If an error occurs while retrieving the possible attributes.
*/
@GET
@Path("connectionGroupAttributes")
public Collection<Form> getConnectionGroupAttributes()
throws GuacamoleException {
// Retrieve all possible connection group attributes
return userContext.getConnectionGroupAttributes();
}
/**
* Gets a map of protocols defined in the system - protocol name to protocol.
*
* @return
* A map of protocol information, where each key is the unique name
* associated with that protocol.
*
* @throws GuacamoleException
* If an error occurs while retrieving the available protocols.
*/
@GET
@Path("protocols")
public Map<String, ProtocolInfo> getProtocols() throws GuacamoleException {
// Get and return a map of all protocols.
Environment env = new LocalEnvironment();
return env.getProtocols();
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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.session;
import com.google.inject.Inject;
import javax.ws.rs.Consumes;
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.GuacamoleSession;
import org.apache.guacamole.rest.auth.AuthenticationService;
/**
* A REST service which exposes all data associated with Guacamole users'
* sessions.
*
* @author Michael Jumper
*/
@Path("/session")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class SessionRESTService {
/**
* A service for authenticating users from auth tokens.
*/
@Inject
private AuthenticationService authenticationService;
/**
* Factory for creating SessionResources which expose a given
* GuacamoleSession.
*/
@Inject
private SessionResourceFactory sessionResourceFactory;
/**
* Retrieves a resource representing the GuacamoleSession associated with
* the given authentication token.
*
* @param authToken
* The authentication token that is used to authenticate the user
* having the session being exposed.
*
* @return
* A resource representing the GuacamoleSession associated with the
* given authentication token.
*
* @throws GuacamoleException
* If the authentication token is invalid.
*/
@Path("/")
public SessionResource getSessionResource(@QueryParam("token") String authToken)
throws GuacamoleException {
// Return a resource exposing the retrieved session
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
return sessionResourceFactory.create(session);
}
}

View File

@@ -0,0 +1,111 @@
/*
* 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.session;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import javax.ws.rs.Consumes;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSession;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.rest.tunnel.TunnelCollectionResource;
/**
* A REST resource which exposes all data associated with a Guacamole user's
* session via the underlying UserContexts.
*
* @author Michael Jumper
*/
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class SessionResource {
/**
* The GuacamoleSession being exposed by this SessionResource.
*/
private final GuacamoleSession session;
/**
* Factory for creating UserContextResources which expose a given
* UserContext.
*/
@Inject
private UserContextResourceFactory userContextResourceFactory;
/**
* Creates a new SessionResource which exposes the data within the given
* GuacamoleSession.
*
* @param session
* The GuacamoleSession which should be exposed through this
* SessionResource.
*/
@AssistedInject
public SessionResource(@Assisted GuacamoleSession session) {
this.session = session;
}
/**
* Retrieves a resource representing the UserContext associated with the
* AuthenticationProvider having the given identifier.
*
* @param authProviderIdentifier
* The unique identifier of the AuthenticationProvider associated with
* the UserContext being retrieved.
*
* @return
* A resource representing the UserContext associated with the
* AuthenticationProvider having the given identifier.
*
* @throws GuacamoleException
* If the AuthenticationProvider identifier is invalid.
*/
@Path("data/{dataSource}")
public UserContextResource getUserContextResource(
@PathParam("dataSource") String authProviderIdentifier)
throws GuacamoleException {
// Pull UserContext defined by the given auth provider identifier
UserContext userContext = session.getUserContext(authProviderIdentifier);
// Return a resource exposing the retrieved UserContext
return userContextResourceFactory.create(userContext);
}
/**
* Retrieves a resource representing all tunnels associated with session
* exposed by this SessionResource.
*
* @return
* A resource representing all tunnels associated with the
* AuthenticationProvider having the given identifier.
*/
@Path("tunnels")
public TunnelCollectionResource getTunnelCollectionResource() {
return new TunnelCollectionResource(session);
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.session;
import org.apache.guacamole.GuacamoleSession;
/**
* Factory which creates resources that expose the contents of a given
* GuacamoleSession.
*
* @author Michael Jumper
*/
public interface SessionResourceFactory {
/**
* Creates a new SessionResource which exposes the contents of the
* given GuacamoleSession.
*
* @param session
* The GuacamoleSession whose contents should be exposed.
*
* @return
* A new SessionResource which exposes the contents of the given
* GuacamoleSession.
*/
SessionResource create(GuacamoleSession session);
}

View File

@@ -0,0 +1,202 @@
/*
* 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.session;
import org.apache.guacamole.rest.directory.DirectoryResource;
import org.apache.guacamole.rest.directory.DirectoryResourceFactory;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import javax.ws.rs.Consumes;
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.net.auth.ActiveConnection;
import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.ConnectionGroup;
import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.rest.activeconnection.APIActiveConnection;
import org.apache.guacamole.rest.connection.APIConnection;
import org.apache.guacamole.rest.connectiongroup.APIConnectionGroup;
import org.apache.guacamole.rest.history.HistoryResource;
import org.apache.guacamole.rest.schema.SchemaResource;
import org.apache.guacamole.rest.user.APIUser;
/**
* A REST resource which exposes the contents of a particular UserContext.
*
* @author Michael Jumper
*/
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserContextResource {
/**
* The UserContext being exposed through this resource.
*/
private final UserContext userContext;
/**
* Factory for creating DirectoryResources which expose a given
* ActiveConnection Directory.
*/
@Inject
private DirectoryResourceFactory<ActiveConnection, APIActiveConnection>
activeConnectionDirectoryResourceFactory;
/**
* Factory for creating DirectoryResources which expose a given
* Connection Directory.
*/
@Inject
private DirectoryResourceFactory<Connection, APIConnection>
connectionDirectoryResourceFactory;
/**
* Factory for creating DirectoryResources which expose a given
* ConnectionGroup Directory.
*/
@Inject
private DirectoryResourceFactory<ConnectionGroup, APIConnectionGroup>
connectionGroupDirectoryResourceFactory;
/**
* Factory for creating DirectoryResources which expose a given
* User Directory.
*/
@Inject
private DirectoryResourceFactory<User, APIUser> userDirectoryResourceFactory;
/**
* Creates a new UserContextResource which exposes the data within the
* given UserContext.
*
* @param userContext
* The UserContext which should be exposed through this
* UserContextResource.
*/
@AssistedInject
public UserContextResource(@Assisted UserContext userContext) {
this.userContext = userContext;
}
/**
* Returns a new resource which represents the ActiveConnection Directory
* contained within the UserContext exposed by this UserContextResource.
*
* @return
* A new resource which represents the ActiveConnection Directory
* contained within the UserContext exposed by this UserContextResource.
*
* @throws GuacamoleException
* If an error occurs while retrieving the ActiveConnection Directory.
*/
@Path("activeConnections")
public DirectoryResource<ActiveConnection, APIActiveConnection>
getActiveConnectionDirectoryResource() throws GuacamoleException {
return activeConnectionDirectoryResourceFactory.create(userContext,
userContext.getActiveConnectionDirectory());
}
/**
* Returns a new resource which represents the Connection Directory
* contained within the UserContext exposed by this UserContextResource.
*
* @return
* A new resource which represents the Connection Directory contained
* within the UserContext exposed by this UserContextResource.
*
* @throws GuacamoleException
* If an error occurs while retrieving the Connection Directory.
*/
@Path("connections")
public DirectoryResource<Connection, APIConnection> getConnectionDirectoryResource()
throws GuacamoleException {
return connectionDirectoryResourceFactory.create(userContext,
userContext.getConnectionDirectory());
}
/**
* Returns a new resource which represents the ConnectionGroup Directory
* contained within the UserContext exposed by this UserContextResource.
*
* @return
* A new resource which represents the ConnectionGroup Directory
* contained within the UserContext exposed by this UserContextResource.
*
* @throws GuacamoleException
* If an error occurs while retrieving the ConnectionGroup Directory.
*/
@Path("connectionGroups")
public DirectoryResource<ConnectionGroup, APIConnectionGroup> getConnectionGroupDirectoryResource()
throws GuacamoleException {
return connectionGroupDirectoryResourceFactory.create(userContext,
userContext.getConnectionGroupDirectory());
}
/**
* Returns a new resource which represents the User Directory contained
* within the UserContext exposed by this UserContextResource.
*
* @return
* A new resource which represents the User Directory contained within
* the UserContext exposed by this UserContextResource.
*
* @throws GuacamoleException
* If an error occurs while retrieving the User Directory.
*/
@Path("users")
public DirectoryResource<User, APIUser> getUserDirectoryResource()
throws GuacamoleException {
return userDirectoryResourceFactory.create(userContext,
userContext.getUserDirectory());
}
/**
* Returns a new resource which represents historical data contained
* within the UserContext exposed by this UserContextResource.
*
* @return
* A new resource which represents the historical data contained within
* the UserContext exposed by this UserContextResource.
*/
@Path("history")
public HistoryResource getHistoryResource() {
return new HistoryResource(userContext);
}
/**
* Returns a new resource which represents meta information describing the
* kind of data which within the UserContext exposed by this
* UserContextResource.
*
* @return
* A new resource which represents the meta information describing the
* kind of data within the UserContext exposed by this
* UserContextResource.
*/
@Path("schema")
public SchemaResource getSchemaResource() {
return new SchemaResource(userContext);
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.session;
import org.apache.guacamole.net.auth.UserContext;
/**
* Factory which creates resources that expose the contents of a given
* UserContext.
*
* @author Michael Jumper
*/
public interface UserContextResourceFactory {
/**
* Creates a new UserContextResource which exposes the contents of the
* given UserContext.
*
* @param userContext
* The UserContext whose contents should be exposed.
*
* @return
* A new UserContextResource which exposes the contents of the given
* UserContext.
*/
UserContextResource create(UserContext userContext);
}

View File

@@ -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 which expose the absolute root of the data available to a Guacamole
* user's session.
*/
package org.apache.guacamole.rest.session;

View File

@@ -0,0 +1,131 @@
/*
* 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.tunnel;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.tunnel.StreamInterceptingTunnel;
/**
* A REST resource providing access to a Guacamole protocol-level stream
* within a tunnel.
*
* @author Michael Jumper
*/
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class StreamResource {
/**
* The tunnel whose stream is exposed through this StreamResource.
*/
private final StreamInterceptingTunnel tunnel;
/**
* The index of the stream being exposed.
*/
private final int streamIndex;
/**
* The media type of the data within the stream being exposed.
*/
private final String mediaType;
/**
* Creates a new StreamResource which provides access to the given
* stream.
*
* @param tunnel
* The tunnel whose stream is being exposed.
*
* @param streamIndex
* The index of the stream to expose via this StreamResource.
*
* @param mediaType
* The media type of the data within the stream.
*/
public StreamResource(StreamInterceptingTunnel tunnel, int streamIndex,
String mediaType) {
this.tunnel = tunnel;
this.streamIndex = streamIndex;
this.mediaType = mediaType;
}
/**
* Intercepts and returns the entire contents the stream represented by
* this StreamResource.
*
* @return
* A response through which the entire contents of the intercepted
* stream will be sent.
*/
@GET
public Response getStreamContents() {
// Intercept all output
StreamingOutput stream = new StreamingOutput() {
@Override
public void write(OutputStream output) throws IOException {
try {
tunnel.interceptStream(streamIndex, output);
}
catch (GuacamoleException e) {
throw new IOException(e);
}
}
};
return Response.ok(stream, mediaType).build();
}
/**
* Intercepts the stream represented by this StreamResource, sending the
* contents of the given InputStream over that stream as "blob"
* instructions.
*
* @param data
* An InputStream containing the data to be sent over the intercepted
* stream.
*
* @throws GuacamoleException
* If the intercepted stream closes with an error.
*/
@POST
@Consumes(MediaType.WILDCARD)
public void setStreamContents(InputStream data) throws GuacamoleException {
// Send input over stream
tunnel.interceptStream(streamIndex, data);
}
}

View File

@@ -0,0 +1,105 @@
/*
* 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.tunnel;
import java.util.Map;
import java.util.Set;
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.core.MediaType;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleResourceNotFoundException;
import org.apache.guacamole.GuacamoleSession;
import org.apache.guacamole.tunnel.StreamInterceptingTunnel;
/**
* A REST resource which exposes the active tunnels of a Guacamole session.
*
* @author Michael Jumper
*/
@Path("/tunnels")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class TunnelCollectionResource {
/**
* The GuacamoleSession containing the tunnels exposed by this resource.
*/
private final GuacamoleSession session;
/**
* Creates a new TunnelCollectionResource which exposes the active tunnels
* of the given GuacamoleSession.
*
* @param session
* The GuacamoleSession whose tunnels should be exposed by this
* resource.
*/
public TunnelCollectionResource(GuacamoleSession session) {
this.session = session;
}
/**
* Returns the UUIDs of all tunnels exposed by this
* TunnelCollectionResource.
*
* @return
* A set containing the UUIDs of all tunnels exposed by this
* TunnelCollectionResource.
*/
@GET
public Set<String> getTunnelUUIDs() {
return session.getTunnels().keySet();
}
/**
* Retrieves the tunnel having the given UUID, returning a TunnelResource
* representing that tunnel. If no such tunnel exists, an exception will be
* thrown.
*
* @param tunnelUUID
* The UUID of the tunnel to return via a TunnelResource.
*
* @return
* A TunnelResource which represents the tunnel having the given UUID.
*
* @throws GuacamoleException
* If no such tunnel exists.
*/
@Path("{tunnel}")
public TunnelResource getTunnel(@PathParam("tunnel") String tunnelUUID)
throws GuacamoleException {
Map<String, StreamInterceptingTunnel> tunnels = session.getTunnels();
// Pull tunnel with given UUID
final StreamInterceptingTunnel tunnel = tunnels.get(tunnelUUID);
if (tunnel == null)
throw new GuacamoleResourceNotFoundException("No such tunnel.");
// Return corresponding tunnel resource
return new TunnelResource(tunnel);
}
}

View File

@@ -1,202 +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.tunnel;
import com.google.inject.Inject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleResourceNotFoundException;
import org.apache.guacamole.GuacamoleSession;
import org.apache.guacamole.rest.auth.AuthenticationService;
import org.apache.guacamole.tunnel.StreamInterceptingTunnel;
/**
* A REST Service for retrieving and managing the tunnels of active
* connections, including any associated objects.
*
* @author Michael Jumper
*/
@Path("/tunnels")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class TunnelRESTService {
/**
* The media type to send as the content type of stream contents if no
* other media type is specified.
*/
private static final String DEFAULT_MEDIA_TYPE = "application/octet-stream";
/**
* A service for authenticating users from auth tokens.
*/
@Inject
private AuthenticationService authenticationService;
/**
* Returns the UUIDs of all currently-active tunnels associated with the
* session identified by the given auth token.
*
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @return
* A set containing the UUIDs of all currently-active tunnels.
*
* @throws GuacamoleException
* If the session associated with the given auth token cannot be
* retrieved.
*/
@GET
public Set<String> getTunnelUUIDs(@QueryParam("token") String authToken)
throws GuacamoleException {
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
return session.getTunnels().keySet();
}
/**
* Intercepts and returns the entire contents of a specific stream.
*
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param tunnelUUID
* The UUID of the tunnel containing the stream being intercepted.
*
* @param streamIndex
* The index of the stream to intercept.
*
* @param mediaType
* The media type (mimetype) of the data within the stream.
*
* @param filename
* The filename to use for the sake of identifying the data returned.
*
* @return
* A response through which the entire contents of the intercepted
* stream will be sent.
*
* @throws GuacamoleException
* If the session associated with the given auth token cannot be
* retrieved, or if no such tunnel exists.
*/
@GET
@Path("/{tunnel}/streams/{index}/{filename}")
public Response getStreamContents(@QueryParam("token") String authToken,
@PathParam("tunnel") String tunnelUUID,
@PathParam("index") final int streamIndex,
@QueryParam("type") @DefaultValue(DEFAULT_MEDIA_TYPE) String mediaType,
@PathParam("filename") String filename)
throws GuacamoleException {
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
Map<String, StreamInterceptingTunnel> tunnels = session.getTunnels();
// Pull tunnel with given UUID
final StreamInterceptingTunnel tunnel = tunnels.get(tunnelUUID);
if (tunnel == null)
throw new GuacamoleResourceNotFoundException("No such tunnel.");
// Intercept all output
StreamingOutput stream = new StreamingOutput() {
@Override
public void write(OutputStream output) throws IOException {
try {
tunnel.interceptStream(streamIndex, output);
}
catch (GuacamoleException e) {
throw new IOException(e);
}
}
};
return Response.ok(stream, mediaType).build();
}
/**
* Intercepts a specific stream, sending the contents of the given
* InputStream over that stream as "blob" instructions.
*
* @param authToken
* The authentication token that is used to authenticate the user
* performing the operation.
*
* @param tunnelUUID
* The UUID of the tunnel containing the stream being intercepted.
*
* @param streamIndex
* The index of the stream to intercept.
*
* @param filename
* The filename to use for the sake of identifying the data being sent.
*
* @param data
* An InputStream containing the data to be sent over the intercepted
* stream.
*
* @throws GuacamoleException
* If the session associated with the given auth token cannot be
* retrieved, if no such tunnel exists, or if the intercepted stream
* itself closes with an error.
*/
@POST
@Consumes(MediaType.WILDCARD)
@Path("/{tunnel}/streams/{index}/{filename}")
public void setStreamContents(@QueryParam("token") String authToken,
@PathParam("tunnel") String tunnelUUID,
@PathParam("index") final int streamIndex,
@PathParam("filename") String filename,
InputStream data)
throws GuacamoleException {
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
Map<String, StreamInterceptingTunnel> tunnels = session.getTunnels();
// Pull tunnel with given UUID
final StreamInterceptingTunnel tunnel = tunnels.get(tunnelUUID);
if (tunnel == null)
throw new GuacamoleResourceNotFoundException("No such tunnel.");
// Send input over stream
tunnel.interceptStream(streamIndex, data);
}
}

View File

@@ -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.tunnel;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
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.tunnel.StreamInterceptingTunnel;
/**
* A REST resource which abstracts the operations available for an individual
* tunnel.
*
* @author Michael Jumper
*/
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class TunnelResource {
/**
* The media type to send as the content type of stream contents if no
* other media type is specified.
*/
private static final String DEFAULT_MEDIA_TYPE = "application/octet-stream";
/**
* The tunnel that this TunnelResource represents.
*/
private final StreamInterceptingTunnel tunnel;
/**
* Creates a new TunnelResource which exposes the operations and
* subresources available for the given tunnel.
*
* @param tunnel
* The tunnel that this TunnelResource should represent.
*/
public TunnelResource(StreamInterceptingTunnel tunnel) {
this.tunnel = tunnel;
}
/**
* Intercepts and returns the entire contents of a specific stream.
*
* @param streamIndex
* The index of the stream to intercept.
*
* @param mediaType
* The media type (mimetype) of the data within the stream.
*
* @param filename
* The filename to use for the sake of identifying the data returned.
*
* @return
* A response through which the entire contents of the intercepted
* stream will be sent.
*
* @throws GuacamoleException
* If the session associated with the given auth token cannot be
* retrieved, or if no such tunnel exists.
*/
@Path("streams/{index}/{filename}")
public StreamResource getStream(@PathParam("index") final int streamIndex,
@QueryParam("type") @DefaultValue(DEFAULT_MEDIA_TYPE) String mediaType,
@PathParam("filename") String filename)
throws GuacamoleException {
return new StreamResource(tunnel, streamIndex, mediaType);
}
}

View File

@@ -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 Guacamole tunnels and underlying
* streams.
*/
package org.apache.guacamole.rest.tunnel;

View File

@@ -0,0 +1,116 @@
/*
* 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.user;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import java.util.UUID;
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.User;
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
* Users.
*
* @author Michael Jumper
*/
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserDirectoryResource extends DirectoryResource<User, APIUser> {
/**
* The UserContext associated with the Directory which contains the
* User exposed by this resource.
*/
private final UserContext userContext;
/**
* The Directory exposed by this resource.
*/
private final Directory<User> directory;
/**
* A factory which can be used to create instances of resources representing
* Users.
*/
private final DirectoryObjectResourceFactory<User, APIUser> resourceFactory;
/**
* Creates a new UserDirectoryResource which exposes the operations and
* subresources available for the given User Directory.
*
* @param userContext
* The UserContext associated with the given Directory.
*
* @param directory
* The Directory being exposed.
*
* @param translator
* A DirectoryObjectTranslator implementation which handles
* Users.
*
* @param resourceFactory
* A factory which can be used to create instances of resources
* representing Users.
*/
@AssistedInject
public UserDirectoryResource(@Assisted UserContext userContext,
@Assisted Directory<User> directory,
DirectoryObjectTranslator<User, APIUser> translator,
DirectoryObjectResourceFactory<User, APIUser> resourceFactory) {
super(userContext, directory, translator, resourceFactory);
this.userContext = userContext;
this.directory = directory;
this.resourceFactory = resourceFactory;
}
@Override
public APIUser createObject(APIUser object) throws GuacamoleException {
// Randomly set the password if it wasn't provided
if (object.getPassword() == null)
object.setPassword(UUID.randomUUID().toString());
return super.createObject(object);
}
@Override
public DirectoryObjectResource<User, APIUser>
getObjectResource(String identifier) throws GuacamoleException {
// If username is own username, just use self - might not have query permissions
if (userContext.self().getIdentifier().equals(identifier))
return resourceFactory.create(userContext, directory, userContext.self());
return super.getObjectResource(identifier);
}
}

View File

@@ -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.user;
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.User;
import org.apache.guacamole.rest.directory.DirectoryObjectResource;
import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
import org.apache.guacamole.rest.directory.DirectoryResource;
/**
* Guice Module which configures injections required for handling User resources
* via the REST API.
*
* @author Michael Jumper
*/
public class UserModule extends AbstractModule {
@Override
protected void configure() {
// Create the required DirectoryResourceFactory implementation
install(new FactoryModuleBuilder()
.implement(
new TypeLiteral<DirectoryResource<User, APIUser>>() {},
UserDirectoryResource.class
)
.build(new TypeLiteral<DirectoryResourceFactory<User, APIUser>>() {}));
// Create the required DirectoryObjectResourceFactory implementation
install(new FactoryModuleBuilder()
.implement(
new TypeLiteral<DirectoryObjectResource<User, APIUser>>() {},
UserResource.class
)
.build(new TypeLiteral<DirectoryObjectResourceFactory<User, APIUser>>() {}));
// Bind translator for converting between User and APIUser
bind(new TypeLiteral<DirectoryObjectTranslator<User, APIUser>>() {})
.to(UserObjectTranslator.class);
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.user;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
/**
* Translator which converts between User objects and APIUser objects.
*
* @author Michael Jumper
*/
public class UserObjectTranslator
implements DirectoryObjectTranslator<User, APIUser> {
@Override
public APIUser toExternalObject(User object)
throws GuacamoleException {
return new APIUser(object);
}
@Override
public User toInternalObject(APIUser object)
throws GuacamoleException {
return new APIUserWrapper(object);
}
@Override
public void applyExternalChanges(User existingObject,
APIUser object) throws GuacamoleException {
// Do not update the user password if no password was provided
if (object.getPassword() != null)
existingObject.setPassword(object.getPassword());
// Update user attributes
existingObject.setAttributes(object.getAttributes());
}
}

View File

@@ -1,644 +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.user;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
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.Context;
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.GuacamoleSecurityException;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials;
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.credentials.GuacamoleCredentialsException;
import org.apache.guacamole.net.auth.permission.ObjectPermission;
import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
import org.apache.guacamole.net.auth.permission.Permission;
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 static org.apache.guacamole.rest.APIPatch.Operation.add;
import static org.apache.guacamole.rest.APIPatch.Operation.remove;
import org.apache.guacamole.rest.ObjectRetrievalService;
import org.apache.guacamole.rest.PATCH;
import org.apache.guacamole.rest.auth.AuthenticationService;
import org.apache.guacamole.rest.permission.APIPermissionSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A REST Service for handling user CRUD operations.
*
* @author James Muehlner
* @author Michael Jumper
*/
@Path("/data/{dataSource}/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserRESTService {
/**
* Logger for this class.
*/
private static final Logger logger = LoggerFactory.getLogger(UserRESTService.class);
/**
* 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";
/**
* 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 users in the given data source (UserContext), filtering
* the returned list by the given permission, 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 from which the users are 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 visible users. If a permission was specified, this
* list will contain only those users for whom the current user has
* that permission.
*
* @throws GuacamoleException
* If an error is encountered while retrieving users.
*/
@GET
public List<APIUser> getUsers(@QueryParam("token") String authToken,
@PathParam("dataSource") String authProviderIdentifier,
@QueryParam("permission") List<ObjectPermission.Type> permissions)
throws GuacamoleException {
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
// An admin user has access to any user
User self = userContext.self();
SystemPermissionSet systemPermissions = self.getSystemPermissions();
boolean isAdmin = systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER);
// Get the directory
Directory<User> userDirectory = userContext.getUserDirectory();
// Filter users, if requested
Collection<String> userIdentifiers = userDirectory.getIdentifiers();
if (!isAdmin && permissions != null && !permissions.isEmpty()) {
ObjectPermissionSet userPermissions = self.getUserPermissions();
userIdentifiers = userPermissions.getAccessibleObjects(permissions, userIdentifiers);
}
// Retrieve all users, converting to API users
List<APIUser> apiUsers = new ArrayList<APIUser>();
for (User user : userDirectory.getAll(userIdentifiers))
apiUsers.add(new APIUser(user));
return apiUsers;
}
/**
* Retrieves an individual user.
*
* @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 from which the requested user is to be retrieved.
*
* @param username
* The username of the user to retrieve.
*
* @return user
* The user having the given username.
*
* @throws GuacamoleException
* If an error occurs while retrieving the user.
*/
@GET
@Path("/{username}")
public APIUser getUser(@QueryParam("token") String authToken,
@PathParam("dataSource") String authProviderIdentifier,
@PathParam("username") String username)
throws GuacamoleException {
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
// Retrieve the requested user
User user = retrievalService.retrieveUser(session, authProviderIdentifier, username);
return new APIUser(user);
}
/**
* Creates a new user and returns the user that was created.
*
* @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 requested user is to be created.
*
* @param user
* The new user to create.
*
* @throws GuacamoleException
* If a problem is encountered while creating the user.
*
* @return
* The newly created user.
*/
@POST
public APIUser createUser(@QueryParam("token") String authToken,
@PathParam("dataSource") String authProviderIdentifier, APIUser user)
throws GuacamoleException {
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
// Get the directory
Directory<User> userDirectory = userContext.getUserDirectory();
// Randomly set the password if it wasn't provided
if (user.getPassword() == null)
user.setPassword(UUID.randomUUID().toString());
// Create the user
userDirectory.add(new APIUserWrapper(user));
return user;
}
/**
* Updates an individual existing user.
*
* @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 requested user is to be updated.
*
* @param username
* The username of the user to update.
*
* @param user
* The data to update the user with.
*
* @throws GuacamoleException
* If an error occurs while updating the user.
*/
@PUT
@Path("/{username}")
public void updateUser(@QueryParam("token") String authToken,
@PathParam("dataSource") String authProviderIdentifier,
@PathParam("username") String username, APIUser user)
throws GuacamoleException {
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
// Get the directory
Directory<User> userDirectory = userContext.getUserDirectory();
// Validate data and path are sane
if (!user.getUsername().equals(username))
throw new GuacamoleClientException("Username in path does not match username provided JSON data.");
// A user may not use this endpoint to modify himself
if (userContext.self().getIdentifier().equals(user.getUsername()))
throw new GuacamoleSecurityException("Permission denied.");
// Get the user
User existingUser = retrievalService.retrieveUser(userContext, username);
// Do not update the user password if no password was provided
if (user.getPassword() != null)
existingUser.setPassword(user.getPassword());
// Update user attributes
existingUser.setAttributes(user.getAttributes());
// Update the user
userDirectory.update(existingUser);
}
/**
* Updates the password for an individual existing user.
*
* @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 requested user is to be updated.
*
* @param username
* The username of the user to update.
*
* @param userPasswordUpdate
* The object containing the old password for the user, as well as the
* new password to set for that user.
*
* @param request
* The HttpServletRequest associated with the password update attempt.
*
* @throws GuacamoleException
* If an error occurs while updating the user's password.
*/
@PUT
@Path("/{username}/password")
public void updatePassword(@QueryParam("token") String authToken,
@PathParam("dataSource") String authProviderIdentifier,
@PathParam("username") String username,
APIUserPasswordUpdate userPasswordUpdate,
@Context HttpServletRequest request) throws GuacamoleException {
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
// Build credentials
Credentials credentials = new Credentials();
credentials.setUsername(username);
credentials.setPassword(userPasswordUpdate.getOldPassword());
credentials.setRequest(request);
credentials.setSession(request.getSession(true));
// Verify that the old password was correct
try {
AuthenticationProvider authProvider = userContext.getAuthenticationProvider();
if (authProvider.authenticateUser(credentials) == null)
throw new GuacamoleSecurityException("Permission denied.");
}
// Pass through any credentials exceptions as simple permission denied
catch (GuacamoleCredentialsException e) {
throw new GuacamoleSecurityException("Permission denied.");
}
// Get the user directory
Directory<User> userDirectory = userContext.getUserDirectory();
// Get the user that we want to updates
User user = retrievalService.retrieveUser(userContext, username);
// Set password to the newly provided one
user.setPassword(userPasswordUpdate.getNewPassword());
// Update the user
userDirectory.update(user);
}
/**
* Deletes an individual existing user.
*
* @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 from which the requested user is to be deleted.
*
* @param username
* The username of the user to delete.
*
* @throws GuacamoleException
* If an error occurs while deleting the user.
*/
@DELETE
@Path("/{username}")
public void deleteUser(@QueryParam("token") String authToken,
@PathParam("dataSource") String authProviderIdentifier,
@PathParam("username") String username)
throws GuacamoleException {
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
// Get the directory
Directory<User> userDirectory = userContext.getUserDirectory();
// Get the user
User existingUser = userDirectory.get(username);
if (existingUser == null)
throw new GuacamoleResourceNotFoundException("No such user: \"" + username + "\"");
// Delete the user
userDirectory.remove(username);
}
/**
* Gets a list of permissions for the user with the given username.
*
* @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 requested user is to be found.
*
* @param username
* The username of the user to retrieve permissions for.
*
* @return
* A list of all permissions granted to the specified user.
*
* @throws GuacamoleException
* If an error occurs while retrieving permissions.
*/
@GET
@Path("/{username}/permissions")
public APIPermissionSet getPermissions(@QueryParam("token") String authToken,
@PathParam("dataSource") String authProviderIdentifier,
@PathParam("username") String username)
throws GuacamoleException {
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
User user;
// If username is own username, just use self - might not have query permissions
if (userContext.self().getIdentifier().equals(username))
user = userContext.self();
// If not self, query corresponding user from directory
else {
user = userContext.getUserDirectory().get(username);
if (user == null)
throw new GuacamoleResourceNotFoundException("No such user: \"" + username + "\"");
}
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 <PermissionType>
* 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 <PermissionType extends Permission> void updatePermissionSet(
APIPatch.Operation operation,
PermissionSetPatch<PermissionType> 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 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 requested user is to be found.
*
* @param username
* The username of the user to modify the permissions of.
*
* @param patches
* The permission patches to apply for this request.
*
* @throws GuacamoleException
* If a problem is encountered while modifying permissions.
*/
@PATCH
@Path("/{username}/permissions")
public void patchPermissions(@QueryParam("token") String authToken,
@PathParam("dataSource") String authProviderIdentifier,
@PathParam("username") String username,
List<APIPatch<String>> patches) throws GuacamoleException {
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
// Get the user
User user = userContext.getUserDirectory().get(username);
if (user == null)
throw new GuacamoleResourceNotFoundException("No such user: \"" + username + "\"");
// Permission patches for all types of permissions
PermissionSetPatch<ObjectPermission> connectionPermissionPatch = new PermissionSetPatch<ObjectPermission>();
PermissionSetPatch<ObjectPermission> connectionGroupPermissionPatch = new PermissionSetPatch<ObjectPermission>();
PermissionSetPatch<ObjectPermission> activeConnectionPermissionPatch = new PermissionSetPatch<ObjectPermission>();
PermissionSetPatch<ObjectPermission> userPermissionPatch = new PermissionSetPatch<ObjectPermission>();
PermissionSetPatch<SystemPermission> systemPermissionPatch = new PermissionSetPatch<SystemPermission>();
// Apply all patch operations individually
for (APIPatch<String> 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());
}
}

View File

@@ -0,0 +1,165 @@
/*
* 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.user;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials;
import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.UserContext;
import org.apache.guacamole.net.auth.credentials.GuacamoleCredentialsException;
import org.apache.guacamole.rest.directory.DirectoryObjectResource;
import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
import org.apache.guacamole.rest.permission.PermissionSetResource;
/**
* A REST resource which abstracts the operations available on an existing
* User.
*
* @author Michael Jumper
*/
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserResource
extends DirectoryObjectResource<User, APIUser> {
/**
* The UserContext associated with the Directory which contains the User
* exposed by this resource.
*/
private final UserContext userContext;
/**
* The Directory which contains the User object represented by this
* UserResource.
*/
private final Directory<User> directory;
/**
* The User object represented by this UserResource.
*/
private final User user;
/**
* Creates a new UserResource which exposes the operations and subresources
* available for the given User.
*
* @param userContext
* The UserContext associated with the given Directory.
*
* @param directory
* The Directory which contains the given User.
*
* @param user
* The User that this UserResource should represent.
*
* @param translator
* A DirectoryObjectTranslator implementation which handles Users.
*/
@AssistedInject
public UserResource(@Assisted UserContext userContext,
@Assisted Directory<User> directory,
@Assisted User user,
DirectoryObjectTranslator<User, APIUser> translator) {
super(directory, user, translator);
this.userContext = userContext;
this.directory = directory;
this.user = user;
}
@Override
public void updateObject(APIUser modifiedObject) throws GuacamoleException {
// A user may not use this endpoint to modify himself
if (userContext.self().getIdentifier().equals(modifiedObject.getUsername()))
throw new GuacamoleSecurityException("Permission denied.");
super.updateObject(modifiedObject);
}
/**
* Updates the password for an individual existing user.
*
* @param userPasswordUpdate
* The object containing the old password for the user, as well as the
* new password to set for that user.
*
* @param request
* The HttpServletRequest associated with the password update attempt.
*
* @throws GuacamoleException
* If an error occurs while updating the user's password.
*/
@PUT
@Path("password")
public void updatePassword(APIUserPasswordUpdate userPasswordUpdate,
@Context HttpServletRequest request) throws GuacamoleException {
// Build credentials
Credentials credentials = new Credentials();
credentials.setUsername(user.getIdentifier());
credentials.setPassword(userPasswordUpdate.getOldPassword());
credentials.setRequest(request);
credentials.setSession(request.getSession(true));
// Verify that the old password was correct
try {
AuthenticationProvider authProvider = userContext.getAuthenticationProvider();
if (authProvider.authenticateUser(credentials) == null)
throw new GuacamoleSecurityException("Permission denied.");
}
// Pass through any credentials exceptions as simple permission denied
catch (GuacamoleCredentialsException e) {
throw new GuacamoleSecurityException("Permission denied.");
}
// Set password to the newly provided one
user.setPassword(userPasswordUpdate.getNewPassword());
directory.update(user);
}
/**
* Returns a resource which abstracts operations available on the overall
* permissions granted to the User represented by this UserResource.
*
* @return
* A resource which representing the permissions granted to the User
* represented by this UserResource.
*/
@Path("permissions")
public PermissionSetResource getPermissions() {
return new PermissionSetResource(user);
}
}

View File

@@ -31,7 +31,6 @@ 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.UserContext;
import org.apache.guacamole.rest.ObjectRetrievalService;
import org.apache.guacamole.rest.auth.AuthenticationService;
import org.apache.guacamole.protocol.GuacamoleClientInformation;
import org.slf4j.Logger;
@@ -61,12 +60,6 @@ public class TunnelRequestService {
@Inject
private AuthenticationService authenticationService;
/**
* Service for convenient retrieval of objects.
*/
@Inject
private ObjectRetrievalService retrievalService;
/**
* Reads and returns the client information provided within the given
* request.
@@ -327,7 +320,7 @@ public class TunnelRequestService {
GuacamoleClientInformation info = getClientInformation(request);
GuacamoleSession session = authenticationService.getGuacamoleSession(authToken);
UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier);
UserContext userContext = session.getUserContext(authProviderIdentifier);
try {

View File

@@ -60,7 +60,7 @@ angular.module('rest').factory('activeConnectionService', ['$injector',
// Retrieve tunnels
return $http({
method : 'GET',
url : 'api/data/' + encodeURIComponent(dataSource) + '/activeConnections',
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/activeConnections',
params : httpParameters
});
@@ -159,7 +159,7 @@ angular.module('rest').factory('activeConnectionService', ['$injector',
// Perform active connection deletion via PATCH
return $http({
method : 'PATCH',
url : 'api/data/' + encodeURIComponent(dataSource) + '/activeConnections',
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/activeConnections',
params : httpParameters,
data : activeConnectionPatch
});

View File

@@ -73,7 +73,7 @@ angular.module('rest').factory('connectionGroupService', ['$injector',
return $http({
cache : cacheService.connections,
method : 'GET',
url : 'api/data/' + encodeURIComponent(dataSource) + '/connectionGroups/' + encodeURIComponent(connectionGroupID) + '/tree',
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connectionGroups/' + encodeURIComponent(connectionGroupID) + '/tree',
params : httpParameters
});
@@ -106,7 +106,7 @@ angular.module('rest').factory('connectionGroupService', ['$injector',
return $http({
cache : cacheService.connections,
method : 'GET',
url : 'api/data/' + encodeURIComponent(dataSource) + '/connectionGroups/' + encodeURIComponent(connectionGroupID),
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connectionGroups/' + encodeURIComponent(connectionGroupID),
params : httpParameters
});
@@ -136,7 +136,7 @@ angular.module('rest').factory('connectionGroupService', ['$injector',
if (!connectionGroup.identifier) {
return $http({
method : 'POST',
url : 'api/data/' + encodeURIComponent(dataSource) + '/connectionGroups',
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connectionGroups',
params : httpParameters,
data : connectionGroup
})
@@ -152,7 +152,7 @@ angular.module('rest').factory('connectionGroupService', ['$injector',
else {
return $http({
method : 'PUT',
url : 'api/data/' + encodeURIComponent(dataSource) + '/connectionGroups/' + encodeURIComponent(connectionGroup.identifier),
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connectionGroups/' + encodeURIComponent(connectionGroup.identifier),
params : httpParameters,
data : connectionGroup
})
@@ -185,7 +185,7 @@ angular.module('rest').factory('connectionGroupService', ['$injector',
// Delete connection group
return $http({
method : 'DELETE',
url : 'api/data/' + encodeURIComponent(dataSource) + '/connectionGroups/' + encodeURIComponent(connectionGroup.identifier),
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connectionGroups/' + encodeURIComponent(connectionGroup.identifier),
params : httpParameters
})

View File

@@ -56,7 +56,7 @@ angular.module('rest').factory('connectionService', ['$injector',
return $http({
cache : cacheService.connections,
method : 'GET',
url : 'api/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(id),
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(id),
params : httpParameters
});
@@ -84,7 +84,7 @@ angular.module('rest').factory('connectionService', ['$injector',
// Retrieve connection history
return $http({
method : 'GET',
url : 'api/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(id) + '/history',
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(id) + '/history',
params : httpParameters
});
@@ -113,7 +113,7 @@ angular.module('rest').factory('connectionService', ['$injector',
return $http({
cache : cacheService.connections,
method : 'GET',
url : 'api/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(id) + '/parameters',
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(id) + '/parameters',
params : httpParameters
});
@@ -143,7 +143,7 @@ angular.module('rest').factory('connectionService', ['$injector',
if (!connection.identifier) {
return $http({
method : 'POST',
url : 'api/data/' + encodeURIComponent(dataSource) + '/connections',
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connections',
params : httpParameters,
data : connection
})
@@ -159,7 +159,7 @@ angular.module('rest').factory('connectionService', ['$injector',
else {
return $http({
method : 'PUT',
url : 'api/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(connection.identifier),
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(connection.identifier),
params : httpParameters,
data : connection
})
@@ -192,7 +192,7 @@ angular.module('rest').factory('connectionService', ['$injector',
// Delete connection
return $http({
method : 'DELETE',
url : 'api/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(connection.identifier),
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/connections/' + encodeURIComponent(connection.identifier),
params : httpParameters
})

View File

@@ -76,7 +76,7 @@ angular.module('rest').factory('historyService', ['$injector',
// Retrieve connection history
return $http({
method : 'GET',
url : 'api/data/' + encodeURIComponent(dataSource) + '/history/connections',
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/history/connections',
params : httpParameters
});

View File

@@ -62,7 +62,7 @@ angular.module('rest').factory('permissionService', ['$injector',
return $http({
cache : cacheService.users,
method : 'GET',
url : 'api/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(userID) + '/permissions',
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(userID) + '/permissions',
params : httpParameters
});
@@ -235,7 +235,7 @@ angular.module('rest').factory('permissionService', ['$injector',
// Patch user permissions
return $http({
method : 'PATCH',
url : 'api/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(userID) + '/permissions',
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(userID) + '/permissions',
params : httpParameters,
data : permissionPatch
})

View File

@@ -58,7 +58,7 @@ angular.module('rest').factory('schemaService', ['$injector',
return $http({
cache : cacheService.schema,
method : 'GET',
url : 'api/schema/' + encodeURIComponent(dataSource) + '/users/attributes',
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/schema/userAttributes',
params : httpParameters
});
@@ -92,7 +92,7 @@ angular.module('rest').factory('schemaService', ['$injector',
return $http({
cache : cacheService.schema,
method : 'GET',
url : 'api/schema/' + encodeURIComponent(dataSource) + '/connections/attributes',
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/schema/connectionAttributes',
params : httpParameters
});
@@ -126,7 +126,7 @@ angular.module('rest').factory('schemaService', ['$injector',
return $http({
cache : cacheService.schema,
method : 'GET',
url : 'api/schema/' + encodeURIComponent(dataSource) + '/connectionGroups/attributes',
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/schema/connectionGroupAttributes',
params : httpParameters
});
@@ -157,7 +157,7 @@ angular.module('rest').factory('schemaService', ['$injector',
return $http({
cache : cacheService.schema,
method : 'GET',
url : 'api/schema/' + encodeURIComponent(dataSource) + '/protocols',
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/schema/protocols',
params : httpParameters
});

View File

@@ -62,7 +62,7 @@ angular.module('rest').factory('tunnelService', ['$injector',
// Retrieve tunnels
return $http({
method : 'GET',
url : 'api/tunnels',
url : 'api/session/tunnels',
params : httpParameters
});
@@ -100,7 +100,7 @@ angular.module('rest').factory('tunnelService', ['$injector',
// Build download URL
var url = $window.location.origin
+ $window.location.pathname
+ 'api/tunnels/' + encodeURIComponent(tunnel)
+ 'api/session/tunnels/' + encodeURIComponent(tunnel)
+ '/streams/' + encodeURIComponent(stream.index)
+ '/' + encodeURIComponent(filename)
+ '?token=' + encodeURIComponent(authenticationService.getCurrentToken());
@@ -163,7 +163,7 @@ angular.module('rest').factory('tunnelService', ['$injector',
// Build upload URL
var url = $window.location.origin
+ $window.location.pathname
+ 'api/tunnels/' + encodeURIComponent(tunnel)
+ 'api/session/tunnels/' + encodeURIComponent(tunnel)
+ '/streams/' + encodeURIComponent(stream.index)
+ '/' + encodeURIComponent(file.name)
+ '?token=' + encodeURIComponent(authenticationService.getCurrentToken());

View File

@@ -50,9 +50,10 @@ angular.module('rest').factory('userService', ['$injector',
* If null, no filtering will be performed. Valid values are listed
* within PermissionSet.ObjectType.
*
* @returns {Promise.<User[]>}
* A promise which will resolve with an array of @link{User} objects
* upon success.
* @returns {Promise.<Object.<String, User>>}
* A promise which will resolve with a map of @link{User} objects
* where each key is the identifier (username) of the corresponding
* user.
*/
service.getUsers = function getUsers(dataSource, permissionTypes) {
@@ -69,7 +70,7 @@ angular.module('rest').factory('userService', ['$injector',
return $http({
cache : cacheService.users,
method : 'GET',
url : 'api/data/' + encodeURIComponent(dataSource) + '/users',
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/users',
params : httpParameters
});
@@ -102,7 +103,7 @@ angular.module('rest').factory('userService', ['$injector',
return $http({
cache : cacheService.users,
method : 'GET',
url : 'api/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(username),
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(username),
params : httpParameters
});
@@ -134,7 +135,7 @@ angular.module('rest').factory('userService', ['$injector',
// Delete user
return $http({
method : 'DELETE',
url : 'api/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(user.username),
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(user.username),
params : httpParameters
})
@@ -172,7 +173,7 @@ angular.module('rest').factory('userService', ['$injector',
// Create user
return $http({
method : 'POST',
url : 'api/data/' + encodeURIComponent(dataSource) + '/users',
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/users',
params : httpParameters,
data : user
})
@@ -210,7 +211,7 @@ angular.module('rest').factory('userService', ['$injector',
// Update user
return $http({
method : 'PUT',
url : 'api/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(user.username),
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(user.username),
params : httpParameters,
data : user
})
@@ -255,7 +256,7 @@ angular.module('rest').factory('userService', ['$injector',
// Update user password
return $http({
method : 'PUT',
url : 'api/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(username) + '/password',
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/users/' + encodeURIComponent(username) + '/password',
params : httpParameters,
data : new UserPasswordUpdate({
oldPassword : oldPassword,

View File

@@ -227,14 +227,14 @@ angular.module('settings').directive('guacSettingsUsers', [function guacSettings
PermissionSet.ObjectPermissionType.DELETE
]);
userPromise.then(function usersReceived(userArrays) {
userPromise.then(function usersReceived(allUsers) {
var addedUsers = {};
$scope.manageableUsers = [];
// For each user in each data source
angular.forEach(dataSources, function addUserList(dataSource) {
angular.forEach(userArrays[dataSource], function addUser(user) {
angular.forEach(allUsers[dataSource], function addUser(user) {
// Do not add the same user twice
if (addedUsers[user.username])