diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml index ac9856240..9c99593bf 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/pom.xml @@ -94,12 +94,6 @@ guice - - - com.google.inject.extensions - guice-assistedinject - - com.google.guava 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 e47ccf259..5ae0ea53f 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 @@ -20,8 +20,6 @@ package org.apache.guacamole.auth.jdbc; import com.google.inject.Scopes; -import com.google.inject.assistedinject.FactoryModuleBuilder; - import javax.sql.DataSource; import org.apache.guacamole.auth.jdbc.user.ModeledUserContext; import org.apache.guacamole.auth.jdbc.connectiongroup.RootConnectionGroup; @@ -40,8 +38,6 @@ import org.apache.guacamole.auth.jdbc.permission.SystemPermissionMapper; import org.apache.guacamole.auth.jdbc.user.UserMapper; import org.apache.guacamole.auth.jdbc.connectiongroup.ConnectionGroupService; import org.apache.guacamole.auth.jdbc.connection.ConnectionService; -import org.apache.guacamole.auth.jdbc.tunnel.AccessEnforcingDelegatingTunnel; -import org.apache.guacamole.auth.jdbc.tunnel.AccessEnforcingDelegatingTunnelFactory; import org.apache.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService; import org.apache.guacamole.auth.jdbc.security.PasswordEncryptionService; import org.apache.guacamole.auth.jdbc.security.SHA256PasswordEncryptionService; @@ -200,14 +196,7 @@ public class JDBCAuthenticationProviderModule extends MyBatisModule { bind(UserGroupPermissionService.class); bind(UserPermissionService.class); bind(UserService.class); - - // Bind Factories - install(new FactoryModuleBuilder() - .implement( - AccessEnforcingDelegatingTunnel.class, - AccessEnforcingDelegatingTunnel.class) - .build(AccessEnforcingDelegatingTunnelFactory.class)); - + } } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCEnvironment.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCEnvironment.java index 778d5b29f..77f038734 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCEnvironment.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCEnvironment.java @@ -230,12 +230,11 @@ public abstract class JDBCEnvironment extends DelegatingEnvironment { /** * Returns a boolean value representing whether access time windows should * be enforced for active connections - i.e. whether a currently-connected - * user should be disconnected upon the closure of an access window, or - * when the user is disabled. + * user should be disconnected upon the closure of an access window. * * @return - * True if a connected user should be disconnected upon an access time - * window closing, or the user being disabled. + * true if a connected user should be disconnected upon an access time + * window closing, false otherwise. * * @throws GuacamoleException * If guacamole.properties cannot be parsed. diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/ActiveConnectionDirectory.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/ActiveConnectionDirectory.java index b9a149933..b0d6324aa 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/ActiveConnectionDirectory.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/ActiveConnectionDirectory.java @@ -44,40 +44,34 @@ public class ActiveConnectionDirectory extends RestrictedObject @Override public ActiveConnection get(String identifier) throws GuacamoleException { - validateUserAccess(); return activeConnectionService.retrieveObject(getCurrentUser(), identifier); } @Override public Collection getAll(Collection identifiers) throws GuacamoleException { - validateUserAccess(); Collection objects = activeConnectionService.retrieveObjects(getCurrentUser(), identifiers); return Collections.unmodifiableCollection(objects); } @Override public Set getIdentifiers() throws GuacamoleException { - validateUserAccess(); return activeConnectionService.getIdentifiers(getCurrentUser()); } @Override public void add(ActiveConnection object) throws GuacamoleException { - validateUserAccess(); activeConnectionService.createObject(getCurrentUser(), object); } @Override public void update(ActiveConnection object) throws GuacamoleException { - validateUserAccess(); TrackedActiveConnection connection = (TrackedActiveConnection) object; activeConnectionService.updateObject(getCurrentUser(), connection); } @Override public void remove(String identifier) throws GuacamoleException { - validateUserAccess(); activeConnectionService.deleteObject(getCurrentUser(), identifier); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/RestrictedObject.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/RestrictedObject.java index 461ce5804..66979d487 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/RestrictedObject.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/RestrictedObject.java @@ -19,13 +19,7 @@ package org.apache.guacamole.auth.jdbc.base; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.GuacamoleUnauthorizedException; -import org.apache.guacamole.auth.jdbc.JDBCEnvironment; import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; -import org.apache.guacamole.auth.jdbc.user.ModeledUser; - -import com.google.inject.Inject; /** * Common base class for objects that are associated with the users that @@ -39,12 +33,6 @@ public abstract class RestrictedObject { */ private ModeledAuthenticatedUser currentUser; - /** - * The environment of the Guacamole server. - */ - @Inject - private JDBCEnvironment environment; - /** * Initializes this object, associating it with the current authenticated * user and populating it with data from the given model object @@ -80,39 +68,4 @@ public abstract class RestrictedObject { this.currentUser = currentUser; } - /** - * Validate that the current user is within a valid access time window - * and not disabled. If the user account is disabled or not within a - * valid access window, a GuacamoleUnauthorizedException will be thrown. - * - * This method can be called by RestrictedObject implementations before - * any operation that's specific to the current logged in user, to make - * sure that their access is still valid and enabled. - * - * If accessWindowCheckEnabled is set to false, the check will be skipped, - * and GuacamoleUnauthorizedException will never be thrown. - * - * @throws GuacamoleException - * If the user is outside of a valid access window, the user is - * disabled, or an error occurs while trying to determine access time - * restriction configuration. - */ - protected void validateUserAccess() throws GuacamoleException { - - // If access windows shouldn't be checked for active sessions, skip - // this check entirely - if (!environment.enforceAccessWindowsForActiveSessions()) - return; - - // If the user is outside of a valid access time window or disabled, - // throw an exception to immediately log them out - ModeledUser modeledUser = getCurrentUser().getUser(); - if ( - !modeledUser.isAccountAccessible() - || !modeledUser.isAccountValid() - || modeledUser.isDisabled() - ) - throw new GuacamoleUnauthorizedException("Permission Denied."); - } - } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionDirectory.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionDirectory.java index 54ca79033..52a127df4 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionDirectory.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connection/ConnectionDirectory.java @@ -45,14 +45,12 @@ public class ConnectionDirectory extends RestrictedObject @Override public Connection get(String identifier) throws GuacamoleException { - validateUserAccess(); return connectionService.retrieveObject(getCurrentUser(), identifier); } @Override @Transactional public Collection getAll(Collection identifiers) throws GuacamoleException { - validateUserAccess(); Collection objects = connectionService.retrieveObjects(getCurrentUser(), identifiers); return Collections.unmodifiableCollection(objects); } @@ -60,21 +58,18 @@ public class ConnectionDirectory extends RestrictedObject @Override @Transactional public Set getIdentifiers() throws GuacamoleException { - validateUserAccess(); return connectionService.getIdentifiers(getCurrentUser()); } @Override @Transactional public void add(Connection object) throws GuacamoleException { - validateUserAccess(); connectionService.createObject(getCurrentUser(), object); } @Override @Transactional public void update(Connection object) throws GuacamoleException { - validateUserAccess(); ModeledConnection connection = (ModeledConnection) object; connectionService.updateObject(getCurrentUser(), connection); } @@ -82,7 +77,6 @@ public class ConnectionDirectory extends RestrictedObject @Override @Transactional public void remove(String identifier) throws GuacamoleException { - validateUserAccess(); connectionService.deleteObject(getCurrentUser(), identifier); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connectiongroup/ConnectionGroupDirectory.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connectiongroup/ConnectionGroupDirectory.java index 030432793..9f3930597 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connectiongroup/ConnectionGroupDirectory.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connectiongroup/ConnectionGroupDirectory.java @@ -45,14 +45,12 @@ public class ConnectionGroupDirectory extends RestrictedObject @Override public ConnectionGroup get(String identifier) throws GuacamoleException { - validateUserAccess(); return connectionGroupService.retrieveObject(getCurrentUser(), identifier); } @Override @Transactional public Collection getAll(Collection identifiers) throws GuacamoleException { - validateUserAccess(); Collection objects = connectionGroupService.retrieveObjects(getCurrentUser(), identifiers); return Collections.unmodifiableCollection(objects); } @@ -60,21 +58,18 @@ public class ConnectionGroupDirectory extends RestrictedObject @Override @Transactional public Set getIdentifiers() throws GuacamoleException { - validateUserAccess(); return connectionGroupService.getIdentifiers(getCurrentUser()); } @Override @Transactional public void add(ConnectionGroup object) throws GuacamoleException { - validateUserAccess(); connectionGroupService.createObject(getCurrentUser(), object); } @Override @Transactional public void update(ConnectionGroup object) throws GuacamoleException { - validateUserAccess(); ModeledConnectionGroup connectionGroup = (ModeledConnectionGroup) object; connectionGroupService.updateObject(getCurrentUser(), connectionGroup); } @@ -82,7 +77,6 @@ public class ConnectionGroupDirectory extends RestrictedObject @Override @Transactional public void remove(String identifier) throws GuacamoleException { - validateUserAccess(); connectionGroupService.deleteObject(getCurrentUser(), identifier); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharingprofile/SharingProfileDirectory.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharingprofile/SharingProfileDirectory.java index 57ccb6d63..632557052 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharingprofile/SharingProfileDirectory.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharingprofile/SharingProfileDirectory.java @@ -44,14 +44,12 @@ public class SharingProfileDirectory extends RestrictedObject @Override public SharingProfile get(String identifier) throws GuacamoleException { - validateUserAccess(); return sharingProfileService.retrieveObject(getCurrentUser(), identifier); } @Override @Transactional public Collection getAll(Collection identifiers) throws GuacamoleException { - validateUserAccess(); return Collections.unmodifiableCollection( sharingProfileService.retrieveObjects(getCurrentUser(), identifiers) ); @@ -60,21 +58,18 @@ public class SharingProfileDirectory extends RestrictedObject @Override @Transactional public Set getIdentifiers() throws GuacamoleException { - validateUserAccess(); return sharingProfileService.getIdentifiers(getCurrentUser()); } @Override @Transactional public void add(SharingProfile object) throws GuacamoleException { - validateUserAccess(); sharingProfileService.createObject(getCurrentUser(), object); } @Override @Transactional public void update(SharingProfile object) throws GuacamoleException { - validateUserAccess(); ModeledSharingProfile sharingProfile = (ModeledSharingProfile) object; sharingProfileService.updateObject(getCurrentUser(), sharingProfile); } @@ -82,7 +77,6 @@ public class SharingProfileDirectory extends RestrictedObject @Override @Transactional public void remove(String identifier) throws GuacamoleException { - validateUserAccess(); sharingProfileService.deleteObject(getCurrentUser(), identifier); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java index f26b8ea13..8c16363a6 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java @@ -46,7 +46,6 @@ import org.apache.guacamole.GuacamoleResourceNotFoundException; import org.apache.guacamole.GuacamoleSecurityException; import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.GuacamoleUpstreamException; -import org.apache.guacamole.auth.jdbc.JDBCEnvironment; import org.apache.guacamole.auth.jdbc.connection.ConnectionMapper; import org.apache.guacamole.net.GuacamoleSocket; import org.apache.guacamole.net.GuacamoleTunnel; @@ -64,7 +63,6 @@ import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile; import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterMapper; import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterModel; import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser; -import org.apache.guacamole.auth.jdbc.user.UserService; import org.apache.guacamole.net.auth.GuacamoleProxyConfiguration; import org.apache.guacamole.protocol.FailoverGuacamoleSocket; import org.slf4j.Logger; @@ -119,24 +117,6 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS @Inject private SharedConnectionMap connectionMap; - /** - * Service for fetching users. - */ - @Inject - private UserService userService; - - /** - * The environment of the Guacamole server. - */ - @Inject - private JDBCEnvironment environment; - - /** - * Provider for creating AccessEnforcingDelegatingTunnel instances. - */ - @Inject - private AccessEnforcingDelegatingTunnelFactory accessEnforcingTunnelFactory; - /** * All active connections through the tunnel having a given UUID. */ @@ -405,21 +385,13 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS * connection, which MUST already be acquired via acquire(). The given * client information will be passed to guacd when the connection is * established. - * + * * The connection will be automatically released when it closes, or if it * fails to establish entirely. * - * If user access window restrictions are enabled for active sessions, - * they will be automatically enforced for any valid database users as the - * tunnel is used. - * * @param activeConnection * The active connection record of the connection in use. * - * @param user - * The user record in the database for the user connecting to the - * connection, if any. - * * @param info * Information describing the Guacamole client connecting to the given * connection. @@ -440,10 +412,8 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS * If an error occurs while the connection is being established, or * while connection configuration information is being retrieved. */ - private GuacamoleTunnel assignGuacamoleTunnel( - ActiveConnectionRecord activeConnection, - ModeledAuthenticatedUser user, GuacamoleClientInformation info, - Map tokens, + private GuacamoleTunnel assignGuacamoleTunnel(ActiveConnectionRecord activeConnection, + GuacamoleClientInformation info, Map tokens, boolean interceptErrors) throws GuacamoleException { // Record new active connection @@ -506,21 +476,12 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS getUnconfiguredGuacamoleSocket(connection.getGuacamoleProxyConfiguration(), cleanupTask), config, info); - // Assign new tunnel - GuacamoleTunnel tunnel; + // Assign and return new tunnel if (interceptErrors) - tunnel = activeConnection.assignGuacamoleTunnel(new FailoverGuacamoleSocket(socket), socket.getConnectionID()); + return activeConnection.assignGuacamoleTunnel(new FailoverGuacamoleSocket(socket), socket.getConnectionID()); else - tunnel = activeConnection.assignGuacamoleTunnel(socket, socket.getConnectionID()); - - // If there is an associated database user, and access window - // restrictions are enabled for active sessions, wrap the tunnel - // in order to check user login validity on every instruction read - if (user != null && environment.enforceAccessWindowsForActiveSessions()) { - tunnel = accessEnforcingTunnelFactory.create(tunnel, user); - } - - return tunnel; + return activeConnection.assignGuacamoleTunnel(socket, socket.getConnectionID()); + } // Execute cleanup if socket could not be created @@ -670,10 +631,8 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS acquire(user, Collections.singletonList(connection), true); // Connect only if the connection was successfully acquired - ActiveConnectionRecord connectionRecord = new ActiveConnectionRecord( - connectionMap, user, connection); - return assignGuacamoleTunnel( - connectionRecord, user, info, tokens, false); + ActiveConnectionRecord connectionRecord = new ActiveConnectionRecord(connectionMap, user, connection); + return assignGuacamoleTunnel(connectionRecord, info, tokens, false); } @@ -718,10 +677,9 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS try { // Connect to acquired child - ActiveConnectionRecord connectionRecord = new ActiveConnectionRecord( - connectionMap, user, connectionGroup, connection); - GuacamoleTunnel tunnel = assignGuacamoleTunnel( - connectionRecord, user, info, tokens, connections.size() > 1); + ActiveConnectionRecord connectionRecord = new ActiveConnectionRecord(connectionMap, user, connectionGroup, connection); + GuacamoleTunnel tunnel = assignGuacamoleTunnel(connectionRecord, + info, tokens, connections.size() > 1); // If session affinity is enabled, prefer this connection going forward if (connectionGroup.isSessionAffinityEnabled()) @@ -778,10 +736,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS user, definition.getActiveConnection(), definition.getSharingProfile()); // Connect to shared connection described by the created record - GuacamoleTunnel tunnel = assignGuacamoleTunnel( - connectionRecord, userService.retrieveAuthenticatedUser( - user.getAuthenticationProvider(), user.getCredentials()), - info, tokens, false); + GuacamoleTunnel tunnel = assignGuacamoleTunnel(connectionRecord, info, tokens, false); // Register tunnel, such that it is closed when the // SharedConnectionDefinition is invalidated diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AccessEnforcingDelegatingTunnel.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AccessEnforcingDelegatingTunnel.java deleted file mode 100644 index 5ee08330c..000000000 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AccessEnforcingDelegatingTunnel.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 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.tunnel; - -import java.util.concurrent.atomic.AtomicReference; - -import javax.annotation.Nonnull; - -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.GuacamoleUnauthorizedException; -import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; -import org.apache.guacamole.auth.jdbc.user.ModeledUser; -import org.apache.guacamole.auth.jdbc.user.UserService; -import org.apache.guacamole.io.GuacamoleReader; -import org.apache.guacamole.net.DelegatingGuacamoleTunnel; -import org.apache.guacamole.net.GuacamoleTunnel; -import org.apache.guacamole.protocol.FilteredGuacamoleReader; -import org.apache.guacamole.protocol.GuacamoleFilter; -import org.apache.guacamole.protocol.GuacamoleInstruction; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.inject.Inject; -import com.google.inject.assistedinject.Assisted; -import com.google.inject.assistedinject.AssistedInject; - -/** - * A tunnel implementation that enforces access window restriction for the - * provided ModeledUser, throwing a GuacamoleUnauthorizedException if the - * user's configured access window has closed, or if the user has become - * disabled. All other tunnel implementation is delegated to the underlying - * tunnel object. - */ -public class AccessEnforcingDelegatingTunnel extends DelegatingGuacamoleTunnel { - - /** - * Logger for this class. - */ - private static final Logger logger = LoggerFactory.getLogger( - AccessEnforcingDelegatingTunnel.class); - - /** - * The number of milliseconds between subsequent refreshes of the user - * from the DB. - */ - private static final long USER_MODEL_REFRESH_INTERVAL = 10000; - - /** - * The user who's access window restrictions should be applied for the - * wrapped tunnel. - */ - private final AtomicReference user; - - /** - * A thread that will continously refresh the user - */ - private final Thread userRefreshThread; - - /** - * A service to use for refreshing the user from the DB. - */ - @Inject - private UserService userService; - - /** - * Create a new tunnel that will enforce the access window restrictions of - * the provided user, during usage of the provided tunnel. - * - * @param tunnel - * The tunnel to delegate to. - * - * @param modeledAuthenticatedUser - * The user whose access restrictions should be applied. - * - */ - @AssistedInject - public AccessEnforcingDelegatingTunnel( - @Nonnull @Assisted GuacamoleTunnel tunnel, - @Nonnull @Assisted ModeledAuthenticatedUser modeledAuthenticatedUser) { - - super(tunnel); - this.user = new AtomicReference<>(modeledAuthenticatedUser.getUser()); - - this.userRefreshThread = new Thread(() -> { - while (true) { - - try { - - // Fetch an up-to-date user record from the DB to ensure - // that any access restrictions modified while this tunnel - // is open will be taken into account - this.user.set(userService.retrieveUser( - modeledAuthenticatedUser.getAuthenticationProvider(), - modeledAuthenticatedUser)); - } - - // If an error occurs while trying to fetch the updated user, - // log the warning / exception and stop the refresh thread - catch (GuacamoleException e) { - - logger.warn( - "Aborting user refresh thread due to error: {}", - e.getMessage()); - logger.debug( - "Exception caught while attempting to refresh user.", e); - - return; - } - - try { - - // Wait a bit before refreshing the user record again - Thread.sleep(USER_MODEL_REFRESH_INTERVAL); - } - - // If interrupted by the tunnel, exit immediately - catch (InterruptedException e) { - return; - } - } - }); - } - - @Override - public GuacamoleReader acquireReader() { - - // Start periodically refreshing the user record - userRefreshThread.start(); - - // Filter received instructions, checking if the user's login - // is still valid for each one. If the login is invalid, - // log them out immediately and close the tunnel. - return new FilteredGuacamoleReader( - super.acquireReader(), - new GuacamoleFilter() { - - @Override - public GuacamoleInstruction filter( - GuacamoleInstruction instruction) throws GuacamoleException { - - // The user record, at most USER_MODEL_REFRESH_INTERVAL - // milliseconds old - ModeledUser modeledUser = user.get(); - - // If the user is outside of a valid access time window, - // or disabled, throw an exception to immediately log them out - if ( - !modeledUser.isAccountAccessible() - || !modeledUser.isAccountValid() - || modeledUser.isDisabled() - ) { - throw new GuacamoleUnauthorizedException("Permission Denied."); - } - - return instruction; - } - } - ); - } - - @Override - public void releaseReader() { - - // Interrupt the refresh thread; it will clean itself up - userRefreshThread.interrupt(); - - super.releaseReader(); - } - - @Override - public void close() throws GuacamoleException { - - // Interrupt the refresh thread; it will clean itself up - userRefreshThread.interrupt(); - - super.close(); - } - -} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AccessEnforcingDelegatingTunnelFactory.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AccessEnforcingDelegatingTunnelFactory.java deleted file mode 100644 index 0c6350766..000000000 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AccessEnforcingDelegatingTunnelFactory.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.tunnel; - -import javax.annotation.Nonnull; - -import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser; -import org.apache.guacamole.net.GuacamoleTunnel; - -/** - * A factory for creating AccessEnforcingDelegatingTunnel instances. - */ -public interface AccessEnforcingDelegatingTunnelFactory { - - /** - * Create and return a new AccessEnforcingDelegatingTunnel wrapping the - * provided tunnel and enforcing the user restrictions associated with the - * provided user. - * - * @param tunnel - * The tunnel to delegate to. - * - * @param user - * The user whose access window restrictions should be applied for the - * wrapped tunel. - * - * @return - * A new AccessEnforcingDelegatingTunnel wrapping the provided tunnel - * and enforcing the user restrictions associated with the provided - * user. - * - */ - public AccessEnforcingDelegatingTunnel create( - @Nonnull GuacamoleTunnel tunnel, - @Nonnull ModeledAuthenticatedUser user); - -} diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java index df25159a9..c3150ca97 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java @@ -21,9 +21,6 @@ package org.apache.guacamole.auth.jdbc.tunnel; import java.util.Date; import java.util.UUID; - -import javax.annotation.Nonnull; - import org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel; import org.apache.guacamole.auth.jdbc.connection.ModeledConnection; import org.apache.guacamole.auth.jdbc.connection.ModeledConnectionRecord; @@ -380,8 +377,6 @@ public class ActiveConnectionRecord extends ModeledConnectionRecord { * @return * The newly-created tunnel associated with this connection record. */ - @Nonnull - @SuppressWarnings("null") public GuacamoleTunnel assignGuacamoleTunnel(final GuacamoleSocket socket, String connectionID) { @@ -405,8 +400,6 @@ public class ActiveConnectionRecord extends ModeledConnectionRecord { this.connectionID = connectionID; // Return newly-created tunnel - // NOTE: Guaranteed to be non-null since it was just set in this - // method, and there is no functionality to set it to null return this.tunnel; } 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 1416b0b39..e5ad5d2ea 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 @@ -29,6 +29,7 @@ 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.JDBCEnvironment; import org.apache.guacamole.auth.jdbc.activeconnection.ActiveConnectionDirectory; import org.apache.guacamole.auth.jdbc.base.ActivityRecordModel; import org.apache.guacamole.auth.jdbc.connection.ConnectionRecordSet; @@ -50,6 +51,8 @@ import org.apache.guacamole.net.auth.SharingProfile; import org.apache.guacamole.net.auth.User; import org.apache.guacamole.net.auth.UserContext; import org.apache.guacamole.net.auth.UserGroup; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * UserContext implementation which is driven by an arbitrary, underlying @@ -58,6 +61,11 @@ import org.apache.guacamole.net.auth.UserGroup; public class ModeledUserContext extends RestrictedObject implements org.apache.guacamole.net.auth.UserContext { + /** + * Logger for this class. + */ + private static final Logger logger = LoggerFactory.getLogger(ModeledUserContext.class); + /** * User directory restricted by the permissions of the user associated * with this context. @@ -130,6 +138,12 @@ public class ModeledUserContext extends RestrictedObject @Inject private UserRecordMapper userRecordMapper; + /** + * The environment of the Guacamole server. + */ + @Inject + private JDBCEnvironment environment; + /** * The activity record associated with this user's Guacamole session. If * this user's session will not have an associated activity record, such as @@ -296,4 +310,31 @@ public class ModeledUserContext extends RestrictedObject } + @Override + public boolean isValid() { + + try { + // If access window enforcement is disabled for active sessions, + // skip validity checks entirely + if (!environment.enforceAccessWindowsForActiveSessions()) + return true; + } + + catch (GuacamoleException e) { + + logger.warn( + "Unable to determine if access window enforcement is" + + " enabled for active sessions; enforcing by default: {}" + , e.getMessage()); + logger.debug("Unable to determine access window enforcement policy.", e); + + } + + // A user context is valid if the associated user's account is valid + // for the current date, and the user is within an access time window + ModeledUser user = getCurrentUser().getUser(); + return user.isAccountValid() && user.isAccountAccessible(); + + } + } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserDirectory.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserDirectory.java index db8bea12f..dffd8e2ec 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserDirectory.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/UserDirectory.java @@ -45,14 +45,12 @@ public class UserDirectory extends RestrictedObject @Override public User get(String identifier) throws GuacamoleException { - validateUserAccess(); return userService.retrieveObject(getCurrentUser(), identifier); } @Override @Transactional public Collection getAll(Collection identifiers) throws GuacamoleException { - validateUserAccess(); Collection objects = userService.retrieveObjects(getCurrentUser(), identifiers); return Collections.unmodifiableCollection(objects); } @@ -60,21 +58,18 @@ public class UserDirectory extends RestrictedObject @Override @Transactional public Set getIdentifiers() throws GuacamoleException { - validateUserAccess(); return userService.getIdentifiers(getCurrentUser()); } @Override @Transactional public void add(User object) throws GuacamoleException { - validateUserAccess(); userService.createObject(getCurrentUser(), object); } @Override @Transactional public void update(User object) throws GuacamoleException { - validateUserAccess(); ModeledUser user = (ModeledUser) object; userService.updateObject(getCurrentUser(), user); } @@ -82,7 +77,6 @@ public class UserDirectory extends RestrictedObject @Override @Transactional public void remove(String identifier) throws GuacamoleException { - validateUserAccess(); userService.deleteObject(getCurrentUser(), identifier); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupDirectory.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupDirectory.java index f61af0bea..911b8521f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupDirectory.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/usergroup/UserGroupDirectory.java @@ -44,14 +44,12 @@ public class UserGroupDirectory extends RestrictedObject @Override public UserGroup get(String identifier) throws GuacamoleException { - validateUserAccess(); return userGroupService.retrieveObject(getCurrentUser(), identifier); } @Override @Transactional public Collection getAll(Collection identifiers) throws GuacamoleException { - validateUserAccess(); Collection objects = userGroupService.retrieveObjects(getCurrentUser(), identifiers); return Collections.unmodifiableCollection(objects); } @@ -59,21 +57,18 @@ public class UserGroupDirectory extends RestrictedObject @Override @Transactional public Set getIdentifiers() throws GuacamoleException { - validateUserAccess(); return userGroupService.getIdentifiers(getCurrentUser()); } @Override @Transactional public void add(UserGroup object) throws GuacamoleException { - validateUserAccess(); userGroupService.createObject(getCurrentUser(), object); } @Override @Transactional public void update(UserGroup object) throws GuacamoleException { - validateUserAccess(); ModeledUserGroup group = (ModeledUserGroup) object; userGroupService.updateObject(getCurrentUser(), group); } @@ -81,7 +76,6 @@ public class UserGroupDirectory extends RestrictedObject @Override @Transactional public void remove(String identifier) throws GuacamoleException { - validateUserAccess(); userGroupService.deleteObject(getCurrentUser(), identifier); } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLGuacamoleProperties.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLGuacamoleProperties.java index c676feaad..ca17757a4 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLGuacamoleProperties.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/conf/MySQLGuacamoleProperties.java @@ -281,7 +281,7 @@ public class MySQLGuacamoleProperties { /** * Whether or not user-specific access time windows should be enforced for active sessions, * i.e. whether users with active sessions should be logged out immediately when an access - * window closes or the user is disabled. + * window closes. */ public static final BooleanGuacamoleProperty MYSQL_ENFORCE_ACCESS_WINDOWS_FOR_ACTIVE_SESSIONS = new BooleanGuacamoleProperty() { diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLGuacamoleProperties.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLGuacamoleProperties.java index 636c0a1c7..3f33ec44f 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLGuacamoleProperties.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/conf/PostgreSQLGuacamoleProperties.java @@ -293,7 +293,7 @@ public class PostgreSQLGuacamoleProperties { /** * Whether or not user-specific access time windows should be enforced for active sessions, * i.e. whether users with active sessions should be logged out immediately when an access - * window closes or the user is disabled. + * window closes. */ public static final BooleanGuacamoleProperty POSTGRESQL_ENFORCE_ACCESS_WINDOWS_FOR_ACTIVE_SESSIONS = new BooleanGuacamoleProperty() { diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerGuacamoleProperties.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerGuacamoleProperties.java index e7b1c6982..1f2500350 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerGuacamoleProperties.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/conf/SQLServerGuacamoleProperties.java @@ -223,7 +223,7 @@ public class SQLServerGuacamoleProperties { /** * Whether or not user-specific access time windows should be enforced for active sessions, * i.e. whether users with active sessions should be logged out immediately when an access - * window closes or the user is disabled. + * window closes. */ public static final BooleanGuacamoleProperty SQLSERVER_ENFORCE_ACCESS_WINDOWS_FOR_ACTIVE_SESSIONS = new BooleanGuacamoleProperty() { diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserContext.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserContext.java index 4b0343181..7431fa899 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserContext.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/DelegatingUserContext.java @@ -162,4 +162,9 @@ public class DelegatingUserContext implements UserContext { return userContext.getPrivileged(); } + @Override + public boolean isValid() { + return userContext.isValid(); + } + } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/UserContext.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/UserContext.java index 3f75b899c..a9ea7ea0c 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/UserContext.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/UserContext.java @@ -40,6 +40,27 @@ public interface UserContext { */ User self(); + /** + * Returns true if the session for the User associated with this user + * context is valid, or false otherwise. If the session is not valid, + * the webapp can be expected to terminate the session within a short + * period of time. + * + * NOTE: The webapp currently checks once a minute, and terminates any + * session marked as invalid. + * + * @return + * true if the session for the User associated with this user + * context is valid, or false otherwise. + */ + default boolean isValid() { + + // A user context is always valid unless explicitly updated by an + // implementation + return true; + + } + /** * Returns an arbitrary REST resource representing this UserContext. The * REST resource returned must be properly annotated with JSR-311 diff --git a/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java b/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java index 3e8d488c3..0458639d1 100644 --- a/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java +++ b/guacamole/src/main/java/org/apache/guacamole/GuacamoleSession.java @@ -132,6 +132,22 @@ public class GuacamoleSession { return Collections.unmodifiableList(userContexts); } + /** + * Returns true if all user contexts associated with this session are + * valid, or false if any user context is not valid. If a session is not + * valid, it may no longer be used, and invalidate() should be invoked. + * + * @return + * true if all user contexts associated with this session are + * valid, or false if any user context is not valid. + */ + public boolean isValid() { + + // Immediately return false if any user context is not valid + return !userContexts.stream().anyMatch( + userContext -> !userContext.isValid()); + } + /** * Returns the UserContext associated with this session that originated * from the AuthenticationProvider with the given identifier. If no such diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/auth/HashTokenSessionMap.java b/guacamole/src/main/java/org/apache/guacamole/rest/auth/HashTokenSessionMap.java index 24f653098..15d11eb62 100644 --- a/guacamole/src/main/java/org/apache/guacamole/rest/auth/HashTokenSessionMap.java +++ b/guacamole/src/main/java/org/apache/guacamole/rest/auth/HashTokenSessionMap.java @@ -94,7 +94,8 @@ public class HashTokenSessionMap implements TokenSessionMap { /** * Task which iterates through all active sessions, evicting those sessions - * which are beyond the session timeout. + * which are beyond the session timeout, or are marked as invalid by an + * extension. */ private class SessionEvictionTask implements Runnable { @@ -105,7 +106,8 @@ public class HashTokenSessionMap implements TokenSessionMap { /** * Creates a new task which automatically evicts sessions which are - * older than the specified timeout. + * older than the specified timeout, or are marked as invalid by an + * extension. * * @param sessionTimeout The maximum age of any session, in * milliseconds. @@ -116,16 +118,16 @@ public class HashTokenSessionMap implements TokenSessionMap { /** * Iterates through all active sessions, evicting those sessions which - * are beyond the session timeout. Internal errors which would - * otherwise stop the session eviction process are caught, logged, and - * the process is allowed to proceed. + * are beyond the session timeout, or are marked as invalid. Internal + * errors which would otherwise stop the session eviction process are + * caught, logged, and the process is allowed to proceed. */ - private void evictExpiredSessions() { + private void evictExpiredOrInvalidSessions() { // Get start time of session check time long sessionCheckStart = System.currentTimeMillis(); - logger.debug("Checking for expired sessions..."); + logger.debug("Checking for expired or invalid sessions..."); // For each session, remove sesions which have expired Iterator> entries = sessionMap.entrySet().iterator(); @@ -136,6 +138,15 @@ public class HashTokenSessionMap implements TokenSessionMap { try { + // Invalidate any sessions which have been flagged as invalid by extensions + if (!session.isValid()) { + logger.debug( + "Session \"{}\" has been invalidated by an extension.", + entry.getKey()); + entries.remove(); + session.invalidate(); + } + // Do not expire sessions which are active if (session.hasTunnels()) continue; @@ -170,13 +181,13 @@ public class HashTokenSessionMap implements TokenSessionMap { @Override public void run() { - // The evictExpiredSessions() function should already + // The evictExpiredOrInvalidSessions() function should already // automatically handle and log all unexpected internal errors, // but wrap the entire call in a try/catch plus additional logging // to ensure that absolutely no errors can result in the entire // thread dying try { - evictExpiredSessions(); + evictExpiredOrInvalidSessions(); } catch (Throwable t) { logger.error("An unexpected internal error prevented the "