From d367add7856e0e384f2e16dd460144059cf347c2 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 11 Dec 2021 15:36:03 -0800 Subject: [PATCH] GUACAMOLE-462: Allow logs associated with history entries to be retrieved via REST. --- .../rest/history/ActivityLogResource.java | 77 +++++++++++ .../rest/history/ActivityRecordResource.java | 82 +++++++++++ .../history/ActivityRecordSetResource.java | 128 ++++++++++++++++-- 3 files changed, 274 insertions(+), 13 deletions(-) create mode 100644 guacamole/src/main/java/org/apache/guacamole/rest/history/ActivityLogResource.java create mode 100644 guacamole/src/main/java/org/apache/guacamole/rest/history/ActivityRecordResource.java diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/history/ActivityLogResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/history/ActivityLogResource.java new file mode 100644 index 000000000..e311884d0 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/ActivityLogResource.java @@ -0,0 +1,77 @@ +/* + * 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.history; + +import javax.ws.rs.GET; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.ActivityLog; + +/** + * A REST resource which exposes the contents of a given ActivityLog. + */ +public class ActivityLogResource { + + /** + * The ActivityLog whose contents are being exposed. + */ + private final ActivityLog log; + + /** + * Creates a new ActivityLogResource which exposes the records within the + * given ActivityLog. + * + * @param log + * The ActivityLog whose contents should be exposed. + */ + public ActivityLogResource(ActivityLog log) { + this.log = log; + } + + /** + * Returns the raw contents of the underlying ActivityLog. If the size of + * the ActivityLog is known, this size is included as the "Content-Length" + * of the response. + * + * @return + * A Response containing the raw contents of the underlying + * ActivityLog. + * + * @throws GuacamoleException + * If an error prevents retrieving the content of the log or its size. + */ + @GET + public Response getContents() throws GuacamoleException { + + // Build base response exposing the raw contents of the underlying log + ResponseBuilder response = Response.ok(log.getContent(), + log.getType().getContentType()); + + // Include size, if known + long size = log.getSize(); + if (size >= 0) + response.header("Content-Length", size); + + return response.build(); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/history/ActivityRecordResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/history/ActivityRecordResource.java new file mode 100644 index 000000000..ae7e3333d --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/ActivityRecordResource.java @@ -0,0 +1,82 @@ +/* + * 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.history; + +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.GuacamoleResourceNotFoundException; +import org.apache.guacamole.net.auth.ActivityLog; +import org.apache.guacamole.net.auth.ActivityRecord; + +/** + * A REST resource which exposes a single ActivityRecord, allowing any + * associated and accessible logs to be retrieved. + */ +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class ActivityRecordResource { + + /** + * The ActivityRecord being exposed. + */ + private final ActivityRecord record; + + /** + * Creates a new ActivityRecordResource which exposes the given record. + * + * @param record + * The ActivityRecord that should be exposed. + */ + public ActivityRecordResource(ActivityRecord record) { + this.record = record; + } + + /** + * Returns an ActivityLogResource representing the log associated with the + * underlying ActivityRecord and having the given name. If no such log + * can be retrieved, either because it does not exist or the current user + * does not have access, an exception is thrown. + * + * @param logName + * The unique name of the log to retrieve. + * + * @return + * An ActivityLogResource representing the log having the given name. + * + * @throws GuacamoleException + * If no such log can be retrieved. + */ + @Path("logs/{name}") + public ActivityLogResource getLog(@PathParam("name") String logName) + throws GuacamoleException { + + ActivityLog log = record.getLogs().get(logName); + if (log != null) + return new ActivityLogResource(log); + + throw new GuacamoleResourceNotFoundException("No such log."); + + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/history/ActivityRecordSetResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/history/ActivityRecordSetResource.java index 8599a9557..5cd245a18 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/history/ActivityRecordSetResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/ActivityRecordSetResource.java @@ -21,12 +21,17 @@ package org.apache.guacamole.rest.history; import java.util.ArrayList; import java.util.List; +import java.util.UUID; 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.GuacamoleResourceNotFoundException; import org.apache.guacamole.net.auth.ActivityRecord; import org.apache.guacamole.net.auth.ActivityRecordSet; @@ -86,6 +91,45 @@ public abstract class ActivityRecordSetResource requiredContents, + List sortPredicates) throws GuacamoleException { + + // Restrict to records which contain the specified strings + for (String required : requiredContents) { + if (!required.isEmpty()) + history = history.contains(required); + } + + // Sort according to specified ordering + for (APISortPredicate predicate : sortPredicates) + history = history.sort(predicate.getProperty(), predicate.isDescending()); + + // Limit to maximum result size + history = history.limit(MAXIMUM_HISTORY_SIZE); + + } + /** * Retrieves the list of activity records stored within the underlying * ActivityRecordSet which match the given, arbitrary criteria. If @@ -118,19 +162,9 @@ public abstract class ActivityRecordSetResource sortPredicates) throws GuacamoleException { - // Restrict to records which contain the specified strings - for (String required : requiredContents) { - if (!required.isEmpty()) - history = history.contains(required); - } - - // Sort according to specified ordering - for (APISortPredicate predicate : sortPredicates) - history = history.sort(predicate.getProperty(), predicate.isDescending()); - - // Limit to maximum result size - history = history.limit(MAXIMUM_HISTORY_SIZE); - + // Apply search/sort criteria + applyCriteria(requiredContents, sortPredicates); + // Convert record set to collection of API records List apiRecords = new ArrayList<>(); for (InternalRecordType record : history.asCollection()) @@ -141,4 +175,72 @@ public abstract class ActivityRecordSetResource requiredContents, + @QueryParam("order") List sortPredicates) + throws GuacamoleException { + + // Parse UUID from provided string + UUID parsedUUID; + try { + parsedUUID = UUID.fromString(uuid); + } + catch (IllegalArgumentException e) { + throw new GuacamoleClientException("Invalid UUID.", e); + } + + // Apply search/sort criteria + applyCriteria(requiredContents, sortPredicates); + + // Locate record having given UUID among all visible records + for (InternalRecordType record : history.asCollection()) { + + // Ignore records lacking any UUID + UUID recordUUID = record.getUUID(); + if (recordUUID == null) + continue; + + if (recordUUID.equals(parsedUUID)) + return new ActivityRecordResource(record); + + } + + throw new GuacamoleResourceNotFoundException("No such history entry."); + + } + }