GUACAMOLE-462: Allow individual records to be identified and retrieved directly.

This commit is contained in:
Michael Jumper
2022-02-09 15:33:56 -08:00
parent 6874f9c6bd
commit 449fcb828e
9 changed files with 124 additions and 59 deletions

View File

@@ -100,6 +100,17 @@ public class ModeledActivityRecord implements ActivityRecord {
return false; return false;
} }
@Override
public String getIdentifier() {
Integer id = model.getRecordID();
if (id == null)
return null;
return id.toString();
}
@Override @Override
public UUID getUUID() { public UUID getUUID() {

View File

@@ -78,6 +78,21 @@ public interface ActivityRecord extends ReadableAttributes {
*/ */
public boolean isActive(); 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 * Returns a UUID that uniquely identifies this record. If provided, this
* UUID MUST be deterministic and unique across all {@link ActivityRecord} * UUID MUST be deterministic and unique across all {@link ActivityRecord}

View File

@@ -57,6 +57,30 @@ public interface ActivityRecordSet<RecordType extends ActivityRecord> {
*/ */
Collection<RecordType> asCollection() throws GuacamoleException; Collection<RecordType> 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 * Returns the subset of records which contain the given value. The
* properties and semantics involved with determining whether a particular * properties and semantics involved with determining whether a particular

View File

@@ -122,6 +122,17 @@ public abstract class DecoratingActivityRecordSet<RecordType extends ActivityRec
}; };
} }
@Override
public RecordType get(String string) throws GuacamoleException {
RecordType record = super.get(string);
if (record != null)
return decorate(record);
return null;
}
@Override @Override
public ActivityRecordSet<RecordType> sort(SortableProperty property, public ActivityRecordSet<RecordType> sort(SortableProperty property,
boolean desc) throws GuacamoleException { boolean desc) throws GuacamoleException {

View File

@@ -81,6 +81,11 @@ public class DelegatingActivityRecord implements ActivityRecord {
return record.isActive(); return record.isActive();
} }
@Override
public String getIdentifier() {
return record.getIdentifier();
}
@Override @Override
public UUID getUUID() { public UUID getUUID() {
return record.getUUID(); return record.getUUID();

View File

@@ -59,6 +59,11 @@ public class DelegatingActivityRecordSet<RecordType extends ActivityRecord>
return recordSet; return recordSet;
} }
@Override
public RecordType get(String identifier) throws GuacamoleException {
return recordSet.get(identifier);
}
@Override @Override
public Collection<RecordType> asCollection() throws GuacamoleException { public Collection<RecordType> asCollection() throws GuacamoleException {
return recordSet.asCollection(); return recordSet.asCollection();

View File

@@ -96,6 +96,7 @@ public class APIActivityRecord {
this.remoteHost = record.getRemoteHost(); this.remoteHost = record.getRemoteHost();
this.username = record.getUsername(); this.username = record.getUsername();
this.active = record.isActive(); this.active = record.isActive();
this.identifier = record.getIdentifier();
this.uuid = record.getUUID(); this.uuid = record.getUUID();
this.attributes = record.getAttributes(); this.attributes = record.getAttributes();
@@ -164,6 +165,18 @@ public class APIActivityRecord {
return active; 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 * Returns a UUID that uniquely identifies this record. If not implemented
* by the extension exposing this history record, this may be null. * by the extension exposing this history record, this may be null.

View File

@@ -19,7 +19,9 @@
package org.apache.guacamole.rest.history; package org.apache.guacamole.rest.history;
import java.util.function.Function;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
@@ -42,14 +44,37 @@ public class ActivityRecordResource {
*/ */
private final ActivityRecord record; 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. * Creates a new ActivityRecordResource which exposes the given record.
* *
* @param record * @param record
* The ActivityRecord that should be exposed. * 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.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;
} }
/** /**

View File

@@ -21,7 +21,6 @@ package org.apache.guacamole.rest.history;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.Path; import javax.ws.rs.Path;
@@ -29,7 +28,6 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam; import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import org.apache.guacamole.GuacamoleClientException;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleResourceNotFoundException; import org.apache.guacamole.GuacamoleResourceNotFoundException;
import org.apache.guacamole.net.auth.ActivityRecord; import org.apache.guacamole.net.auth.ActivityRecord;
@@ -176,70 +174,28 @@ public abstract class ActivityRecordSetResource<InternalRecordType extends Activ
} }
/** /**
* Retrieves record having the given UUID from among the list of activity * Retrieves record having the given identifier from among the set of
* records stored within the underlying ActivityRecordSet which match the * activity records stored within the underlying ActivityRecordSet.
* given, arbitrary criteria. If specified, the returned records will also
* be sorted according to the given sort predicates. As the number of
* activity records retrieved at any given time may be limited by the
* extension providing those records, the sorting and search criteria may
* impact whether the record having a particular UUID can be located, even
* if it is known that the record exists.
* *
* @param uuid * @param identifier
* The UUID of the record to retrieve. * The unique identifier of the record to retrieve.
*
* @param requiredContents
* The set of strings that each must occur somewhere within the
* relevant records, whether within the associated username,
* the name of some associated object (such as a connection), or any
* associated date. If non-empty, any record not matching each of the
* strings within the collection will not be considered.
*
* @param sortPredicates
* A list of predicates to apply while sorting the relevant records,
* describing the properties involved and the sort order for those
* properties.
* *
* @return * @return
* The record having the given UUID which matches the provided * A resource representing the record having the given identifier.
* criteria.
* *
* @throws GuacamoleException * @throws GuacamoleException
* If an error occurs while applying the given filter criteria or * If an error occurs while locating the requested record, or if the
* sort predicates, or if the requested record cannot be found. * requested record cannot be found.
*/ */
@Path("{uuid}") @Path("{identifier}")
public ActivityRecordResource getRecord(@PathParam("uuid") String uuid, public ActivityRecordResource getRecord(@PathParam("identifier") String identifier)
@QueryParam("contains") List<String> requiredContents,
@QueryParam("order") List<APISortPredicate> sortPredicates)
throws GuacamoleException { throws GuacamoleException {
// Parse UUID from provided string InternalRecordType record = history.get(identifier);
UUID parsedUUID; if (record == null)
try { throw new GuacamoleResourceNotFoundException("Not found: \"" + identifier + "\"");
parsedUUID = UUID.fromString(uuid);
}
catch (IllegalArgumentException e) {
throw new GuacamoleClientException("Invalid UUID.", e);
}
// Apply search/sort criteria return new ActivityRecordResource(record, toExternalRecord(record));
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.");
} }