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); + + } + + }