GUAC-1193: Implement history REST service.

This commit is contained in:
Michael Jumper
2015-09-23 15:22:58 -07:00
parent 16d047e81e
commit 805c647cdd
6 changed files with 348 additions and 4 deletions

View File

@@ -31,6 +31,7 @@ import org.glyptodon.guacamole.net.basic.rest.auth.TokenRESTService;
import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionRESTService;
import org.glyptodon.guacamole.net.basic.rest.connectiongroup.ConnectionGroupRESTService;
import org.glyptodon.guacamole.net.basic.rest.activeconnection.ActiveConnectionRESTService;
import org.glyptodon.guacamole.net.basic.rest.history.HistoryRESTService;
import org.glyptodon.guacamole.net.basic.rest.language.LanguageRESTService;
import org.glyptodon.guacamole.net.basic.rest.schema.SchemaRESTService;
import org.glyptodon.guacamole.net.basic.rest.user.UserRESTService;
@@ -59,6 +60,7 @@ public class RESTServletModule extends ServletModule {
bind(ActiveConnectionRESTService.class);
bind(ConnectionGroupRESTService.class);
bind(ConnectionRESTService.class);
bind(HistoryRESTService.class);
bind(LanguageRESTService.class);
bind(SchemaRESTService.class);
bind(TokenRESTService.class);

View File

@@ -52,6 +52,7 @@ import org.glyptodon.guacamole.net.basic.GuacamoleSession;
import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure;
import org.glyptodon.guacamole.net.basic.rest.ObjectRetrievalService;
import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
import org.glyptodon.guacamole.net.basic.rest.history.APIConnectionRecord;
import org.glyptodon.guacamole.protocol.GuacamoleConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@@ -20,14 +20,14 @@
* THE SOFTWARE.
*/
package org.glyptodon.guacamole.net.basic.rest.connection;
package org.glyptodon.guacamole.net.basic.rest.history;
import java.util.Date;
import org.glyptodon.guacamole.net.auth.ConnectionRecord;
/**
* A connection record which may be exposed through the REST endpoints.
*
*
* @author Michael Jumper
*/
public class APIConnectionRecord {
@@ -47,7 +47,7 @@ public class APIConnectionRecord {
* The host from which the connection originated, if known.
*/
private final String remoteHost;
/**
* The name of the user who used or is using the connection.
*/
@@ -126,5 +126,5 @@ public class APIConnectionRecord {
public boolean isActive() {
return active;
}
}

View File

@@ -0,0 +1,166 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.glyptodon.guacamole.net.basic.rest.history;
import org.glyptodon.guacamole.net.auth.ConnectionRecordSet;
import org.glyptodon.guacamole.net.basic.rest.APIError;
import org.glyptodon.guacamole.net.basic.rest.APIException;
/**
* A sort predicate which species the property to use when sorting connection
* records, along with the sort order.
*
* @author Michael Jumper
*/
public class APIConnectionRecordSortPredicate {
/**
* The prefix which will be included before the name of a sortable property
* to indicate that the sort order is descending, not ascending.
*/
public static final String DESCENDING_PREFIX = "-";
/**
* All possible property name strings and their corresponding
* ConnectionRecordSet.SortableProperty values.
*/
public enum SortableProperty {
/**
* The name (not identifier) of the connection associated with the
* connection record.
*/
connection(ConnectionRecordSet.SortableProperty.CONNECTION_NAME),
/**
* The username (identifier) of the user associated with the connection
* record.
*/
username(ConnectionRecordSet.SortableProperty.USER_IDENTIFIER),
/**
* The date that the connection associated with the connection record
* began (connected).
*/
startDate(ConnectionRecordSet.SortableProperty.START_DATE),
/**
* The date that the connection associated with the connection record
* ended (disconnected).
*/
endDate(ConnectionRecordSet.SortableProperty.END_DATE);
/**
* The ConnectionRecordSet.SortableProperty that this property name
* string represents.
*/
public final ConnectionRecordSet.SortableProperty recordProperty;
/**
* Creates a new SortableProperty which associates the property name
* string (identical to its own name) with the given
* ConnectionRecordSet.SortableProperty value.
*
* @param recordProperty
* The ConnectionRecordSet.SortableProperty value to associate with
* the new SortableProperty.
*/
SortableProperty(ConnectionRecordSet.SortableProperty recordProperty) {
this.recordProperty = recordProperty;
}
}
/**
* The property to use when sorting ConnectionRecords.
*/
private ConnectionRecordSet.SortableProperty property;
/**
* Whether the requested sort order is descending (true) or ascending
* (false).
*/
private boolean descending;
/**
* Parses the given string value, determining the requested sort property
* and ordering. Possible values consist of any valid property name, and
* may include an optional prefix to denote descending sort order. Each
* possible property name is enumerated by the SortableValue enum.
*
* @param value
* The sort predicate string to parse, which must consist ONLY of a
* valid property name, possibly preceded by the DESCENDING_PREFIX.
*
* @throws APIException
* If the provided sort predicate string is invalid.
*/
public APIConnectionRecordSortPredicate(String value)
throws APIException {
// Parse whether sort order is descending
if (value.startsWith(DESCENDING_PREFIX)) {
descending = true;
value = value.substring(DESCENDING_PREFIX.length());
}
// Parse sorting property into ConnectionRecordSet.SortableProperty
try {
this.property = SortableProperty.valueOf(value).recordProperty;
}
// Bail out if sort property is not valid
catch (IllegalArgumentException e) {
throw new APIException(
APIError.Type.BAD_REQUEST,
String.format("Invalid sort property: \"%s\"", value)
);
}
}
/**
* Returns the SortableProperty defined by ConnectionRecordSet which
* represents the property requested.
*
* @return
* The ConnectionRecordSet.SortableProperty which refers to the same
* property as the string originally provided when this
* APIConnectionRecordSortPredicate was created.
*/
public ConnectionRecordSet.SortableProperty getProperty() {
return property;
}
/**
* Returns whether the requested sort order is descending.
*
* @return
* true if the sort order is descending, false if the sort order is
* ascending.
*/
public boolean isDescending() {
return descending;
}
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.glyptodon.guacamole.net.basic.rest.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;
import org.glyptodon.guacamole.GuacamoleException;
import org.glyptodon.guacamole.net.auth.ConnectionRecord;
import org.glyptodon.guacamole.net.auth.ConnectionRecordSet;
import org.glyptodon.guacamole.net.auth.UserContext;
import org.glyptodon.guacamole.net.basic.GuacamoleSession;
import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure;
import org.glyptodon.guacamole.net.basic.rest.ObjectRetrievalService;
import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A REST Service 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);
/**
* The maximum number of history records to return in any one response.
*/
private static final int MAXIMUM_HISTORY_SIZE = 10000;
/**
* A service for authenticating users from auth tokens.
*/
@Inject
private AuthenticationService authenticationService;
/**
* Service for convenient retrieval of objects.
*/
@Inject
private ObjectRetrievalService retrievalService;
/**
* 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,
* the name of the associated connection, or any associated date. If
* non-empty, any connection record not matching each of the strings
* within the collection will be excluded from the results.
*
* @param sortPredicates
* A list of predicates to apply while sorting the resulting connection
* records, describing the properties involved and the sort order for
* those properties.
*
* @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("/connections")
@AuthProviderRESTExposure
public List<APIConnectionRecord> getConnectionHistory(@QueryParam("token") String authToken,
@PathParam("dataSource") String authProviderIdentifier,
@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();
// Restrict to records which contain the specified strings
for (String required : requiredContents)
history = history.contains(required);
// Sort according to specified ordering
for (APIConnectionRecordSortPredicate predicate : sortPredicates)
history = history.sort(predicate.getProperty(), predicate.isDescending());
// Limit to maximum result size
history = history.limit(MAXIMUM_HISTORY_SIZE);
// Convert record set to collection of API connection records
List<APIConnectionRecord> apiRecords = new ArrayList<APIConnectionRecord>();
for (ConnectionRecord record : history.asCollection())
apiRecords.add(new APIConnectionRecord(record));
// Return the converted history
return apiRecords;
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2015 Glyptodon LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Classes related to retrieval or maintenance of history records using the
* Guacamole REST API.
*/
package org.glyptodon.guacamole.net.basic.rest.history;