From 5f243c42df79df09f46dcc60cc914fc5820cd76b Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 7 Apr 2015 11:36:55 -0700 Subject: [PATCH 1/2] GUAC-1126: Clear managed connections, if any, upon login. New sessions should start with a clean environment (fixes GUAC-1066). --- .../app/client/services/guacClientManager.js | 14 +++++++++++--- .../app/login/controllers/loginController.js | 11 +++++++++-- guacamole/src/main/webapp/app/login/loginModule.js | 2 +- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/guacamole/src/main/webapp/app/client/services/guacClientManager.js b/guacamole/src/main/webapp/app/client/services/guacClientManager.js index c3d0e0f56..67d20b98d 100644 --- a/guacamole/src/main/webapp/app/client/services/guacClientManager.js +++ b/guacamole/src/main/webapp/app/client/services/guacClientManager.js @@ -127,14 +127,22 @@ angular.module('client').factory('guacClientManager', ['$injector', }; - // Disconnect all clients when window is unloaded - $window.addEventListener('unload', function disconnectAllClients() { + /** + * Disconnects and removes all currently-connected clients. + */ + service.clear = function clear() { // Disconnect each managed client for (var id in service.managedClients) service.managedClients[id].client.disconnect(); - }); + // Clear managed clients + service.managedClients = {}; + + }; + + // Disconnect all clients when window is unloaded + $window.addEventListener('unload', service.clear); return service; diff --git a/guacamole/src/main/webapp/app/login/controllers/loginController.js b/guacamole/src/main/webapp/app/login/controllers/loginController.js index 565abc6c7..34765b449 100644 --- a/guacamole/src/main/webapp/app/login/controllers/loginController.js +++ b/guacamole/src/main/webapp/app/login/controllers/loginController.js @@ -24,8 +24,9 @@ angular.module('login').controller('loginController', ['$scope', '$injector', function loginController($scope, $injector) { // Required services - var $location = $injector.get("$location"); - var authenticationService = $injector.get("authenticationService"); + var $location = $injector.get('$location'); + var authenticationService = $injector.get('authenticationService'); + var guacClientManager = $injector.get('guacClientManager'); var userPageService = $injector.get('userPageService'); /** @@ -53,10 +54,16 @@ angular.module('login').controller('loginController', ['$scope', '$injector', // Redirect to main view upon success .success(function success(data, status, headers, config) { + + // Provide user with clean environment + guacClientManager.clear(); + + // Redirect to main view userPageService.getHomePage() .then(function homePageRetrieved(homePage) { $location.url(homePage.url); }); + }) // Reset and focus password upon failure diff --git a/guacamole/src/main/webapp/app/login/loginModule.js b/guacamole/src/main/webapp/app/login/loginModule.js index 54e531233..289aefa9d 100644 --- a/guacamole/src/main/webapp/app/login/loginModule.js +++ b/guacamole/src/main/webapp/app/login/loginModule.js @@ -23,4 +23,4 @@ /** * The module for the login functionality. */ -angular.module('login', ['element', 'navigation']); +angular.module('login', ['client', 'element', 'navigation']); From 360ec0929e5ca0641fe85eff73864a46b1b26586 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 7 Apr 2015 12:23:33 -0700 Subject: [PATCH 2/2] GUAC-1126: Automatically invalidate any existing token upon login. --- .../app/auth/service/authenticationService.js | 76 ++++++++++++++++--- .../app/login/controllers/loginController.js | 6 +- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/guacamole/src/main/webapp/app/auth/service/authenticationService.js b/guacamole/src/main/webapp/app/auth/service/authenticationService.js index 9b1983821..c98b36bcc 100644 --- a/guacamole/src/main/webapp/app/auth/service/authenticationService.js +++ b/guacamole/src/main/webapp/app/auth/service/authenticationService.js @@ -23,8 +23,13 @@ /** * A service for authenticating a user against the REST API. */ -angular.module('auth').factory('authenticationService', ['$http', '$cookieStore', - function authenticationService($http, $cookieStore) { +angular.module('auth').factory('authenticationService', ['$injector', + function authenticationService($injector) { + + // Required services + var $cookieStore = $injector.get('$cookieStore'); + var $http = $injector.get('$http'); + var $q = $injector.get('$q'); var service = {}; @@ -58,19 +63,65 @@ angular.module('auth').factory('authenticationService', ['$http', '$cookieStore' * A promise which succeeds only if the login operation was successful. */ service.authenticate = function authenticate(parameters) { - return $http({ + + var authenticationProcess = $q.defer(); + + /** + * Stores the given authentication data within the browser and marks + * the authentication process as completed. + * + * @param {Object} data + * The authentication data returned by the token REST endpoint. + */ + var completeAuthentication = function completeAuthentication(data) { + + // Store auth data + $cookieStore.put(AUTH_COOKIE_ID, { + authToken : data.authToken, + userID : data.userID + }); + + // Process is complete + authenticationProcess.resolve(); + + }; + + // Attempt authentication + $http({ method: 'POST', url: 'api/tokens', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: $.param(parameters), - }).success(function success(data, status, headers, config) { - $cookieStore.put(AUTH_COOKIE_ID, { - authToken : data.authToken, - userID : data.userID - }); + }) + + // If authentication succeeds, handle received auth data + .success(function authenticationSuccessful(data) { + + var currentToken = service.getCurrentToken(); + + // If a new token was received, ensure the old token is invalidated + if (currentToken && data.authToken !== currentToken) { + service.logout() + ['finally'](function logoutComplete() { + completeAuthentication(data); + }); + } + + // Otherwise, just finish the auth process + else + completeAuthentication(data); + + }) + + // If authentication fails, propogate failure to returned promise + .error(function authenticationFailed() { + authenticationProcess.reject(); }); + + return authenticationProcess.promise; + }; /** @@ -142,10 +193,17 @@ angular.module('auth').factory('authenticationService', ['$http', '$cookieStore' * successful. */ service.logout = function logout() { + + // Clear authentication data + var token = service.getCurrentToken(); + $cookieStore.remove(AUTH_COOKIE_ID); + + // Delete old token return $http({ method: 'DELETE', - url: 'api/tokens/' + encodeURIComponent(service.getCurrentToken()) + url: 'api/tokens/' + token }); + }; /** diff --git a/guacamole/src/main/webapp/app/login/controllers/loginController.js b/guacamole/src/main/webapp/app/login/controllers/loginController.js index 34765b449..6d5a781f2 100644 --- a/guacamole/src/main/webapp/app/login/controllers/loginController.js +++ b/guacamole/src/main/webapp/app/login/controllers/loginController.js @@ -49,11 +49,11 @@ angular.module('login').controller('loginController', ['$scope', '$injector', */ $scope.login = function login() { - // Attempt login + // Attempt login once existing session is destroyed authenticationService.login($scope.username, $scope.password) // Redirect to main view upon success - .success(function success(data, status, headers, config) { + .then(function loginSuccessful() { // Provide user with clean environment guacClientManager.clear(); @@ -67,7 +67,7 @@ angular.module('login').controller('loginController', ['$scope', '$injector', }) // Reset and focus password upon failure - .error(function error(data, status, headers, config) { + ['catch'](function loginFailed() { $scope.loginError = true; $scope.passwordFocused = true; $scope.password = '';