mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
GUACAMOLE-1744: Merge automatically clean up UI if session has expired in the background.
This commit is contained in:
@@ -261,6 +261,43 @@ angular.module('auth').factory('authenticationService', ['$injector',
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the session associated with a particular token is
|
||||||
|
* still valid, without performing an operation that would result in that
|
||||||
|
* session being marked as active. If no token is provided, the session of
|
||||||
|
* the current user is checked.
|
||||||
|
*
|
||||||
|
* @param {string} [token]
|
||||||
|
* The authentication token to pass with the "Guacamole-Token" header.
|
||||||
|
* If omitted, and the user is logged in, the user's current
|
||||||
|
* authentication token will be used.
|
||||||
|
*
|
||||||
|
* @returns {Promise.<!boolean>}
|
||||||
|
* A promise that resolves with the boolean value "true" if the session
|
||||||
|
* is valid, and resolves with the boolean value "false" otherwise,
|
||||||
|
* including if an error prevents session validity from being
|
||||||
|
* determined. The promise is never rejected.
|
||||||
|
*/
|
||||||
|
service.getValidity = function getValidity(token) {
|
||||||
|
|
||||||
|
// NOTE: Because this is a HEAD request, we will not receive a JSON
|
||||||
|
// response body. We will only have a simple yes/no regarding whether
|
||||||
|
// the auth token can be expected to be usable.
|
||||||
|
return service.request({
|
||||||
|
method: 'HEAD',
|
||||||
|
url: 'api/session'
|
||||||
|
}, token)
|
||||||
|
|
||||||
|
.then(function sessionIsValid() {
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
|
||||||
|
['catch'](function sessionIsNotValid() {
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes a request to revoke an authentication token using the token REST
|
* Makes a request to revoke an authentication token using the token REST
|
||||||
* API endpoint, returning a promise that succeeds only if the token was
|
* API endpoint, returning a promise that succeeds only if the token was
|
||||||
|
@@ -23,14 +23,30 @@
|
|||||||
angular.module('index').controller('indexController', ['$scope', '$injector',
|
angular.module('index').controller('indexController', ['$scope', '$injector',
|
||||||
function indexController($scope, $injector) {
|
function indexController($scope, $injector) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of milliseconds that should elapse between client-side
|
||||||
|
* session checks. This DOES NOT impact whether a session expires at all;
|
||||||
|
* such checks will always be server-side. This only affects how quickly
|
||||||
|
* the client-side view can recognize that a user's session has expired
|
||||||
|
* absent any action taken by the user.
|
||||||
|
*
|
||||||
|
* @type {!number}
|
||||||
|
*/
|
||||||
|
const SESSION_VALIDITY_RECHECK_INTERVAL = 15000;
|
||||||
|
|
||||||
|
// Required types
|
||||||
|
const ManagedClientState = $injector.get('ManagedClientState');
|
||||||
|
|
||||||
// Required services
|
// Required services
|
||||||
const $document = $injector.get('$document');
|
const $document = $injector.get('$document');
|
||||||
const $location = $injector.get('$location');
|
const $interval = $injector.get('$interval');
|
||||||
const $route = $injector.get('$route');
|
const $location = $injector.get('$location');
|
||||||
const $window = $injector.get('$window');
|
const $route = $injector.get('$route');
|
||||||
const clipboardService = $injector.get('clipboardService');
|
const $window = $injector.get('$window');
|
||||||
const guacNotification = $injector.get('guacNotification');
|
const authenticationService = $injector.get('authenticationService');
|
||||||
const guacClientManager = $injector.get('guacClientManager');
|
const clipboardService = $injector.get('clipboardService');
|
||||||
|
const guacNotification = $injector.get('guacNotification');
|
||||||
|
const guacClientManager = $injector.get('guacClientManager');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The error that prevents the current page from rendering at all. If no
|
* The error that prevents the current page from rendering at all. If no
|
||||||
@@ -202,6 +218,48 @@ angular.module('index').controller('indexController', ['$scope', '$injector',
|
|||||||
keyboard.reset();
|
keyboard.reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the current user has at least one active connection
|
||||||
|
* running within the current tab.
|
||||||
|
*
|
||||||
|
* @returns {!boolean}
|
||||||
|
* true if the current user has at least one active connection running
|
||||||
|
* in the current browser tab, false otherwise.
|
||||||
|
*/
|
||||||
|
var hasActiveTunnel = function hasActiveTunnel() {
|
||||||
|
|
||||||
|
var clients = guacClientManager.getManagedClients();
|
||||||
|
for (var id in clients) {
|
||||||
|
|
||||||
|
switch (clients[id].clientState.connectionState) {
|
||||||
|
case ManagedClientState.ConnectionState.CONNECTING:
|
||||||
|
case ManagedClientState.ConnectionState.WAITING:
|
||||||
|
case ManagedClientState.ConnectionState.CONNECTED:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// If we're logged in and not connected to anything, periodically check
|
||||||
|
// whether the current session is still valid. If the session has expired,
|
||||||
|
// refresh the auth state to reshow the login screen (rather than wait for
|
||||||
|
// the user to take some action and discover that they are not logged in
|
||||||
|
// after all). There is no need to do this if a connection is active as
|
||||||
|
// that connection activity will already automatically check session
|
||||||
|
// validity.
|
||||||
|
$interval(function cleanUpViewIfSessionInvalid() {
|
||||||
|
if ($scope.applicationState === ApplicationState.READY && !hasActiveTunnel()) {
|
||||||
|
authenticationService.getValidity().then(function validityDetermined(valid) {
|
||||||
|
if (!valid)
|
||||||
|
$scope.reAuthenticate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, SESSION_VALIDITY_RECHECK_INTERVAL);
|
||||||
|
|
||||||
// Release all keys upon form submission (there may not be corresponding
|
// Release all keys upon form submission (there may not be corresponding
|
||||||
// keyup events for key presses involved in submitting a form)
|
// keyup events for key presses involved in submitting a form)
|
||||||
$document.on('submit', function formSubmitted() {
|
$document.on('submit', function formSubmitted() {
|
||||||
|
@@ -99,36 +99,42 @@ public class GuacamoleSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the authenticated user associated with this session.
|
* Returns the authenticated user associated with this session. Invoking
|
||||||
|
* this function automatically updates this session's last access time.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* The authenticated user associated with this session.
|
* The authenticated user associated with this session.
|
||||||
*/
|
*/
|
||||||
public AuthenticatedUser getAuthenticatedUser() {
|
public AuthenticatedUser getAuthenticatedUser() {
|
||||||
|
this.access();
|
||||||
return authenticatedUser;
|
return authenticatedUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces the authenticated user associated with this session with the
|
* Replaces the authenticated user associated with this session with the
|
||||||
* given authenticated user.
|
* given authenticated user. Invoking this function automatically updates
|
||||||
|
* this session's last access time.
|
||||||
*
|
*
|
||||||
* @param authenticatedUser
|
* @param authenticatedUser
|
||||||
* The authenticated user to associated with this session.
|
* The authenticated user to associated with this session.
|
||||||
*/
|
*/
|
||||||
public void setAuthenticatedUser(AuthenticatedUser authenticatedUser) {
|
public void setAuthenticatedUser(AuthenticatedUser authenticatedUser) {
|
||||||
|
this.access();
|
||||||
this.authenticatedUser = authenticatedUser;
|
this.authenticatedUser = authenticatedUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of all UserContexts associated with this session. Each
|
* Returns a list of all UserContexts associated with this session. Each
|
||||||
* AuthenticationProvider currently loaded by Guacamole may provide its own
|
* AuthenticationProvider currently loaded by Guacamole may provide its own
|
||||||
* UserContext for any successfully-authenticated user.
|
* UserContext for any successfully-authenticated user. Invoking this
|
||||||
|
* function automatically updates this session's last access time.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* An unmodifiable list of all UserContexts associated with this
|
* An unmodifiable list of all UserContexts associated with this
|
||||||
* session.
|
* session.
|
||||||
*/
|
*/
|
||||||
public List<DecoratedUserContext> getUserContexts() {
|
public List<DecoratedUserContext> getUserContexts() {
|
||||||
|
this.access();
|
||||||
return Collections.unmodifiableList(userContexts);
|
return Collections.unmodifiableList(userContexts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,6 +142,8 @@ public class GuacamoleSession {
|
|||||||
* Returns true if all user contexts associated with this session are
|
* 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, 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.
|
* valid, it may no longer be used, and invalidate() should be invoked.
|
||||||
|
* Invoking this function does not affect the last access time of this
|
||||||
|
* session.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* true if all user contexts associated with this session are
|
* true if all user contexts associated with this session are
|
||||||
@@ -151,7 +159,8 @@ public class GuacamoleSession {
|
|||||||
/**
|
/**
|
||||||
* Returns the UserContext associated with this session that originated
|
* Returns the UserContext associated with this session that originated
|
||||||
* from the AuthenticationProvider with the given identifier. If no such
|
* from the AuthenticationProvider with the given identifier. If no such
|
||||||
* UserContext exists, an exception is thrown.
|
* UserContext exists, an exception is thrown. Invoking this function
|
||||||
|
* automatically updates this session's last access time.
|
||||||
*
|
*
|
||||||
* @param authProviderIdentifier
|
* @param authProviderIdentifier
|
||||||
* The unique identifier of the AuthenticationProvider that created the
|
* The unique identifier of the AuthenticationProvider that created the
|
||||||
@@ -188,20 +197,24 @@ public class GuacamoleSession {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces all UserContexts associated with this session with the given
|
* Replaces all UserContexts associated with this session with the given
|
||||||
* List of UserContexts.
|
* List of UserContexts. Invoking this function automatically updates this
|
||||||
|
* session's last access time.
|
||||||
*
|
*
|
||||||
* @param userContexts
|
* @param userContexts
|
||||||
* The List of UserContexts to associate with this session.
|
* The List of UserContexts to associate with this session.
|
||||||
*/
|
*/
|
||||||
public void setUserContexts(List<DecoratedUserContext> userContexts) {
|
public void setUserContexts(List<DecoratedUserContext> userContexts) {
|
||||||
|
this.access();
|
||||||
this.userContexts = userContexts;
|
this.userContexts = userContexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether this session has any associated active tunnels.
|
* Returns whether this session has any associated active tunnels. Invoking
|
||||||
|
* this function does not affect the last access time of this session.
|
||||||
*
|
*
|
||||||
* @return true if this session has any associated active tunnels,
|
* @return
|
||||||
* false otherwise.
|
* true if this session has any associated active tunnels, false
|
||||||
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean hasTunnels() {
|
public boolean hasTunnels() {
|
||||||
return !tunnels.isEmpty();
|
return !tunnels.isEmpty();
|
||||||
@@ -214,10 +227,14 @@ public class GuacamoleSession {
|
|||||||
* session. A tunnel need not be present here to be used by the user
|
* session. A tunnel need not be present here to be used by the user
|
||||||
* associated with this session, but tunnels not in this set will not
|
* associated with this session, but tunnels not in this set will not
|
||||||
* be taken into account when determining whether a session is in use.
|
* be taken into account when determining whether a session is in use.
|
||||||
|
* Invoking this function automatically updates this session's last access
|
||||||
|
* time.
|
||||||
*
|
*
|
||||||
* @return A map of all active tunnels associated with this session.
|
* @return
|
||||||
|
* A map of all active tunnels associated with this session.
|
||||||
*/
|
*/
|
||||||
public Map<String, UserTunnel> getTunnels() {
|
public Map<String, UserTunnel> getTunnels() {
|
||||||
|
this.access();
|
||||||
return tunnels;
|
return tunnels;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,16 +245,23 @@ public class GuacamoleSession {
|
|||||||
* @param tunnel The tunnel to associate with this session.
|
* @param tunnel The tunnel to associate with this session.
|
||||||
*/
|
*/
|
||||||
public void addTunnel(UserTunnel tunnel) {
|
public void addTunnel(UserTunnel tunnel) {
|
||||||
|
this.access();
|
||||||
tunnels.put(tunnel.getUUID().toString(), tunnel);
|
tunnels.put(tunnel.getUUID().toString(), tunnel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disassociates the tunnel having the given UUID from this session.
|
* Disassociates the tunnel having the given UUID from this session.
|
||||||
|
* Invoking this function automatically updates this session's last access
|
||||||
|
* time.
|
||||||
*
|
*
|
||||||
* @param uuid The UUID of the tunnel to disassociate from this session.
|
* @param uuid
|
||||||
* @return true if the tunnel existed and was removed, false otherwise.
|
* The UUID of the tunnel to disassociate from this session.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* true if the tunnel existed and was removed, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean removeTunnel(String uuid) {
|
public boolean removeTunnel(String uuid) {
|
||||||
|
this.access();
|
||||||
return tunnels.remove(uuid) != null;
|
return tunnels.remove(uuid) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +275,8 @@ public class GuacamoleSession {
|
|||||||
/**
|
/**
|
||||||
* Returns the time this session was last accessed, as the number of
|
* Returns the time this session was last accessed, as the number of
|
||||||
* milliseconds since midnight January 1, 1970 GMT. Session access must
|
* milliseconds since midnight January 1, 1970 GMT. Session access must
|
||||||
* be explicitly marked through calls to the access() function.
|
* be explicitly marked through calls to the access() function. Invoking
|
||||||
|
* this function does not affect the last access time of this session.
|
||||||
*
|
*
|
||||||
* @return The time this session was last accessed.
|
* @return The time this session was last accessed.
|
||||||
*/
|
*/
|
||||||
|
@@ -208,12 +208,12 @@ public class HashTokenSessionMap implements TokenSessionMap {
|
|||||||
if (authToken == null)
|
if (authToken == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// Update the last access time and return the GuacamoleSession
|
// Return the GuacamoleSession having the given auth token (NOTE: We
|
||||||
GuacamoleSession session = sessionMap.get(authToken);
|
// do not update the access time here, as it is necessary to be able
|
||||||
if (session != null)
|
// to retrieve and check the session without causing that session to
|
||||||
session.access();
|
// be marked as active. Instead, those updates occur as needed when
|
||||||
|
// functions within the GuacamoleSession are invoked.)
|
||||||
return session;
|
return sessionMap.get(authToken);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,6 +24,7 @@ import com.google.inject.assistedinject.AssistedInject;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.DELETE;
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.HEAD;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
@@ -184,4 +185,15 @@ public class SessionResource {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether this session resource represented a valid session at the
|
||||||
|
* time it was created. This function always succeeds. It is possible for
|
||||||
|
* an HTTP request aimed at this operation to fail, but that failure occurs
|
||||||
|
* further up the chain when locating the session.
|
||||||
|
*/
|
||||||
|
@HEAD
|
||||||
|
public void checkValidity() {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user