From 0cca98d0b270d4daa7c9b6d5ccd34d57e693cbcd Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sat, 11 Dec 2021 14:58:30 -0800 Subject: [PATCH] GUACAMOLE-462: Directly support associating session recordings with history entries at API level. --- .../net/auth/AbstractActivityLog.java | 65 ++++++++ .../guacamole/net/auth/ActivityLog.java | 142 ++++++++++++++++++ .../guacamole/net/auth/ActivityRecord.java | 14 ++ .../net/auth/DelegatingActivityRecord.java | 5 + .../guacamole/net/auth/FileActivityLog.java | 74 +++++++++ .../rest/history/APIActivityLog.java | 74 +++++++++ .../rest/history/APIActivityRecord.java | 30 +++- 7 files changed, 402 insertions(+), 2 deletions(-) create mode 100644 guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractActivityLog.java create mode 100644 guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActivityLog.java create mode 100644 guacamole-ext/src/main/java/org/apache/guacamole/net/auth/FileActivityLog.java create mode 100644 guacamole/src/main/java/org/apache/guacamole/rest/history/APIActivityLog.java diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractActivityLog.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractActivityLog.java new file mode 100644 index 000000000..bff1271a4 --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractActivityLog.java @@ -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.net.auth; + +import org.apache.guacamole.language.TranslatableMessage; + +/** + * Base implementation of an ActivityLog, providing storage and simple + * getters/setters for its main properties. + */ +public abstract class AbstractActivityLog implements ActivityLog { + + /** + * The type of this ActivityLog. + */ + private final Type type; + + /** + * A human-readable description of this log. + */ + private final TranslatableMessage description; + + /** + * Creates a new AbstractActivityLog having the given type and + * human-readable description. + * + * @param type + * The type of this ActivityLog. + * + * @param description + * A human-readable message that describes this log. + */ + public AbstractActivityLog(Type type, TranslatableMessage description) { + this.type = type; + this.description = description; + } + + @Override + public Type getType() { + return type; + } + + @Override + public TranslatableMessage getDescription() { + return description; + } + +} diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActivityLog.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActivityLog.java new file mode 100644 index 000000000..f55ea493d --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/ActivityLog.java @@ -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.net.auth; + +import java.io.InputStream; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.language.TranslatableMessage; + +/** + * An arbitrary log of an activity whose content may be exposed to a user with + * sufficient privileges. Types of content that might be exposed in this way + * include textual server logs, Guacamole session recordings, and typescripts. + */ +public interface ActivityLog { + + /** + * The value returned by {@link #getSize()} if the number of available + * bytes within {@link #getContent()} is unknown. + */ + public static final long UNKNOWN_SIZE = -1; + + /** + * All possible types of {@link ActivityLog}. + */ + enum Type { + + /** + * A Guacamole session recording in the form of a Guacamole protocol + * dump. + */ + GUACAMOLE_SESSION_RECORDING("application/octet-stream"), + + /** + * A text log from a server-side process, such as the Guacamole web + * application or guacd. + */ + SERVER_LOG("text/plain"), + + /** + * A text session recording in the form of a standard typescript. + */ + TYPESCRIPT("application/octet-stream"), + + /** + * The timing file related to a typescript. + */ + TYPESCRIPT_TIMING("text/plain"); + + /** + * The MIME type of the content of an activity log of this type. + */ + private final String contentType; + + /** + * Creates a new Type that may be associated with content having the + * given MIME type. + * + * @param contentType + * The MIME type of the content of an activity log of this type. + */ + Type(String contentType) { + this.contentType = contentType; + } + + /** + * Returns the MIME type of the content of an activity log of this + * type, as might be sent via the HTTP "Content-Type" header. + * + * @return + * The MIME type of the content of an activity log of this type. + */ + public String getContentType() { + return contentType; + } + + } + + /** + * Returns the type of this activity log. The type of an activity log + * dictates how its content should be interpreted or exposed. + * + * @return + * The type of this activity log. + */ + Type getType(); + + /** + * Returns a human-readable message that describes this log. This message + * should provide sufficient information for a user with access to this + * log to understand its context and/or purpose. + * + * @return + * A human-readable message that describes this log. + */ + TranslatableMessage getDescription(); + + /** + * Returns the number of bytes available for reading within the content of + * this log. If this value is unknown, -1 ({@link #UNKNOWN_SIZE}) should be + * returned. + * + * @return + * The number of bytes available for reading within the content of + * this log, or -1 ({@link #UNKNOWN_SIZE}) if this value is unknown. + * + * @throws GuacamoleException + * If the size of the content of this log cannot be determined due to + * an error. + */ + long getSize() throws GuacamoleException; + + /** + * Returns an InputStream that allows the content of this log to be read. + * Multiple instances of this InputStream may be open at any given time. It + * is the responsibility of the caller to close the returned InputStream. + * + * @return + * An InputStream that allows the content of this log to be read. + * + * @throws GuacamoleException + * If the content of this log cannot be read due to an error. + */ + InputStream getContent() throws GuacamoleException; + +} 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 13d4bf3a2..cdf38b69e 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 @@ -92,6 +92,20 @@ public interface ActivityRecord extends ReadableAttributes { return null; } + /** + * Returns a Map of logs related to this record and accessible by the + * current user, such as Guacamole session recordings. Each log is + * associated with a corresponding, arbitrary, unique name. If the user + * does not have access to any logs, or if no logs are available, this may + * be an empty map. + * + * @return + * A Map of logs related to this record. + */ + public default Map getLogs() { + return Collections.emptyMap(); + } + @Override public default Map getAttributes() { return Collections.emptyMap(); 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 1a9b30ee0..63af2986f 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 @@ -86,6 +86,11 @@ public class DelegatingActivityRecord implements ActivityRecord { return record.getUUID(); } + @Override + public Map getLogs() { + return record.getLogs(); + } + @Override public Map getAttributes() { return record.getAttributes(); diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/FileActivityLog.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/FileActivityLog.java new file mode 100644 index 000000000..df3cc31c2 --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/FileActivityLog.java @@ -0,0 +1,74 @@ +/* + * 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.net.auth; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleResourceNotFoundException; +import org.apache.guacamole.language.TranslatableMessage; + +/** + * ActivityLog implementation that exposes the content of a local file. + */ +public class FileActivityLog extends AbstractActivityLog { + + /** + * The File providing the content of this log. + */ + private final File content; + + /** + * Creates a new FileActivityLog that exposes the content of the given + * local file as an {@link ActivityLog}. + * + * @param type + * The type of this ActivityLog. + * + * @param description + * A human-readable message that describes this log. + * + * @param content + * The File that should be used to provide the content of this log. + */ + public FileActivityLog(Type type, TranslatableMessage description, File content) { + super(type, description); + this.content = content; + } + + @Override + public long getSize() throws GuacamoleException { + return content.length(); + } + + @Override + public InputStream getContent() throws GuacamoleException { + try { + return new FileInputStream(content); + } + catch (FileNotFoundException e) { + throw new GuacamoleResourceNotFoundException("Associated file " + + "does not exist or cannot be read.", e); + } + } + +} diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/history/APIActivityLog.java b/guacamole/src/main/java/org/apache/guacamole/rest/history/APIActivityLog.java new file mode 100644 index 000000000..385f6f855 --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/APIActivityLog.java @@ -0,0 +1,74 @@ +/* + * 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 org.apache.guacamole.language.TranslatableMessage; +import org.apache.guacamole.net.auth.ActivityLog; + +/** + * A activity log which may be exposed through the REST endpoints. + */ +public class APIActivityLog { + + /** + * The type of this ActivityLog. + */ + private final ActivityLog.Type type; + + /** + * A human-readable description of this log. + */ + private final TranslatableMessage description; + + /** + * Creates a new APIActivityLog, copying the data from the given activity + * log. + * + * @param log + * The log to copy data from. + */ + public APIActivityLog(ActivityLog log) { + this.type = log.getType(); + this.description = log.getDescription(); + } + + /** + * Returns the type of this activity log. The type of an activity log + * dictates how its content should be interpreted or exposed, however the + * content of a log is not directly exposed by this class. + * + * @return + * The type of this activity log. + */ + public ActivityLog.Type getType() { + return type; + } + + /** + * Returns a human-readable message that describes this log. + * + * @return + * A human-readable message that describes this log. + */ + public TranslatableMessage getDescription() { + return description; + } + +} 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 0eb6f62e0..562bf7960 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 @@ -22,6 +22,7 @@ package org.apache.guacamole.rest.history; import java.util.Date; import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; import org.apache.guacamole.net.auth.ActivityRecord; /** @@ -74,7 +75,13 @@ public class APIActivityRecord { * all attributes associated with this record. */ private final Map attributes; - + + /** + * A map of all logs associated and accessible via this record, associated + * with their corresponding unique names. + */ + private final Map logs; + /** * Creates a new APIActivityRecord, copying the data from the given activity * record. @@ -83,6 +90,7 @@ public class APIActivityRecord { * The record to copy data from. */ public APIActivityRecord(ActivityRecord record) { + this.startDate = record.getStartDate(); this.endDate = record.getEndDate(); this.remoteHost = record.getRemoteHost(); @@ -90,6 +98,12 @@ public class APIActivityRecord { this.active = record.isActive(); this.uuid = record.getUUID(); this.attributes = record.getAttributes(); + + this.logs = record.getLogs().entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, + (entry) -> new APIActivityLog(entry.getValue()) + )); + } /** @@ -172,5 +186,17 @@ public class APIActivityRecord { public Map getAttributes() { return attributes; } - + + /** + * Returns a Map of logs related to this record and accessible by the + * current user, such as Guacamole session recordings. Each log is + * associated with a corresponding, arbitrary, unique name. + * + * @return + * A Map of logs related to this record. + */ + public Map getLogs() { + return logs; + } + }