From 11d356a70b83668d668af9fdecf337af0009ed81 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 18 Apr 2018 20:42:27 -0700 Subject: [PATCH 1/4] GUACAMOLE-549: Add service for manipulating items within localStorage. --- .../storage/services/localStorageService.js | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 guacamole/src/main/webapp/app/storage/services/localStorageService.js diff --git a/guacamole/src/main/webapp/app/storage/services/localStorageService.js b/guacamole/src/main/webapp/app/storage/services/localStorageService.js new file mode 100644 index 000000000..a8c68460e --- /dev/null +++ b/guacamole/src/main/webapp/app/storage/services/localStorageService.js @@ -0,0 +1,145 @@ +/* + * 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. + */ + +/** + * Service for setting, removing, and retrieving localStorage keys. If access + * to localStorage is disabled, or the browser does not support localStorage, + * key values are temporarily stored in memory instead. If necessary, the same + * functionality is also available at the localStorageServiceProvider level. + */ +angular.module('storage').provider('localStorageService', [function localStorageServiceProvider() { + + /** + * Reference to this provider. + * + * @type localStorageServiceProvider + */ + var provider = this; + + /** + * Internal cache of key/value pairs stored within localStorage, updated + * lazily as keys are retrieved, updated, or removed. If localStorage is + * not actually available, then this cache will be the sole storage + * location for these key/value pairs. + * + * @type Object. + */ + var storedItems = {}; + + /** + * Stores the given value within localStorage under the given key. If access + * to localStorage is not provided/implemented by the browser, the key/value + * pair will be stored internally (in memory) only, with the stored value + * remaining retrievable via getItem() until the browser tab/window is + * closed. + * + * @param {String} key + * The arbitrary, unique key under which the value should be stored. + * + * @param {Object} value + * The object to store under the given key. This may be any object that + * can be serialized as JSON, and will automatically be serialized as + * JSON prior to storage. + */ + provider.setItem = function setItem(key, value) { + + // Store given value internally + var data = JSON.stringify(value); + storedItems[key] = data; + + // Additionally store value within localStorage if allowed + try { + if (window.localStorage) + localStorage.setItem(key, data); + } + catch (ignore) {} + + }; + + /** + * Removes the item having the given key from localStorage. If access to + * localStorage is not provided/implemented by the browser, the item is + * removed solely from internal, in-memory storage. If no such item exists, + * this function has no effect. + * + * @param {String} key + * The arbitrary, unique key of the item to remove from localStorage. + */ + provider.removeItem = function removeItem(key) { + + // Evict key from internal storage + delete storedItems[key]; + + // Remove key from localStorage if allowed + try { + if (window.localStorage) + localStorage.removeItem(key); + } + catch (ignore) {} + + }; + + /** + * Retrieves the value currently stored within localStorage for the item + * having the given key. If access to localStorage is not + * provided/implemented by the browser, the item is retrieved from + * internal, in-memory storage. The retrieved value is automatically + * deserialized from JSON prior to being returned. + * + * @param {String} key + * The arbitrary, unique key of the item to retrieve from localStorage. + * + * @returns {Object} + * The value stored within localStorage under the given key, + * automatically deserialized from JSON, or null if no such item is + * present. + */ + provider.getItem = function getItem(key) { + + // Attempt to refresh internal storage from localStorage + try { + if (window.localStorage) + storedItems[key] = localStorage.getItem(key); + } + catch (ignore) {} + + // Pull and parse value from internal storage, if present + var data = storedItems[key]; + if (data) + return JSON.parse(data); + + // No value defined for given key + return null; + + }; + + // Factory method required by provider + this.$get = ['$injector', function localStorageServiceFactory($injector) { + + // Pass through all get/set/remove calls to the provider + // implementations of the same + return { + setItem : provider.setItem, + removeItem : provider.removeItem, + getItem : provider.getItem + }; + + }]; + +}]); From 831e4e0989a96f304d848f67de2fb178e8d736f6 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 18 Apr 2018 20:43:04 -0700 Subject: [PATCH 2/4] GUACAMOLE-549: Migrate recent connection history to localStorageService. --- .../main/webapp/app/history/historyModule.js | 4 ++- .../app/history/services/guacHistory.js | 33 ++++++++----------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/guacamole/src/main/webapp/app/history/historyModule.js b/guacamole/src/main/webapp/app/history/historyModule.js index ce2ab7358..c7ec7e103 100644 --- a/guacamole/src/main/webapp/app/history/historyModule.js +++ b/guacamole/src/main/webapp/app/history/historyModule.js @@ -20,4 +20,6 @@ /** * The module for code relating to connection history. */ -angular.module('history', []); +angular.module('history', [ + 'storage' +]); diff --git a/guacamole/src/main/webapp/app/history/services/guacHistory.js b/guacamole/src/main/webapp/app/history/services/guacHistory.js index b47f9f509..74046ef9f 100644 --- a/guacamole/src/main/webapp/app/history/services/guacHistory.js +++ b/guacamole/src/main/webapp/app/history/services/guacHistory.js @@ -20,7 +20,14 @@ /** * A service for reading and manipulating the Guacamole connection history. */ -angular.module('history').factory('guacHistory', ['HistoryEntry', function guacHistory(HistoryEntry) { +angular.module('history').factory('guacHistory', ['$injector', + function guacHistory($injector) { + + // Required types + var HistoryEntry = $injector.get('HistoryEntry'); + + // Required services + var localStorageService = $injector.get('localStorageService'); var service = {}; @@ -73,27 +80,15 @@ angular.module('history').factory('guacHistory', ['HistoryEntry', function guacH if (service.recentConnections.length > IDEAL_LENGTH) service.recentConnections.length = IDEAL_LENGTH; - // Save updated history, ignore inability to use localStorage - try { - if (localStorage) - localStorage.setItem(GUAC_HISTORY_STORAGE_KEY, JSON.stringify(service.recentConnections)); - } - catch (ignore) {} + // Save updated history + localStorageService.setItem(GUAC_HISTORY_STORAGE_KEY, service.recentConnections); }; - // Get stored connection history, ignore inability to use localStorage - try { - - if (localStorage) { - var storedHistory = JSON.parse(localStorage.getItem(GUAC_HISTORY_STORAGE_KEY) || "[]"); - if (storedHistory instanceof Array) - service.recentConnections = storedHistory; - - } - - } - catch (ignore) {} + // Init stored connection history from localStorage + var storedHistory = localStorageService.getItem(GUAC_HISTORY_STORAGE_KEY) || []; + if (storedHistory instanceof Array) + service.recentConnections = storedHistory; return service; From 1686e6f149fb6d3ea80217ed18792d22ded45e6f Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 18 Apr 2018 20:43:30 -0700 Subject: [PATCH 3/4] GUACAMOLE-549: Migrate storage/retrieval of local preferences to localStorageService. --- .../settings/services/preferenceService.js | 37 +++++++------------ .../webapp/app/settings/settingsModule.js | 3 +- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/guacamole/src/main/webapp/app/settings/services/preferenceService.js b/guacamole/src/main/webapp/app/settings/services/preferenceService.js index 9af428169..bcd86336b 100644 --- a/guacamole/src/main/webapp/app/settings/services/preferenceService.js +++ b/guacamole/src/main/webapp/app/settings/services/preferenceService.js @@ -21,7 +21,11 @@ * A service for setting and retrieving browser-local preferences. Preferences * may be any JSON-serializable type. */ -angular.module('settings').provider('preferenceService', function preferenceServiceProvider() { +angular.module('settings').provider('preferenceService', ['$injector', + function preferenceServiceProvider($injector) { + + // Required providers + var localStorageServiceProvider = $injector.get('localStorageServiceProvider'); /** * Reference to the provider itself. @@ -128,24 +132,18 @@ angular.module('settings').provider('preferenceService', function preferenceServ }; - // Get stored preferences, ignore inability to use localStorage - try { - - if (localStorage) { - var preferencesJSON = localStorage.getItem(GUAC_PREFERENCES_STORAGE_KEY); - if (preferencesJSON) - angular.extend(provider.preferences, JSON.parse(preferencesJSON)); - } - - } - catch (ignore) {} + // Get stored preferences from localStorage + var storedPreferences = localStorageServiceProvider.getItem(GUAC_PREFERENCES_STORAGE_KEY); + if (storedPreferences) + angular.extend(provider.preferences, storedPreferences); // Factory method required by provider this.$get = ['$injector', function preferenceServiceFactory($injector) { // Required services - var $rootScope = $injector.get('$rootScope'); - var $window = $injector.get('$window'); + var $rootScope = $injector.get('$rootScope'); + var $window = $injector.get('$window'); + var localStorageService = $injector.get('localStorageService'); var service = {}; @@ -168,14 +166,7 @@ angular.module('settings').provider('preferenceService', function preferenceServ * Persists the current values of all preferences, if possible. */ service.save = function save() { - - // Save updated preferences, ignore inability to use localStorage - try { - if (localStorage) - localStorage.setItem(GUAC_PREFERENCES_STORAGE_KEY, JSON.stringify(service.preferences)); - } - catch (ignore) {} - + localStorageService.setItem(GUAC_PREFERENCES_STORAGE_KEY, service.preferences); }; // Persist settings when window is unloaded @@ -195,4 +186,4 @@ angular.module('settings').provider('preferenceService', function preferenceServ }]; -}); +}]); diff --git a/guacamole/src/main/webapp/app/settings/settingsModule.js b/guacamole/src/main/webapp/app/settings/settingsModule.js index 7adc0d164..62ad1c813 100644 --- a/guacamole/src/main/webapp/app/settings/settingsModule.js +++ b/guacamole/src/main/webapp/app/settings/settingsModule.js @@ -26,5 +26,6 @@ angular.module('settings', [ 'list', 'navigation', 'notification', - 'rest' + 'rest', + 'storage' ]); From 884a9c0ee987f9cb49a69ceb17eb0e2267eed058 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Wed, 18 Apr 2018 20:44:08 -0700 Subject: [PATCH 4/4] GUACAMOLE-549: Store auth token within localStorage rather than cookie. --- guacamole/pom.xml | 6 ----- .../src/main/webapp/app/auth/authModule.js | 4 +++- .../app/auth/service/authenticationService.js | 22 +++++++++---------- guacamole/src/main/webapp/index.html | 1 - 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/guacamole/pom.xml b/guacamole/pom.xml index 9fa9fce54..12d6c5c59 100644 --- a/guacamole/pom.xml +++ b/guacamole/pom.xml @@ -327,12 +327,6 @@ 1.3.16 runtime - - org.webjars.bower - angular-cookies - 1.3.16 - runtime - org.webjars.bower angular-route diff --git a/guacamole/src/main/webapp/app/auth/authModule.js b/guacamole/src/main/webapp/app/auth/authModule.js index ff8851ee0..7faaf87f7 100644 --- a/guacamole/src/main/webapp/app/auth/authModule.js +++ b/guacamole/src/main/webapp/app/auth/authModule.js @@ -20,4 +20,6 @@ /** * The module for authentication and management of tokens. */ -angular.module('auth', ['ngCookies']); +angular.module('auth', [ + 'storage' +]); diff --git a/guacamole/src/main/webapp/app/auth/service/authenticationService.js b/guacamole/src/main/webapp/app/auth/service/authenticationService.js index 74f0570b4..2b64a5b81 100644 --- a/guacamole/src/main/webapp/app/auth/service/authenticationService.js +++ b/guacamole/src/main/webapp/app/auth/service/authenticationService.js @@ -46,10 +46,10 @@ angular.module('auth').factory('authenticationService', ['$injector', var Error = $injector.get('Error'); // Required services - var $cookieStore = $injector.get('$cookieStore'); - var $http = $injector.get('$http'); - var $q = $injector.get('$q'); - var $rootScope = $injector.get('$rootScope'); + var $http = $injector.get('$http'); + var $q = $injector.get('$q'); + var $rootScope = $injector.get('$rootScope'); + var localStorageService = $injector.get('localStorageService'); var service = {}; @@ -62,12 +62,12 @@ angular.module('auth').factory('authenticationService', ['$injector', var cachedResult = null; /** - * The unique identifier of the local cookie which stores the result of the - * last authentication attempt. + * The unique identifier of the local storage key which stores the result + * of the last authentication attempt. * * @type String */ - var AUTH_COOKIE_ID = "GUAC_AUTH"; + var AUTH_STORAGE_KEY = 'GUAC_AUTH'; /** * Retrieves the last successful authentication result. If the user has not @@ -85,7 +85,7 @@ angular.module('auth').factory('authenticationService', ['$injector', return cachedResult; // Return explicit null if no auth data is currently stored - var data = $cookieStore.get(AUTH_COOKIE_ID); + var data = localStorageService.getItem(AUTH_STORAGE_KEY); if (!data) return null; @@ -107,7 +107,7 @@ angular.module('auth').factory('authenticationService', ['$injector', // Clear the currently-stored result if the last attempt failed if (!data) { cachedResult = null; - $cookieStore.remove(AUTH_COOKIE_ID); + localStorageService.removeItem(AUTH_STORAGE_KEY); } // Otherwise store the authentication attempt directly @@ -116,9 +116,9 @@ angular.module('auth').factory('authenticationService', ['$injector', // Always store in cache cachedResult = data; - // Store cookie ONLY if not anonymous + // Persist result past tab/window closure ONLY if not anonymous if (data.username !== AuthenticationResult.ANONYMOUS_USERNAME) - $cookieStore.put(AUTH_COOKIE_ID, data); + localStorageService.setItem(AUTH_STORAGE_KEY, data); } diff --git a/guacamole/src/main/webapp/index.html b/guacamole/src/main/webapp/index.html index 376245fa8..14321de42 100644 --- a/guacamole/src/main/webapp/index.html +++ b/guacamole/src/main/webapp/index.html @@ -62,7 +62,6 @@ -