From 3d7b8ee89b278e7bfc0ccad1fa78092450761086 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Mon, 11 Sep 2017 19:47:59 -0700 Subject: [PATCH 01/15] GUACAMOLE-394: Pull connection last active time from history records. --- .../auth/jdbc/connection/ConnectionModel.java | 33 ++++++++++++++++++ .../jdbc/connection/ModeledConnection.java | 2 +- .../auth/jdbc/connection/ConnectionMapper.xml | 34 ++++++++++++------- .../auth/jdbc/connection/ConnectionMapper.xml | 34 ++++++++++++------- .../auth/jdbc/connection/ConnectionMapper.xml | 34 ++++++++++++------- 5 files changed, 100 insertions(+), 37 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionModel.java index 788daa1b0..da454025d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionModel.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionModel.java @@ -19,6 +19,7 @@ package org.apache.guacamole.auth.jdbc.connection; +import java.util.Date; import java.util.HashSet; import java.util.Set; import org.apache.guacamole.auth.jdbc.base.ChildObjectModel; @@ -92,6 +93,12 @@ public class ConnectionModel extends ChildObjectModel { */ private EncryptionMethod proxyEncryptionMethod; + /** + * The date and time that this connection was last used, or null if this + * connection has never been used. + */ + private Date lastActive; + /** * Creates a new, empty connection. */ @@ -341,6 +348,32 @@ public class ConnectionModel extends ChildObjectModel { this.sharingProfileIdentifiers = sharingProfileIdentifiers; } + /** + * Returns the date and time that this connection was last used, or null if + * this connection has never been used. + * + * @return + * The date and time that this connection was last used, or null if this + * connection has never been used. + */ + public Date getLastActive() { + return lastActive; + } + + /** + * Sets the date and time that this connection was last used. This value is + * expected to be set automatically via queries, derived from connection + * history records. It does NOT correspond to an actual column, and values + * set manually through invoking this function will not persist. + * + * @param lastActive + * The date and time that this connection was last used, or null if this + * connection has never been used. + */ + public void setLastActive(Date lastActive) { + this.lastActive = lastActive; + } + @Override public String getIdentifier() { diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ModeledConnection.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ModeledConnection.java index c596b2771..eb392bc7b 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ModeledConnection.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ModeledConnection.java @@ -235,7 +235,7 @@ public class ModeledConnection extends ModeledChildDirectoryObject + SELECT - connection_id, - connection_name, + guacamole_connection.connection_id, + guacamole_connection.connection_name, parent_id, protocol, max_connections, @@ -99,13 +100,16 @@ proxy_port, proxy_encryption_method, connection_weight, - failover_only + failover_only, + MAX(start_date) AS last_active FROM guacamole_connection - WHERE connection_id IN + JOIN guacamole_connection_history ON guacamole_connection_history.connection_id = guacamole_connection.connection_id + WHERE guacamole_connection.connection_id IN #{identifier,jdbcType=VARCHAR} - ; + + GROUP BY guacamole_connection.connection_id; SELECT primary_connection_id, sharing_profile_id FROM guacamole_sharing_profile @@ -123,7 +127,7 @@ SELECT guacamole_connection.connection_id, - connection_name, + guacamole_connection.connection_name, parent_id, protocol, max_connections, @@ -132,16 +136,19 @@ proxy_port, proxy_encryption_method, connection_weight, - failover_only + failover_only, + MAX(start_date) AS last_active FROM guacamole_connection JOIN guacamole_connection_permission ON guacamole_connection_permission.connection_id = guacamole_connection.connection_id + JOIN guacamole_connection_history ON guacamole_connection_history.connection_id = guacamole_connection.connection_id WHERE guacamole_connection.connection_id IN #{identifier,jdbcType=VARCHAR} AND user_id = #{user.objectID,jdbcType=INTEGER} - AND permission = 'READ'; + AND permission = 'READ' + GROUP BY guacamole_connection.connection_id; SELECT primary_connection_id, guacamole_sharing_profile.sharing_profile_id FROM guacamole_sharing_profile @@ -160,8 +167,8 @@ diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml index dd9265dfa..09bd51027 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml @@ -39,6 +39,7 @@ javaType="org.apache.guacamole.net.auth.GuacamoleProxyConfiguration$EncryptionMethod"/> + SELECT - connection_id, - connection_name, + guacamole_connection.connection_id, + guacamole_connection.connection_name, parent_id, protocol, max_connections, @@ -99,13 +100,16 @@ proxy_port, proxy_encryption_method, connection_weight, - failover_only + failover_only, + MAX(start_date) AS last_active FROM guacamole_connection - WHERE connection_id IN + JOIN guacamole_connection_history ON guacamole_connection_history.connection_id = guacamole_connection.connection_id + WHERE guacamole_connection.connection_id IN #{identifier,jdbcType=INTEGER}::integer - ; + + GROUP BY guacamole_connection.connection_id; SELECT primary_connection_id, sharing_profile_id FROM guacamole_sharing_profile @@ -123,7 +127,7 @@ SELECT guacamole_connection.connection_id, - connection_name, + guacamole_connection.connection_name, parent_id, protocol, max_connections, @@ -132,16 +136,19 @@ proxy_port, proxy_encryption_method, connection_weight, - failover_only + failover_only, + MAX(start_date) AS last_active FROM guacamole_connection JOIN guacamole_connection_permission ON guacamole_connection_permission.connection_id = guacamole_connection.connection_id + JOIN guacamole_connection_history ON guacamole_connection_history.connection_id = guacamole_connection.connection_id WHERE guacamole_connection.connection_id IN #{identifier,jdbcType=INTEGER}::integer AND user_id = #{user.objectID,jdbcType=INTEGER} - AND permission = 'READ'; + AND permission = 'READ' + GROUP BY guacamole_connection.connection_id; SELECT primary_connection_id, guacamole_sharing_profile.sharing_profile_id FROM guacamole_sharing_profile @@ -160,8 +167,8 @@ diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml index 3e6819f06..d27b5282d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml @@ -39,6 +39,7 @@ javaType="org.apache.guacamole.net.auth.GuacamoleProxyConfiguration$EncryptionMethod"/> + SELECT - connection_id, - connection_name, + [guacamole_connection].connection_id, + [guacamole_connection].connection_name, parent_id, protocol, max_connections, @@ -99,13 +100,16 @@ proxy_port, proxy_encryption_method, connection_weight, - failover_only + failover_only, + MAX(start_date) AS last_active FROM [guacamole_connection] - WHERE connection_id IN + JOIN [guacamole_connection_history] ON [guacamole_connection_history].connection_id = [guacamole_connection].connection_id + WHERE [guacamole_connection].connection_id IN #{identifier,jdbcType=INTEGER} - ; + + GROUP BY [guacamole_connection].connection_id; SELECT primary_connection_id, sharing_profile_id FROM [guacamole_sharing_profile] @@ -123,7 +127,7 @@ SELECT [guacamole_connection].connection_id, - connection_name, + [guacamole_connection].connection_name, parent_id, protocol, max_connections, @@ -132,16 +136,19 @@ proxy_port, proxy_encryption_method, connection_weight, - failover_only + failover_only, + MAX(start_date) AS last_active FROM [guacamole_connection] JOIN [guacamole_connection_permission] ON [guacamole_connection_permission].connection_id = [guacamole_connection].connection_id + JOIN [guacamole_connection_history] ON [guacamole_connection_history].connection_id = [guacamole_connection].connection_id WHERE [guacamole_connection].connection_id IN #{identifier,jdbcType=INTEGER} AND user_id = #{user.objectID,jdbcType=INTEGER} - AND permission = 'READ'; + AND permission = 'READ' + GROUP BY [guacamole_connection].connection_id; SELECT primary_connection_id, [guacamole_sharing_profile].sharing_profile_id FROM [guacamole_sharing_profile] @@ -160,8 +167,8 @@ From c991ea46bc406dbf3973ebffb7a374e71379229e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 12 Sep 2017 12:18:05 -0700 Subject: [PATCH 02/15] GUACAMOLE-394: Split ConnectionRecord model and ModeledConnectionRecord into ActivityRecordModel and ModeledActivityRecord, etc. mirroring changes to guacamole-ext. --- .../auth/jdbc/base/ActivityRecordModel.java | 167 ++++++++++++++++++ .../ActivityRecordSearchTerm.java} | 18 +- .../ActivityRecordSortPredicate.java} | 18 +- .../auth/jdbc/base/ModeledActivityRecord.java | 73 ++++++++ .../connection/ConnectionRecordMapper.java | 10 +- .../connection/ConnectionRecordModel.java | 135 +------------- .../jdbc/connection/ConnectionRecordSet.java | 14 +- .../jdbc/connection/ConnectionService.java | 6 +- .../connection/ModeledConnectionRecord.java | 31 +--- 9 files changed, 282 insertions(+), 190 deletions(-) create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordModel.java rename extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/{connection/ConnectionRecordSearchTerm.java => base/ActivityRecordSearchTerm.java} (93%) rename extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/{connection/ConnectionRecordSortPredicate.java => base/ActivityRecordSortPredicate.java} (77%) create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledActivityRecord.java diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordModel.java new file mode 100644 index 000000000..86f2204a2 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordModel.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.jdbc.base; + +import java.util.Date; + +/** + * A single activity record representing an arbitrary activity performed by a + * user. + */ +public class ActivityRecordModel { + + /** + * The database ID of the user associated with this activity record. + */ + private Integer userID; + + /** + * The username of the user that performed the activity. + */ + private String username; + + /** + * The remote host associated with the user that performed the activity. + */ + private String remoteHost; + + /** + * The time the activity was initiated by the associated user. + */ + private Date startDate; + + /** + * The time the activity ended, or null if the end time is not known or + * the activity is still in progress. + */ + private Date endDate; + + /** + * Returns the database ID of the user associated with this activity + * record. + * + * @return + * The database ID of the user associated with this activity record. + */ + public Integer getUserID() { + return userID; + } + + /** + * Sets the database ID of the user associated with this activity record. + * + * @param userID + * The database ID of the user to associate with this activity + * record. + */ + public void setUserID(Integer userID) { + this.userID = userID; + } + + /** + * Returns the username of the user that performed the activity associated + * with this record. + * + * @return + * The username of the user that performed the activity associated with + * this record. + */ + public String getUsername() { + return username; + } + + /** + * Sets the username of the user that performed the activity associated + * with this record. + * + * @param username + * The username of the user that performed the activity associated with + * this record. + */ + public void setUsername(String username) { + this.username = username; + } + + /** + * Returns the remote host associated with the user that performed the + * activity. + * + * @return + * The remote host associated with the user that performed the activity. + */ + public String getRemoteHost() { + return remoteHost; + } + + /** + * Sets the remote host associated with the user that performed the + * activity. + * + * @param remoteHost + * The remote host associated with the user that performed the activity. + */ + public void setRemoteHost(String remoteHost) { + this.remoteHost = remoteHost; + } + + /** + * Returns the time the activity was initiated by the associated user. + * + * @return + * The time the activity was initiated by the associated user. + */ + public Date getStartDate() { + return startDate; + } + + /** + * Sets the time the activity was initiated by the associated user. + * + * @param startDate + * The time the activity was initiated by the associated user. + */ + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + /** + * Returns the time the activity ended, or null if the end time is not + * known or the activity is still in progress. + * + * @return + * The time the activity ended, or null if the end time is not known or + * the activity is still in progress. + */ + public Date getEndDate() { + return endDate; + } + + /** + * Sets the time the activity ended, if known. + * + * @param endDate + * The time the activity ended, or null if the end time is not known or + * the activity is still in progress. + */ + public void setEndDate(Date endDate) { + this.endDate = endDate; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordSearchTerm.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordSearchTerm.java similarity index 93% rename from extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordSearchTerm.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordSearchTerm.java index 844eff7c0..54af1a694 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordSearchTerm.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordSearchTerm.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.guacamole.auth.jdbc.connection; +package org.apache.guacamole.auth.jdbc.base; import java.util.Calendar; import java.util.Date; @@ -25,11 +25,11 @@ 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. + * A search term for querying historical records of arbitrary activities. This + * will contain a the search term in string form and, if that string appears to + * be a date. a corresponding date range. */ -public class ConnectionRecordSearchTerm { +public class ActivityRecordSearchTerm { /** * A pattern that can match a year, year and month, or year and month and @@ -180,7 +180,7 @@ public class ConnectionRecordSearchTerm { } /** - * Creates a new ConnectionRecordSearchTerm representing the given string. + * Creates a new ActivityRecordSearchTerm 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. @@ -188,7 +188,7 @@ public class ConnectionRecordSearchTerm { * @param term * The string that should be searched for. */ - public ConnectionRecordSearchTerm(String term) { + public ActivityRecordSearchTerm(String term) { // Search terms absolutely must not be null if (term == null) @@ -281,10 +281,10 @@ public class ConnectionRecordSearchTerm { @Override public boolean equals(Object obj) { - if (obj == null || !(obj instanceof ConnectionRecordSearchTerm)) + if (obj == null || !(obj instanceof ActivityRecordSearchTerm)) return false; - return ((ConnectionRecordSearchTerm) obj).getTerm().equals(getTerm()); + return ((ActivityRecordSearchTerm) obj).getTerm().equals(getTerm()); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordSortPredicate.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordSortPredicate.java similarity index 77% rename from extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordSortPredicate.java rename to extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordSortPredicate.java index 69eee780b..ab0d3ce11 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordSortPredicate.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordSortPredicate.java @@ -17,18 +17,18 @@ * under the License. */ -package org.apache.guacamole.auth.jdbc.connection; +package org.apache.guacamole.auth.jdbc.base; import org.apache.guacamole.net.auth.ActivityRecordSet; /** - * A sort predicate which species the property to use when sorting connection + * A sort predicate which species the property to use when sorting activity * records, along with the sort order. */ -public class ConnectionRecordSortPredicate { +public class ActivityRecordSortPredicate { /** - * The property to use when sorting ConnectionRecords. + * The property to use when sorting ActivityRecords. */ private final ActivityRecordSet.SortableProperty property; @@ -38,26 +38,26 @@ public class ConnectionRecordSortPredicate { private final boolean descending; /** - * Creates a new ConnectionRecordSortPredicate with the given sort property + * Creates a new ActivityRecordSortPredicate with the given sort property * and sort order. * * @param property - * The property to use when sorting ConnectionRecords. + * The property to use when sorting ActivityRecords. * * @param descending * Whether the sort order is descending (true) or ascending (false). */ - public ConnectionRecordSortPredicate(ActivityRecordSet.SortableProperty property, + public ActivityRecordSortPredicate(ActivityRecordSet.SortableProperty property, boolean descending) { this.property = property; this.descending = descending; } /** - * Returns the property that should be used when sorting ConnectionRecords. + * Returns the property that should be used when sorting ActivityRecords. * * @return - * The property that should be used when sorting ConnectionRecords. + * The property that should be used when sorting ActivityRecords. */ public ActivityRecordSet.SortableProperty getProperty() { return property; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledActivityRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledActivityRecord.java new file mode 100644 index 000000000..95b1a256d --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledActivityRecord.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.jdbc.base; + + +import java.util.Date; +import org.apache.guacamole.net.auth.ActivityRecord; + +/** + * An ActivityRecord which is backed by a database model. + */ +public class ModeledActivityRecord implements ActivityRecord { + + /** + * The model object backing this activity record. + */ + private final ActivityRecordModel model; + + /** + * Creates a new ModeledActivityRecord backed by the given model object. + * Changes to this record will affect the backing model object, and changes + * to the backing model object will affect this record. + * + * @param model + * The model object to use to back this activity record. + */ + public ModeledActivityRecord(ActivityRecordModel model) { + this.model = model; + } + + @Override + public Date getStartDate() { + return model.getStartDate(); + } + + @Override + public Date getEndDate() { + return model.getEndDate(); + } + + @Override + public String getRemoteHost() { + return model.getRemoteHost(); + } + + @Override + public String getUsername() { + return model.getUsername(); + } + + @Override + public boolean isActive() { + return false; + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.java index aefff9239..637fd0fed 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.java @@ -21,6 +21,8 @@ package org.apache.guacamole.auth.jdbc.connection; import java.util.Collection; import java.util.List; +import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm; +import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate; import org.apache.ibatis.annotations.Param; import org.apache.guacamole.auth.jdbc.user.UserModel; @@ -75,8 +77,8 @@ public interface ConnectionRecordMapper { * @return * The results of the search performed with the given parameters. */ - List search(@Param("terms") Collection terms, - @Param("sortPredicates") List sortPredicates, + List search(@Param("terms") Collection terms, + @Param("sortPredicates") List sortPredicates, @Param("limit") int limit); /** @@ -104,8 +106,8 @@ public interface ConnectionRecordMapper { * 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("terms") Collection 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/apache/guacamole/auth/jdbc/connection/ConnectionRecordModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordModel.java index f142f4e8f..29c55569d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordModel.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordModel.java @@ -19,14 +19,14 @@ package org.apache.guacamole.auth.jdbc.connection; -import java.util.Date; +import org.apache.guacamole.auth.jdbc.base.ActivityRecordModel; /** * A single connection record representing a past usage of a particular * connection. If the connection was being shared, the sharing profile used to * join the connection is included in the record. */ -public class ConnectionRecordModel { +public class ConnectionRecordModel extends ActivityRecordModel { /** * The identifier of the connection associated with this connection record. @@ -53,32 +53,6 @@ public class ConnectionRecordModel { */ private String sharingProfileName; - /** - * The database ID of the user associated with this connection record. - */ - private Integer userID; - - /** - * The username of the user associated with this connection record. - */ - private String username; - - /** - * The remote host associated with this connection record. - */ - private String remoteHost; - - /** - * The time the connection was initiated by the associated user. - */ - private Date startDate; - - /** - * The time the connection ended, or null if the end time is not known or - * the connection is still running. - */ - private Date endDate; - /** * Returns the identifier of the connection associated with this connection * record. @@ -179,109 +153,4 @@ public class ConnectionRecordModel { this.sharingProfileName = sharingProfileName; } - /** - * Returns the database ID of the user associated with this connection - * record. - * - * @return - * The database ID of the user associated with this connection record. - */ - public Integer getUserID() { - return userID; - } - - /** - * Sets the database ID of the user associated with this connection record. - * - * @param userID - * The database ID of the user to associate with this connection - * record. - */ - public void setUserID(Integer userID) { - this.userID = userID; - } - - /** - * Returns the username of the user associated with this connection record. - * - * @return - * The username of the user associated with this connection record. - */ - public String getUsername() { - return username; - } - - /** - * Sets the username of the user associated with this connection record. - * - * @param username - * The username of the user to associate with this connection record. - */ - public void setUsername(String username) { - this.username = username; - } - - /** - * Returns the remote host associated with this connection record. - * - * @return - * The remote host associated with this connection record. - */ - public String getRemoteHost() { - return remoteHost; - } - - /** - * Sets the remote host associated with this connection record. - * - * @param remoteHost - * The remote host to associate with this connection record. - */ - public void setRemoteHost(String remoteHost) { - this.remoteHost = remoteHost; - } - - /** - * Returns the date that the associated connection was established. - * - * @return - * The date the associated connection was established. - */ - public Date getStartDate() { - return startDate; - } - - /** - * Sets the date that the associated connection was established. - * - * @param startDate - * The date that the associated connection was established. - */ - public void setStartDate(Date startDate) { - this.startDate = startDate; - } - - /** - * Returns the date that the associated connection ended, or null if no - * end date was recorded. The lack of an end date does not necessarily - * mean that the connection is still active. - * - * @return - * The date the associated connection ended, or null if no end date was - * recorded. - */ - public Date getEndDate() { - return endDate; - } - - /** - * Sets the date that the associated connection ended. - * - * @param endDate - * The date that the associated connection ended. - */ - public void setEndDate(Date endDate) { - this.endDate = endDate; - } - } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordSet.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordSet.java index 7b3d629c4..df2a0a9ea 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordSet.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordSet.java @@ -26,6 +26,8 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm; +import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate; import org.apache.guacamole.auth.jdbc.base.RestrictedObject; import org.apache.guacamole.net.auth.ActivityRecordSet; import org.apache.guacamole.net.auth.ActivityRecordSet.SortableProperty; @@ -52,8 +54,8 @@ public class ConnectionRecordSet extends RestrictedObject * connection record not matching each of the strings within the collection * will be excluded from the results. */ - private final Set requiredContents = - new HashSet(); + private final Set requiredContents = + new HashSet(); /** * The maximum number of connection history records that should be returned @@ -66,8 +68,8 @@ public class ConnectionRecordSet extends RestrictedObject * records, describing the properties involved and the sort order for those * properties. */ - private final List connectionRecordSortPredicates = - new ArrayList(); + private final List connectionRecordSortPredicates = + new ArrayList(); @Override public Collection asCollection() @@ -79,7 +81,7 @@ public class ConnectionRecordSet extends RestrictedObject @Override public ConnectionRecordSet contains(String value) throws GuacamoleException { - requiredContents.add(new ConnectionRecordSearchTerm(value)); + requiredContents.add(new ActivityRecordSearchTerm(value)); return this; } @@ -93,7 +95,7 @@ public class ConnectionRecordSet extends RestrictedObject public ConnectionRecordSet sort(SortableProperty property, boolean desc) throws GuacamoleException { - connectionRecordSortPredicates.add(new ConnectionRecordSortPredicate( + connectionRecordSortPredicates.add(new ActivityRecordSortPredicate( property, desc )); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionService.java index f25632429..983f395f7 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionService.java @@ -34,6 +34,8 @@ import org.apache.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; import org.apache.guacamole.GuacamoleClientException; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleSecurityException; +import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm; +import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate; import org.apache.guacamole.auth.jdbc.base.ModeledChildDirectoryObjectService; import org.apache.guacamole.auth.jdbc.permission.ConnectionPermissionMapper; import org.apache.guacamole.auth.jdbc.permission.ObjectPermissionMapper; @@ -460,8 +462,8 @@ public class ConnectionService extends ModeledChildDirectoryObjectService retrieveHistory(ModeledAuthenticatedUser user, - Collection requiredContents, - List sortPredicates, int limit) + Collection requiredContents, + List sortPredicates, int limit) throws GuacamoleException { List searchResults; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java index a5e83d413..9f34385cf 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ModeledConnectionRecord.java @@ -20,13 +20,14 @@ package org.apache.guacamole.auth.jdbc.connection; -import java.util.Date; +import org.apache.guacamole.auth.jdbc.base.ModeledActivityRecord; import org.apache.guacamole.net.auth.ConnectionRecord; /** * A ConnectionRecord which is backed by a database model. */ -public class ModeledConnectionRecord implements ConnectionRecord { +public class ModeledConnectionRecord extends ModeledActivityRecord + implements ConnectionRecord { /** * The model object backing this connection record. @@ -42,6 +43,7 @@ public class ModeledConnectionRecord implements ConnectionRecord { * The model object to use to back this connection record. */ public ModeledConnectionRecord(ConnectionRecordModel model) { + super(model); this.model = model; } @@ -65,29 +67,4 @@ public class ModeledConnectionRecord implements ConnectionRecord { return model.getSharingProfileName(); } - @Override - public Date getStartDate() { - return model.getStartDate(); - } - - @Override - public Date getEndDate() { - return model.getEndDate(); - } - - @Override - public String getRemoteHost() { - return model.getRemoteHost(); - } - - @Override - public String getUsername() { - return model.getUsername(); - } - - @Override - public boolean isActive() { - return false; - } - } From 5aa2172eacfc066ddd925b959ca693e66eb1f35b Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 12 Sep 2017 13:18:00 -0700 Subject: [PATCH 03/15] GUACAMOLE-394: Add guacamole_user_history table to database schema. --- .../schema/001-create-schema.sql | 24 ++++++++++++++ .../schema/upgrade/upgrade-pre-0.9.14.sql | 24 ++++++++++++++ .../schema/001-create-schema.sql | 30 ++++++++++++++++++ .../schema/upgrade/upgrade-pre-0.9.14.sql | 30 ++++++++++++++++++ .../schema/001-create-schema.sql | 31 +++++++++++++++++++ 5 files changed, 139 insertions(+) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/001-create-schema.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/001-create-schema.sql index 1873d1c04..d65bd3312 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/001-create-schema.sql +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/001-create-schema.sql @@ -351,6 +351,30 @@ CREATE TABLE `guacamole_connection_history` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +-- +-- User login/logout history +-- + +CREATE TABLE guacamole_user_history ( + + `history_id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) DEFAULT NULL, + `username` varchar(128) NOT NULL, + `remote_host` varchar(256) DEFAULT NULL, + `start_date` datetime NOT NULL, + `end_date` datetime DEFAULT NULL, + + PRIMARY KEY (history_id), + KEY `user_id` (`user_id`), + KEY `start_date` (`start_date`), + KEY `end_date` (`end_date`), + + CONSTRAINT guacamole_user_history_ibfk_1 + FOREIGN KEY (user_id) + REFERENCES guacamole_user (user_id) ON DELETE SET NULL + +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + -- -- User password history -- diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/upgrade/upgrade-pre-0.9.14.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/upgrade/upgrade-pre-0.9.14.sql index 01be93acd..5a0a0b229 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/upgrade/upgrade-pre-0.9.14.sql +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/schema/upgrade/upgrade-pre-0.9.14.sql @@ -37,3 +37,27 @@ ALTER TABLE guacamole_connection ALTER TABLE guacamole_connection_history ADD COLUMN remote_host VARCHAR(256) DEFAULT NULL; + +-- +-- User login/logout history +-- + +CREATE TABLE guacamole_user_history ( + + `history_id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) DEFAULT NULL, + `username` varchar(128) NOT NULL, + `remote_host` varchar(256) DEFAULT NULL, + `start_date` datetime NOT NULL, + `end_date` datetime DEFAULT NULL, + + PRIMARY KEY (history_id), + KEY `user_id` (`user_id`), + KEY `start_date` (`start_date`), + KEY `end_date` (`end_date`), + + CONSTRAINT guacamole_user_history_ibfk_1 + FOREIGN KEY (user_id) + REFERENCES guacamole_user (user_id) ON DELETE SET NULL + +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql index e4015d3ff..4840c91d2 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/001-create-schema.sql @@ -438,6 +438,36 @@ CREATE INDEX guacamole_connection_history_start_date CREATE INDEX guacamole_connection_history_end_date ON guacamole_connection_history(end_date); +-- +-- User login/logout history +-- + +CREATE TABLE guacamole_user_history ( + + history_id serial NOT NULL, + user_id integer DEFAULT NULL, + username varchar(128) NOT NULL, + remote_host varchar(256) DEFAULT NULL, + start_date timestamptz NOT NULL, + end_date timestamptz DEFAULT NULL, + + PRIMARY KEY (history_id), + + CONSTRAINT guacamole_user_history_ibfk_1 + FOREIGN KEY (user_id) + REFERENCES guacamole_user (user_id) ON DELETE SET NULL + +); + +CREATE INDEX guacamole_user_history_user_id + ON guacamole_user_history(user_id); + +CREATE INDEX guacamole_user_history_start_date + ON guacamole_user_history(start_date); + +CREATE INDEX guacamole_user_history_end_date + ON guacamole_user_history(end_date); + -- -- User password history -- diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/upgrade/upgrade-pre-0.9.14.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/upgrade/upgrade-pre-0.9.14.sql index 157e89638..39e8e5991 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/upgrade/upgrade-pre-0.9.14.sql +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/schema/upgrade/upgrade-pre-0.9.14.sql @@ -37,3 +37,33 @@ ALTER TABLE guacamole_connection ALTER TABLE guacamole_connection_history ADD COLUMN remote_host VARCHAR(256) DEFAULT NULL; + +-- +-- User login/logout history +-- + +CREATE TABLE guacamole_user_history ( + + history_id serial NOT NULL, + user_id integer DEFAULT NULL, + username varchar(128) NOT NULL, + remote_host varchar(256) DEFAULT NULL, + start_date timestamptz NOT NULL, + end_date timestamptz DEFAULT NULL, + + PRIMARY KEY (history_id), + + CONSTRAINT guacamole_user_history_ibfk_1 + FOREIGN KEY (user_id) + REFERENCES guacamole_user (user_id) ON DELETE SET NULL + +); + +CREATE INDEX guacamole_user_history_user_id + ON guacamole_user_history(user_id); + +CREATE INDEX guacamole_user_history_start_date + ON guacamole_user_history(start_date); + +CREATE INDEX guacamole_user_history_end_date + ON guacamole_user_history(end_date); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/schema/001-create-schema.sql b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/schema/001-create-schema.sql index 91ba3770b..19d48e464 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/schema/001-create-schema.sql +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/schema/001-create-schema.sql @@ -504,6 +504,37 @@ CREATE NONCLUSTERED INDEX [IX_guacamole_connection_history_end_date] ON [guacamole_connection_history] ([end_date]); GO +-- +-- User login/logout history +-- + +CREATE TABLE [guacamole_user_history] ( + + [history_id] [int] IDENTITY(1,1) NOT NULL, + [user_id] [int] DEFAULT NULL, + [username] [nvarchar](128) NOT NULL, + [remote_host] [nvarchar](256) DEFAULT NULL, + [start_date] [datetime] NOT NULL, + [end_date] [datetime] DEFAULT NULL, + + PRIMARY KEY (history_id), + + CONSTRAINT FK_guacamole_user_history_user_id + FOREIGN KEY (user_id) + REFERENCES guacamole_user (user_id) ON DELETE SET NULL + +); + +CREATE NONCLUSTERED INDEX [IX_guacamole_user_history_user_id] + ON [guacamole_user_history] ([user_id]); + +CREATE NONCLUSTERED INDEX [IX_guacamole_user_history_start_date] + ON [guacamole_user_history] ([start_date]); + +CREATE NONCLUSTERED INDEX [IX_guacamole_user_history_end_date] + ON [guacamole_user_history] ([end_date]); +GO + -- -- The user_password_history table stores password history -- for users, allowing for enforcing rules associated with From 6f6b4e5d960f01d934155336168b9395cf349100 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 12 Sep 2017 12:50:09 -0700 Subject: [PATCH 04/15] GUACAMOLE-394: Add mapper for user login records. --- .../JDBCAuthenticationProviderModule.java | 2 + .../auth/jdbc/user/UserRecordMapper.java | 113 ++++++++++++ .../auth/jdbc/user/UserRecordMapper.xml | 173 ++++++++++++++++++ .../auth/jdbc/user/UserRecordMapper.xml | 173 ++++++++++++++++++ .../auth/jdbc/user/UserRecordMapper.xml | 173 ++++++++++++++++++ 5 files changed, 634 insertions(+) create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.java create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java index c9274dc7a..0f72559e2 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCAuthenticationProviderModule.java @@ -76,6 +76,7 @@ import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterMapp import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileService; import org.apache.guacamole.auth.jdbc.tunnel.RestrictedGuacamoleTunnelService; import org.apache.guacamole.auth.jdbc.user.PasswordRecordMapper; +import org.apache.guacamole.auth.jdbc.user.UserRecordMapper; import org.mybatis.guice.MyBatisModule; import org.mybatis.guice.datasource.builtin.PooledDataSourceProvider; @@ -126,6 +127,7 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule { addMapperClass(SharingProfilePermissionMapper.class); addMapperClass(UserMapper.class); addMapperClass(UserPermissionMapper.class); + addMapperClass(UserRecordMapper.class); // Bind core implementations of guacamole-ext classes bind(ActiveConnectionDirectory.class); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.java new file mode 100644 index 000000000..68f0c94e4 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.jdbc.user; + +import java.util.Collection; +import java.util.List; +import org.apache.guacamole.auth.jdbc.base.ActivityRecordModel; +import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm; +import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate; +import org.apache.ibatis.annotations.Param; + +/** + * Mapper for user login activity records. + */ +public interface UserRecordMapper { + + /** + * Returns a collection of all user login records associated with the user + * having the given username. + * + * @param username + * The username of the user whose login records are to be retrieved. + * + * @return + * A collection of all user login records associated with the user + * having the given username. This collection will be empty if no such + * user exists. + */ + List select(@Param("username") String username); + + /** + * Inserts the given user login record. + * + * @param record + * The user login record to insert. + * + * @return + * The number of rows inserted. + */ + int insert(@Param("record") ActivityRecordModel record); + + /** + * Searches for up to limit user login 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 + * The maximum number of records that should be returned. + * + * @return + * The results of the search performed with the given parameters. + */ + List search(@Param("terms") Collection terms, + @Param("sortPredicates") List sortPredicates, + @Param("limit") int limit); + + /** + * Searches for up to limit user login 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/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml new file mode 100644 index 000000000..046745240 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + INSERT INTO guacamole_user_history ( + remote_host, + user_id, + username, + start_date, + end_date + ) + VALUES ( + #{record.remoteHost,jdbcType=VARCHAR}, + (SELECT user_id FROM guacamole_user + WHERE username = #{record.username,jdbcType=VARCHAR}), + #{record.username,jdbcType=VARCHAR}, + #{record.startDate,jdbcType=TIMESTAMP}, + #{record.endDate,jdbcType=TIMESTAMP} + ) + + + + + + + + + + diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml new file mode 100644 index 000000000..046745240 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + INSERT INTO guacamole_user_history ( + remote_host, + user_id, + username, + start_date, + end_date + ) + VALUES ( + #{record.remoteHost,jdbcType=VARCHAR}, + (SELECT user_id FROM guacamole_user + WHERE username = #{record.username,jdbcType=VARCHAR}), + #{record.username,jdbcType=VARCHAR}, + #{record.startDate,jdbcType=TIMESTAMP}, + #{record.endDate,jdbcType=TIMESTAMP} + ) + + + + + + + + + + diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml new file mode 100644 index 000000000..fafa863bf --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + INSERT INTO [guacamole_user_history] ( + remote_host, + user_id, + username, + start_date, + end_date + ) + VALUES ( + #{record.remoteHost,jdbcType=VARCHAR}, + (SELECT user_id FROM [guacamole_user] + WHERE username = #{record.username,jdbcType=VARCHAR}), + #{record.username,jdbcType=VARCHAR}, + #{record.startDate,jdbcType=TIMESTAMP}, + #{record.endDate,jdbcType=TIMESTAMP} + ) + + + + + + + + + + From a3dd919940a92fc8922b894f251c0667352f4c42 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 12 Sep 2017 12:58:02 -0700 Subject: [PATCH 05/15] GUACAMOLE-394: History must be LEFT JOINed when calculating last active date, or inactive connections will disappear entirely. --- .../guacamole/auth/jdbc/connection/ConnectionMapper.xml | 6 +++--- .../guacamole/auth/jdbc/connection/ConnectionMapper.xml | 6 +++--- .../guacamole/auth/jdbc/connection/ConnectionMapper.xml | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml index 14b739e36..7c2093592 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml @@ -103,7 +103,7 @@ failover_only, MAX(start_date) AS last_active FROM guacamole_connection - JOIN guacamole_connection_history ON guacamole_connection_history.connection_id = guacamole_connection.connection_id + LEFT JOIN guacamole_connection_history ON guacamole_connection_history.connection_id = guacamole_connection.connection_id WHERE guacamole_connection.connection_id IN @@ -140,7 +140,7 @@ MAX(start_date) AS last_active FROM guacamole_connection JOIN guacamole_connection_permission ON guacamole_connection_permission.connection_id = guacamole_connection.connection_id - JOIN guacamole_connection_history ON guacamole_connection_history.connection_id = guacamole_connection.connection_id + LEFT JOIN guacamole_connection_history ON guacamole_connection_history.connection_id = guacamole_connection.connection_id WHERE guacamole_connection.connection_id IN @@ -180,7 +180,7 @@ failover_only, MAX(start_date) AS last_active FROM guacamole_connection - JOIN guacamole_connection_history ON guacamole_connection_history.connection_id = guacamole_connection.connection_id + LEFT JOIN guacamole_connection_history ON guacamole_connection_history.connection_id = guacamole_connection.connection_id WHERE parent_id = #{parentIdentifier,jdbcType=VARCHAR} parent_id IS NULL diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml index 09bd51027..b9356b06b 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml @@ -103,7 +103,7 @@ failover_only, MAX(start_date) AS last_active FROM guacamole_connection - JOIN guacamole_connection_history ON guacamole_connection_history.connection_id = guacamole_connection.connection_id + LEFT JOIN guacamole_connection_history ON guacamole_connection_history.connection_id = guacamole_connection.connection_id WHERE guacamole_connection.connection_id IN @@ -140,7 +140,7 @@ MAX(start_date) AS last_active FROM guacamole_connection JOIN guacamole_connection_permission ON guacamole_connection_permission.connection_id = guacamole_connection.connection_id - JOIN guacamole_connection_history ON guacamole_connection_history.connection_id = guacamole_connection.connection_id + LEFT JOIN guacamole_connection_history ON guacamole_connection_history.connection_id = guacamole_connection.connection_id WHERE guacamole_connection.connection_id IN @@ -180,7 +180,7 @@ failover_only, MAX(start_date) AS last_active FROM guacamole_connection - JOIN guacamole_connection_history ON guacamole_connection_history.connection_id = guacamole_connection.connection_id + LEFT JOIN guacamole_connection_history ON guacamole_connection_history.connection_id = guacamole_connection.connection_id WHERE parent_id = #{parentIdentifier,jdbcType=INTEGER}::integer parent_id IS NULL diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml index d27b5282d..b31b45f80 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionMapper.xml @@ -103,7 +103,7 @@ failover_only, MAX(start_date) AS last_active FROM [guacamole_connection] - JOIN [guacamole_connection_history] ON [guacamole_connection_history].connection_id = [guacamole_connection].connection_id + LEFT JOIN [guacamole_connection_history] ON [guacamole_connection_history].connection_id = [guacamole_connection].connection_id WHERE [guacamole_connection].connection_id IN @@ -140,7 +140,7 @@ MAX(start_date) AS last_active FROM [guacamole_connection] JOIN [guacamole_connection_permission] ON [guacamole_connection_permission].connection_id = [guacamole_connection].connection_id - JOIN [guacamole_connection_history] ON [guacamole_connection_history].connection_id = [guacamole_connection].connection_id + LEFT JOIN [guacamole_connection_history] ON [guacamole_connection_history].connection_id = [guacamole_connection].connection_id WHERE [guacamole_connection].connection_id IN @@ -180,7 +180,7 @@ failover_only, MAX(start_date) AS last_active FROM [guacamole_connection] - JOIN [guacamole_connection_history] ON [guacamole_connection_history].connection_id = [guacamole_connection].connection_id + LEFT JOIN [guacamole_connection_history] ON [guacamole_connection_history].connection_id = [guacamole_connection].connection_id WHERE parent_id = #{parentIdentifier,jdbcType=INTEGER} parent_id IS NULL From 8694bc802a753a9acd6950c5c7d88843df886cdc Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 12 Sep 2017 13:07:15 -0700 Subject: [PATCH 06/15] GUACAMOLE-394: Determine last active date for users based on history table. --- .../guacamole/auth/jdbc/user/ModeledUser.java | 2 +- .../guacamole/auth/jdbc/user/UserModel.java | 32 +++++++++++++++++++ .../guacamole/auth/jdbc/user/UserMapper.xml | 32 ++++++++++++------- .../guacamole/auth/jdbc/user/UserMapper.xml | 32 ++++++++++++------- .../guacamole/auth/jdbc/user/UserMapper.xml | 32 ++++++++++++------- 5 files changed, 96 insertions(+), 34 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java index fc43e36b1..7d80facb9 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java @@ -796,7 +796,7 @@ public class ModeledUser extends ModeledDirectoryObject implements Us @Override public Date getLastActive() { - return null; + return getModel().getLastActive(); } @Override diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserModel.java index afaeb5521..a4f9c7adf 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserModel.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserModel.java @@ -114,6 +114,12 @@ public class UserModel extends ObjectModel { */ private String organizationalRole; + /** + * The date and time that this user was last active, or null if this user + * has never logged in. + */ + private Date lastActive; + /** * Creates a new, empty user. */ @@ -465,4 +471,30 @@ public class UserModel extends ObjectModel { this.organizationalRole = organizationalRole; } + /** + * Returns the date and time that this user was last active, or null if + * this user has never logged in. + * + * @return + * The date and time that this user was last active, or null if this + * user has never logged in. + */ + public Date getLastActive() { + return lastActive; + } + + /** + * Sets the date and time that this user was last active. This value is + * expected to be set automatically via queries, derived from user history + * records. It does NOT correspond to an actual column, and values set + * manually through invoking this function will not persist. + * + * @param lastActive + * The date and time that this user was last active, or null if this + * user has never logged in. + */ + public void setLastActive(Date lastActive) { + this.lastActive = lastActive; + } + } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml index 4ab11827d..c9e4f70db 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml @@ -41,6 +41,7 @@ + @@ -63,8 +64,8 @@ @@ -93,7 +97,7 @@ SELECT guacamole_user.user_id, - username, + guacamole_user.username, password_hash, password_salt, password_date, @@ -107,16 +111,19 @@ full_name, email_address, organization, - organizational_role + organizational_role, + MAX(start_date) AS last_active FROM guacamole_user JOIN guacamole_user_permission ON affected_user_id = guacamole_user.user_id - WHERE username IN + LEFT JOIN guacamole_user_history ON guacamole_user_history.user_id = guacamole_user.user_id + WHERE guacamole_user.username IN #{identifier,jdbcType=VARCHAR} AND guacamole_user_permission.user_id = #{user.objectID,jdbcType=INTEGER} AND permission = 'READ' + GROUP BY guacamole_user.user_id @@ -124,8 +131,8 @@ diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml index 569a8ac45..c106a8fab 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml @@ -41,6 +41,7 @@ + @@ -63,8 +64,8 @@ @@ -93,7 +97,7 @@ SELECT guacamole_user.user_id, - username, + guacamole_user.username, password_hash, password_salt, password_date, @@ -107,16 +111,19 @@ full_name, email_address, organization, - organizational_role + organizational_role, + MAX(start_date) AS last_active FROM guacamole_user JOIN guacamole_user_permission ON affected_user_id = guacamole_user.user_id - WHERE username IN + LEFT JOIN guacamole_user_history ON guacamole_user_history.user_id = guacamole_user.user_id + WHERE guacamole_user.username IN #{identifier,jdbcType=VARCHAR} AND guacamole_user_permission.user_id = #{user.objectID,jdbcType=INTEGER} AND permission = 'READ' + GROUP BY guacamole_user.user_id @@ -124,8 +131,8 @@ diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml index 6df6cf26d..ec6063296 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml @@ -41,6 +41,7 @@ + @@ -63,8 +64,8 @@ @@ -93,7 +97,7 @@ SELECT [guacamole_user].user_id, - username, + [guacamole_user].username, password_hash, password_salt, password_date, @@ -107,16 +111,19 @@ full_name, email_address, organization, - organizational_role + organizational_role, + MAX(start_date) AS last_active FROM [guacamole_user] JOIN [guacamole_user_permission] ON affected_user_id = [guacamole_user].user_id - WHERE username IN + LEFT JOIN [guacamole_user_history] ON [guacamole_user_history].user_id = [guacamole_user].user_id + WHERE [guacamole_user].username IN #{identifier,jdbcType=VARCHAR} AND [guacamole_user_permission].user_id = #{user.objectID,jdbcType=INTEGER} AND permission = 'READ' + GROUP BY [guacamole_user].user_id @@ -124,8 +131,8 @@ From adf016a005cd2c797f7d5e566b4e266770b3e174 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 12 Sep 2017 13:33:29 -0700 Subject: [PATCH 07/15] GUACAMOLE-394: Map last active date for users to timestamp. --- .../org/apache/guacamole/auth/jdbc/user/ModeledUser.java | 2 +- .../java/org/apache/guacamole/auth/jdbc/user/UserModel.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java index 7d80facb9..b039a4e28 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java @@ -795,7 +795,7 @@ public class ModeledUser extends ModeledDirectoryObject implements Us } @Override - public Date getLastActive() { + public Timestamp getLastActive() { return getModel().getLastActive(); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserModel.java index a4f9c7adf..a6cf997d3 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserModel.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserModel.java @@ -118,7 +118,7 @@ public class UserModel extends ObjectModel { * The date and time that this user was last active, or null if this user * has never logged in. */ - private Date lastActive; + private Timestamp lastActive; /** * Creates a new, empty user. @@ -479,7 +479,7 @@ public class UserModel extends ObjectModel { * The date and time that this user was last active, or null if this * user has never logged in. */ - public Date getLastActive() { + public Timestamp getLastActive() { return lastActive; } @@ -493,7 +493,7 @@ public class UserModel extends ObjectModel { * The date and time that this user was last active, or null if this * user has never logged in. */ - public void setLastActive(Date lastActive) { + public void setLastActive(Timestamp lastActive) { this.lastActive = lastActive; } From 2414c9a2457ae57cf3df30f1d40535d1895525fb Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 12 Sep 2017 13:56:58 -0700 Subject: [PATCH 08/15] GUACAMOLE-394: Separate core of ConnectionRecordSet into ModeledActivityRecordSet. --- .../jdbc/base/ModeledActivityRecordSet.java | 132 ++++++++++++++++++ .../jdbc/connection/ConnectionRecordSet.java | 67 ++------- 2 files changed, 141 insertions(+), 58 deletions(-) create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledActivityRecordSet.java diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledActivityRecordSet.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledActivityRecordSet.java new file mode 100644 index 000000000..d2590184b --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ModeledActivityRecordSet.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.jdbc.base; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.net.auth.ActivityRecord; +import org.apache.guacamole.net.auth.ActivityRecordSet; +import org.apache.guacamole.net.auth.ActivityRecordSet.SortableProperty; +import org.apache.guacamole.net.auth.AuthenticatedUser; + +/** + * A JDBC implementation of ActivityRecordSet. Calls to asCollection() will + * query history records using an implementation-specific mechanism. Which + * records are returned will be determined by the values passed in earlier. + * + * @param + * The type of ActivityRecord contained within this set. + */ +public abstract class ModeledActivityRecordSet + extends RestrictedObject implements ActivityRecordSet { + + /** + * The set of strings that each must occur somewhere within the returned + * records, whether within the associated username, an associated date, or + * other related data. If non-empty, any 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 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 records, + * describing the properties involved and the sort order for those + * properties. + */ + private final List sortPredicates = + new ArrayList(); + + /** + * Retrieves the history records matching the given criteria. Retrieves up + * to limit 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 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 + * A collection of all history records matching the given criteria. + * + * @throws GuacamoleException + * If permission to read the history records is denied. + */ + protected abstract Collection retrieveHistory( + AuthenticatedUser user, + Set requiredContents, + List sortPredicates, + int limit) throws GuacamoleException; + + @Override + public Collection asCollection() + throws GuacamoleException { + return retrieveHistory(getCurrentUser(), requiredContents, + sortPredicates, limit); + } + + @Override + public ModeledActivityRecordSet contains(String value) + throws GuacamoleException { + requiredContents.add(new ActivityRecordSearchTerm(value)); + return this; + } + + @Override + public ModeledActivityRecordSet limit(int limit) throws GuacamoleException { + this.limit = Math.min(this.limit, limit); + return this; + } + + @Override + public ModeledActivityRecordSet sort(SortableProperty property, boolean desc) + throws GuacamoleException { + + sortPredicates.add(new ActivityRecordSortPredicate( + property, + desc + )); + + return this; + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordSet.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordSet.java index df2a0a9ea..f4574f4e0 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordSet.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordSet.java @@ -20,17 +20,14 @@ package org.apache.guacamole.auth.jdbc.connection; import com.google.inject.Inject; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm; import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate; -import org.apache.guacamole.auth.jdbc.base.RestrictedObject; -import org.apache.guacamole.net.auth.ActivityRecordSet; -import org.apache.guacamole.net.auth.ActivityRecordSet.SortableProperty; +import org.apache.guacamole.auth.jdbc.base.ModeledActivityRecordSet; +import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.ConnectionRecord; /** @@ -38,8 +35,7 @@ import org.apache.guacamole.net.auth.ConnectionRecord; * asCollection() will query connection history records from the database. Which * records are returned will be determined by the values passed in earlier. */ -public class ConnectionRecordSet extends RestrictedObject - implements ActivityRecordSet { +public class ConnectionRecordSet extends ModeledActivityRecordSet { /** * Service for managing connection objects. @@ -47,60 +43,15 @@ public class ConnectionRecordSet extends RestrictedObject @Inject private ConnectionService connectionService; - /** - * 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() + protected Collection retrieveHistory( + AuthenticatedUser user, Set requiredContents, + List sortPredicates, int limit) throws GuacamoleException { + + // Retrieve history from database return connectionService.retrieveHistory(getCurrentUser(), - requiredContents, connectionRecordSortPredicates, limit); - } - - @Override - public ConnectionRecordSet contains(String value) - throws GuacamoleException { - requiredContents.add(new ActivityRecordSearchTerm(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 ActivityRecordSortPredicate( - property, - desc - )); - - return this; + requiredContents, sortPredicates, limit); } From 7e8accab62a2ea2be89cc24de5a152c7147c983e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 12 Sep 2017 14:21:32 -0700 Subject: [PATCH 09/15] GUACAMOLE-394: Implement full retrieval of user login history. --- .../guacamole/auth/jdbc/user/ModeledUser.java | 8 +- .../auth/jdbc/user/ModeledUserContext.java | 10 +- .../auth/jdbc/user/UserRecordSet.java | 59 ++++++++ .../guacamole/auth/jdbc/user/UserService.java | 133 +++++++++++++++++- 4 files changed, 207 insertions(+), 3 deletions(-) create mode 100644 extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserRecordSet.java diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java index b039a4e28..5ffc458e5 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUser.java @@ -144,6 +144,12 @@ public class ModeledUser extends ModeledDirectoryObject implements Us ACCOUNT_RESTRICTIONS )); + /** + * Service for managing users. + */ + @Inject + private UserService userService; + /** * Service for hashing passwords. */ @@ -801,7 +807,7 @@ public class ModeledUser extends ModeledDirectoryObject implements Us @Override public List getHistory() throws GuacamoleException { - return Collections.emptyList(); + return userService.retrieveHistory(getCurrentUser(), this); } } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java index 1b238abf6..fc2c9729f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java @@ -99,6 +99,12 @@ public class ModeledUserContext extends RestrictedObject */ @Inject private Provider connectionRecordSetProvider; + + /** + * Provider for creating user record sets. + */ + @Inject + private Provider userRecordSetProvider; @Override public void init(ModeledAuthenticatedUser currentUser) { @@ -167,7 +173,9 @@ public class ModeledUserContext extends RestrictedObject @Override public ActivityRecordSet getUserHistory() throws GuacamoleException { - return new SimpleActivityRecordSet(); + UserRecordSet userRecordSet = userRecordSetProvider.get(); + userRecordSet.init(getCurrentUser()); + return userRecordSet; } @Override diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserRecordSet.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserRecordSet.java new file mode 100644 index 000000000..c1b4897e7 --- /dev/null +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserRecordSet.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.auth.jdbc.user; + +import com.google.inject.Inject; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm; +import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate; +import org.apache.guacamole.auth.jdbc.base.ModeledActivityRecordSet; +import org.apache.guacamole.net.auth.ActivityRecord; +import org.apache.guacamole.net.auth.AuthenticatedUser; + +/** + * A JDBC implementation of ActivityRecordSet for retrieving user login history. + * Calls to asCollection() will query user login records from the database. + * Which records are returned will be determined by the values passed in + * earlier. + */ +public class UserRecordSet extends ModeledActivityRecordSet { + + /** + * Service for managing user objects. + */ + @Inject + private UserService userService; + + @Override + protected Collection retrieveHistory( + AuthenticatedUser user, Set requiredContents, + List sortPredicates, int limit) + throws GuacamoleException { + + // Retrieve history from database + return userService.retrieveHistory(getCurrentUser(), + requiredContents, sortPredicates, limit); + + } + +} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java index 3dc025fcd..090963fd2 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserService.java @@ -21,16 +21,24 @@ package org.apache.guacamole.auth.jdbc.user; import com.google.inject.Inject; import com.google.inject.Provider; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.List; import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectMapper; import org.apache.guacamole.auth.jdbc.base.ModeledDirectoryObjectService; import org.apache.guacamole.GuacamoleClientException; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleSecurityException; import org.apache.guacamole.GuacamoleUnsupportedException; +import org.apache.guacamole.auth.jdbc.base.ActivityRecordModel; +import org.apache.guacamole.auth.jdbc.base.ActivityRecordSearchTerm; +import org.apache.guacamole.auth.jdbc.base.ActivityRecordSortPredicate; +import org.apache.guacamole.auth.jdbc.base.ModeledActivityRecord; +import org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel; import org.apache.guacamole.auth.jdbc.permission.ObjectPermissionMapper; import org.apache.guacamole.auth.jdbc.permission.ObjectPermissionModel; import org.apache.guacamole.auth.jdbc.permission.UserPermissionMapper; @@ -38,8 +46,10 @@ import org.apache.guacamole.auth.jdbc.security.PasswordEncryptionService; import org.apache.guacamole.auth.jdbc.security.PasswordPolicyService; import org.apache.guacamole.form.Field; import org.apache.guacamole.form.PasswordField; +import org.apache.guacamole.net.auth.ActivityRecord; import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticationProvider; +import org.apache.guacamole.net.auth.ConnectionRecord; import org.apache.guacamole.net.auth.User; import org.apache.guacamole.net.auth.credentials.CredentialsInfo; import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException; @@ -116,7 +126,13 @@ public class UserService extends ModeledDirectoryObjectService getObjectInstances(List models) { + + // Create new list of records by manually converting each model + List objects = new ArrayList(models.size()); + for (ActivityRecordModel model : models) + objects.add(getObjectInstance(model)); + + return objects; + + } + + /** + * Retrieves the login history of the given user, including any active + * sessions. + * + * @param authenticatedUser + * The user retrieving the login history. + * + * @param user + * The user whose history is being retrieved. + * + * @return + * The login history of the given user, including any active sessions. + * + * @throws GuacamoleException + * If permission to read the login history is denied. + */ + public List retrieveHistory(ModeledAuthenticatedUser authenticatedUser, + ModeledUser user) throws GuacamoleException { + + String username = user.getIdentifier(); + + // Retrieve history only if READ permission is granted + if (hasObjectPermission(authenticatedUser, username, ObjectPermission.Type.READ)) + return getObjectInstances(userRecordMapper.select(username)); + + // The user does not have permission to read the history + throw new GuacamoleSecurityException("Permission denied."); + + } + + /** + * Retrieves user login history records matching the given criteria. + * Retrieves up to limit user 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 login 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 login history of the given user, including any active sessions. + * + * @throws GuacamoleException + * If permission to read the user login history is denied. + */ + public List retrieveHistory(ModeledAuthenticatedUser 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 = userRecordMapper.search(requiredContents, + sortPredicates, limit); + + // Otherwise only return explicitly readable history records + else + searchResults = userRecordMapper.searchReadable(user.getUser().getModel(), + requiredContents, sortPredicates, limit); + + return getObjectInstances(searchResults); + + } + + } From 446a9d0e12d99e93be614ecac49870c9484a6dce Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 12 Sep 2017 14:49:18 -0700 Subject: [PATCH 10/15] GUACAMOLE-394: Automatically insert user history records upon login. --- .../auth/jdbc/user/ModeledUserContext.java | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java index fc2c9729f..0bf01fb2e 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java @@ -26,9 +26,11 @@ import org.apache.guacamole.auth.jdbc.connection.ConnectionDirectory; import com.google.inject.Inject; import com.google.inject.Provider; import java.util.Collection; +import java.util.Date; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.auth.jdbc.base.RestrictedObject; import org.apache.guacamole.auth.jdbc.activeconnection.ActiveConnectionDirectory; +import org.apache.guacamole.auth.jdbc.base.ActivityRecordModel; import org.apache.guacamole.auth.jdbc.connection.ConnectionRecordSet; import org.apache.guacamole.auth.jdbc.connection.ModeledConnection; import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; @@ -44,7 +46,6 @@ import org.apache.guacamole.net.auth.ConnectionGroup; import org.apache.guacamole.net.auth.Directory; import org.apache.guacamole.net.auth.SharingProfile; import org.apache.guacamole.net.auth.User; -import org.apache.guacamole.net.auth.simple.SimpleActivityRecordSet; /** * UserContext implementation which is driven by an arbitrary, underlying @@ -105,7 +106,18 @@ public class ModeledUserContext extends RestrictedObject */ @Inject private Provider userRecordSetProvider; - + + /** + * Mapper for user login records. + */ + @Inject + private UserRecordMapper userRecordMapper; + + /** + * The activity record associated with this user's Guacamole session. + */ + private ActivityRecordModel userRecord; + @Override public void init(ModeledAuthenticatedUser currentUser) { @@ -118,6 +130,16 @@ public class ModeledUserContext extends RestrictedObject sharingProfileDirectory.init(currentUser); activeConnectionDirectory.init(currentUser); + // Create login record for user + userRecord = new ActivityRecordModel(); + userRecord.setUserID(currentUser.getUser().getModel().getObjectID()); + userRecord.setUsername(currentUser.getIdentifier()); + userRecord.setStartDate(new Date()); + userRecord.setRemoteHost(currentUser.getCredentials().getRemoteHostname()); + + // Insert record representing login + userRecordMapper.insert(userRecord); + } @Override From 3ccb4d4ac1d04ecc172cd2f8f2f16ecdf2895efd Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 12 Sep 2017 15:16:53 -0700 Subject: [PATCH 11/15] GUACAMOLE-394: No need to explicitly set ID - it's automatically pulled from the username. --- .../org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java index 0bf01fb2e..0b3b1215f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java @@ -132,7 +132,6 @@ public class ModeledUserContext extends RestrictedObject // Create login record for user userRecord = new ActivityRecordModel(); - userRecord.setUserID(currentUser.getUser().getModel().getObjectID()); userRecord.setUsername(currentUser.getIdentifier()); userRecord.setStartDate(new Date()); userRecord.setRemoteHost(currentUser.getCredentials().getRemoteHostname()); From 28e7d215aceb2e5c572bdfc00207ef37d33ca8e8 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 12 Sep 2017 18:03:22 -0700 Subject: [PATCH 12/15] GUACAMOLE-394: Automatically update the end time of user history records upon logout. --- .../auth/jdbc/base/ActivityRecordModel.java | 26 +++++++++++++++++++ .../auth/jdbc/user/ModeledUserContext.java | 6 ++++- .../auth/jdbc/user/UserRecordMapper.java | 11 ++++++++ .../auth/jdbc/user/UserRecordMapper.xml | 16 +++++++++++- .../auth/jdbc/user/UserRecordMapper.xml | 16 +++++++++++- .../auth/jdbc/user/UserRecordMapper.xml | 16 +++++++++++- 6 files changed, 87 insertions(+), 4 deletions(-) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordModel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordModel.java index 86f2204a2..fbf62097f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordModel.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/ActivityRecordModel.java @@ -27,6 +27,11 @@ import java.util.Date; */ public class ActivityRecordModel { + /** + * The ID of this object in the database, if any. + */ + private Integer recordID; + /** * The database ID of the user associated with this activity record. */ @@ -53,6 +58,27 @@ public class ActivityRecordModel { */ private Date endDate; + /** + * Returns the ID of this record in the database, if it exists. + * + * @return + * The ID of this record in the database, or null if this record was + * not retrieved from the database. + */ + public Integer getRecordID() { + return recordID; + } + + /** + * Sets the database ID of this record to the given value. + * + * @param recordID + * The ID to assign to this object. + */ + public void setRecordID(Integer recordID) { + this.recordID = recordID; + } + /** * Returns the database ID of the user associated with this activity * record. diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java index 0b3b1215f..5bfcda675 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/ModeledUserContext.java @@ -231,7 +231,11 @@ public class ModeledUserContext extends RestrictedObject @Override public void invalidate() { - // Nothing to invalidate + + // Record logout time + userRecord.setEndDate(new Date()); + userRecordMapper.update(userRecord); + } } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.java index 68f0c94e4..b2177bf1f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.java @@ -56,6 +56,17 @@ public interface UserRecordMapper { */ int insert(@Param("record") ActivityRecordModel record); + /** + * Updates the given user login record. + * + * @param record + * The user login record to update. + * + * @return + * The number of rows updated. + */ + int update(@Param("record") ActivityRecordModel record); + /** * Searches for up to limit user login records that contain * the given terms, sorted by the given predicates, regardless of whether diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml index 046745240..bbae03b07 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserRecordMapper.xml @@ -25,6 +25,7 @@ + @@ -52,7 +53,8 @@ - + INSERT INTO guacamole_user_history ( remote_host, @@ -72,6 +74,18 @@ + + + UPDATE guacamole_user_history + SET remote_host = #{record.remoteHost,jdbcType=VARCHAR}, + user_id = (SELECT user_id FROM guacamole_user + WHERE username = #{record.username,jdbcType=VARCHAR}), + username = #{record.username,jdbcType=VARCHAR}, + start_date = #{record.startDate,jdbcType=TIMESTAMP}, + end_date = #{record.endDate,jdbcType=TIMESTAMP} + WHERE history_id = #{record.recordID,jdbcType=INTEGER} + + - + INSERT INTO guacamole_user_history ( remote_host, @@ -72,6 +74,18 @@ + + + UPDATE guacamole_user_history + SET remote_host = #{record.remoteHost,jdbcType=VARCHAR}, + user_id = (SELECT user_id FROM guacamole_user + WHERE username = #{record.username,jdbcType=VARCHAR}), + username = #{record.username,jdbcType=VARCHAR}, + start_date = #{record.startDate,jdbcType=TIMESTAMP}, + end_date = #{record.endDate,jdbcType=TIMESTAMP} + WHERE history_id = #{record.recordID,jdbcType=INTEGER}::integer + + - + INSERT INTO [guacamole_user_history] ( remote_host, @@ -72,6 +74,18 @@ + + + UPDATE [guacamole_user_history] + SET remote_host = #{record.remoteHost,jdbcType=VARCHAR}, + user_id = (SELECT user_id FROM [guacamole_user] + WHERE username = #{record.username,jdbcType=VARCHAR}), + username = #{record.username,jdbcType=VARCHAR}, + start_date = #{record.startDate,jdbcType=TIMESTAMP}, + end_date = #{record.endDate,jdbcType=TIMESTAMP} + WHERE history_id = #{record.recordID,jdbcType=INTEGER} + + diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml index ec6063296..24db0132b 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/user/UserMapper.xml @@ -80,15 +80,17 @@ email_address, organization, organizational_role, - MAX(start_date) AS last_active + ( + SELECT MAX(start_date) + FROM [guacamole_user_history] + WHERE [guacamole_user_history].user_id = [guacamole_user].user_id + ) AS last_active FROM [guacamole_user] - LEFT JOIN [guacamole_user_history] ON [guacamole_user_history].user_id = [guacamole_user].user_id WHERE [guacamole_user].username IN #{identifier,jdbcType=VARCHAR} - - GROUP BY [guacamole_user].user_id + ; @@ -112,10 +114,13 @@ email_address, organization, organizational_role, - MAX(start_date) AS last_active + ( + SELECT MAX(start_date) + FROM [guacamole_user_history] + WHERE [guacamole_user_history].user_id = [guacamole_user].user_id + ) AS last_active FROM [guacamole_user] JOIN [guacamole_user_permission] ON affected_user_id = [guacamole_user].user_id - LEFT JOIN [guacamole_user_history] ON [guacamole_user_history].user_id = [guacamole_user].user_id WHERE [guacamole_user].username IN @@ -123,7 +128,6 @@ AND [guacamole_user_permission].user_id = #{user.objectID,jdbcType=INTEGER} AND permission = 'READ' - GROUP BY [guacamole_user].user_id @@ -147,12 +151,15 @@ email_address, organization, organizational_role, - MAX(start_date) AS last_active + ( + SELECT MAX(start_date) + FROM [guacamole_user_history] + WHERE [guacamole_user_history].user_id = [guacamole_user].user_id + ) AS last_active FROM [guacamole_user] LEFT JOIN [guacamole_user_history] ON [guacamole_user_history].user_id = [guacamole_user].user_id WHERE [guacamole_user].username = #{username,jdbcType=VARCHAR} - GROUP BY [guacamole_user].user_id