GUACAMOLE-1152: Merge correct handling of client vs. server exceptions.
diff --git a/Dockerfile b/Dockerfile
index 0943dc3..2b70f67 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -25,7 +25,7 @@
# such as `--build-arg TOMCAT_JRE=jre8-alpine`
#
ARG TOMCAT_VERSION=8.5
-ARG TOMCAT_JRE=jre8
+ARG TOMCAT_JRE=jdk8
# Use official maven image for the build
FROM maven:3-jdk-8 AS builder
diff --git a/extensions/guacamole-auth-cas/src/main/resources/guac-manifest.json b/extensions/guacamole-auth-cas/src/main/resources/guac-manifest.json
index e34c3a6..dfee2f8 100644
--- a/extensions/guacamole-auth-cas/src/main/resources/guac-manifest.json
+++ b/extensions/guacamole-auth-cas/src/main/resources/guac-manifest.json
@@ -14,18 +14,6 @@
"translations/en.json",
"translations/ja.json",
"translations/ru.json"
- ],
-
- "js" : [
- "cas.min.js"
- ],
-
- "css" : [
- "cas.min.css"
- ],
-
- "resources" : {
- "templates/casTicketField.html" : "text/html"
- }
+ ]
}
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLAuthenticationProviderModule.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLAuthenticationProviderModule.java
index 4e4f35e..92bc541 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLAuthenticationProviderModule.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLAuthenticationProviderModule.java
@@ -95,7 +95,7 @@
File trustStore = environment.getMySQLSSLTrustStore();
if (trustStore != null)
driverProperties.setProperty("trustCertificateKeyStoreUrl",
- trustStore.getAbsolutePath());
+ trustStore.toURI().toString());
String trustPassword = environment.getMySQLSSLTrustPassword();
if (trustPassword != null)
@@ -105,7 +105,7 @@
File clientStore = environment.getMySQLSSLClientStore();
if (clientStore != null)
driverProperties.setProperty("clientCertificateKeyStoreUrl",
- clientStore.getAbsolutePath());
+ clientStore.toURI().toString());
String clientPassword = environment.getMYSQLSSLClientPassword();
if (clientPassword != null)
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLEnvironment.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLEnvironment.java
index a27c2aa..f452319 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLEnvironment.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLEnvironment.java
@@ -369,7 +369,7 @@
* If guacamole.properties cannot be parsed.
*/
public File getMySQLSSLClientStore() throws GuacamoleException {
- return getProperty(MySQLGuacamoleProperties.MYSQL_SSL_TRUST_STORE);
+ return getProperty(MySQLGuacamoleProperties.MYSQL_SSL_CLIENT_STORE);
}
/**
@@ -384,7 +384,7 @@
* If guacamole.properties cannot be parsed.
*/
public String getMYSQLSSLClientPassword() throws GuacamoleException {
- return getProperty(MySQLGuacamoleProperties.MYSQL_SSL_TRUST_PASSWORD);
+ return getProperty(MySQLGuacamoleProperties.MYSQL_SSL_CLIENT_PASSWORD);
}
@Override
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/PostgreSQLAuthenticationProviderModule.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/PostgreSQLAuthenticationProviderModule.java
index 331707c..280cead 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/PostgreSQLAuthenticationProviderModule.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/PostgreSQLAuthenticationProviderModule.java
@@ -70,6 +70,15 @@
myBatisProperties.setProperty("mybatis.pooled.pingEnabled", "true");
myBatisProperties.setProperty("mybatis.pooled.pingQuery", "SELECT 1");
+ // Only set if > 0. Underlying backend does not take 0 as not-set.
+ int defaultStatementTimeout = environment.getPostgreSQLDefaultStatementTimeout();
+ if (defaultStatementTimeout > 0) {
+ myBatisProperties.setProperty(
+ "mybatis.configuration.defaultStatementTimeout",
+ String.valueOf(defaultStatementTimeout)
+ );
+ }
+
// Use UTF-8 in database
driverProperties.setProperty("characterEncoding", "UTF-8");
@@ -110,6 +119,12 @@
}
+ // Handle case where TCP connection to database is silently dropped
+ driverProperties.setProperty(
+ "socketTimeout",
+ String.valueOf(environment.getPostgreSQLSocketTimeout())
+ );
+
}
@Override
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLEnvironment.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLEnvironment.java
index e81e694..012877c 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLEnvironment.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLEnvironment.java
@@ -49,6 +49,19 @@
private static final int DEFAULT_PORT = 5432;
/**
+ * The default number of seconds the driver will wait for a response from
+ * the database, before aborting the query.
+ * A value of 0 (the default) means the timeout is disabled.
+ */
+ private static final int DEFAULT_STATEMENT_TIMEOUT = 0;
+
+ /**
+ * The default number of seconds to wait for socket read operations.
+ * A value of 0 (the default) means the timeout is disabled.
+ */
+ private static final int DEFAULT_SOCKET_TIMEOUT = 0;
+
+ /**
* Whether a database user account is required by default for authentication
* to succeed.
*/
@@ -249,6 +262,41 @@
public String getPostgreSQLPassword() throws GuacamoleException {
return getRequiredProperty(PostgreSQLGuacamoleProperties.POSTGRESQL_PASSWORD);
}
+
+ /**
+ * Returns the defaultStatementTimeout set for PostgreSQL connections.
+ * If unspecified, this will default to 0,
+ * and should not be passed through to the backend.
+ *
+ * @return
+ * The statement timeout (in seconds)
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the property value.
+ */
+ public int getPostgreSQLDefaultStatementTimeout() throws GuacamoleException {
+ return getProperty(
+ PostgreSQLGuacamoleProperties.POSTGRESQL_DEFAULT_STATEMENT_TIMEOUT,
+ DEFAULT_STATEMENT_TIMEOUT
+ );
+ }
+
+ /**
+ * Returns the socketTimeout property to set on PostgreSQL connections.
+ * If unspecified, this will default to 0 (no timeout)
+ *
+ * @return
+ * The socketTimeout to use when waiting on read operations (in seconds)
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the property value.
+ */
+ public int getPostgreSQLSocketTimeout() throws GuacamoleException {
+ return getProperty(
+ PostgreSQLGuacamoleProperties.POSTGRESQL_SOCKET_TIMEOUT,
+ DEFAULT_SOCKET_TIMEOUT
+ );
+ }
@Override
public boolean isRecursiveQuerySupported(SqlSession session) {
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLGuacamoleProperties.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLGuacamoleProperties.java
index d2ae253..271d9c0 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLGuacamoleProperties.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLGuacamoleProperties.java
@@ -95,6 +95,36 @@
};
/**
+ * The number of seconds the driver will wait for a response from
+ * the database, before aborting the query.
+ * A value of 0 (the default) means the timeout is disabled.
+ */
+ public static final IntegerGuacamoleProperty
+ POSTGRESQL_DEFAULT_STATEMENT_TIMEOUT = new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "postgresql-default-statement-timeout"; }
+
+ };
+
+ /**
+ * The number of seconds to wait for socket read operations.
+ * If reading from the server takes longer than this value, the
+ * connection will be closed. This can be used to handle network problems
+ * such as a dropped connection to the database. Similar to
+ * postgresql-default-statement-timeout, it will have the effect of
+ * aborting queries that take too long.
+ * A value of 0 (the default) means the timeout is disabled.
+ */
+ public static final IntegerGuacamoleProperty
+ POSTGRESQL_SOCKET_TIMEOUT = new IntegerGuacamoleProperty() {
+
+ @Override
+ public String getName() { return "postgresql-socket-timeout"; }
+
+ };
+
+ /**
* Whether a user account within the database is required for authentication
* to succeed, even if the user has been authenticated via another
* authentication provider.
diff --git a/guacamole-docker/bin/build-guacamole.sh b/guacamole-docker/bin/build-guacamole.sh
index 1b0b0ec..5ee3115 100755
--- a/guacamole-docker/bin/build-guacamole.sh
+++ b/guacamole-docker/bin/build-guacamole.sh
@@ -159,3 +159,28 @@
--strip-components=1 \
"*.jar"
fi
+
+#
+# Copy header auth extension if it was built
+#
+
+if [ -f extensions/guacamole-auth-header/target/guacamole-auth-header*.jar ]; then
+ mkdir -p "$DESTINATION/header"
+ cp extensions/guacamole-auth-header/target/guacamole-auth-header*.jar "$DESTINATION/header"
+fi
+
+#
+# Copy CAS auth extension if it was built
+#
+
+if [ -f extensions/guacamole-auth-cas/target/*.tar.gz ]; then
+ mkdir -p "$DESTINATION/cas"
+ tar -xzf extensions/guacamole-auth-cas/target/*.tar.gz \
+ -C "$DESTINATION/cas/" \
+ --wildcards \
+ --no-anchored \
+ --no-wildcards-match-slash \
+ --strip-components=1 \
+ "*.jar"
+fi
+
diff --git a/guacamole-docker/bin/start.sh b/guacamole-docker/bin/start.sh
index 3df1198..5123a8a 100755
--- a/guacamole-docker/bin/start.sh
+++ b/guacamole-docker/bin/start.sh
@@ -354,10 +354,18 @@
"postgresql-default-max-group-connections-per-user" \
"$POSTGRES_DEFAULT_MAX_GROUP_CONNECTIONS_PER_USER"
+ set_optional_property \
+ "postgresql-default-statement-timeout" \
+ "$POSTGRES_DEFAULT_STATEMENT_TIMEOUT"
+
set_optional_property \
"postgresql-user-required" \
"$POSTGRES_USER_REQUIRED"
+ set_optional_property \
+ "postgresql-socket-timeout" \
+ "$POSTGRES_SOCKET_TIMEOUT"
+
set_optional_property \
"postgresql-ssl-mode" \
"$POSTGRESQL_SSL_MODE"
@@ -413,35 +421,26 @@
fi
# Update config file
- set_property "ldap-hostname" "$LDAP_HOSTNAME"
- set_optional_property "ldap-port" "$LDAP_PORT"
- set_optional_property "ldap-encryption-method" "$LDAP_ENCRYPTION_METHOD"
- set_optional_property "ldap-max-search-results" "$LDAP_MAX_SEARCH_RESULTS"
- set_optional_property "ldap-search-bind-dn" "$LDAP_SEARCH_BIND_DN"
- set_optional_property "ldap-user-attributes" "$LDAP_USER_ATTRIBUTES"
+ set_property "ldap-hostname" "$LDAP_HOSTNAME"
+ set_property "ldap-user-base-dn" "$LDAP_USER_BASE_DN"
- set_optional_property \
- "ldap-search-bind-password" \
- "$LDAP_SEARCH_BIND_PASSWORD"
-
- set_property "ldap-user-base-dn" "$LDAP_USER_BASE_DN"
- set_optional_property "ldap-username-attribute" "$LDAP_USERNAME_ATTRIBUTE"
- set_optional_property "ldap-member-attribute" "$LDAP_MEMBER_ATTRIBUTE"
- set_optional_property "ldap-user-search-filter" "$LDAP_USER_SEARCH_FILTER"
- set_optional_property "ldap-config-base-dn" "$LDAP_CONFIG_BASE_DN"
- set_optional_property "ldap-group-base-dn" "$LDAP_GROUP_BASE_DN"
-
- set_optional_property \
- "ldap-group-name-attribute" \
- "$LDAP_GROUP_NAME_ATTRIBUTE"
-
- set_optional_property \
- "ldap-dereference-aliases" \
- "$LDAP_DEREFERENCE_ALIASES"
-
- set_optional_property "ldap-follow-referrals" "$LDAP_FOLLOW_REFERRALS"
- set_optional_property "ldap-max-referral-hops" "$LDAP_MAX_REFERRAL_HOPS"
- set_optional_property "ldap-operation-timeout" "$LDAP_OPERATION_TIMEOUT"
+ set_optional_property "ldap-port" "$LDAP_PORT"
+ set_optional_property "ldap-encryption-method" "$LDAP_ENCRYPTION_METHOD"
+ set_optional_property "ldap-max-search-results" "$LDAP_MAX_SEARCH_RESULTS"
+ set_optional_property "ldap-search-bind-dn" "$LDAP_SEARCH_BIND_DN"
+ set_optional_property "ldap-user-attributes" "$LDAP_USER_ATTRIBUTES"
+ set_optional_property "ldap-search-bind-password" "$LDAP_SEARCH_BIND_PASSWORD"
+ set_optional_property "ldap-username-attribute" "$LDAP_USERNAME_ATTRIBUTE"
+ set_optional_property "ldap-member-attribute" "$LDAP_MEMBER_ATTRIBUTE"
+ set_optional_property "ldap-user-search-filter" "$LDAP_USER_SEARCH_FILTER"
+ set_optional_property "ldap-config-base-dn" "$LDAP_CONFIG_BASE_DN"
+ set_optional_property "ldap-group-base-dn" "$LDAP_GROUP_BASE_DN"
+ set_optional_property "ldap-member-attribute-type" "$LDAP_MEMBER_ATTRIBUTE_TYPE"
+ set_optional_property "ldap-group-name-attribute" "$LDAP_GROUP_NAME_ATTRIBUTE"
+ set_optional_property "ldap-dereference-aliases" "$LDAP_DEREFERENCE_ALIASES"
+ set_optional_property "ldap-follow-referrals" "$LDAP_FOLLOW_REFERRALS"
+ set_optional_property "ldap-max-referral-hops" "$LDAP_MAX_REFERRAL_HOPS"
+ set_optional_property "ldap-operation-timeout" "$LDAP_OPERATION_TIMEOUT"
# Add required .jar files to GUACAMOLE_EXT
ln -s /opt/guacamole/ldap/guacamole-auth-*.jar "$GUACAMOLE_EXT"
@@ -641,6 +640,50 @@
}
##
+## Adds properties to guacamole.properties which configure the header
+## authentication provider.
+##
+associate_header() {
+ # Update config file
+ set_optional_property "http-auth-header" "$HTTP_AUTH_HEADER"
+
+ # Add required .jar files to GUACAMOLE_EXT
+ ln -s /opt/guacamole/header/guacamole-auth-*.jar "$GUACAMOLE_EXT"
+}
+
+##
+## Adds properties to guacamole.properties witch configure the CAS
+## authentication service.
+##
+associate_cas() {
+ # Verify required parameters are present
+ if [ -z "$CAS_AUTHORIZATION_ENDPOINT" ] || \
+ [ -z "$CAS_REDIRECT_URI" ]
+ then
+ cat <<END
+FATAL: Missing required environment variables
+-----------------------------------------------------------------------------------
+If using the CAS authentication extension, you must provide each of the
+following environment variables:
+
+ CAS_AUTHORIZATION_ENDPOINT The URL of the CAS authentication server.
+
+ CAS_REDIRECT_URI The URI to redirect back to upon successful authentication.
+
+END
+ exit 1;
+ fi
+
+ # Update config file
+ set_property "cas-authorization-endpoint" "$CAS_AUTHORIZATION_ENDPOINT"
+ set_property "cas-redirect-uri" "$CAS_REDIRECT_URI"
+ set_optional_property "cas-clearpass-key" "$CAS_CLEARPASS_KEY"
+
+ # Add required .jar files to GUACAMOLE_EXT
+ ln -s /opt/guacamole/cas/guacamole-auth-*.jar "$GUACAMOLE_EXT"
+}
+
+##
## Starts Guacamole under Tomcat, replacing the current process with the
## Tomcat process. As the current process will be replaced, this MUST be the
## last function run within the script.
@@ -782,6 +825,16 @@
associate_duo
fi
+# Use header if specified.
+if [ "$HEADER_ENABLED" = "true" ]; then
+ associate_header
+fi
+
+# Use CAS if specified.
+if [ -n "$CAS_AUTHORIZATION_ENDPOINT" ]; then
+ associate_cas
+fi
+
# Set logback level if specified
if [ -n "$LOGBACK_LEVEL" ]; then
unzip -o -j /opt/guacamole/guacamole.war WEB-INF/classes/logback.xml -d $GUACAMOLE_HOME
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
new file mode 100644
index 0000000..8599a95
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/ActivityRecordSetResource.java
@@ -0,0 +1,144 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.ActivityRecord;
+import org.apache.guacamole.net.auth.ActivityRecordSet;
+
+/**
+ * A REST resource which abstracts the operations available on an
+ * ActivityRecordSet, such as the connection or user history available via the
+ * UserContext.
+ *
+ * @param <InternalRecordType>
+ * The type of ActivityRecord that is contained
+ * within the ActivityRecordSet represented by this resource. To avoid
+ * coupling the REST API too tightly to the extension API, these objects
+ * are not directly serialized or deserialized when handling REST requests.
+ *
+ * @param <ExternalRecordType>
+ * The type of object used in interchange (ie: serialized/deserialized as
+ * JSON) between REST clients and this resource to represent the
+ * InternalRecordType.
+ */
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public abstract class ActivityRecordSetResource<InternalRecordType extends ActivityRecord,
+ ExternalRecordType extends APIActivityRecord> {
+
+ /**
+ * The maximum number of history records to return in any one response.
+ */
+ private static final int MAXIMUM_HISTORY_SIZE = 1000;
+
+ /**
+ * The ActivityRecordSet whose records are being exposed.
+ */
+ private ActivityRecordSet<InternalRecordType> history;
+
+ /**
+ * Creates a new ActivityRecordSetResource which exposes the records within
+ * the given ActivityRecordSet.
+ *
+ * @param history
+ * The ActivityRecordSet whose records should be exposed.
+ */
+ public ActivityRecordSetResource(ActivityRecordSet<InternalRecordType> history) {
+ this.history = history;
+ }
+
+ /**
+ * Converts the given internal record object to a record object which is
+ * decoupled from the extension API and is intended to be used in
+ * interchange via the REST API.
+ *
+ * @param record
+ * The record to convert for the sake of interchange.
+ *
+ * @return
+ * A new record object containing the same data as the given internal
+ * record, but intended for use in interchange.
+ */
+ protected abstract ExternalRecordType toExternalRecord(InternalRecordType record);
+
+ /**
+ * Retrieves the list of activity records stored within the underlying
+ * ActivityRecordSet which match the given, arbitrary criteria. If
+ * specified, the returned records will also be sorted according to the
+ * given sort predicates.
+ *
+ * @param requiredContents
+ * The set of strings that each must occur somewhere within the
+ * returned 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 be excluded from the results.
+ *
+ * @param sortPredicates
+ * A list of predicates to apply while sorting the resulting records,
+ * describing the properties involved and the sort order for those
+ * properties.
+ *
+ * @return
+ * The list of records which match the provided criteria, optionally
+ * sorted as specified.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while applying the given filter criteria or
+ * sort predicates.
+ */
+ @GET
+ public List<ExternalRecordType> getRecords(
+ @QueryParam("contains") List<String> requiredContents,
+ @QueryParam("order") List<APISortPredicate> 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);
+
+ // Convert record set to collection of API records
+ List<ExternalRecordType> apiRecords = new ArrayList<>();
+ for (InternalRecordType record : history.asCollection())
+ apiRecords.add(toExternalRecord(record));
+
+ // Return the converted history
+ return apiRecords;
+
+ }
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/history/ConnectionHistoryResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/history/ConnectionHistoryResource.java
new file mode 100644
index 0000000..42e8a85
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/ConnectionHistoryResource.java
@@ -0,0 +1,49 @@
+/*
+ * 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.net.auth.ActivityRecordSet;
+import org.apache.guacamole.net.auth.ConnectionRecord;
+
+/**
+ * A REST resource for retrieving and managing the history records of Guacamole
+ * connections. Connection history records describe the start/end times of each
+ * usage of a connection (when a user connects and disconnects), as well as the
+ * specific user that connected/disconnected.
+ */
+public class ConnectionHistoryResource extends ActivityRecordSetResource<ConnectionRecord, APIConnectionRecord> {
+
+ /**
+ * Creates a new ConnectionHistoryResource which exposes the connection
+ * history records of the given ActivityRecordSet.
+ *
+ * @param history
+ * The ActivityRecordSet whose records should be exposed.
+ */
+ public ConnectionHistoryResource(ActivityRecordSet<ConnectionRecord> history) {
+ super(history);
+ }
+
+ @Override
+ protected APIConnectionRecord toExternalRecord(ConnectionRecord record) {
+ return new APIConnectionRecord(record);
+ }
+
+}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryResource.java
index 559ebd5..8da8355 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryResource.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryResource.java
@@ -19,18 +19,11 @@
package org.apache.guacamole.rest.history;
-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.Produces;
-import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import org.apache.guacamole.GuacamoleException;
-import org.apache.guacamole.net.auth.ActivityRecord;
-import org.apache.guacamole.net.auth.ActivityRecordSet;
-import org.apache.guacamole.net.auth.ConnectionRecord;
import org.apache.guacamole.net.auth.UserContext;
/**
@@ -42,11 +35,6 @@
public class HistoryResource {
/**
- * The maximum number of history records to return in any one response.
- */
- private static final int MAXIMUM_HISTORY_SIZE = 1000;
-
- /**
* The UserContext whose associated connection history is being exposed.
*/
private final UserContext userContext;
@@ -63,114 +51,37 @@
}
/**
- * Retrieves the usage history for all connections, restricted by optional
- * filter parameters.
- *
- * @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.
+ * Retrieves the usage history for all connections. Filtering may be
+ * applied via the returned ConnectionHistoryResource.
*
* @return
- * A list of connection records, describing the start and end times of
- * various usages of this connection.
+ * A resource which exposes connection records that may optionally be
+ * filtered, each record describing the start and end times that a
+ * particular connection was used.
*
* @throws GuacamoleException
* If an error occurs while retrieving the connection history.
*/
- @GET
@Path("connections")
- public List<APIConnectionRecord> getConnectionHistory(
- @QueryParam("contains") List<String> requiredContents,
- @QueryParam("order") List<APISortPredicate> sortPredicates)
- throws GuacamoleException {
-
- // Retrieve overall connection history
- ActivityRecordSet<ConnectionRecord> history = userContext.getConnectionHistory();
-
- // 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);
-
- // 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;
-
+ public ConnectionHistoryResource getConnectionHistory() throws GuacamoleException {
+ return new ConnectionHistoryResource(userContext.getConnectionHistory());
}
/**
- * Retrieves the login history for all users, restricted by optional filter
- * parameters.
- *
- * @param requiredContents
- * The set of strings that each must occur somewhere within the
- * returned user records, whether within the associated username or any
- * associated date. If non-empty, any user 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 user
- * records, describing the properties involved and the sort order for
- * those properties.
+ * Retrieves the login history for all users. Filtering may be applied via
+ * the returned UserHistoryResource.
*
* @return
- * A list of user records, describing the start and end times of user
- * sessions.
+ * A resource which exposes user records that may optionally be
+ * filtered, each record describing the start and end times of a user
+ * session.
*
* @throws GuacamoleException
* If an error occurs while retrieving the user history.
*/
- @GET
@Path("users")
- public List<APIActivityRecord> getUserHistory(
- @QueryParam("contains") List<String> requiredContents,
- @QueryParam("order") List<APISortPredicate> sortPredicates)
- throws GuacamoleException {
-
- // Retrieve overall user history
- ActivityRecordSet<ActivityRecord> history = userContext.getUserHistory();
-
- // 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);
-
- // Convert record set to collection of API user records
- List<APIActivityRecord> apiRecords = new ArrayList<APIActivityRecord>();
- for (ActivityRecord record : history.asCollection())
- apiRecords.add(new APIActivityRecord(record));
-
- // Return the converted history
- return apiRecords;
-
+ public UserHistoryResource getUserHistory() throws GuacamoleException {
+ return new UserHistoryResource(userContext.getUserHistory());
}
}
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/history/UserHistoryResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/history/UserHistoryResource.java
new file mode 100644
index 0000000..6324873
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/UserHistoryResource.java
@@ -0,0 +1,48 @@
+/*
+ * 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.net.auth.ActivityRecord;
+import org.apache.guacamole.net.auth.ActivityRecordSet;
+
+/**
+ * A REST resource for retrieving and managing the history records of Guacamole
+ * user sessions. User session history records describe the start/end times of
+ * individual user sessions (when specific users logged in and out).
+ */
+public class UserHistoryResource extends ActivityRecordSetResource<ActivityRecord, APIActivityRecord> {
+
+ /**
+ * Creates a new UserHistoryResource which exposes the user session history
+ * records of the given ActivityRecordSet.
+ *
+ * @param history
+ * The ActivityRecordSet whose records should be exposed.
+ */
+ public UserHistoryResource(ActivityRecordSet<ActivityRecord> history) {
+ super(history);
+ }
+
+ @Override
+ protected APIActivityRecord toExternalRecord(ActivityRecord record) {
+ return new APIActivityRecord(record);
+ }
+
+}
diff --git a/guacamole/src/main/webapp/app/client/controllers/clientController.js b/guacamole/src/main/webapp/app/client/controllers/clientController.js
index c8941b3..326d868 100644
--- a/guacamole/src/main/webapp/app/client/controllers/clientController.js
+++ b/guacamole/src/main/webapp/app/client/controllers/clientController.js
@@ -678,8 +678,8 @@
// Deal with substitute key presses
if (substituteKeysPressed[keysym]) {
event.preventDefault();
- delete substituteKeysPressed[keysym];
$scope.$broadcast('guacSyntheticKeyup', substituteKeysPressed[keysym]);
+ delete substituteKeysPressed[keysym];
}
// Mark key as released
diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js
index 1d81773..b058270 100644
--- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js
+++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js
@@ -242,7 +242,7 @@
$scope.managementPermissions = ManagementPermissions.fromPermissionSet(
values.permissions,
PermissionSet.SystemPermissionType.CREATE_CONNECTION,
- PermissionSet.hasConnectionPermission,
+ PermissionSet.hasConnectionGroupPermission,
identifier);
}, requestService.DIE);