From 449fcb828e7b333bc7968f7e33ca6cfee17fc9d0 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 9 Feb 2022 15:33:56 -0800 Subject: [PATCH] GUACAMOLE-462: Allow individual records to be identified and retrieved directly. --- .../auth/jdbc/base/ModeledActivityRecord.java | 11 +++ .../guacamole/net/auth/ActivityRecord.java | 15 ++++ .../guacamole/net/auth/ActivityRecordSet.java | 24 +++++++ .../net/auth/DecoratingActivityRecordSet.java | 11 +++ .../net/auth/DelegatingActivityRecord.java | 5 ++ .../net/auth/DelegatingActivityRecordSet.java | 5 ++ .../rest/history/APIActivityRecord.java | 13 ++++ .../rest/history/ActivityRecordResource.java | 27 ++++++- .../history/ActivityRecordSetResource.java | 72 ++++--------------- 9 files changed, 124 insertions(+), 59 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledActivityRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledActivityRecord.java index 927e78c3f..5c2c3285b 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledActivityRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledActivityRecord.java @@ -100,6 +100,17 @@ public class ModeledActivityRecord implements ActivityRecord { return false; } + @Override + public String getIdentifier() { + + Integer id = model.getRecordID(); + if (id == null) + return null; + + return id.toString(); + + } + @Override public UUID getUUID() { diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActivityRecord.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActivityRecord.java index cdf38b69e..df321d71a 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActivityRecord.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActivityRecord.java @@ -78,6 +78,21 @@ public interface ActivityRecord extends ReadableAttributes { */ public boolean isActive(); + /** + * Returns the unique identifier assigned to this record, if any. If this + * record is not uniquely identifiable, this may be null. If provided, this + * unique identifier MUST be unique across all {@link ActivityRecord} + * objects within the same {@link ActivityRecordSet}. + * + * @return + * The unique identifier assigned to this record, or null if this + * record has no such identifier. + */ + public default String getIdentifier() { + UUID uuid = getUUID(); + return uuid != null ? uuid.toString() : null; + } + /** * Returns a UUID that uniquely identifies this record. If provided, this * UUID MUST be deterministic and unique across all {@link ActivityRecord} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActivityRecordSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActivityRecordSet.java index 4cce03e65..f5b930558 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActivityRecordSet.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActivityRecordSet.java @@ -57,6 +57,30 @@ public interface ActivityRecordSet { */ Collection asCollection() throws GuacamoleException; + /** + * Returns the record having the given unique identifier, if records within + * this set have unique identifiers. If records within this set do not have + * defined unique identifiers, this function has no effect. + * + * @param identifier + * The unique identifier of the record to retrieve. + * + * @return + * The record having the given unique identifier, or null if there is + * no such record. + * + * @throws GuacamoleException + * If an error occurs while retrieving the record. + */ + default RecordType get(String identifier) throws GuacamoleException { + return asCollection().stream() + .filter((record) -> { + String recordIdentifier = record.getIdentifier(); + return recordIdentifier != null && recordIdentifier.equals(identifier); + }) + .findFirst().orElse(null); + } + /** * Returns the subset of records which contain the given value. The * properties and semantics involved with determining whether a particular diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DecoratingActivityRecordSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DecoratingActivityRecordSet.java index 295f842f9..3461d9d57 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DecoratingActivityRecordSet.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DecoratingActivityRecordSet.java @@ -122,6 +122,17 @@ public abstract class DecoratingActivityRecordSet sort(SortableProperty property, boolean desc) throws GuacamoleException { diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingActivityRecord.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingActivityRecord.java index 63af2986f..155612b25 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingActivityRecord.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingActivityRecord.java @@ -81,6 +81,11 @@ public class DelegatingActivityRecord implements ActivityRecord { return record.isActive(); } + @Override + public String getIdentifier() { + return record.getIdentifier(); + } + @Override public UUID getUUID() { return record.getUUID(); diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingActivityRecordSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingActivityRecordSet.java index 32ecd2198..84ae9541c 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingActivityRecordSet.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingActivityRecordSet.java @@ -59,6 +59,11 @@ public class DelegatingActivityRecordSet return recordSet; } + @Override + public RecordType get(String identifier) throws GuacamoleException { + return recordSet.get(identifier); + } + @Override public Collection asCollection() throws GuacamoleException { return recordSet.asCollection(); diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/history/APIActivityRecord.java b/guacamole/src/main/java/org/apache/guacamole/rest/history/APIActivityRecord.java index 562bf7960..0825c5037 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/history/APIActivityRecord.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/APIActivityRecord.java @@ -96,6 +96,7 @@ public class APIActivityRecord { this.remoteHost = record.getRemoteHost(); this.username = record.getUsername(); this.active = record.isActive(); + this.identifier = record.getIdentifier(); this.uuid = record.getUUID(); this.attributes = record.getAttributes(); @@ -164,6 +165,18 @@ public class APIActivityRecord { return active; } + /** + * Returns the unique identifier assigned to this record, if any. If this + * record is not uniquely identifiable, this may be null. + * + * @return + * The unique identifier assigned to this record, or null if this + * record has no such identifier. + */ + public String getIdentifier() { + return identifier; + } + /** * Returns a UUID that uniquely identifies this record. If not implemented * by the extension exposing this history record, this may be null. 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 index ae7e3333d..5b5faf4a6 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/history/ActivityRecordResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/ActivityRecordResource.java @@ -19,7 +19,9 @@ package org.apache.guacamole.rest.history; +import java.util.function.Function; 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; @@ -42,14 +44,37 @@ public class ActivityRecordResource { */ private final ActivityRecord record; + /** + * The REST API object representing the ActivityRecord being exposed. + */ + private final APIActivityRecord externalRecord; + /** * Creates a new ActivityRecordResource which exposes the given record. * * @param record * The ActivityRecord that should be exposed. + * + * @param externalRecord + * The REST API object representing the ActivityRecord being exposed. */ - public ActivityRecordResource(ActivityRecord record) { + public ActivityRecordResource(ActivityRecord record, + APIActivityRecord externalRecord) { this.record = record; + this.externalRecord = externalRecord; + } + + /** + * Returns the record represented by this ActivityRecordResource, in a + * format intended for interchange. + * + * @return + * The record that this ActivityRecordResource represents, in a format + * intended for interchange. + */ + @GET + public APIActivityRecord getRecord() { + return externalRecord; } /** 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 5cd245a18..b6fdc49d9 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,7 +21,6 @@ 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; @@ -29,7 +28,6 @@ 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; @@ -176,70 +174,28 @@ public abstract class ActivityRecordSetResource requiredContents, - @QueryParam("order") List sortPredicates) - throws GuacamoleException { + @Path("{identifier}") + public ActivityRecordResource getRecord(@PathParam("identifier") String identifier) + throws GuacamoleException { - // Parse UUID from provided string - UUID parsedUUID; - try { - parsedUUID = UUID.fromString(uuid); - } - catch (IllegalArgumentException e) { - throw new GuacamoleClientException("Invalid UUID.", e); - } + InternalRecordType record = history.get(identifier); + if (record == null) + throw new GuacamoleResourceNotFoundException("Not found: \"" + identifier + "\""); - // 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."); + return new ActivityRecordResource(record, toExternalRecord(record)); }