From 5de7682cf7f927f5081f967d0a4960f046d27434 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Tue, 22 Sep 2015 23:33:42 -0700 Subject: [PATCH 01/25] GUAC-1193: Create connection history interface, and add simple implementation. --- .../guacamole/auth/jdbc/user/UserContext.java | 8 + .../guacamole/auth/ldap/user/UserContext.java | 8 + .../net/auth/ConnectionRecordSet.java | 175 ++++++++++++++++++ .../guacamole/net/auth/UserContext.java | 13 ++ .../simple/SimpleConnectionRecordSet.java | 75 ++++++++ .../net/auth/simple/SimpleUserContext.java | 7 + 6 files changed, 286 insertions(+) create mode 100644 guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecordSet.java create mode 100644 guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionRecordSet.java diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java index 9ac828bce..06bd5256e 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java @@ -39,8 +39,10 @@ import org.glyptodon.guacamole.net.auth.ActiveConnection; import org.glyptodon.guacamole.net.auth.AuthenticationProvider; import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionGroup; +import org.glyptodon.guacamole.net.auth.ConnectionRecordSet; import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.User; +import org.glyptodon.guacamole.net.auth.simple.SimpleConnectionRecordSet; /** * UserContext implementation which is driven by an arbitrary, underlying @@ -136,6 +138,12 @@ public class UserContext extends RestrictedObject return activeConnectionDirectory; } + @Override + public ConnectionRecordSet getConnectionHistory() + throws GuacamoleException { + return new SimpleConnectionRecordSet(); + } + @Override public ConnectionGroup getRootConnectionGroup() throws GuacamoleException { diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/user/UserContext.java b/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/user/UserContext.java index 75fa66eff..fd2e27eb7 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/user/UserContext.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/user/UserContext.java @@ -35,10 +35,12 @@ import org.glyptodon.guacamole.net.auth.AuthenticatedUser; import org.glyptodon.guacamole.net.auth.AuthenticationProvider; import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionGroup; +import org.glyptodon.guacamole.net.auth.ConnectionRecordSet; import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.User; import org.glyptodon.guacamole.net.auth.simple.SimpleConnectionGroup; import org.glyptodon.guacamole.net.auth.simple.SimpleConnectionGroupDirectory; +import org.glyptodon.guacamole.net.auth.simple.SimpleConnectionRecordSet; import org.glyptodon.guacamole.net.auth.simple.SimpleDirectory; import org.glyptodon.guacamole.net.auth.simple.SimpleUser; import org.slf4j.Logger; @@ -194,6 +196,12 @@ public class UserContext implements org.glyptodon.guacamole.net.auth.UserContext return new SimpleDirectory(); } + @Override + public ConnectionRecordSet getConnectionHistory() + throws GuacamoleException { + return new SimpleConnectionRecordSet(); + } + @Override public Collection
getUserAttributes() { return Collections.emptyList(); diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecordSet.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecordSet.java new file mode 100644 index 000000000..3e40474a4 --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecordSet.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.net.auth; + +import java.util.Collection; +import java.util.Date; +import org.glyptodon.guacamole.GuacamoleException; + +/** + * The set of all available connection records, or a subset of those records. + * + * @author James Muehlner + * @author Michael Jumper + */ +public interface ConnectionRecordSet { + + /** + * All properties of connection records which can be used as sorting + * criteria. + */ + enum SortableProperty { + + /** + * The name (not identifier) of the connection associated with the + * connection record. + */ + CONNECTION_NAME, + + /** + * The identifier (username) of the user that used the connection + * associated with the connection record. + */ + USER_IDENTIFIER, + + /** + * The date and time when the connection associated with the + * connection record began. + */ + START_DATE, + + /** + * The date and time when the connection associated with the + * connection record ended. + */ + END_DATE + + }; + + /** + * Returns all connection records within this set as a standard Collection. + * + * @return + * A collection containing all connection records within this set. + * + * @throws GuacamoleException + * If an error occurs while retrieving the connection records within + * this set. + */ + Collection asCollection() throws GuacamoleException; + + /** + * Returns the subset of connection records to only those that began after + * the given date and time. + * + * @param date + * The date and time after which all connection records within the + * resulting subset should begin. + * + * @return + * The subset of connection history records which begin after the + * specified date and time. + * + * @throws GuacamoleException + * If an error occurs while restricting the current subset. + */ + ConnectionRecordSet after(Date date) throws GuacamoleException; + + /** + * Returns the subset of connection records to only those that ended before + * the given date and time. + * + * @param date + * The date and time before which all connection records within the + * resulting subset should end. + * + * @return + * The subset of connection history records which end before the + * specified date and time. + * + * @throws GuacamoleException + * If an error occurs while restricting the current subset. + */ + ConnectionRecordSet before(Date date) throws GuacamoleException; + + /** + * Returns the subset of connection records to only those where the + * connection name or user identifier contain the given value. + * + * @param value + * The value which all connection records within the resulting subset + * should contain within their associated connection name or user + * identifier. + * + * @return + * The subset of connection history records which contain the specified + * value within their associated connection name or user identifier. + * + * @throws GuacamoleException + * If an error occurs while restricting the current subset. + */ + ConnectionRecordSet contains(String value) throws GuacamoleException; + + /** + * Returns the subset of connection history records containing only the + * first limit records. If the subset has fewer than + * limit records, then this function has no effect. + * + * @param limit + * The maximum number of records that the new subset should contain. + * + * @return + * The subset of connection history records that containing only the + * first limit records. + * + * @throws GuacamoleException + * If an error occurs while limiting the current subset. + */ + ConnectionRecordSet limit(int limit) throws GuacamoleException; + + /** + * Returns a ConnectionRecordSet containing identically the records within + * this set, sorted according to the specified criteria. The sort operation + * performed is guaranteed to be stable with respect to any past call to + * sort(). + * + * @param property + * The property by which the connection records within the resulting + * set should be sorted. + * + * @param desc + * Whether the records should be sorted according to the specified + * property in descending order. If false, records will be sorted + * according to the specified property in ascending order. + * + * @return + * The ConnnectionRecordSet, sorted according to the specified + * criteria. + * + * @throws GuacamoleException + * If an error occurs while sorting the current subset. + */ + ConnectionRecordSet sort(SortableProperty property, boolean desc) + throws GuacamoleException; + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/UserContext.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/UserContext.java index f668a3586..f38fad8f8 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/UserContext.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/UserContext.java @@ -109,6 +109,19 @@ public interface UserContext { Directory getActiveConnectionDirectory() throws GuacamoleException; + /** + * Retrieves all connection records visible to current user. The resulting + * set of connection records can be further filtered and ordered using the + * methods defined on ConnectionRecordSet. + * + * @return + * A set of all connection records visible to the current user. + * + * @throws GuacamoleException + * If an error occurs while retrieving the connection records. + */ + ConnectionRecordSet getConnectionHistory() throws GuacamoleException; + /** * Retrieves a connection group which can be used to view and manipulate * connections, but only as allowed by the permissions given to the user of diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionRecordSet.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionRecordSet.java new file mode 100644 index 000000000..c9bb2b4bc --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionRecordSet.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.net.auth.simple; + +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.auth.ConnectionRecord; +import org.glyptodon.guacamole.net.auth.ConnectionRecordSet; + +/** + * An immutable and empty ConnectionRecordSet. + * + * @author Michael Jumper + */ +public class SimpleConnectionRecordSet implements ConnectionRecordSet { + + @Override + public Collection asCollection() + throws GuacamoleException { + return Collections.emptyList(); + } + + @Override + public ConnectionRecordSet after(Date date) + throws GuacamoleException { + return this; + } + + @Override + public ConnectionRecordSet before(Date date) + throws GuacamoleException { + return this; + } + + @Override + public ConnectionRecordSet contains(String value) + throws GuacamoleException { + return this; + } + + @Override + public ConnectionRecordSet limit(int limit) + throws GuacamoleException { + return this; + } + + @Override + public ConnectionRecordSet sort(SortableProperty property, boolean desc) + throws GuacamoleException { + return this; + } + +} diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserContext.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserContext.java index 9293d46b3..a47fd0bf5 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserContext.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleUserContext.java @@ -33,6 +33,7 @@ import org.glyptodon.guacamole.net.auth.ActiveConnection; import org.glyptodon.guacamole.net.auth.AuthenticationProvider; import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionGroup; +import org.glyptodon.guacamole.net.auth.ConnectionRecordSet; import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.User; import org.glyptodon.guacamole.net.auth.UserContext; @@ -200,6 +201,12 @@ public class SimpleUserContext implements UserContext { return new SimpleDirectory(); } + @Override + public ConnectionRecordSet getConnectionHistory() + throws GuacamoleException { + return new SimpleConnectionRecordSet(); + } + @Override public Collection getUserAttributes() { return Collections.emptyList(); From 16d047e81e8055ae1f2be4010d21d382827354eb Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 23 Sep 2015 14:41:19 -0700 Subject: [PATCH 02/25] GUAC-1193: Remove after() and before() functions from ConnectionRecordSet. They would not be used. --- .../net/auth/ConnectionRecordSet.java | 38 +------------------ .../simple/SimpleConnectionRecordSet.java | 13 ------- 2 files changed, 2 insertions(+), 49 deletions(-) diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecordSet.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecordSet.java index 3e40474a4..58bab513f 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecordSet.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecordSet.java @@ -23,7 +23,6 @@ package org.glyptodon.guacamole.net.auth; import java.util.Collection; -import java.util.Date; import org.glyptodon.guacamole.GuacamoleException; /** @@ -78,43 +77,10 @@ public interface ConnectionRecordSet { */ Collection asCollection() throws GuacamoleException; - /** - * Returns the subset of connection records to only those that began after - * the given date and time. - * - * @param date - * The date and time after which all connection records within the - * resulting subset should begin. - * - * @return - * The subset of connection history records which begin after the - * specified date and time. - * - * @throws GuacamoleException - * If an error occurs while restricting the current subset. - */ - ConnectionRecordSet after(Date date) throws GuacamoleException; - - /** - * Returns the subset of connection records to only those that ended before - * the given date and time. - * - * @param date - * The date and time before which all connection records within the - * resulting subset should end. - * - * @return - * The subset of connection history records which end before the - * specified date and time. - * - * @throws GuacamoleException - * If an error occurs while restricting the current subset. - */ - ConnectionRecordSet before(Date date) throws GuacamoleException; - /** * Returns the subset of connection records to only those where the - * connection name or user identifier contain the given value. + * connection name, user identifier, or any associated date field contain + * the given value. * * @param value * The value which all connection records within the resulting subset diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionRecordSet.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionRecordSet.java index c9bb2b4bc..28be3955b 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionRecordSet.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/simple/SimpleConnectionRecordSet.java @@ -24,7 +24,6 @@ package org.glyptodon.guacamole.net.auth.simple; import java.util.Collection; import java.util.Collections; -import java.util.Date; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.net.auth.ConnectionRecord; import org.glyptodon.guacamole.net.auth.ConnectionRecordSet; @@ -42,18 +41,6 @@ public class SimpleConnectionRecordSet implements ConnectionRecordSet { return Collections.emptyList(); } - @Override - public ConnectionRecordSet after(Date date) - throws GuacamoleException { - return this; - } - - @Override - public ConnectionRecordSet before(Date date) - throws GuacamoleException { - return this; - } - @Override public ConnectionRecordSet contains(String value) throws GuacamoleException { From 805c647cddc19ccf5b6e8263e3ba00e7432170b1 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 23 Sep 2015 15:22:58 -0700 Subject: [PATCH 03/25] GUAC-1193: Implement history REST service. --- .../net/basic/rest/RESTServletModule.java | 2 + .../connection/ConnectionRESTService.java | 1 + .../APIConnectionRecord.java | 8 +- .../APIConnectionRecordSortPredicate.java | 166 ++++++++++++++++++ .../rest/history/HistoryRESTService.java | 147 ++++++++++++++++ .../net/basic/rest/history/package-info.java | 28 +++ 6 files changed, 348 insertions(+), 4 deletions(-) rename guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/{connection => history}/APIConnectionRecord.java (98%) create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecordSortPredicate.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/HistoryRESTService.java create mode 100644 guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/package-info.java diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java index 478c2182e..b48ff9d37 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/RESTServletModule.java @@ -31,6 +31,7 @@ import org.glyptodon.guacamole.net.basic.rest.auth.TokenRESTService; import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionRESTService; import org.glyptodon.guacamole.net.basic.rest.connectiongroup.ConnectionGroupRESTService; import org.glyptodon.guacamole.net.basic.rest.activeconnection.ActiveConnectionRESTService; +import org.glyptodon.guacamole.net.basic.rest.history.HistoryRESTService; import org.glyptodon.guacamole.net.basic.rest.language.LanguageRESTService; import org.glyptodon.guacamole.net.basic.rest.schema.SchemaRESTService; import org.glyptodon.guacamole.net.basic.rest.user.UserRESTService; @@ -59,6 +60,7 @@ public class RESTServletModule extends ServletModule { bind(ActiveConnectionRESTService.class); bind(ConnectionGroupRESTService.class); bind(ConnectionRESTService.class); + bind(HistoryRESTService.class); bind(LanguageRESTService.class); bind(SchemaRESTService.class); bind(TokenRESTService.class); diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java index 270e3c302..2d122cd01 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java @@ -52,6 +52,7 @@ import org.glyptodon.guacamole.net.basic.GuacamoleSession; import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; import org.glyptodon.guacamole.net.basic.rest.ObjectRetrievalService; import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; +import org.glyptodon.guacamole.net.basic.rest.history.APIConnectionRecord; import org.glyptodon.guacamole.protocol.GuacamoleConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionRecord.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecord.java similarity index 98% rename from guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionRecord.java rename to guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecord.java index e3912ecbe..510ebaec9 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/APIConnectionRecord.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecord.java @@ -20,14 +20,14 @@ * THE SOFTWARE. */ -package org.glyptodon.guacamole.net.basic.rest.connection; +package org.glyptodon.guacamole.net.basic.rest.history; import java.util.Date; import org.glyptodon.guacamole.net.auth.ConnectionRecord; /** * A connection record which may be exposed through the REST endpoints. - * + * * @author Michael Jumper */ public class APIConnectionRecord { @@ -47,7 +47,7 @@ public class APIConnectionRecord { * The host from which the connection originated, if known. */ private final String remoteHost; - + /** * The name of the user who used or is using the connection. */ @@ -126,5 +126,5 @@ public class APIConnectionRecord { public boolean isActive() { return active; } - + } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecordSortPredicate.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecordSortPredicate.java new file mode 100644 index 000000000..5c4ebbb94 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecordSortPredicate.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.net.basic.rest.history; + +import org.glyptodon.guacamole.net.auth.ConnectionRecordSet; +import org.glyptodon.guacamole.net.basic.rest.APIError; +import org.glyptodon.guacamole.net.basic.rest.APIException; + +/** + * A sort predicate which species the property to use when sorting connection + * records, along with the sort order. + * + * @author Michael Jumper + */ +public class APIConnectionRecordSortPredicate { + + /** + * The prefix which will be included before the name of a sortable property + * to indicate that the sort order is descending, not ascending. + */ + public static final String DESCENDING_PREFIX = "-"; + + /** + * All possible property name strings and their corresponding + * ConnectionRecordSet.SortableProperty values. + */ + public enum SortableProperty { + + /** + * The name (not identifier) of the connection associated with the + * connection record. + */ + connection(ConnectionRecordSet.SortableProperty.CONNECTION_NAME), + + /** + * The username (identifier) of the user associated with the connection + * record. + */ + username(ConnectionRecordSet.SortableProperty.USER_IDENTIFIER), + + /** + * The date that the connection associated with the connection record + * began (connected). + */ + startDate(ConnectionRecordSet.SortableProperty.START_DATE), + + /** + * The date that the connection associated with the connection record + * ended (disconnected). + */ + endDate(ConnectionRecordSet.SortableProperty.END_DATE); + + /** + * The ConnectionRecordSet.SortableProperty that this property name + * string represents. + */ + public final ConnectionRecordSet.SortableProperty recordProperty; + + /** + * Creates a new SortableProperty which associates the property name + * string (identical to its own name) with the given + * ConnectionRecordSet.SortableProperty value. + * + * @param recordProperty + * The ConnectionRecordSet.SortableProperty value to associate with + * the new SortableProperty. + */ + SortableProperty(ConnectionRecordSet.SortableProperty recordProperty) { + this.recordProperty = recordProperty; + } + + } + + /** + * The property to use when sorting ConnectionRecords. + */ + private ConnectionRecordSet.SortableProperty property; + + /** + * Whether the requested sort order is descending (true) or ascending + * (false). + */ + private boolean descending; + + /** + * Parses the given string value, determining the requested sort property + * and ordering. Possible values consist of any valid property name, and + * may include an optional prefix to denote descending sort order. Each + * possible property name is enumerated by the SortableValue enum. + * + * @param value + * The sort predicate string to parse, which must consist ONLY of a + * valid property name, possibly preceded by the DESCENDING_PREFIX. + * + * @throws APIException + * If the provided sort predicate string is invalid. + */ + public APIConnectionRecordSortPredicate(String value) + throws APIException { + + // Parse whether sort order is descending + if (value.startsWith(DESCENDING_PREFIX)) { + descending = true; + value = value.substring(DESCENDING_PREFIX.length()); + } + + // Parse sorting property into ConnectionRecordSet.SortableProperty + try { + this.property = SortableProperty.valueOf(value).recordProperty; + } + + // Bail out if sort property is not valid + catch (IllegalArgumentException e) { + throw new APIException( + APIError.Type.BAD_REQUEST, + String.format("Invalid sort property: \"%s\"", value) + ); + } + + } + + /** + * Returns the SortableProperty defined by ConnectionRecordSet which + * represents the property requested. + * + * @return + * The ConnectionRecordSet.SortableProperty which refers to the same + * property as the string originally provided when this + * APIConnectionRecordSortPredicate was created. + */ + public ConnectionRecordSet.SortableProperty getProperty() { + return property; + } + + /** + * Returns whether the requested sort order is descending. + * + * @return + * true if the sort order is descending, false if the sort order is + * ascending. + */ + public boolean isDescending() { + return descending; + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/HistoryRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/HistoryRESTService.java new file mode 100644 index 000000000..0534d3b47 --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/HistoryRESTService.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.net.basic.rest.history; + +import com.google.inject.Inject; +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.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.net.auth.ConnectionRecord; +import org.glyptodon.guacamole.net.auth.ConnectionRecordSet; +import org.glyptodon.guacamole.net.auth.UserContext; +import org.glyptodon.guacamole.net.basic.GuacamoleSession; +import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; +import org.glyptodon.guacamole.net.basic.rest.ObjectRetrievalService; +import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A REST Service for retrieving and managing the history records of Guacamole + * objects. + * + * @author Michael Jumper + */ +@Path("/data/{dataSource}/history") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class HistoryRESTService { + + /** + * Logger for this class. + */ + private static final Logger logger = LoggerFactory.getLogger(HistoryRESTService.class); + + /** + * The maximum number of history records to return in any one response. + */ + private static final int MAXIMUM_HISTORY_SIZE = 10000; + + /** + * A service for authenticating users from auth tokens. + */ + @Inject + private AuthenticationService authenticationService; + + /** + * Service for convenient retrieval of objects. + */ + @Inject + private ObjectRetrievalService retrievalService; + + /** + * Retrieves the usage history for all connections, restricted by optional + * filter parameters. + * + * @param authToken + * The authentication token that is used to authenticate the user + * performing the operation. + * + * @param authProviderIdentifier + * The unique identifier of the AuthenticationProvider associated with + * the UserContext containing the connection whose history is to be + * retrieved. + * + * @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. + * + * @return + * A list of connection records, describing the start and end times of + * various usages of this connection. + * + * @throws GuacamoleException + * If an error occurs while retrieving the connection history. + */ + @GET + @Path("/connections") + @AuthProviderRESTExposure + public List getConnectionHistory(@QueryParam("token") String authToken, + @PathParam("dataSource") String authProviderIdentifier, + @QueryParam("contains") List requiredContents, + @QueryParam("order") List sortPredicates) + throws GuacamoleException { + + GuacamoleSession session = authenticationService.getGuacamoleSession(authToken); + UserContext userContext = retrievalService.retrieveUserContext(session, authProviderIdentifier); + + // Retrieve overall connection history + ConnectionRecordSet history = userContext.getConnectionHistory(); + + // Restrict to records which contain the specified strings + for (String required : requiredContents) + history = history.contains(required); + + // Sort according to specified ordering + for (APIConnectionRecordSortPredicate 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 apiRecords = new ArrayList(); + for (ConnectionRecord record : history.asCollection()) + apiRecords.add(new APIConnectionRecord(record)); + + // Return the converted history + return apiRecords; + + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/package-info.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/package-info.java new file mode 100644 index 000000000..0cd154b8f --- /dev/null +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/package-info.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Classes related to retrieval or maintenance of history records using the + * Guacamole REST API. + */ +package org.glyptodon.guacamole.net.basic.rest.history; + From e1908bf6e80a0fbcd8913fb4f84fcc24dc55583f Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 23 Sep 2015 15:46:38 -0700 Subject: [PATCH 04/25] GUAC-1193: Implement and document historyService - a JavaScript service for accessing the history REST endpoint. --- .../app/rest/services/historyService.js | 90 +++++++++++++++++++ .../app/rest/types/ConnectionHistoryEntry.js | 35 ++++++++ 2 files changed, 125 insertions(+) create mode 100644 guacamole/src/main/webapp/app/rest/services/historyService.js diff --git a/guacamole/src/main/webapp/app/rest/services/historyService.js b/guacamole/src/main/webapp/app/rest/services/historyService.js new file mode 100644 index 000000000..a29521bc3 --- /dev/null +++ b/guacamole/src/main/webapp/app/rest/services/historyService.js @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Service for operating on history records via the REST API. + */ +angular.module('rest').factory('historyService', ['$injector', + function historyService($injector) { + + // Required services + var $http = $injector.get('$http'); + var authenticationService = $injector.get('authenticationService'); + + var service = {}; + + /** + * Makes a request to the REST API to get the usage history of all + * accessible connections, returning a promise that provides the + * corresponding array of @link{ConnectionHistoryEntry} objects if + * successful. + * + * @param {String} dataSource + * The unique identifier of the data source containing the connection + * history records to be retrieved. This identifier corresponds to an + * AuthenticationProvider within the Guacamole web application. + * + * @param {String[]} [requiredContents] + * The set of arbitrary strings to filter with. A ConnectionHistoryEntry + * must contain each of these values within the associated username, + * connection name, start date, or end date to appear in the result. If + * null, no filtering will be performed. + * + * @param {String[]} [sortPredicates] + * The set of predicates to sort against. The resulting array of + * ConnectionHistoryEntry objects will be sorted according to the + * properties and sort orders defined by each predicate. If null, the + * order of the resulting entries is undefined. Valid values are listed + * within ConnectionHistoryEntry.SortPredicate. + * + * @returns {Promise.} + * A promise which will resolve with an array of + * @link{ConnectionHistoryEntry} objects upon success. + */ + service.getConnectionHistory = function getConnectionHistory(dataSource, + requiredContents, sortPredicates) { + + // Build HTTP parameters set + var httpParameters = { + token : authenticationService.getCurrentToken() + }; + + // Filter according to contents if restrictions are specified + if (requiredContents) + httpParameters.contains = requiredContents; + + // Sort according to provided predicates, if any + if (sortPredicates) + httpParameters.order = sortPredicates; + + // Retrieve connection history + return $http({ + method : 'GET', + url : 'api/data/' + encodeURIComponent(dataSource) + '/history/connections', + params : httpParameters + }); + + }; + + return service; + +}]); diff --git a/guacamole/src/main/webapp/app/rest/types/ConnectionHistoryEntry.js b/guacamole/src/main/webapp/app/rest/types/ConnectionHistoryEntry.js index 299c65c13..1fe9ba89a 100644 --- a/guacamole/src/main/webapp/app/rest/types/ConnectionHistoryEntry.js +++ b/guacamole/src/main/webapp/app/rest/types/ConnectionHistoryEntry.js @@ -87,6 +87,41 @@ angular.module('rest').factory('ConnectionHistoryEntry', [function defineConnect }; + /** + * All possible predicates for sorting ConnectionHistoryEntry objects using + * the REST API. By default, each predicate indicates ascending order. To + * indicate descending order, add "-" to the beginning of the predicate. + * + * @type Object. + */ + ConnectionHistoryEntry.SortPredicate = { + + /** + * The name of the connection associated with the history entry (not + * the connection identifier). + */ + CONNECTION_NAME : 'connection', + + /** + * The username of the user associated with the history entry (the user + * identifier). + */ + USER_IDENTIFIER : 'username', + + /** + * The date and time that the connection associated with the history + * entry began (connected). + */ + START_DATE : 'startDate', + + /** + * The date and time that the connection associated with the history + * entry ended (disconnected). + */ + END_DATE : 'endDate' + + }; + return ConnectionHistoryEntry; }]); \ No newline at end of file From 3c5f72b288c988a94f72dcb035b2ee83f2da6bd2 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 23 Sep 2015 16:02:17 -0700 Subject: [PATCH 05/25] GUAC-1193: Include connection identifier within history records. --- .../connection/ModeledConnectionRecord.java | 5 +++++ .../jdbc/tunnel/ActiveConnectionRecord.java | 5 +++++ .../guacamole/net/auth/ConnectionRecord.java | 10 ++++++++++ .../basic/rest/history/APIConnectionRecord.java | 17 +++++++++++++++++ .../app/rest/types/ConnectionHistoryEntry.js | 7 +++++++ 5 files changed, 44 insertions(+) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java index 83e472c73..18e491065 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java @@ -51,6 +51,11 @@ public class ModeledConnectionRecord implements ConnectionRecord { this.model = model; } + @Override + public String getConnectionIdentifier() { + return model.getConnectionIdentifier(); + } + @Override public Date getStartDate() { return model.getStartDate(); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java index 6401e705c..3c0cbf5bd 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java @@ -164,6 +164,11 @@ public class ActiveConnectionRecord implements ConnectionRecord { return balancingGroup != null; } + @Override + public String getConnectionIdentifier() { + return connection.getIdentifier(); + } + @Override public Date getStartDate() { return startDate; diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java index bb43565d5..1d3568e4b 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java @@ -32,6 +32,16 @@ import java.util.Date; */ public interface ConnectionRecord { + /** + * Returns the identifier of the connection associated with this + * connection record. + * + * @return + * The identifier of the connection associated with this connection + * record. + */ + public String getConnectionIdentifier(); + /** * Returns the date and time the connection began. * diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecord.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecord.java index 510ebaec9..a9e14586f 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecord.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecord.java @@ -32,6 +32,11 @@ import org.glyptodon.guacamole.net.auth.ConnectionRecord; */ public class APIConnectionRecord { + /** + * The identifier of the connection associated with this record. + */ + private final String connectionIdentifier; + /** * The date and time the connection began. */ @@ -66,6 +71,7 @@ public class APIConnectionRecord { * The record to copy data from. */ public APIConnectionRecord(ConnectionRecord record) { + this.connectionIdentifier = record.getConnectionIdentifier(); this.startDate = record.getStartDate(); this.endDate = record.getEndDate(); this.remoteHost = record.getRemoteHost(); @@ -73,6 +79,17 @@ public class APIConnectionRecord { this.active = record.isActive(); } + /** + * Returns the identifier of the connection associated with this + * record. + * + * @return + * The identifier of the connection associated with this record. + */ + public String getConnectionIdentifier() { + return connectionIdentifier; + } + /** * Returns the date and time the connection began. * diff --git a/guacamole/src/main/webapp/app/rest/types/ConnectionHistoryEntry.js b/guacamole/src/main/webapp/app/rest/types/ConnectionHistoryEntry.js index 1fe9ba89a..1ffb78e37 100644 --- a/guacamole/src/main/webapp/app/rest/types/ConnectionHistoryEntry.js +++ b/guacamole/src/main/webapp/app/rest/types/ConnectionHistoryEntry.js @@ -41,6 +41,13 @@ angular.module('rest').factory('ConnectionHistoryEntry', [function defineConnect // Use empty object by default template = template || {}; + /** + * The identifier of the connection associated with this history entry. + * + * @type String + */ + this.connectionIdentifier = template.connectionIdentifier; + /** * The time that usage began, in seconds since 1970-01-01 00:00:00 UTC. * From 1b5663f5c03770d2fcda0c5b82b7ccd64abdde8e Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Tue, 6 Oct 2015 22:47:04 -0700 Subject: [PATCH 06/25] GUAC-1193: Document possibility of side effects when using subset functions of ConnectionRecordSet. --- .../guacamole/net/auth/ConnectionRecordSet.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecordSet.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecordSet.java index 58bab513f..ef6f9c336 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecordSet.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecordSet.java @@ -80,7 +80,10 @@ public interface ConnectionRecordSet { /** * Returns the subset of connection records to only those where the * connection name, user identifier, or any associated date field contain - * the given value. + * the given value. This function may also affect the contents of the + * current ConnectionRecordSet. The contents of the current + * ConnectionRecordSet should NOT be relied upon after this function is + * called. * * @param value * The value which all connection records within the resulting subset @@ -99,7 +102,10 @@ public interface ConnectionRecordSet { /** * Returns the subset of connection history records containing only the * first limit records. If the subset has fewer than - * limit records, then this function has no effect. + * limit records, then this function has no effect. This + * function may also affect the contents of the current + * ConnectionRecordSet. The contents of the current ConnectionRecordSet + * should NOT be relied upon after this function is called. * * @param limit * The maximum number of records that the new subset should contain. @@ -117,7 +123,9 @@ public interface ConnectionRecordSet { * Returns a ConnectionRecordSet containing identically the records within * this set, sorted according to the specified criteria. The sort operation * performed is guaranteed to be stable with respect to any past call to - * sort(). + * sort(). This function may also affect the contents of the current + * ConnectionRecordSet. The contents of the current ConnectionRecordSet + * should NOT be relied upon after this function is called. * * @param property * The property by which the connection records within the resulting From ae9a39edb92cad8394743e518650d42b2c41efd2 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Tue, 6 Oct 2015 23:05:18 -0700 Subject: [PATCH 07/25] GUAC-1193: Do not search for empty terms. --- .../net/basic/rest/history/HistoryRESTService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/HistoryRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/HistoryRESTService.java index 0534d3b47..5f8584772 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/HistoryRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/HistoryRESTService.java @@ -124,8 +124,10 @@ public class HistoryRESTService { ConnectionRecordSet history = userContext.getConnectionHistory(); // Restrict to records which contain the specified strings - for (String required : requiredContents) - history = history.contains(required); + for (String required : requiredContents) { + if (!required.isEmpty()) + history = history.contains(required); + } // Sort according to specified ordering for (APIConnectionRecordSortPredicate predicate : sortPredicates) From a631aa803bd3a243f9d6fde8e7cb79028cdf3392 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Tue, 6 Oct 2015 23:06:21 -0700 Subject: [PATCH 08/25] GUAC-1193: Implement JDBC ConnectionRecordSet. Add MySQL mapping. --- .../connection/ConnectionRecordMapper.java | 22 ++ .../ConnectionRecordSearchTerm.java | 296 ++++++++++++++++++ .../jdbc/connection/ConnectionRecordSet.java | 123 ++++++++ .../ConnectionRecordSortPredicate.java | 82 +++++ .../guacamole/auth/jdbc/user/UserContext.java | 12 +- .../connection/ConnectionRecordMapper.xml | 58 ++++ 6 files changed, 591 insertions(+), 2 deletions(-) create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSearchTerm.java create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSet.java create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSortPredicate.java diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.java index 8816fb69c..eaca812e5 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.java @@ -23,6 +23,7 @@ package org.glyptodon.guacamole.auth.jdbc.connection; import java.util.List; +import java.util.Set; import org.apache.ibatis.annotations.Param; /** @@ -56,5 +57,26 @@ public interface ConnectionRecordMapper { * The number of rows inserted. */ int insert(@Param("record") ConnectionRecordModel record); + + /** + * Searches for up to limit connection records that contain + * the given terms, sorted by the given predicates. + * + * @param terms + * The search terms that must match the returned records. + * + * @param sortPredicates + * A list of predicates to sort the returned records by, in order of + * priority. + * + * @param limit + * The maximum number of records that should be returned. + * + * @return + * The results of the search performed with the given parameters. + */ + List search(@Param("terms") Set terms, + @Param("sortPredicates") List sortPredicates, + @Param("limit") int limit); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSearchTerm.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSearchTerm.java new file mode 100644 index 000000000..e2e62b32e --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSearchTerm.java @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.auth.jdbc.connection; + +import java.util.Calendar; +import java.util.Date; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A search term for querying historical connection records. This will contain + * a the search term in string form and, if that string appears to be a date. a + * corresponding date range. + * + * @author James Muehlner + */ +public class ConnectionRecordSearchTerm { + + /** + * A pattern that can match a year, year and month, or year and month and + * day. + */ + private static final Pattern DATE_PATTERN = + Pattern.compile("(\\d+)(?:-(\\d+)?(?:-(\\d+)?)?)?"); + + /** + * The index of the group within DATE_PATTERN containing the + * year number. + */ + private static final int YEAR_GROUP = 1; + + /** + * The index of the group within DATE_PATTERN containing the + * month number, if any. + */ + private static final int MONTH_GROUP = 2; + + /** + * The index of the group within DATE_PATTERN containing the + * day number, if any. + */ + private static final int DAY_GROUP = 3; + + /** + * The start of the date range for records that should be retrieved, if the + * provided search term appears to be a date. + */ + private final Date startDate; + + /** + * The end of the date range for records that should be retrieved, if the + * provided search term appears to be a date. + */ + private final Date endDate; + + /** + * The string that should be searched for. + */ + private final String term; + + /** + * Parse the given string as an integer, returning the provided default + * value if the string is null. + * + * @param str + * The string to parse as an integer. + * + * @param defaultValue + * The value to return if str is null. + * + * @return + * The parsed value, or the provided default value if str + * is null. + */ + private static int parseInt(String str, int defaultValue) { + + if (str == null) + return defaultValue; + + return Integer.parseInt(str); + + } + + /** + * Returns a new calendar representing the last millisecond of the same + * year as calendar. + * + * @param calendar + * The calendar defining the year whose end (last millisecond) is to be + * returned. + * + * @return + * A new calendar representing the last millisecond of the same year as + * calendar. + */ + private static Calendar getEndOfYear(Calendar calendar) { + + // Get first day of next year + Calendar endOfYear = Calendar.getInstance(); + endOfYear.clear(); + endOfYear.set(Calendar.YEAR, calendar.get(Calendar.YEAR) + 1); + + // Transform into the last millisecond of the given year + endOfYear.add(Calendar.MILLISECOND, -1); + + return endOfYear; + + } + + /** + * Returns a new calendar representing the last millisecond of the same + * month and year as calendar. + * + * @param calendar + * The calendar defining the month and year whose end (last millisecond) + * is to be returned. + * + * @return + * A new calendar representing the last millisecond of the same month + * and year as calendar. + */ + private static Calendar getEndOfMonth(Calendar calendar) { + + // Copy given calender only up to given month + Calendar endOfMonth = Calendar.getInstance(); + endOfMonth.clear(); + endOfMonth.set(Calendar.YEAR, calendar.get(Calendar.YEAR)); + endOfMonth.set(Calendar.MONTH, calendar.get(Calendar.MONTH)); + + // Advance to the last millisecond of the given month + endOfMonth.add(Calendar.MONTH, 1); + endOfMonth.add(Calendar.MILLISECOND, -1); + + return endOfMonth; + + } + + /** + * Returns a new calendar representing the last millisecond of the same + * year, month, and day as calendar. + * + * @param calendar + * The calendar defining the year, month, and day whose end + * (last millisecond) is to be returned. + * + * @return + * A new calendar representing the last millisecond of the same year, + * month, and day as calendar. + */ + private static Calendar getEndOfDay(Calendar calendar) { + + // Copy given calender only up to given month + Calendar endOfMonth = Calendar.getInstance(); + endOfMonth.clear(); + endOfMonth.set(Calendar.YEAR, calendar.get(Calendar.YEAR)); + endOfMonth.set(Calendar.MONTH, calendar.get(Calendar.MONTH)); + endOfMonth.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH)); + + // Advance to the last millisecond of the given day + endOfMonth.add(Calendar.DAY_OF_MONTH, 1); + endOfMonth.add(Calendar.MILLISECOND, -1); + + return endOfMonth; + + } + + /** + * Creates a new ConnectionRecordSearchTerm representing the given string. + * If the given string appears to be a date, the start and end dates of the + * implied date range will be automatically determined and made available + * via getStartDate() and getEndDate() respectively. + * + * @param term + * The string that should be searched for. + */ + public ConnectionRecordSearchTerm(String term) { + + // Search terms absolutely must not be null + if (term == null) + throw new NullPointerException("Search terms may not be null"); + + this.term = term; + + // Parse start/end of date range if term appears to be a date + Matcher matcher = DATE_PATTERN.matcher(term); + if (matcher.matches()) { + + // Retrieve date components from term + String year = matcher.group(YEAR_GROUP); + String month = matcher.group(MONTH_GROUP); + String day = matcher.group(DAY_GROUP); + + // Parse start date from term + Calendar startCalendar = Calendar.getInstance(); + startCalendar.clear(); + startCalendar.set( + Integer.parseInt(year), + parseInt(month, 0), + parseInt(day, 1) + ); + + Calendar endCalendar; + + // Derive end date from start date + if (month == null) { + endCalendar = getEndOfYear(startCalendar); + } + else if (day == null) { + endCalendar = getEndOfMonth(startCalendar); + } + else { + endCalendar = getEndOfDay(startCalendar); + } + + // Convert results back into dates + this.startDate = startCalendar.getTime(); + this.endDate = endCalendar.getTime(); + + } + + // The search term doesn't look like a date + else { + this.startDate = null; + this.endDate = null; + } + + } + + /** + * Returns the start of the date range for records that should be retrieved, + * if the provided search term appears to be a date. + * + * @return + * The start of the date range. + */ + public Date getStartDate() { + return startDate; + } + + /** + * Returns the end of the date range for records that should be retrieved, + * if the provided search term appears to be a date. + * + * @return + * The end of the date range. + */ + public Date getEndDate() { + return endDate; + } + + /** + * Returns the string that should be searched for. + * + * @return + * The search term. + */ + public String getTerm() { + return term; + } + + @Override + public int hashCode() { + return term.hashCode(); + } + + @Override + public boolean equals(Object obj) { + + if (obj == null || !(obj instanceof ConnectionRecordSearchTerm)) + return false; + + return ((ConnectionRecordSearchTerm) obj).getTerm().equals(getTerm()); + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSet.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSet.java new file mode 100644 index 000000000..355f59fad --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSet.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.auth.jdbc.connection; + +import com.google.inject.Inject; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject; +import org.glyptodon.guacamole.net.auth.ConnectionRecord; + +/** + * A JDBC implementation of ConnectionRecordSet. Calls to asCollection() will + * query connection history records from the database. Which records are + * returned will be determined by the values passed in earlier. + * + * @author James Muehlner + */ +public class ConnectionRecordSet extends RestrictedObject + implements org.glyptodon.guacamole.net.auth.ConnectionRecordSet { + + /** + * Mapper for accessing connection history. + */ + @Inject + private ConnectionRecordMapper connectionRecordMapper; + + /** + * 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. + */ + private final Set requiredContents = + new HashSet(); + + /** + * The maximum number of connection history records that should be returned + * by a call to asCollection(). + */ + private int limit = Integer.MAX_VALUE; + + /** + * A list of predicates to apply while sorting the resulting connection + * records, describing the properties involved and the sort order for those + * properties. + */ + private final List connectionRecordSortPredicates = + new ArrayList(); + + @Override + public Collection asCollection() + throws GuacamoleException { + + // Perform the search against the database + List searchResults = + connectionRecordMapper.search(requiredContents, + connectionRecordSortPredicates, limit); + + List modeledSearchResults = + new ArrayList(); + + // Convert raw DB records into ConnectionRecords + for(ConnectionRecordModel model : searchResults) { + modeledSearchResults.add(new ModeledConnectionRecord(model)); + } + + return modeledSearchResults; + + } + + @Override + public ConnectionRecordSet contains(String value) + throws GuacamoleException { + requiredContents.add(new ConnectionRecordSearchTerm(value)); + return this; + } + + @Override + public ConnectionRecordSet limit(int limit) throws GuacamoleException { + this.limit = Math.min(this.limit, limit); + return this; + } + + @Override + public ConnectionRecordSet sort(SortableProperty property, boolean desc) + throws GuacamoleException { + + connectionRecordSortPredicates.add(new ConnectionRecordSortPredicate( + property, + desc + )); + + return this; + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSortPredicate.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSortPredicate.java new file mode 100644 index 000000000..279321b44 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSortPredicate.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.auth.jdbc.connection; + +import org.glyptodon.guacamole.net.auth.ConnectionRecordSet; + +/** + * A sort predicate which species the property to use when sorting connection + * records, along with the sort order. + * + * @author James Muehlner + */ +public class ConnectionRecordSortPredicate { + + /** + * The property to use when sorting ConnectionRecords. + */ + private final ConnectionRecordSet.SortableProperty property; + + /** + * Whether the sort order is descending (true) or ascending (false). + */ + private final boolean descending; + + /** + * Creates a new ConnectionRecordSortPredicate with the given sort property + * and sort order. + * + * @param property + * The property to use when sorting ConnectionRecords. + * + * @param descending + * Whether the sort order is descending (true) or ascending (false). + */ + public ConnectionRecordSortPredicate(ConnectionRecordSet.SortableProperty property, + boolean descending) { + this.property = property; + this.descending = descending; + } + + /** + * Returns the property that should be used when sorting ConnectionRecords. + * + * @return + * The property that should be used when sorting ConnectionRecords. + */ + public ConnectionRecordSet.SortableProperty getProperty() { + return property; + } + + /** + * Returns whether the sort order is descending. + * + * @return + * true if the sort order is descending, false if the sort order is + * ascending. + */ + public boolean isDescending() { + return descending; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java index 06bd5256e..72dea43f4 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java @@ -32,6 +32,7 @@ import java.util.Collection; import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.auth.jdbc.base.RestrictedObject; import org.glyptodon.guacamole.auth.jdbc.activeconnection.ActiveConnectionDirectory; +import org.glyptodon.guacamole.auth.jdbc.connection.ConnectionRecordSet; import org.glyptodon.guacamole.auth.jdbc.connection.ModeledConnection; import org.glyptodon.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; import org.glyptodon.guacamole.form.Form; @@ -39,7 +40,6 @@ import org.glyptodon.guacamole.net.auth.ActiveConnection; import org.glyptodon.guacamole.net.auth.AuthenticationProvider; import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionGroup; -import org.glyptodon.guacamole.net.auth.ConnectionRecordSet; import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.User; import org.glyptodon.guacamole.net.auth.simple.SimpleConnectionRecordSet; @@ -94,6 +94,12 @@ public class UserContext extends RestrictedObject @Inject private Provider rootGroupProvider; + /** + * Provider for creating connection record sets. + */ + @Inject + private Provider connectionRecordSetProvider; + @Override public void init(AuthenticatedUser currentUser) { @@ -141,7 +147,9 @@ public class UserContext extends RestrictedObject @Override public ConnectionRecordSet getConnectionHistory() throws GuacamoleException { - return new SimpleConnectionRecordSet(); + ConnectionRecordSet connectionRecordSet = connectionRecordSetProvider.get(); + connectionRecordSet.init(getCurrentUser()); + return connectionRecordSet; } @Override diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml index b5775f607..fb45aeb2d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -72,4 +72,62 @@ + + + \ No newline at end of file From ef128b492d500a3f970045e91894d67f6f4df887 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 8 Oct 2015 13:53:49 -0700 Subject: [PATCH 09/25] GUAC-1193: Fix description of query. --- .../guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml index fb45aeb2d..7a2565de6 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -72,7 +72,7 @@ - + + + SELECT + guacamole_connection_history.connection_id, + guacamole_connection_history.user_id, + guacamole_user.username, + guacamole_connection_history.start_date, + guacamole_connection_history.end_date + FROM guacamole_connection_history + JOIN guacamole_connection ON guacamole_connection_history.connection_id = guacamole_connection.connection_id + JOIN guacamole_user ON guacamole_connection_history.user_id = guacamole_user.user_id + + + + + ( + + guacamole_connection_history.user_id IN ( + SELECT user_id + FROM guacamole_user + WHERE username LIKE #{termPattern,jdbcType=VARCHAR} + ) + + OR guacamole_connection_history.connection_id IN ( + SELECT connection_id + FROM guacamole_connection + WHERE connection_name LIKE #{termPattern,jdbcType=VARCHAR} + ) + + + OR ( + (start_date BETWEEN #{term.startDate,jdbcType=DATE} AND #{term.endDate,jdbcType=DATE}) + AND (end_date BETWEEN #{term.startDate,jdbcType=DATE} AND #{term.endDate,jdbcType=DATE}) + ) + + + ) + + + + + + guacamole_connection.connection_name + guacamole_user.username + guacamole_connection_history.start_date + guacamole_connection_history.end_date + 1 + + DESC + + + LIMIT #{limit,jdbcType=INTEGER} + + + \ No newline at end of file From ac71bf553ab61883e6f66569516bb782021ea0bf Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 8 Oct 2015 14:00:03 -0700 Subject: [PATCH 11/25] GUAC-1193: Remove unused import. --- .../guacamole/auth/jdbc/connection/ConnectionRecordSet.java | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSet.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSet.java index 355f59fad..0de048d3c 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSet.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSet.java @@ -25,7 +25,6 @@ package org.glyptodon.guacamole.auth.jdbc.connection; import com.google.inject.Inject; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; From b02fdb673a841d4788f414138de41711114d8f9b Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 8 Oct 2015 14:35:06 -0700 Subject: [PATCH 12/25] GUAC-1193: Fix ordering of record search results (OGNL comparison for enums is not string-based). --- .../jdbc/connection/ConnectionRecordMapper.xml | 14 ++++++++++---- .../jdbc/connection/ConnectionRecordMapper.xml | 14 ++++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml index 7a2565de6..113c0d209 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -113,14 +113,20 @@ ) + + + + + + - guacamole_connection.connection_name - guacamole_user.username - guacamole_connection_history.start_date - guacamole_connection_history.end_date + guacamole_connection.connection_name + guacamole_user.username + guacamole_connection_history.start_date + guacamole_connection_history.end_date 1 DESC diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml index fd532d43e..8a7ac1fff 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -113,14 +113,20 @@ ) + + + + + + - guacamole_connection.connection_name - guacamole_user.username - guacamole_connection_history.start_date - guacamole_connection_history.end_date + guacamole_connection.connection_name + guacamole_user.username + guacamole_connection_history.start_date + guacamole_connection_history.end_date 1 DESC From 9b99fd7323f037a72844db0ee029566c98bd9881 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 8 Oct 2015 16:32:55 -0700 Subject: [PATCH 13/25] GUAC-1193: Add searchReadable() query, limited by available permissions. --- .../connection/ConnectionRecordMapper.java | 54 ++++++++++--- .../connection/ConnectionRecordMapper.xml | 78 ++++++++++++++++++- .../connection/ConnectionRecordMapper.xml | 76 ++++++++++++++++++ 3 files changed, 197 insertions(+), 11 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.java index eaca812e5..e829e7a53 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.java @@ -22,9 +22,10 @@ package org.glyptodon.guacamole.auth.jdbc.connection; +import java.util.Collection; import java.util.List; -import java.util.Set; import org.apache.ibatis.annotations.Param; +import org.glyptodon.guacamole.auth.jdbc.user.UserModel; /** * Mapper for connection record objects. @@ -59,24 +60,57 @@ public interface ConnectionRecordMapper { int insert(@Param("record") ConnectionRecordModel record); /** - * Searches for up to limit connection records that contain - * the given terms, sorted by the given predicates. - * + * Searches for up to limit connection records that contain + * the given terms, sorted by the given predicates, regardless of whether + * the data they are associated with is is readable by any particular user. + * This should only be called on behalf of a system administrator. If + * records are needed by a non-administrative user who must have explicit + * read rights, use searchReadable() instead. + * * @param terms * The search terms that must match the returned records. - * + * * @param sortPredicates * A list of predicates to sort the returned records by, in order of * priority. * - * @param limit + * @param limit * The maximum number of records that should be returned. - * + * * @return - * The results of the search performed with the given parameters. + * The results of the search performed with the given parameters. */ - List search(@Param("terms") Set terms, + List search(@Param("terms") Collection terms, @Param("sortPredicates") List sortPredicates, @Param("limit") int limit); - + + /** + * Searches for up to limit connection records that contain + * the given terms, sorted by the given predicates. Only records that are + * associated with data explicitly readable by the given user will be + * returned. If records are needed by a system administrator (who, by + * definition, does not need explicit read rights), use search() instead. + * + * @param user + * The user whose permissions should determine whether a record is + * returned. + * + * @param terms + * The search terms that must match the returned records. + * + * @param sortPredicates + * A list of predicates to sort the returned records by, in order of + * priority. + * + * @param limit + * The maximum number of records that should be returned. + * + * @return + * The results of the search performed with the given parameters. + */ + List searchReadable(@Param("user") UserModel user, + @Param("terms") Collection terms, + @Param("sortPredicates") List sortPredicates, + @Param("limit") int limit); + } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml index 113c0d209..eeb00bca1 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -84,7 +84,83 @@ FROM guacamole_connection_history JOIN guacamole_connection ON guacamole_connection_history.connection_id = guacamole_connection.connection_id JOIN guacamole_user ON guacamole_connection_history.user_id = guacamole_user.user_id - + + + + + ( + + guacamole_connection_history.user_id IN ( + SELECT user_id + FROM guacamole_user + WHERE username LIKE #{termPattern,jdbcType=VARCHAR} + ) + + OR guacamole_connection_history.connection_id IN ( + SELECT connection_id + FROM guacamole_connection + WHERE connection_name LIKE #{termPattern,jdbcType=VARCHAR} + ) + + + OR ( + (start_date BETWEEN #{term.startDate,jdbcType=DATE} AND #{term.endDate,jdbcType=DATE}) + AND (end_date BETWEEN #{term.startDate,jdbcType=DATE} AND #{term.endDate,jdbcType=DATE}) + ) + + + ) + + + + + + + + + + + + guacamole_connection.connection_name + guacamole_user.username + guacamole_connection_history.start_date + guacamole_connection_history.end_date + 1 + + DESC + + + LIMIT #{limit,jdbcType=INTEGER} + + + + + + + + \ No newline at end of file From 3cdcb1004fe727fcda6159d85fa01680463b4b51 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 8 Oct 2015 16:33:33 -0700 Subject: [PATCH 14/25] GUAC-1193: Move history record query and permission checks into ConnectionService. --- .../jdbc/connection/ConnectionRecordSet.java | 22 +---- .../jdbc/connection/ConnectionService.java | 95 ++++++++++++++++++- 2 files changed, 94 insertions(+), 23 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSet.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSet.java index 0de048d3c..f9d00a104 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSet.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSet.java @@ -43,10 +43,10 @@ public class ConnectionRecordSet extends RestrictedObject implements org.glyptodon.guacamole.net.auth.ConnectionRecordSet { /** - * Mapper for accessing connection history. + * Service for managing connection objects. */ @Inject - private ConnectionRecordMapper connectionRecordMapper; + private ConnectionService connectionService; /** * The set of strings that each must occur somewhere within the returned @@ -75,22 +75,8 @@ public class ConnectionRecordSet extends RestrictedObject @Override public Collection asCollection() throws GuacamoleException { - - // Perform the search against the database - List searchResults = - connectionRecordMapper.search(requiredContents, - connectionRecordSortPredicates, limit); - - List modeledSearchResults = - new ArrayList(); - - // Convert raw DB records into ConnectionRecords - for(ConnectionRecordModel model : searchResults) { - modeledSearchResults.add(new ModeledConnectionRecord(model)); - } - - return modeledSearchResults; - + return connectionService.retrieveHistory(getCurrentUser(), + requiredContents, connectionRecordSortPredicates, limit); } @Override diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java index c15e5e067..5d8d82a42 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionService.java @@ -343,6 +343,43 @@ public class ConnectionService extends ModeledGroupedDirectoryObjectService getObjectInstances(List models) { + + // Create new list of records by manually converting each model + List objects = new ArrayList(models.size()); + for (ConnectionRecordModel model : models) + objects.add(getObjectInstance(model)); + + return objects; + + } + /** * Retrieves the connection history of the given connection, including any * active connections. @@ -364,7 +401,7 @@ public class ConnectionService extends ModeledGroupedDirectoryObjectServicelimit connection history records matching + * the given terms and sorted by the given predicates. Only history records + * associated with data that the given user can read are returned. + * + * @param user + * The user retrieving the connection history. + * + * @param requiredContents + * The search terms that must be contained somewhere within each of the + * returned records. + * + * @param sortPredicates + * A list of predicates to sort the returned records by, in order of + * priority. + * + * @param limit + * The maximum number of records that should be returned. + * + * @return + * The connection history of the given connection, including any + * active connections. + * + * @throws GuacamoleException + * If permission to read the connection history is denied. + */ + public List retrieveHistory(AuthenticatedUser user, + Collection requiredContents, + List sortPredicates, int limit) + throws GuacamoleException { + + List searchResults; + + // Bypass permission checks if the user is a system admin + if (user.getUser().isAdministrator()) + searchResults = connectionRecordMapper.search(requiredContents, + sortPredicates, limit); + + // Otherwise only return explicitly readable history records + else + searchResults = connectionRecordMapper.searchReadable(user.getUser().getModel(), + requiredContents, sortPredicates, limit); + + return getObjectInstances(searchResults); + + } + /** * Connects to the given connection as the given user, using the given * client information. If the user does not have permission to read the From 3c271da9b420af76d17e2b116978e82cfcec885b Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 8 Oct 2015 17:54:43 -0700 Subject: [PATCH 15/25] GUAC-1193: Do not use within , as it only substitutes the last bound value for *ALL* usages of the created variable. Do not use LIKE, as we would then have to escape the search term in Java. --- .../auth/jdbc/connection/ConnectionRecordMapper.xml | 10 ++++------ .../auth/jdbc/connection/ConnectionRecordMapper.xml | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml index eeb00bca1..eaf69fffa 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -88,19 +88,18 @@ - ( guacamole_connection_history.user_id IN ( SELECT user_id FROM guacamole_user - WHERE username LIKE #{termPattern,jdbcType=VARCHAR} + WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN username) > 0 ) OR guacamole_connection_history.connection_id IN ( SELECT connection_id FROM guacamole_connection - WHERE connection_name LIKE #{termPattern,jdbcType=VARCHAR} + WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN connection_name) > 0 ) @@ -164,19 +163,18 @@ - ( guacamole_connection_history.user_id IN ( SELECT user_id FROM guacamole_user - WHERE username LIKE #{termPattern,jdbcType=VARCHAR} + WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN username) > 0 ) OR guacamole_connection_history.connection_id IN ( SELECT connection_id FROM guacamole_connection - WHERE connection_name LIKE #{termPattern,jdbcType=VARCHAR} + WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN connection_name) > 0 ) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml index 09641f2a1..a93679e82 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -88,19 +88,18 @@ - ( guacamole_connection_history.user_id IN ( SELECT user_id FROM guacamole_user - WHERE username LIKE #{termPattern,jdbcType=VARCHAR} + WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN username) > 0 ) OR guacamole_connection_history.connection_id IN ( SELECT connection_id FROM guacamole_connection - WHERE connection_name LIKE #{termPattern,jdbcType=VARCHAR} + WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN connection_name) > 0 ) @@ -164,19 +163,18 @@ - ( guacamole_connection_history.user_id IN ( SELECT user_id FROM guacamole_user - WHERE username LIKE #{termPattern,jdbcType=VARCHAR} + WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN username) > 0 ) OR guacamole_connection_history.connection_id IN ( SELECT connection_id FROM guacamole_connection - WHERE connection_name LIKE #{termPattern,jdbcType=VARCHAR} + WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN connection_name) > 0 ) From 03c1ac18762c30f9ce4f634391b453a8b910c247 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Tue, 13 Oct 2015 20:04:24 -0700 Subject: [PATCH 16/25] GUAC-1193 Add required connection name property to connection record. --- .../connection/ConnectionRecordModel.java | 31 +++++++++++++++++++ .../connection/ModeledConnectionRecord.java | 5 +++ .../jdbc/tunnel/ActiveConnectionRecord.java | 5 +++ .../guacamole/auth/jdbc/user/UserContext.java | 1 - .../connection/ConnectionRecordMapper.xml | 15 ++++++--- .../connection/ConnectionRecordMapper.xml | 15 ++++++--- .../guacamole/net/auth/ConnectionRecord.java | 9 ++++++ .../rest/history/APIConnectionRecord.java | 26 +++++++++++++--- 8 files changed, 91 insertions(+), 16 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordModel.java index aa35fb27c..9ac157d17 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordModel.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordModel.java @@ -37,6 +37,11 @@ public class ConnectionRecordModel { */ private String connectionIdentifier; + /** + * The name of the connection associated with this connection record. + */ + private String connectionName; + /** * The database ID of the user associated with this connection record. */ @@ -82,6 +87,32 @@ public class ConnectionRecordModel { this.connectionIdentifier = connectionIdentifier; } + + /** + * Returns the name of the connection associated with this connection + * record. + * + * @return + * The name of the connection associated with this connection + * record. + */ + public String getConnectionName() { + return connectionName; + } + + + /** + * Sets the name of the connection associated with this connection + * record. + * + * @param connectionName + * The name of the connection to associate with this connection + * record. + */ + public void setConnectionName(String connectionName) { + this.connectionName = connectionName; + } + /** * Returns the database ID of the user associated with this connection * record. diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java index 18e491065..dbd9b0b28 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java @@ -56,6 +56,11 @@ public class ModeledConnectionRecord implements ConnectionRecord { return model.getConnectionIdentifier(); } + @Override + public String getConnectionName() { + return model.getConnectionName(); + } + @Override public Date getStartDate() { return model.getStartDate(); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java index 3c0cbf5bd..1c2a40a7b 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java @@ -169,6 +169,11 @@ public class ActiveConnectionRecord implements ConnectionRecord { return connection.getIdentifier(); } + @Override + public String getConnectionName() { + return connection.getName(); + } + @Override public Date getStartDate() { return startDate; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java index 72dea43f4..41e7a6f5d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/user/UserContext.java @@ -42,7 +42,6 @@ import org.glyptodon.guacamole.net.auth.Connection; import org.glyptodon.guacamole.net.auth.ConnectionGroup; import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.User; -import org.glyptodon.guacamole.net.auth.simple.SimpleConnectionRecordSet; /** * UserContext implementation which is driven by an arbitrary, underlying diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml index eaf69fffa..ad118923f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -28,11 +28,12 @@ - - - - - + + + + + + @@ -40,6 +41,7 @@ SELECT connection_id, + connection_name, guacamole_connection_history.user_id, username, start_date, @@ -59,6 +61,7 @@ INSERT INTO guacamole_connection_history ( connection_id, + connection_name, user_id, start_date, end_date @@ -77,6 +80,7 @@ SELECT guacamole_connection_history.connection_id, + guacamole_connection.connection_name, guacamole_connection_history.user_id, guacamole_user.username, guacamole_connection_history.start_date, @@ -140,6 +144,7 @@ SELECT guacamole_connection_history.connection_id, + guacamole_connection.connection_name, guacamole_connection_history.user_id, guacamole_user.username, guacamole_connection_history.start_date, diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml index a93679e82..1fa7490a5 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -28,11 +28,12 @@ - - - - - + + + + + + @@ -40,6 +41,7 @@ SELECT connection_id, + connection_name, guacamole_connection_history.user_id, username, start_date, @@ -59,6 +61,7 @@ INSERT INTO guacamole_connection_history ( connection_id, + connection_name, user_id, start_date, end_date @@ -77,6 +80,7 @@ SELECT guacamole_connection_history.connection_id, + guacamole_connection.connection_name, guacamole_connection_history.user_id, guacamole_user.username, guacamole_connection_history.start_date, @@ -140,6 +144,7 @@ SELECT guacamole_connection_history.connection_id, + guacamole_connection.connection_name, guacamole_connection_history.user_id, guacamole_user.username, guacamole_connection_history.start_date, diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java index 1d3568e4b..f5533cf78 100644 --- a/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/net/auth/ConnectionRecord.java @@ -41,6 +41,15 @@ public interface ConnectionRecord { * record. */ public String getConnectionIdentifier(); + + /** + * Returns the name of the connection associated with this connection + * record. + * + * @return + * The name of the connection associated with this connection record. + */ + public String getConnectionName(); /** * Returns the date and time the connection began. diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecord.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecord.java index a9e14586f..8c38148cc 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecord.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecord.java @@ -37,6 +37,11 @@ public class APIConnectionRecord { */ private final String connectionIdentifier; + /** + * The identifier of the connection associated with this record. + */ + private final String connectionName; + /** * The date and time the connection began. */ @@ -72,11 +77,12 @@ public class APIConnectionRecord { */ public APIConnectionRecord(ConnectionRecord record) { this.connectionIdentifier = record.getConnectionIdentifier(); - this.startDate = record.getStartDate(); - this.endDate = record.getEndDate(); - this.remoteHost = record.getRemoteHost(); - this.username = record.getUsername(); - this.active = record.isActive(); + this.connectionName = record.getConnectionName(); + this.startDate = record.getStartDate(); + this.endDate = record.getEndDate(); + this.remoteHost = record.getRemoteHost(); + this.username = record.getUsername(); + this.active = record.isActive(); } /** @@ -90,6 +96,16 @@ public class APIConnectionRecord { return connectionIdentifier; } + /** + * Returns the name of the connection associated with this record. + * + * @return + * The name of the connection associated with this record. + */ + public String getConnectionName() { + return connectionName; + } + /** * Returns the date and time the connection began. * From fdbc68bb9294d3824494d66aa95a5d260e98b6da Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Tue, 13 Oct 2015 23:38:55 -0700 Subject: [PATCH 17/25] GUAC-1193: Implement front end for connection history. --- .../ConnectionRecordSearchTerm.java | 2 +- .../connection/ConnectionRecordMapper.xml | 28 ++-- .../connection/ConnectionRecordMapper.xml | 28 ++-- .../APIConnectionRecordSortPredicate.java | 2 +- .../navigation/services/userPageService.js | 17 ++- .../app/rest/types/ConnectionHistoryEntry.js | 9 +- .../guacSettingsConnectionHistory.js | 142 ++++++++++++++++++ .../webapp/app/settings/styles/history.css | 59 ++++++++ .../app/settings/templates/settings.html | 9 +- .../templates/settingsConnectionHistory.html | 74 +++++++++ .../src/main/webapp/translations/en.json | 28 +++- 11 files changed, 357 insertions(+), 41 deletions(-) create mode 100644 guacamole/src/main/webapp/app/settings/directives/guacSettingsConnectionHistory.js create mode 100644 guacamole/src/main/webapp/app/settings/styles/history.css create mode 100644 guacamole/src/main/webapp/app/settings/templates/settingsConnectionHistory.html diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSearchTerm.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSearchTerm.java index e2e62b32e..a03d478f9 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSearchTerm.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordSearchTerm.java @@ -215,7 +215,7 @@ public class ConnectionRecordSearchTerm { startCalendar.clear(); startCalendar.set( Integer.parseInt(year), - parseInt(month, 0), + parseInt(month, 1) - 1, parseInt(day, 1) ); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml index ad118923f..f6b6698a9 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -40,19 +40,20 @@ @@ -61,7 +62,6 @@ INSERT INTO guacamole_connection_history ( connection_id, - connection_name, user_id, start_date, end_date @@ -108,8 +108,8 @@ OR ( - (start_date BETWEEN #{term.startDate,jdbcType=DATE} AND #{term.endDate,jdbcType=DATE}) - AND (end_date BETWEEN #{term.startDate,jdbcType=DATE} AND #{term.endDate,jdbcType=DATE}) + (start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP}) + AND (end_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP}) ) @@ -184,8 +184,8 @@ OR ( - (start_date BETWEEN #{term.startDate,jdbcType=DATE} AND #{term.endDate,jdbcType=DATE}) - AND (end_date BETWEEN #{term.startDate,jdbcType=DATE} AND #{term.endDate,jdbcType=DATE}) + (start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP}) + AND (end_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP}) ) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml index 1fa7490a5..b3a23eadc 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/glyptodon/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -40,19 +40,20 @@ @@ -61,7 +62,6 @@ INSERT INTO guacamole_connection_history ( connection_id, - connection_name, user_id, start_date, end_date @@ -108,8 +108,8 @@ OR ( - (start_date BETWEEN #{term.startDate,jdbcType=DATE} AND #{term.endDate,jdbcType=DATE}) - AND (end_date BETWEEN #{term.startDate,jdbcType=DATE} AND #{term.endDate,jdbcType=DATE}) + (start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP}) + AND (end_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP}) ) @@ -184,8 +184,8 @@ OR ( - (start_date BETWEEN #{term.startDate,jdbcType=DATE} AND #{term.endDate,jdbcType=DATE}) - AND (end_date BETWEEN #{term.startDate,jdbcType=DATE} AND #{term.endDate,jdbcType=DATE}) + (start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP}) + AND (end_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP}) ) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecordSortPredicate.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecordSortPredicate.java index 5c4ebbb94..17872d77d 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecordSortPredicate.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/APIConnectionRecordSortPredicate.java @@ -50,7 +50,7 @@ public class APIConnectionRecordSortPredicate { * The name (not identifier) of the connection associated with the * connection record. */ - connection(ConnectionRecordSet.SortableProperty.CONNECTION_NAME), + connectionName(ConnectionRecordSet.SortableProperty.CONNECTION_NAME), /** * The username (identifier) of the user associated with the connection diff --git a/guacamole/src/main/webapp/app/navigation/services/userPageService.js b/guacamole/src/main/webapp/app/navigation/services/userPageService.js index 114ee2efb..9001aa1e0 100644 --- a/guacamole/src/main/webapp/app/navigation/services/userPageService.js +++ b/guacamole/src/main/webapp/app/navigation/services/userPageService.js @@ -173,6 +173,7 @@ angular.module('navigation').factory('userPageService', ['$injector', var canManageUsers = []; var canManageConnections = []; + var canViewConnectionRecords = []; var canManageSessions = []; // Inspect the contents of each provided permission set @@ -234,12 +235,13 @@ angular.module('navigation').factory('userPageService', ['$injector', ) canManageConnections.push(dataSource); - // Determine whether the current user needs access to the session management UI + // Determine whether the current user needs access to the session management UI or view connection history if ( // A user must be a system administrator to manage sessions PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) ) canManageSessions.push(dataSource); + canViewConnectionRecords.push(dataSource); }); @@ -250,7 +252,18 @@ angular.module('navigation').factory('userPageService', ['$injector', url : '/settings/sessions' })); } - + + // If user can manage connections, add links for connection management pages + angular.forEach(canViewConnectionRecords, function addConnectionHistoryLink(dataSource) { + pages.push(new PageDefinition({ + name : [ + 'USER_MENU.ACTION_VIEW_HISTORY', + translationStringService.canonicalize('DATA_SOURCE_' + dataSource) + '.NAME' + ], + url : '/settings/' + encodeURIComponent(dataSource) + '/history' + })); + }); + // If user can manage users, add link to user management page if (canManageUsers.length) { pages.push(new PageDefinition({ diff --git a/guacamole/src/main/webapp/app/rest/types/ConnectionHistoryEntry.js b/guacamole/src/main/webapp/app/rest/types/ConnectionHistoryEntry.js index 1ffb78e37..7e892538c 100644 --- a/guacamole/src/main/webapp/app/rest/types/ConnectionHistoryEntry.js +++ b/guacamole/src/main/webapp/app/rest/types/ConnectionHistoryEntry.js @@ -48,6 +48,13 @@ angular.module('rest').factory('ConnectionHistoryEntry', [function defineConnect */ this.connectionIdentifier = template.connectionIdentifier; + /** + * The name of the connection associated with this history entry. + * + * @type String + */ + this.connectionName = template.connectionName; + /** * The time that usage began, in seconds since 1970-01-01 00:00:00 UTC. * @@ -107,7 +114,7 @@ angular.module('rest').factory('ConnectionHistoryEntry', [function defineConnect * The name of the connection associated with the history entry (not * the connection identifier). */ - CONNECTION_NAME : 'connection', + CONNECTION_NAME : 'connectionName', /** * The username of the user associated with the history entry (the user diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnectionHistory.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnectionHistory.js new file mode 100644 index 000000000..7de5587e1 --- /dev/null +++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnectionHistory.js @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * A directive for viewing connection history records. + */ +angular.module('settings').directive('guacSettingsConnectionHistory', [function guacSettingsConnectionHistory() { + + return { + // Element only + restrict: 'E', + replace: true, + + scope: { + }, + + templateUrl: 'app/settings/templates/settingsConnectionHistory.html', + controller: ['$scope', '$injector', function settingsConnectionHistoryController($scope, $injector) { + + // Get required types + var SortOrder = $injector.get('SortOrder'); + + // Get required services + var $routeParams = $injector.get('$routeParams'); + var $translate = $injector.get('$translate'); + var historyService = $injector.get('historyService'); + + /** + * The identifier of the currently-selected data source. + * + * @type String + */ + $scope.dataSource = $routeParams.dataSource; + + /** + * The identifier of the currently-selected data source. + * + * @type String + */ + $scope.historyRecords = null; + + /** + * The search terms to use when filtering the history records. + * + * @type String + */ + $scope.searchString = ''; + + /** + * The date format for use for start/end dates. + * + * @type String + */ + $scope.dateFormat = null; + + /** + * SortOrder instance which stores the sort order of the history + * records. + * + * @type SortOrder + */ + $scope.order = new SortOrder([ + 'username', + 'startDate', + 'endDate', + 'connectionName' + ]); + + // Get session date format + $translate('SETTINGS_CONNECTION_HISTORY.FORMAT_DATE') + .then(function dateFormatReceived(retrievedDateFormat) { + + // Store received date format + $scope.dateFormat = retrievedDateFormat; + + }); + + /** + * Returns true if the connection history records have been loaded, + * indicating that information needed to render the page is fully + * loaded. + * + * @returns {Boolean} + * true if the history records have been loaded, false + * otherwise. + * + */ + $scope.isLoaded = function isLoaded() { + return $scope.historyRecords !== null + && $scope.dateFormat !== null; + }; + + /** + * Query the API for the connection record history, filtered by + * searchString, and ordered by order. + */ + $scope.search = function search() { + + // Clear current results + $scope.historyRecords = null; + + // Fetch history records + historyService.getConnectionHistory( + $scope.dataSource, + $scope.searchString.split(/\s+/), + $scope.order.predicate + ) + .success(function historyRetrieved(historyRecords) { + + // Store retrieved permissions + $scope.historyRecords = historyRecords; + + }); + + }; + + // Initialize search results + $scope.search(); + + }] + }; + +}]); diff --git a/guacamole/src/main/webapp/app/settings/styles/history.css b/guacamole/src/main/webapp/app/settings/styles/history.css new file mode 100644 index 000000000..92e553baa --- /dev/null +++ b/guacamole/src/main/webapp/app/settings/styles/history.css @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +.settings.connectionHistory .filter { + + /* IE10 */ + display: -ms-flexbox; + -ms-flex-align: stretch; + -ms-flex-direction: row; + + /* Ancient Mozilla */ + display: -moz-box; + -moz-box-align: stretch; + -moz-box-orient: horizontal; + + /* Ancient WebKit */ + display: -webkit-box; + -webkit-box-align: stretch; + -webkit-box-orient: horizontal; + + /* Old WebKit */ + display: -webkit-flex; + -webkit-align-items: stretch; + -webkit-flex-direction: row; + + /* W3C */ + display: flex; + align-items: stretch; + flex-direction: row; + +} + +.settings.connectionHistory .filter .search-button { + margin-top: 0; + margin-bottom: 0; +} + +.settings.connectionHistory .history-list { + width: 100%; +} \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/settings/templates/settings.html b/guacamole/src/main/webapp/app/settings/templates/settings.html index e138e6f68..9880ee29c 100644 --- a/guacamole/src/main/webapp/app/settings/templates/settings.html +++ b/guacamole/src/main/webapp/app/settings/templates/settings.html @@ -33,9 +33,10 @@ THE SOFTWARE. - - - - + + + + + diff --git a/guacamole/src/main/webapp/app/settings/templates/settingsConnectionHistory.html b/guacamole/src/main/webapp/app/settings/templates/settingsConnectionHistory.html new file mode 100644 index 000000000..a27a2d06b --- /dev/null +++ b/guacamole/src/main/webapp/app/settings/templates/settingsConnectionHistory.html @@ -0,0 +1,74 @@ +
+ + + +

{{'SETTINGS_CONNECTION_HISTORY.HELP_CONNECTION_HISTORY' | translate}}

+ + +
+ + +
+ + +
+ + + + + + + + + + + + + + + + + + + +
+ {{'SETTINGS_CONNECTION_HISTORY.TABLE_HEADER_SESSION_USERNAME' | translate}} + + {{'SETTINGS_CONNECTION_HISTORY.TABLE_HEADER_SESSION_STARTDATE' | translate}} + + {{'SETTINGS_CONNECTION_HISTORY.TABLE_HEADER_SESSION_ENDDATE' | translate}} + + {{'SETTINGS_CONNECTION_HISTORY.TABLE_HEADER_SESSION_CONNECTION_NAME' | translate}} +
{{historyRecord.username}}{{historyRecord.startDate | date : dateFormat}}{{historyRecord.endDate | date : dateFormat}}{{historyRecord.connectionName}}
+ + +

+ {{'SETTINGS_CONNECTION_HISTORY.INFO_NO_HISTORY' | translate}} +

+ + + +
+ +
diff --git a/guacamole/src/main/webapp/translations/en.json b/guacamole/src/main/webapp/translations/en.json index 918e75efd..b58c209de 100644 --- a/guacamole/src/main/webapp/translations/en.json +++ b/guacamole/src/main/webapp/translations/en.json @@ -20,7 +20,9 @@ "ACTION_NAVIGATE_BACK" : "Back", "ACTION_NAVIGATE_HOME" : "Home", "ACTION_SAVE" : "Save", + "ACTION_SEARCH" : "Search", "ACTION_UPDATE_PASSWORD" : "Update Password", + "ACTION_VIEW_HISTORY" : "History", "DIALOG_HEADER_ERROR" : "Error", @@ -473,6 +475,23 @@ }, + "SETTINGS_CONNECTION_HISTORY" : { + + "ACTION_SEARCH" : "@:APP.ACTION_SEARCH", + + "FORMAT_DATE" : "@:APP.FORMAT_DATE_TIME_PRECISE", + + "HELP_CONNECTION_HISTORY" : "All connection history records are listed here. If you wish to filter the connection records, you can enter a query.", + + "INFO_NO_HISTORY" : "No matching connection history", + + "TABLE_HEADER_SESSION_CONNECTION_NAME" : "Connection name", + "TABLE_HEADER_SESSION_ENDDATE" : "End Time", + "TABLE_HEADER_SESSION_STARTDATE" : "Start Time", + "TABLE_HEADER_SESSION_USERNAME" : "Username" + + }, + "SETTINGS_CONNECTIONS" : { "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", @@ -561,10 +580,10 @@ "SECTION_HEADER_SESSIONS" : "Active Sessions", - "TABLE_HEADER_SESSION_USERNAME" : "Username", - "TABLE_HEADER_SESSION_STARTDATE" : "Active since", - "TABLE_HEADER_SESSION_REMOTEHOST" : "Remote host", "TABLE_HEADER_SESSION_CONNECTION_NAME" : "Connection name", + "TABLE_HEADER_SESSION_REMOTEHOST" : "Remote host", + "TABLE_HEADER_SESSION_STARTDATE" : "Active since", + "TABLE_HEADER_SESSION_USERNAME" : "Username", "TEXT_CONFIRM_DELETE" : "Are you sure you want to kill all selected sessions? The users using these sessions will be immediately disconnected." @@ -578,7 +597,8 @@ "ACTION_MANAGE_SESSIONS" : "@:APP.ACTION_MANAGE_SESSIONS", "ACTION_MANAGE_SETTINGS" : "@:APP.ACTION_MANAGE_SETTINGS", "ACTION_MANAGE_USERS" : "@:APP.ACTION_MANAGE_USERS", - "ACTION_NAVIGATE_HOME" : "@:APP.ACTION_NAVIGATE_HOME" + "ACTION_NAVIGATE_HOME" : "@:APP.ACTION_NAVIGATE_HOME", + "ACTION_VIEW_HISTORY" : "@:APP.ACTION_VIEW_HISTORY" } From e221933e03be50a87d141abbae7574bc62aea077 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 15 Oct 2015 13:25:19 -0700 Subject: [PATCH 18/25] GUAC-1193: Remove use of @AuthProviderRESTExposure from history service (not used as of GUAC-1364). --- .../guacamole/net/basic/rest/history/HistoryRESTService.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/HistoryRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/HistoryRESTService.java index 5f8584772..a99a5ec9c 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/HistoryRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/history/HistoryRESTService.java @@ -37,7 +37,6 @@ import org.glyptodon.guacamole.net.auth.ConnectionRecord; import org.glyptodon.guacamole.net.auth.ConnectionRecordSet; import org.glyptodon.guacamole.net.auth.UserContext; import org.glyptodon.guacamole.net.basic.GuacamoleSession; -import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; import org.glyptodon.guacamole.net.basic.rest.ObjectRetrievalService; import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; import org.slf4j.Logger; @@ -110,7 +109,6 @@ public class HistoryRESTService { */ @GET @Path("/connections") - @AuthProviderRESTExposure public List getConnectionHistory(@QueryParam("token") String authToken, @PathParam("dataSource") String authProviderIdentifier, @QueryParam("contains") List requiredContents, From fc1192628836b8f59501ad4733d7fe2f7f8969ea Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 15 Oct 2015 13:50:49 -0700 Subject: [PATCH 19/25] GUAC-1193: Add filter placeholder to search input field. --- .../settings/templates/settingsConnectionHistory.html | 2 +- guacamole/src/main/webapp/translations/de.json | 10 +++++++++- guacamole/src/main/webapp/translations/en.json | 10 +++++++--- guacamole/src/main/webapp/translations/fr.json | 10 +++++++++- guacamole/src/main/webapp/translations/it.json | 10 +++++++++- guacamole/src/main/webapp/translations/nl.json | 10 +++++++++- guacamole/src/main/webapp/translations/ru.json | 10 +++++++++- 7 files changed, 53 insertions(+), 9 deletions(-) diff --git a/guacamole/src/main/webapp/app/settings/templates/settingsConnectionHistory.html b/guacamole/src/main/webapp/app/settings/templates/settingsConnectionHistory.html index a27a2d06b..900ba9a74 100644 --- a/guacamole/src/main/webapp/app/settings/templates/settingsConnectionHistory.html +++ b/guacamole/src/main/webapp/app/settings/templates/settingsConnectionHistory.html @@ -26,7 +26,7 @@
- +
diff --git a/guacamole/src/main/webapp/translations/de.json b/guacamole/src/main/webapp/translations/de.json index 8ff9539dd..674ed646c 100644 --- a/guacamole/src/main/webapp/translations/de.json +++ b/guacamole/src/main/webapp/translations/de.json @@ -30,6 +30,8 @@ "FIELD_HEADER_PASSWORD" : "Passwort:", "FIELD_HEADER_PASSWORD_AGAIN" : "Wiederhole Passwort:", + "FIELD_PLACEHOLDER_FILTER" : "Filter", + "FORMAT_DATE_TIME_PRECISE" : "dd-MM-yyyy HH:mm:ss", "INFO_ACTIVE_USER_COUNT" : "In Benutzung durch {USERS} Benutzer.", @@ -456,6 +458,12 @@ }, + "SETTINGS_CONNECTION_HISTORY" : { + + "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER" + + }, + "SETTINGS_PREFERENCES" : { "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", @@ -518,7 +526,7 @@ "DIALOG_HEADER_CONFIRM_DELETE" : "Beende Sitzung", "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR", - "FIELD_PLACEHOLDER_FILTER" : "Filter", + "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER", "FORMAT_STARTDATE" : "@:APP.FORMAT_DATE_TIME_PRECISE", diff --git a/guacamole/src/main/webapp/translations/en.json b/guacamole/src/main/webapp/translations/en.json index b58c209de..0a16bd34c 100644 --- a/guacamole/src/main/webapp/translations/en.json +++ b/guacamole/src/main/webapp/translations/en.json @@ -32,6 +32,8 @@ "FIELD_HEADER_PASSWORD" : "Password:", "FIELD_HEADER_PASSWORD_AGAIN" : "Re-enter Password:", + "FIELD_PLACEHOLDER_FILTER" : "Filter", + "FORMAT_DATE_TIME_PRECISE" : "yyyy-MM-dd HH:mm:ss", "INFO_ACTIVE_USER_COUNT" : "Currently in use by {USERS} {USERS, plural, one{user} other{users}}.", @@ -479,6 +481,8 @@ "ACTION_SEARCH" : "@:APP.ACTION_SEARCH", + "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER", + "FORMAT_DATE" : "@:APP.FORMAT_DATE_TIME_PRECISE", "HELP_CONNECTION_HISTORY" : "All connection history records are listed here. If you wish to filter the connection records, you can enter a query.", @@ -486,8 +490,8 @@ "INFO_NO_HISTORY" : "No matching connection history", "TABLE_HEADER_SESSION_CONNECTION_NAME" : "Connection name", - "TABLE_HEADER_SESSION_ENDDATE" : "End Time", - "TABLE_HEADER_SESSION_STARTDATE" : "Start Time", + "TABLE_HEADER_SESSION_ENDDATE" : "End time", + "TABLE_HEADER_SESSION_STARTDATE" : "Start time", "TABLE_HEADER_SESSION_USERNAME" : "Username" }, @@ -570,7 +574,7 @@ "DIALOG_HEADER_CONFIRM_DELETE" : "Kill Sessions", "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR", - "FIELD_PLACEHOLDER_FILTER" : "Filter", + "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER", "FORMAT_STARTDATE" : "@:APP.FORMAT_DATE_TIME_PRECISE", diff --git a/guacamole/src/main/webapp/translations/fr.json b/guacamole/src/main/webapp/translations/fr.json index 09b3a49c6..f14336d65 100644 --- a/guacamole/src/main/webapp/translations/fr.json +++ b/guacamole/src/main/webapp/translations/fr.json @@ -30,6 +30,8 @@ "FIELD_HEADER_PASSWORD" : "Mot de passe:", "FIELD_HEADER_PASSWORD_AGAIN" : "Répéter mot de passe:", + "FIELD_PLACEHOLDER_FILTER" : "Filtre", + "FORMAT_DATE_TIME_PRECISE" : "dd-MM-yyyy HH:mm:ss", "INFO_ACTIVE_USER_COUNT" : "Actuellement utilisé par {USERS} {USERS, plural, one{utilisateur} other{utilisateurs}}.", @@ -456,6 +458,12 @@ }, + "SETTINGS_CONNECTION_HISTORY" : { + + "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER" + + }, + "SETTINGS_PREFERENCES" : { "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", @@ -518,7 +526,7 @@ "DIALOG_HEADER_CONFIRM_DELETE" : "Fermer Sessions", "DIALOG_HEADER_ERROR" : "Erreur", - "FIELD_PLACEHOLDER_FILTER" : "Filtre", + "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER", "FORMAT_STARTDATE" : "@:APP.FORMAT_DATE_TIME_PRECISE", diff --git a/guacamole/src/main/webapp/translations/it.json b/guacamole/src/main/webapp/translations/it.json index bea3aad65..4dff9f94a 100644 --- a/guacamole/src/main/webapp/translations/it.json +++ b/guacamole/src/main/webapp/translations/it.json @@ -30,6 +30,8 @@ "FIELD_HEADER_PASSWORD" : "Password:", "FIELD_HEADER_PASSWORD_AGAIN" : "Re-inserisci la password:", + "FIELD_PLACEHOLDER_FILTER" : "Filtro", + "FORMAT_DATE_TIME_PRECISE" : "dd-MM-yyyy HH:mm:ss", "INFO_ACTIVE_USER_COUNT" : "Ora utilizzato da {USERS} {USERS, plural, one{user} other{users}}.", @@ -456,6 +458,12 @@ }, + "SETTINGS_CONNECTION_HISTORY" : { + + "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER" + + }, + "SETTINGS_PREFERENCES" : { "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", @@ -518,7 +526,7 @@ "DIALOG_HEADER_CONFIRM_DELETE" : "Termina Sessione", "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR", - "FIELD_PLACEHOLDER_FILTER" : "Filtro", + "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER", "FORMAT_STARTDATE" : "@:APP.FORMAT_DATE_TIME_PRECISE", diff --git a/guacamole/src/main/webapp/translations/nl.json b/guacamole/src/main/webapp/translations/nl.json index c2dded721..255d08fdd 100644 --- a/guacamole/src/main/webapp/translations/nl.json +++ b/guacamole/src/main/webapp/translations/nl.json @@ -30,6 +30,8 @@ "FIELD_HEADER_PASSWORD" : "Wachtwoord:", "FIELD_HEADER_PASSWORD_AGAIN" : "Nogmaals uw wachtwoord:", + "FIELD_PLACEHOLDER_FILTER" : "Filter", + "FORMAT_DATE_TIME_PRECISE" : "yyyy-MM-dd HH:mm:ss", "INFO_ACTIVE_USER_COUNT" : "Op dit moment in gebruik door {USERS} {USERS, plural, one{gebruiker} other{gebruikers}}.", @@ -447,6 +449,12 @@ }, + "SETTINGS_CONNECTION_HISTORY" : { + + "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER" + + }, + "SETTINGS_PREFERENCES" : { "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", @@ -509,7 +517,7 @@ "DIALOG_HEADER_CONFIRM_DELETE" : "Beeindig Sessie", "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR", - "FIELD_PLACEHOLDER_FILTER" : "Filter", + "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER", "FORMAT_STARTDATE" : "@:APP.FORMAT_DATE_TIME_PRECISE", diff --git a/guacamole/src/main/webapp/translations/ru.json b/guacamole/src/main/webapp/translations/ru.json index d1607fe85..22883b910 100644 --- a/guacamole/src/main/webapp/translations/ru.json +++ b/guacamole/src/main/webapp/translations/ru.json @@ -29,6 +29,8 @@ "FIELD_HEADER_PASSWORD" : "Пароль:", "FIELD_HEADER_PASSWORD_AGAIN" : "Повтор пароля:", + "FIELD_PLACEHOLDER_FILTER" : "Фильтр", + "FORMAT_DATE_TIME_PRECISE" : "yyyy-MM-dd HH:mm:ss", "INFO_ACTIVE_USER_COUNT" : "Подключено пользователей {USERS}.", @@ -424,6 +426,12 @@ }, + "SETTINGS_CONNECTION_HISTORY" : { + + "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER" + + }, + "SETTINGS_PREFERENCES" : { "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", @@ -486,7 +494,7 @@ "DIALOG_HEADER_CONFIRM_DELETE" : "Завершение сессий", "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR", - "FIELD_PLACEHOLDER_FILTER" : "Фильтр", + "FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER", "FORMAT_STARTDATE" : "@:APP.FORMAT_DATE_TIME_PRECISE", From 83edfb9832e7945fc55db13c2a4854acceba389a Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 15 Oct 2015 14:03:14 -0700 Subject: [PATCH 20/25] GUAC-1193: Use filter token parser to split search string. --- .../guacSettingsConnectionHistory.js | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnectionHistory.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnectionHistory.js index 7de5587e1..7284a1135 100644 --- a/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnectionHistory.js +++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnectionHistory.js @@ -37,7 +37,8 @@ angular.module('settings').directive('guacSettingsConnectionHistory', [function controller: ['$scope', '$injector', function settingsConnectionHistoryController($scope, $injector) { // Get required types - var SortOrder = $injector.get('SortOrder'); + var FilterToken = $injector.get('FilterToken'); + var SortOrder = $injector.get('SortOrder'); // Get required services var $routeParams = $injector.get('$routeParams'); @@ -118,10 +119,37 @@ angular.module('settings').directive('guacSettingsConnectionHistory', [function // Clear current results $scope.historyRecords = null; + // Tokenize search string + var tokens = FilterToken.tokenize($scope.searchString); + + // Transform tokens into list of required string contents + var requiredContents = []; + angular.forEach(tokens, function addRequiredContents(token) { + + // Transform depending on token type + switch (token.type) { + + // For string literals, use parsed token value + case 'LITERAL': + requiredContents.push(token.value); + + // Ignore whitespace + case 'WHITESPACE': + break; + + // For all other token types, use the relevant portion + // of the original search string + default: + requiredContents.push(token.consumed); + + } + + }); + // Fetch history records historyService.getConnectionHistory( - $scope.dataSource, - $scope.searchString.split(/\s+/), + $scope.dataSource, + requiredContents, $scope.order.predicate ) .success(function historyRetrieved(historyRecords) { From aa2261c2824647c56e011430d7cf3a71f96c30fa Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 15 Oct 2015 14:07:26 -0700 Subject: [PATCH 21/25] GUAC-1193: Use actual form and submit button - allow automatic handling of searching via pressing "Enter", etc. --- .../app/settings/templates/settingsConnectionHistory.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/guacamole/src/main/webapp/app/settings/templates/settingsConnectionHistory.html b/guacamole/src/main/webapp/app/settings/templates/settingsConnectionHistory.html index 900ba9a74..d0d4f7547 100644 --- a/guacamole/src/main/webapp/app/settings/templates/settingsConnectionHistory.html +++ b/guacamole/src/main/webapp/app/settings/templates/settingsConnectionHistory.html @@ -25,10 +25,10 @@

{{'SETTINGS_CONNECTION_HISTORY.HELP_CONNECTION_HISTORY' | translate}}

-
+ - -
+ +
From 854e7e81968fd8764226ee0c88c0cab31990c7e3 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 15 Oct 2015 14:38:19 -0700 Subject: [PATCH 22/25] GUAC-1193: Reword history search instructions. --- guacamole/src/main/webapp/translations/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guacamole/src/main/webapp/translations/en.json b/guacamole/src/main/webapp/translations/en.json index 0a16bd34c..d8085a36a 100644 --- a/guacamole/src/main/webapp/translations/en.json +++ b/guacamole/src/main/webapp/translations/en.json @@ -485,9 +485,9 @@ "FORMAT_DATE" : "@:APP.FORMAT_DATE_TIME_PRECISE", - "HELP_CONNECTION_HISTORY" : "All connection history records are listed here. If you wish to filter the connection records, you can enter a query.", + "HELP_CONNECTION_HISTORY" : "History records for past connections are listed here and can be sorted by clicking the column headers. To search for specific records, enter a filter string and click \"Search\". Only records which match the provided filter string will be listed.", - "INFO_NO_HISTORY" : "No matching connection history", + "INFO_NO_HISTORY" : "No matching records", "TABLE_HEADER_SESSION_CONNECTION_NAME" : "Connection name", "TABLE_HEADER_SESSION_ENDDATE" : "End time", From a24e754c803fa7e79dda922c4bbbd799db6ed8f8 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 15 Oct 2015 15:23:07 -0700 Subject: [PATCH 23/25] GUAC-1193: Fix documentation of historyRecords property. --- .../app/settings/directives/guacSettingsConnectionHistory.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnectionHistory.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnectionHistory.js index 7284a1135..edc7e9db8 100644 --- a/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnectionHistory.js +++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnectionHistory.js @@ -53,9 +53,10 @@ angular.module('settings').directive('guacSettingsConnectionHistory', [function $scope.dataSource = $routeParams.dataSource; /** - * The identifier of the currently-selected data source. + * All matching connection history records, or null if these + * records have not yet been retrieved. * - * @type String + * @type ConnectionHistoryEntry[] */ $scope.historyRecords = null; From e71d7e3a4f532f2a96fa0882d22a5f78a9d25812 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 15 Oct 2015 15:24:56 -0700 Subject: [PATCH 24/25] GUAC-1193: Fix year of copyright notice. --- guacamole/src/main/webapp/app/settings/styles/history.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guacamole/src/main/webapp/app/settings/styles/history.css b/guacamole/src/main/webapp/app/settings/styles/history.css index 92e553baa..4daa5c69a 100644 --- a/guacamole/src/main/webapp/app/settings/styles/history.css +++ b/guacamole/src/main/webapp/app/settings/styles/history.css @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Glyptodon LLC + * Copyright (C) 2015 Glyptodon LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal From e5d1458ef76a8669c678c57f02825b771915850c Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 15 Oct 2015 15:25:50 -0700 Subject: [PATCH 25/25] GUAC-1193: We're displaying history records, not user sessions. --- .../app/settings/templates/settingsConnectionHistory.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guacamole/src/main/webapp/app/settings/templates/settingsConnectionHistory.html b/guacamole/src/main/webapp/app/settings/templates/settingsConnectionHistory.html index d0d4f7547..9a54d6883 100644 --- a/guacamole/src/main/webapp/app/settings/templates/settingsConnectionHistory.html +++ b/guacamole/src/main/webapp/app/settings/templates/settingsConnectionHistory.html @@ -33,7 +33,7 @@
- +