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 index d2590184b..60446aab5 100644 --- 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 @@ -48,7 +48,7 @@ public abstract class ModeledActivityRecordSet requiredContents = - new HashSet(); + new HashSet<>(); /** * The maximum number of history records that should be returned by a call @@ -62,7 +62,7 @@ public abstract class ModeledActivityRecordSet sortPredicates = - new ArrayList(); + new ArrayList<>(); /** * Retrieves the history records matching the given criteria. Retrieves up 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 7380b213b..720ded317 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 @@ -62,8 +62,12 @@ public interface ConnectionRecordMapper { * 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. + * read rights, use {@link searchReadable()} instead. * + * @param identifier + * The optional connection identifier to which records should be limited, + * or null if all records should be retrieved. + * * @param terms * The search terms that must match the returned records. * @@ -77,7 +81,8 @@ public interface ConnectionRecordMapper { * @return * The results of the search performed with the given parameters. */ - List search(@Param("terms") Collection terms, + List search(@Param("identifier") String identifier, + @Param("terms") Collection terms, @Param("sortPredicates") List sortPredicates, @Param("limit") int limit); @@ -86,8 +91,13 @@ public interface ConnectionRecordMapper { * 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. + * definition, does not need explicit read rights), use {@link search()} + * instead. * + * @param identifier + * The optional connection identifier for which records should be + * retrieved, or null if all readable records should be retrieved. + * * @param user * The user whose permissions should determine whether a record is * returned. @@ -111,7 +121,8 @@ public interface ConnectionRecordMapper { * @return * The results of the search performed with the given parameters. */ - List searchReadable(@Param("user") UserModel user, + List searchReadable(@Param("identifier") String identifier, + @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-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 f4574f4e0..2f559e930 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 @@ -27,6 +27,7 @@ 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.auth.jdbc.user.ModeledAuthenticatedUser; import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.ConnectionRecord; @@ -43,6 +44,30 @@ public class ConnectionRecordSet extends ModeledActivityRecordSet retrieveHistory( AuthenticatedUser user, Set requiredContents, @@ -50,7 +75,7 @@ public class ConnectionRecordSet extends ModeledActivityRecordSet parameters = connection.getConfiguration().getParameters(); // Convert parameters to model objects - Collection parameterModels = new ArrayList(parameters.size()); + Collection parameterModels = new ArrayList<>(parameters.size()); for (Map.Entry parameterEntry : parameters.entrySet()) { // Get parameter name and value @@ -329,7 +329,7 @@ public class ConnectionService extends ModeledChildDirectoryObjectService retrieveParameters(ModeledAuthenticatedUser user, String identifier) { - Map parameterMap = new HashMap(); + Map parameterMap = new HashMap<>(); // Determine whether we have permission to read parameters boolean canRetrieveParameters; @@ -382,7 +382,7 @@ public class ConnectionService extends ModeledChildDirectoryObjectService getObjectInstances(List models) { // Create new list of records by manually converting each model - List objects = new ArrayList(models.size()); + List objects = new ArrayList<>(models.size()); for (ConnectionRecordModel model : models) objects.add(getObjectInstance(model)); @@ -411,28 +411,16 @@ public class ConnectionService extends ModeledChildDirectoryObjectService models = connectionRecordMapper.select(identifier); - - // Get currently-active connections - List records = new ArrayList(tunnelService.getActiveConnections(connection)); - Collections.reverse(records); - - // Add past connections from model objects - for (ConnectionRecordModel model : models) - records.add(getObjectInstance(model)); - - // Return converted history list - return records; - - } - - // The user does not have permission to read the history - throw new GuacamoleSecurityException("Permission denied."); + + // Get current active connections. + List records = new ArrayList<>(tunnelService.getActiveConnections(connection)); + Collections.reverse(records); + + // Add in the history records. + records.addAll(retrieveHistory(identifier, user, Collections.emptyList(), + Collections.emptyList(), Integer.MAX_VALUE)); + + return records; } @@ -442,6 +430,60 @@ public class ConnectionService extends ModeledChildDirectoryObjectService retrieveHistory(String identifier, + ModeledAuthenticatedUser user, + Collection requiredContents, + List sortPredicates, int limit) + throws GuacamoleException { + + List searchResults; + + // Bypass permission checks if the user is privileged + if (user.isPrivileged()) + searchResults = connectionRecordMapper.search(identifier, requiredContents, + sortPredicates, limit); + + // Otherwise only return explicitly readable history records + else + searchResults = connectionRecordMapper.searchReadable(identifier, + user.getUser().getModel(), requiredContents, sortPredicates, + limit, user.getEffectiveUserGroups()); + + return getObjectInstances(searchResults); + + } + + /** + * Retrieves the connection history records matching the given criteria. + * Retrieves up to limit connection history records matching + * the given terms and sorted by the given predicates. Only history records + * associated with data that the given user can read are returned. + * * @param user * The user retrieving the connection history. * @@ -467,22 +509,9 @@ public class ConnectionService extends ModeledChildDirectoryObjectService requiredContents, List sortPredicates, int limit) throws GuacamoleException { - - List searchResults; - - // Bypass permission checks if the user is privileged - if (user.isPrivileged()) - searchResults = connectionRecordMapper.search(requiredContents, - sortPredicates, limit); - - // Otherwise only return explicitly readable history records - else - searchResults = connectionRecordMapper.searchReadable( - user.getUser().getModel(), requiredContents, sortPredicates, - limit, user.getEffectiveUserGroups()); - - return getObjectInstances(searchResults); - + + return retrieveHistory(null, user, requiredContents, sortPredicates, limit); + } /** 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 b49262668..20861bae9 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 @@ -40,6 +40,7 @@ import org.apache.guacamole.form.Form; import org.apache.guacamole.form.NumericField; import org.apache.guacamole.form.TextField; import org.apache.guacamole.net.GuacamoleTunnel; +import org.apache.guacamole.net.auth.ActivityRecordSet; import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.ConnectionRecord; import org.apache.guacamole.net.auth.GuacamoleProxyConfiguration; @@ -196,6 +197,12 @@ public class ModeledConnection extends ModeledChildDirectoryObject configProvider; + /** + * Provider for creating connection record sets. + */ + @Inject + private Provider connectionRecordSetProvider; + /** * The manually-set GuacamoleConfiguration, if any. */ @@ -252,10 +259,13 @@ public class ModeledConnection extends ModeledChildDirectoryObject getHistory() throws GuacamoleException { - return connectionService.retrieveHistory(getCurrentUser(), this); + public ActivityRecordSet getConnectionHistory() + throws GuacamoleException { + ConnectionRecordSet connectionRecordSet = connectionRecordSetProvider.get(); + connectionRecordSet.init(getCurrentUser(), this.getIdentifier()); + return connectionRecordSet; } @Override diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connection/SharedConnection.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connection/SharedConnection.java index cf0083167..48a0cbdc6 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connection/SharedConnection.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connection/SharedConnection.java @@ -22,7 +22,6 @@ package org.apache.guacamole.auth.jdbc.sharing.connection; import com.google.inject.Inject; import java.util.Collections; import java.util.Date; -import java.util.List; import java.util.Map; import java.util.Set; import org.apache.guacamole.GuacamoleException; @@ -31,7 +30,6 @@ import org.apache.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser; import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.auth.Connection; -import org.apache.guacamole.net.auth.ConnectionRecord; import org.apache.guacamole.protocol.GuacamoleClientInformation; import org.apache.guacamole.protocol.GuacamoleConfiguration; @@ -152,12 +150,6 @@ public class SharedConnection implements Connection { return null; } - @Override - public List getHistory() - throws GuacamoleException { - return Collections.emptyList(); - } - @Override public Set getSharingProfileIdentifiers() throws GuacamoleException { diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUser.java index f0a48b87d..80d22198c 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/user/SharedUser.java @@ -21,11 +21,9 @@ package org.apache.guacamole.auth.jdbc.sharing.user; import java.util.Collections; import java.util.Date; -import java.util.List; import java.util.Map; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.auth.jdbc.sharing.permission.SharedObjectPermissionSet; -import org.apache.guacamole.net.auth.ActivityRecord; import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.ConnectionGroup; @@ -99,14 +97,6 @@ public class SharedUser implements User { } - @Override - public List getHistory() throws GuacamoleException { - - // History is not recorded for shared users - return Collections.emptyList(); - - } - @Override public String getPassword() { return null; 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 54f052b75..fe04cec19 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 @@ -47,6 +47,7 @@ import org.apache.guacamole.form.TextField; import org.apache.guacamole.form.TimeField; import org.apache.guacamole.form.TimeZoneField; import org.apache.guacamole.net.auth.ActivityRecord; +import org.apache.guacamole.net.auth.ActivityRecordSet; import org.apache.guacamole.net.auth.Permissions; import org.apache.guacamole.net.auth.RelatedObjectSet; import org.apache.guacamole.net.auth.User; @@ -182,6 +183,12 @@ public class ModeledUser extends ModeledPermissions implements User { */ @Inject private Provider parentUserGroupSetProvider; + + /** + * Provider for creating user record sets. + */ + @Inject + private Provider userRecordSetProvider; /** * Whether attributes which control access restrictions should be exposed @@ -748,8 +755,11 @@ public class ModeledUser extends ModeledPermissions implements User { } @Override - public List getHistory() throws GuacamoleException { - return userService.retrieveHistory(getCurrentUser(), this); + public ActivityRecordSet getUserHistory() + throws GuacamoleException { + UserRecordSet userRecordSet = userRecordSetProvider.get(); + userRecordSet.init(getCurrentUser(), this.getIdentifier()); + return userRecordSet; } @Override 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 92501ab0b..fe15f41e4 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 @@ -73,8 +73,12 @@ public interface UserRecordMapper { * 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. + * read rights, use {@link searchReadable()} instead. * + * @param username + * The optional username to which records should be limited, or null + * if all records should be retrieved. + * * @param terms * The search terms that must match the returned records. * @@ -88,7 +92,8 @@ public interface UserRecordMapper { * @return * The results of the search performed with the given parameters. */ - List search(@Param("terms") Collection terms, + List search(@Param("username") String username, + @Param("terms") Collection terms, @Param("sortPredicates") List sortPredicates, @Param("limit") int limit); @@ -97,11 +102,16 @@ public interface UserRecordMapper { * 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. + * definition, does not need explicit read rights), use {@link search()} + * instead. * + * @param username + * The optional username to which records should be limited, or null + * if all readable records should be retrieved. + * * @param user - * The user whose permissions should determine whether a record is - * returned. + * The user whose permissions should determine whether a record is + * returned. * * @param terms * The search terms that must match the returned records. @@ -122,7 +132,8 @@ public interface UserRecordMapper { * @return * The results of the search performed with the given parameters. */ - List searchReadable(@Param("user") UserModel user, + List searchReadable(@Param("username") String username, + @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-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 index c1b4897e7..c5c9f6a6c 100644 --- 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 @@ -44,6 +44,31 @@ public class UserRecordSet extends ModeledActivityRecordSet { @Inject private UserService userService; + /** + * The identifier that indicates which user object these records should be + * limited to, if any. If null is specified (the default) then all records + * that are readable by the current user will be retrieved. + */ + private String identifier = null; + + /** + * Initialize this UserRecordSet with currentUser requesting the login + * records, and, optionally, the identifier of the user to which records + * should be limited. + * + * @param currentUser + * The user requesting login history. + * + * @param identifier + * The identifier of the user whose login history should be contained + * in this record set, or null if the record set should contain all + * records readable by the currentUser. + */ + protected void init(ModeledAuthenticatedUser currentUser, String identifier) { + super.init(currentUser); + this.identifier = identifier; + } + @Override protected Collection retrieveHistory( AuthenticatedUser user, Set requiredContents, @@ -51,7 +76,7 @@ public class UserRecordSet extends ModeledActivityRecordSet { throws GuacamoleException { // Retrieve history from database - return userService.retrieveHistory(getCurrentUser(), + return userService.retrieveHistory(identifier, 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 d22e909a1..6f6d05625 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 @@ -32,7 +32,6 @@ 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; @@ -593,13 +592,9 @@ public class UserService extends ModeledDirectoryObjectService retrieveHistory(String username, + ModeledAuthenticatedUser user, + Collection requiredContents, + List sortPredicates, int limit) + throws GuacamoleException { + + List searchResults; + + // Bypass permission checks if the user is privileged + if (user.isPrivileged()) + searchResults = userRecordMapper.search(username, requiredContents, + sortPredicates, limit); + + // Otherwise only return explicitly readable history records + else + searchResults = userRecordMapper.searchReadable(username, + user.getUser().getModel(), + requiredContents, sortPredicates, limit, user.getEffectiveUserGroups()); + + return getObjectInstances(searchResults); + + } + + /** + * 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. * @@ -633,21 +681,9 @@ public class UserService extends ModeledDirectoryObjectService requiredContents, List sortPredicates, int limit) throws GuacamoleException { - - List searchResults; - - // Bypass permission checks if the user is privileged - if (user.isPrivileged()) - searchResults = userRecordMapper.search(requiredContents, - sortPredicates, limit); - - // Otherwise only return explicitly readable history records - else - searchResults = userRecordMapper.searchReadable(user.getUser().getModel(), - requiredContents, sortPredicates, limit, user.getEffectiveUserGroups()); - - return getObjectInstances(searchResults); - + + return retrieveHistory(null, user, requiredContents, sortPredicates, limit); + } } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml index d74d4c4cb..daeec396f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -108,28 +108,35 @@ LEFT JOIN guacamole_user ON guacamole_connection_history.user_id = guacamole_user.user_id - - ( + + + + guacamole_connection_history.connection_id = #{identifier,jdbcType=VARCHAR} + + + + ( + + guacamole_connection_history.user_id IN ( + SELECT user_id + FROM guacamole_user + WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN username) > 0 + ) + + OR guacamole_connection_history.connection_id IN ( + SELECT connection_id + FROM guacamole_connection + WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN connection_name) > 0 + ) + + + OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} + - guacamole_connection_history.user_id IN ( - SELECT user_id - FROM guacamole_user - WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN username) > 0 ) - - OR guacamole_connection_history.connection_id IN ( - SELECT connection_id - FROM guacamole_connection - WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN connection_name) > 0 - ) - - - OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} - - - ) - + + + @@ -186,31 +193,38 @@ AND guacamole_user_permission.permission = 'READ' - - ( + + + + guacamole_connection_history.connection_id = #{identifier,jdbcType=VARCHAR} + + + + ( + + guacamole_connection_history.user_id IN ( + SELECT user_id + FROM guacamole_user + JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id + WHERE + POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + AND guacamole_entity.type = 'USER' + ) + + OR guacamole_connection_history.connection_id IN ( + SELECT connection_id + FROM guacamole_connection + WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN connection_name) > 0 + ) + + + OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} + - guacamole_connection_history.user_id IN ( - SELECT user_id - FROM guacamole_user - JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id - WHERE - POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 - AND guacamole_entity.type = 'USER' ) - - OR guacamole_connection_history.connection_id IN ( - SELECT connection_id - FROM guacamole_connection - WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN connection_name) > 0 - ) - - - OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} - - - ) - + + + 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 d9c02ef54..46edb96cd 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 @@ -105,25 +105,32 @@ FROM guacamole_user_history - - ( + + + + guacamole_user_history.username = #{username,jdbcType=VARCHAR} + + + + ( + + guacamole_user_history.user_id IN ( + SELECT user_id + FROM guacamole_user + JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id + WHERE + POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + AND guacamole_entity.type = 'USER'), + ) + + + OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} + - guacamole_user_history.user_id IN ( - SELECT user_id - FROM guacamole_user - JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id - WHERE - POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 - AND guacamole_entity.type = 'USER'), ) - - - OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} - - - ) - + + + @@ -164,25 +171,32 @@ AND guacamole_user_permission.permission = 'READ' - - ( + + + + guacamole_entity.name = #{username,jdbcType=VARCHAR} + + + + ( + + guacamole_user_history.user_id IN ( + SELECT user_id + FROM guacamole_user + JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id + WHERE + POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + AND guacamole_entity.type = 'USER' + ) + + + OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} + - guacamole_user_history.user_id IN ( - SELECT user_id - FROM guacamole_user - JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id - WHERE - POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 - AND guacamole_entity.type = 'USER' ) - - - OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} - - - ) - + + + diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml index e8e88764b..2d2ed5bc9 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -106,28 +106,35 @@ FROM guacamole_connection_history - - ( + + + + guacamole_connection_history.connection_id = #{identifier,jdbcType=INTEGER}::integer + + + + ( + + guacamole_connection_history.user_id IN ( + SELECT user_id + FROM guacamole_user + WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN username) > 0 + ) + + OR guacamole_connection_history.connection_id IN ( + SELECT connection_id + FROM guacamole_connection + WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN connection_name) > 0 + ) + + + OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} + - guacamole_connection_history.user_id IN ( - SELECT user_id - FROM guacamole_user - WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN username) > 0 ) - - OR guacamole_connection_history.connection_id IN ( - SELECT connection_id - FROM guacamole_connection - WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN connection_name) > 0 - ) - - - OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} - - - ) - + + + @@ -184,31 +191,38 @@ AND guacamole_user_permission.permission = 'READ' - - ( + + + + guacamole_connection_history.connection_id = #{identifier,jdbcType=INTEGER}::integer + + + + ( + + guacamole_connection_history.user_id IN ( + SELECT user_id + FROM guacamole_user + JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id + WHERE + POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + AND guacamole_entity.type = 'USER'::guacamole_entity_type + ) + + OR guacamole_connection_history.connection_id IN ( + SELECT connection_id + FROM guacamole_connection + WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN connection_name) > 0 + ) + + + OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} + - guacamole_connection_history.user_id IN ( - SELECT user_id - FROM guacamole_user - JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id - WHERE - POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 - AND guacamole_entity.type = 'USER'::guacamole_entity_type ) + - OR guacamole_connection_history.connection_id IN ( - SELECT connection_id - FROM guacamole_connection - WHERE POSITION(#{term.term,jdbcType=VARCHAR} IN connection_name) > 0 - ) - - - OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} - - - ) - + diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/resources/org/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 index 6311a2546..b943bb6ff 100644 --- 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 @@ -105,25 +105,32 @@ FROM guacamole_user_history - - ( + + + + guacamole_user_history.username = #{username,jdbcType=VARCHAR} + + + + ( + + guacamole_user_history.user_id IN ( + SELECT user_id + FROM guacamole_user + JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id + WHERE + POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + AND guacamole_entity.type = 'USER'::guacamole_entity_type), + ) + + + OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} + - guacamole_user_history.user_id IN ( - SELECT user_id - FROM guacamole_user - JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id - WHERE - POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 - AND guacamole_entity.type = 'USER'::guacamole_entity_type), ) - - - OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} - - - ) - + + + @@ -164,25 +171,31 @@ AND guacamole_user_permission.permission = 'READ' - - ( + + + guacamole_entity.name = #{username,jdbcType=VARCHAR} + + + + ( + + guacamole_user_history.user_id IN ( + SELECT user_id + FROM guacamole_user + JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id + WHERE + POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 + AND guacamole_entity.type = 'USER'::guacamole_entity_type + ) + + + OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} + - guacamole_user_history.user_id IN ( - SELECT user_id - FROM guacamole_user - JOIN guacamole_entity ON guacamole_user.entity_id = guacamole_entity.entity_id - WHERE - POSITION(#{term.term,jdbcType=VARCHAR} IN guacamole_entity.name) > 0 - AND guacamole_entity.type = 'USER'::guacamole_entity_type ) - - - OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} - - - ) - + + + diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml index 5d97b0b88..e24f2e863 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/resources/org/apache/guacamole/auth/jdbc/connection/ConnectionRecordMapper.xml @@ -106,28 +106,35 @@ FROM [guacamole_connection_history] - - ( + + + + [guacamole_connection_history].connection_id = #{identifier,jdbcType=INTEGER} + + + + ( + + [guacamole_connection_history].user_id IN ( + SELECT user_id + FROM [guacamole_user] + WHERE CHARINDEX(#{term.term,jdbcType=VARCHAR}, username) > 0 + ) + + OR [guacamole_connection_history].connection_id IN ( + SELECT connection_id + FROM [guacamole_connection] + WHERE CHARINDEX(#{term.term,jdbcType=VARCHAR}, connection_name) > 0 + ) + + + OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} + - [guacamole_connection_history].user_id IN ( - SELECT user_id - FROM [guacamole_user] - WHERE CHARINDEX(#{term.term,jdbcType=VARCHAR}, username) > 0 ) - - OR [guacamole_connection_history].connection_id IN ( - SELECT connection_id - FROM [guacamole_connection] - WHERE CHARINDEX(#{term.term,jdbcType=VARCHAR}, connection_name) > 0 - ) - - - OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} - - - ) - + + + @@ -182,31 +189,38 @@ AND [guacamole_user_permission].permission = 'READ' - - ( + + + + [guacamole_connection_history].connection_id = #{identifier,jdbcType=INTEGER} + + + + ( + + [guacamole_connection_history].user_id IN ( + SELECT user_id + FROM [guacamole_user] + JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id + WHERE + CHARINDEX(#{term.term,jdbcType=VARCHAR}, [guacamole_entity].name) > 0 + AND [guacamole_entity].type = 'USER' + ) + + OR [guacamole_connection_history].connection_id IN ( + SELECT connection_id + FROM [guacamole_connection] + WHERE CHARINDEX(#{term.term,jdbcType=VARCHAR}, connection_name) > 0 + ) + + + OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} + - [guacamole_connection_history].user_id IN ( - SELECT user_id - FROM [guacamole_user] - JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id - WHERE - CHARINDEX(#{term.term,jdbcType=VARCHAR}, [guacamole_entity].name) > 0 - AND [guacamole_entity].type = 'USER' ) - - OR [guacamole_connection_history].connection_id IN ( - SELECT connection_id - FROM [guacamole_connection] - WHERE CHARINDEX(#{term.term,jdbcType=VARCHAR}, connection_name) > 0 - ) - - - OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.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 index 7cc5efabb..179ef39ae 100644 --- 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 @@ -105,25 +105,32 @@ FROM [guacamole_user_history] - - ( + + + + [guacamole_user_history].username = #{username,jdbcType=VARCHAR} + + + + ( + + [guacamole_user_history].user_id IN ( + SELECT user_id + FROM [guacamole_user] + JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id + WHERE + CHARINDEX(#{term.term,jdbcType=VARCHAR}, [guacamole_entity].name) > 0 + AND [guacamole_entity].type = 'USER'), + ) + + + OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} + - [guacamole_user_history].user_id IN ( - SELECT user_id - FROM [guacamole_user] - JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id - WHERE - CHARINDEX(#{term.term,jdbcType=VARCHAR}, [guacamole_entity].name) > 0 - AND [guacamole_entity].type = 'USER'), ) + - - OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} - - - ) - + @@ -162,25 +169,32 @@ AND [guacamole_user_permission].permission = 'READ' - - ( + + + + [guacamole_entity].name = #{username,jdbcType=VARCHAR} + + + + ( + + [guacamole_user_history].user_id IN ( + SELECT user_id + FROM [guacamole_user] + JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id + WHERE + CHARINDEX(#{term.term,jdbcType=VARCHAR}, [guacamole_entity].name) > 0 + AND [guacamole_entity].type = 'USER' + ) + + + OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} + - [guacamole_user_history].user_id IN ( - SELECT user_id - FROM [guacamole_user] - JOIN [guacamole_entity] ON [guacamole_user].entity_id = [guacamole_entity].entity_id - WHERE - CHARINDEX(#{term.term,jdbcType=VARCHAR}, [guacamole_entity].name) > 0 - AND [guacamole_entity].type = 'USER' ) - - - OR start_date BETWEEN #{term.startDate,jdbcType=TIMESTAMP} AND #{term.endDate,jdbcType=TIMESTAMP} - - - ) - + + + diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUser.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUser.java index b502f7a1b..f4e85ae6f 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUser.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/AbstractUser.java @@ -21,7 +21,6 @@ package org.apache.guacamole.net.auth; import java.util.Collections; import java.util.Date; -import java.util.List; import java.util.Map; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; @@ -85,18 +84,7 @@ public abstract class AbstractUser extends AbstractIdentifiable public Date getLastActive() { return null; } - - /** - * {@inheritDoc} - * - *

This implementation simply an immutable, empty list. Implementations - * that wish to expose user login history should override this function. - */ - @Override - public List getHistory() throws GuacamoleException { - return Collections.emptyList(); - } - + /** * {@inheritDoc} * diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Connection.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Connection.java index 5b1d13d54..3d8e64d64 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Connection.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/Connection.java @@ -19,10 +19,13 @@ package org.apache.guacamole.net.auth; +import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Set; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleUnsupportedException; import org.apache.guacamole.protocol.GuacamoleConfiguration; /** @@ -98,17 +101,56 @@ public interface Connection extends Identifiable, Connectable, Attributes { * of this Connection, including any active users. ConnectionRecords * in this list will be sorted in descending order of end time (active * connections are first), and then in descending order of start time - * (newer connections are first). + * (newer connections are first). If connection history tracking is + * not implemented this method should throw GuacamoleUnsupportedException. * - * @return A list of ConnectionRecrods representing the usage history - * of this Connection. + * @deprecated + * This function has been deprecated in favor of + * {@link getConnectionHistory}, which returns the connection history + * as an ActivityRecordSet that can be easily sorted and filtered. + * While the getHistory() method is provided for API compatibility, + * new implementations should avoid use of this method and, instead, + * implement the getConnectionHistory() method. + * + * @return + * A list of ConnectionRecrods representing the usage history of this + * Connection. * - * @throws GuacamoleException If an error occurs while reading the history - * of this connection, or if permission is - * denied. + * @throws GuacamoleException + * If history tracking is not implemented, if an error occurs while + * reading the history of this connection, or if permission is + * denied. */ - public List getHistory() throws GuacamoleException; + @Deprecated + default List getHistory() + throws GuacamoleException { + return Collections.unmodifiableList(new ArrayList<>(getConnectionHistory().asCollection())); + } + /** + * Returns an ActivityRecordSet containing ConnectionRecords that + * represent the usage history of this Connection, including any active + * users. ConnectionRecords in this list will be sorted in descending order + * of end time (active connections are first), and then in descending order + * of start time (newer connections are first). If connection history + * tracking has not been implemented, or has been implemented using the + * deprecated {@link getHistory} method, this function should throw + * GuacamoleUnsupportedExpcetion. + * + * @return + * An ActivityRecordSet containing ConnectionRecords representing the + * usage history of this Connection. + * + * @throws GuacamoleException + * If history tracking is not implemented, if an error occurs while + * reading the history of this connection, or if permission is + * denied. + */ + default ActivityRecordSet getConnectionHistory() + throws GuacamoleException { + throw new GuacamoleUnsupportedException("This implementation of Connection does not provide connection history."); + } + /** * Returns identifiers of all readable sharing profiles that can be used to * join this connection when it is active. The level of access granted to a diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnection.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnection.java index 95b6e9326..cc99baaff 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnection.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingConnection.java @@ -134,11 +134,18 @@ public class DelegatingConnection implements Connection { return connection.getLastActive(); } + @Deprecated @Override public List getHistory() throws GuacamoleException { return connection.getHistory(); } + + @Override + public ActivityRecordSet getConnectionHistory() + throws GuacamoleException { + return connection.getConnectionHistory(); + } @Override public Set getSharingProfileIdentifiers() diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUser.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUser.java index add7be669..9da0fb561 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUser.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUser.java @@ -93,11 +93,18 @@ public class DelegatingUser implements User { return user.getLastActive(); } + @Deprecated @Override public List getHistory() throws GuacamoleException { return user.getHistory(); } + + @Override + public ActivityRecordSet getUserHistory() + throws GuacamoleException { + return user.getUserHistory(); + } @Override public SystemPermissionSet getSystemPermissions() diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/User.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/User.java index 4a8b43a8d..788e8d674 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/User.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/User.java @@ -19,9 +19,12 @@ package org.apache.guacamole.net.auth; +import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.List; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleUnsupportedException; /** * A user of the Guacamole web application. @@ -93,17 +96,50 @@ public interface User extends Identifiable, Attributes, Permissions { * of this user, including any active sessions. ActivityRecords * in this list will be sorted in descending order of end time (active * sessions are first), and then in descending order of start time - * (newer sessions are first). + * (newer sessions are first). If user login history is not implemented + * this method should throw GuacamoleUnsupportedException. * + * @deprecated + * This function is deprecated in favor of {@link getUserHistory}, which + * returns the login history as an ActivityRecordSet which supports + * various sort and filter functions. While this continues to be defined + * for API compatibility, new implementation should avoid this function + * and use getUserHistory(), instead. + * * @return * A list of ActivityRecords representing the login history of this * User. * * @throws GuacamoleException - * If an error occurs while reading the history of this user, or if - * permission is denied. + * If history tracking is not implemented, if an error occurs while + * reading the history of this user, or if permission is denied. */ - List getHistory() throws GuacamoleException; + @Deprecated + default List getHistory() throws GuacamoleException { + return Collections.unmodifiableList(new ArrayList<>(getUserHistory().asCollection())); + } + + /** + * Returns an ActivityRecordSet containing ActivityRecords representing + * the login history for this user, including any active sessions. + * ActivityRecords in this list will be sorted in descending order of end + * time (active sessions are first), and then in descending order of start + * time (newer sessions are first). If login history tracking is not + * implemented, or is only implemented using the deprecated {@link getHistory} + * method, this method should throw GuacamoleUnsupportedException. + * + * @return + * An ActivityRecordSet containing ActivityRecords representing the + * login history for this user. + * + * @throws GuacamoleException + * If history tracking is not implemented, if an error occurs while + * reading the history of this user, or if permission is denied. + */ + default ActivityRecordSet getUserHistory() + throws GuacamoleException { + throw new GuacamoleUnsupportedException("The default implementation of User does not provide login history."); + } /** * Returns a set of all readable user groups of which this user is a member. diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleActivityRecordSet.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleActivityRecordSet.java index a9a3c3eed..21aca5737 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleActivityRecordSet.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleActivityRecordSet.java @@ -35,10 +35,34 @@ import org.apache.guacamole.net.auth.ActivityRecordSet.SortableProperty; public class SimpleActivityRecordSet implements ActivityRecordSet { + /** + * The records associated with this record set, if any. + */ + private final Collection records; + + /** + * Create a new SimpleActivityRecordSet that contains an empty set of + * records. + */ + public SimpleActivityRecordSet() { + records = Collections.emptyList(); + } + + /** + * Create a new SimpleActivityRecordSet that contains the provided records + * which will back this record set. + * + * @param records + * The records that this SimpleActivityRecordSet should contain. + */ + public SimpleActivityRecordSet(Collection records) { + this.records = Collections.unmodifiableCollection(records); + } + @Override public Collection asCollection() throws GuacamoleException { - return Collections.emptyList(); + return records; } @Override diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleConnection.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleConnection.java index 2934cbe9f..ba61f7a7c 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleConnection.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleConnection.java @@ -21,7 +21,6 @@ package org.apache.guacamole.net.auth.simple; import java.util.Collections; import java.util.Date; -import java.util.List; import java.util.Map; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleServerException; @@ -33,6 +32,7 @@ import org.apache.guacamole.net.InetGuacamoleSocket; import org.apache.guacamole.net.SSLGuacamoleSocket; import org.apache.guacamole.net.SimpleGuacamoleTunnel; import org.apache.guacamole.net.auth.AbstractConnection; +import org.apache.guacamole.net.auth.ActivityRecordSet; import org.apache.guacamole.net.auth.ConnectionRecord; import org.apache.guacamole.net.auth.GuacamoleProxyConfiguration; import org.apache.guacamole.protocol.ConfiguredGuacamoleSocket; @@ -283,10 +283,11 @@ public class SimpleConnection extends AbstractConnection { public Date getLastActive() { return null; } - + @Override - public List getHistory() throws GuacamoleException { - return Collections.emptyList(); + public ActivityRecordSet getConnectionHistory() + throws GuacamoleException { + return new SimpleActivityRecordSet<>(); } } diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connection/APIConnectionWrapper.java b/guacamole/src/main/java/org/apache/guacamole/rest/connection/APIConnectionWrapper.java index 704db2397..6b407b450 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/connection/APIConnectionWrapper.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/connection/APIConnectionWrapper.java @@ -19,15 +19,13 @@ package org.apache.guacamole.rest.connection; -import java.util.Collections; import java.util.Date; -import java.util.List; import java.util.Map; import java.util.Set; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleUnsupportedException; import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.auth.Connection; -import org.apache.guacamole.net.auth.ConnectionRecord; import org.apache.guacamole.protocol.GuacamoleClientInformation; import org.apache.guacamole.protocol.GuacamoleConfiguration; @@ -123,24 +121,19 @@ public class APIConnectionWrapper implements Connection { } @Override - public Set getSharingProfileIdentifiers() { - throw new UnsupportedOperationException("Operation not supported."); + public Set getSharingProfileIdentifiers() throws GuacamoleException { + throw new GuacamoleUnsupportedException("Operation not supported."); } @Override public GuacamoleTunnel connect(GuacamoleClientInformation info, Map tokens) throws GuacamoleException { - throw new UnsupportedOperationException("Operation not supported."); + throw new GuacamoleUnsupportedException("Operation not supported."); } @Override public Date getLastActive() { return null; } - - @Override - public List getHistory() throws GuacamoleException { - return Collections.emptyList(); - } } diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java index 3149987d1..7d4f0d552 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java @@ -22,8 +22,6 @@ package org.apache.guacamole.rest.connection; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -32,8 +30,8 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleSecurityException; +import org.apache.guacamole.GuacamoleUnsupportedException; import org.apache.guacamole.net.auth.Connection; -import org.apache.guacamole.net.auth.ConnectionRecord; import org.apache.guacamole.net.auth.Directory; import org.apache.guacamole.net.auth.Permissions; import org.apache.guacamole.rest.directory.DirectoryView; @@ -43,13 +41,16 @@ import org.apache.guacamole.net.auth.permission.ObjectPermission; import org.apache.guacamole.net.auth.permission.ObjectPermissionSet; import org.apache.guacamole.net.auth.permission.SystemPermission; import org.apache.guacamole.net.auth.permission.SystemPermissionSet; -import org.apache.guacamole.rest.history.APIConnectionRecord; +import org.apache.guacamole.net.auth.simple.SimpleActivityRecordSet; import org.apache.guacamole.protocol.GuacamoleConfiguration; import org.apache.guacamole.rest.directory.DirectoryObjectResource; import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; import org.apache.guacamole.rest.directory.DirectoryResource; import org.apache.guacamole.rest.directory.DirectoryResourceFactory; +import org.apache.guacamole.rest.history.ConnectionHistoryResource; import org.apache.guacamole.rest.sharingprofile.APISharingProfile; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A REST resource which abstracts the operations available on an existing @@ -59,6 +60,11 @@ import org.apache.guacamole.rest.sharingprofile.APISharingProfile; @Consumes(MediaType.APPLICATION_JSON) public class ConnectionResource extends DirectoryObjectResource { + /** + * Logger for this class. + */ + private static final Logger logger = LoggerFactory.getLogger(ConnectionResource.class); + /** * The UserContext associated with the Directory which contains the * Connection exposed by this resource. @@ -150,18 +156,29 @@ public class ConnectionResource extends DirectoryObjectResource getConnectionHistory() + public ConnectionHistoryResource getConnectionHistory() throws GuacamoleException { - // Retrieve the requested connection's history - List apiRecords = new ArrayList(); - for (ConnectionRecord record : connection.getHistory()) - apiRecords.add(new APIConnectionRecord(record)); - - // Return the converted history - return apiRecords; + // Try the current getConnectionHistory() method, first, for connection history. + try { + return new ConnectionHistoryResource(connection.getConnectionHistory()); + } + catch (GuacamoleUnsupportedException e) { + logger.debug("Call to getConnectionHistory() is unsupported, falling back to getHistory().", e); + } + + // Fall back to the deprecated getHistory() method. + try { + return new ConnectionHistoryResource(new SimpleActivityRecordSet<>(connection.getHistory())); + } + catch (GuacamoleUnsupportedException e) { + logger.debug("Call to getHistory() is unsupported, no connection history records will be returned.", e); + } + + // If all fails, return an empty connection history set. + return new ConnectionHistoryResource(new SimpleActivityRecordSet<>()); } @@ -184,7 +201,7 @@ public class ConnectionResource extends DirectoryObjectResource sharingProfiles = new DirectoryView( + Directory sharingProfiles = new DirectoryView<>( userContext.getSharingProfileDirectory(), connection.getSharingProfileIdentifiers() ); diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryResource.java index 8da835580..704988cbf 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/history/HistoryResource.java @@ -24,7 +24,9 @@ import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleUnsupportedException; import org.apache.guacamole.net.auth.UserContext; +import org.apache.guacamole.net.auth.simple.SimpleActivityRecordSet; /** * A REST resource for retrieving and managing the history records of Guacamole @@ -64,7 +66,12 @@ public class HistoryResource { */ @Path("connections") public ConnectionHistoryResource getConnectionHistory() throws GuacamoleException { - return new ConnectionHistoryResource(userContext.getConnectionHistory()); + try { + return new ConnectionHistoryResource(userContext.getConnectionHistory()); + } + catch (GuacamoleUnsupportedException e) { + return new ConnectionHistoryResource(new SimpleActivityRecordSet<>()); + } } /** @@ -81,7 +88,12 @@ public class HistoryResource { */ @Path("users") public UserHistoryResource getUserHistory() throws GuacamoleException { - return new UserHistoryResource(userContext.getUserHistory()); + try { + return new UserHistoryResource(userContext.getUserHistory()); + } + catch (GuacamoleUnsupportedException e) { + return new UserHistoryResource(new SimpleActivityRecordSet<>()); + } } } diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/user/APIUserWrapper.java b/guacamole/src/main/java/org/apache/guacamole/rest/user/APIUserWrapper.java index c4375668d..b086314f6 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/user/APIUserWrapper.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/user/APIUserWrapper.java @@ -19,13 +19,10 @@ package org.apache.guacamole.rest.user; -import java.util.Collections; import java.util.Date; -import java.util.List; import java.util.Map; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleUnsupportedException; -import org.apache.guacamole.net.auth.ActivityRecord; import org.apache.guacamole.net.auth.Permissions; import org.apache.guacamole.net.auth.RelatedObjectSet; import org.apache.guacamole.net.auth.User; @@ -139,9 +136,4 @@ public class APIUserWrapper implements User { return null; } - @Override - public List getHistory() throws GuacamoleException { - return Collections.emptyList(); - } - } diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/user/UserResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/user/UserResource.java index d7d4bdc3f..f31ce5dc8 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/user/UserResource.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/user/UserResource.java @@ -21,8 +21,6 @@ package org.apache.guacamole.rest.user; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; -import java.util.ArrayList; -import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -33,19 +31,22 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleSecurityException; -import org.apache.guacamole.net.auth.ActivityRecord; +import org.apache.guacamole.GuacamoleUnsupportedException; import org.apache.guacamole.net.auth.AuthenticationProvider; import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.User; import org.apache.guacamole.net.auth.Directory; import org.apache.guacamole.net.auth.UserContext; import org.apache.guacamole.net.auth.credentials.GuacamoleCredentialsException; +import org.apache.guacamole.net.auth.simple.SimpleActivityRecordSet; import org.apache.guacamole.rest.directory.DirectoryObjectResource; import org.apache.guacamole.rest.directory.DirectoryObjectTranslator; -import org.apache.guacamole.rest.history.APIActivityRecord; +import org.apache.guacamole.rest.history.UserHistoryResource; import org.apache.guacamole.rest.identifier.RelatedObjectSetResource; import org.apache.guacamole.rest.permission.APIPermissionSet; import org.apache.guacamole.rest.permission.PermissionSetResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A REST resource which abstracts the operations available on an existing @@ -56,6 +57,11 @@ import org.apache.guacamole.rest.permission.PermissionSetResource; public class UserResource extends DirectoryObjectResource { + /** + * Logger for this class. + */ + private static final Logger logger = LoggerFactory.getLogger(UserResource.class); + /** * The UserContext associated with the Directory which contains the User * exposed by this resource. @@ -110,18 +116,29 @@ public class UserResource * @throws GuacamoleException * If an error occurs while retrieving the user history. */ - @GET + @SuppressWarnings("deprecation") @Path("history") - public List getUserHistory() + public UserHistoryResource getUserHistory() throws GuacamoleException { - // Retrieve the requested user's history - List apiRecords = new ArrayList(); - for (ActivityRecord record : user.getHistory()) - apiRecords.add(new APIActivityRecord(record)); - - // Return the converted history - return apiRecords; + // First try to retrieve history using the current getUserHistory() method. + try { + return new UserHistoryResource(user.getUserHistory()); + } + catch (GuacamoleUnsupportedException e) { + logger.debug("Call to getUserHistory() is unsupported, falling back to deprecated method getHistory().", e); + } + + // Fall back to deprecated getHistory() method. + try { + return new UserHistoryResource(new SimpleActivityRecordSet<>(user.getHistory())); + } + catch (GuacamoleUnsupportedException e) { + logger.debug("Call to getHistory() is unsupported, no user history records will be returned.", e); + } + + // If both are unimplemented, return an empty history set. + return new UserHistoryResource(new SimpleActivityRecordSet<>()); }