From 2b5915b257f821f53efef929f90050127de3c36d Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Fri, 13 Oct 2017 17:07:20 -0400 Subject: [PATCH] convert to promises. loginService cipher refactor --- src/background.js | 44 +- src/models/api/requestModels.js | 47 +- .../app/vault/vaultAttachmentsController.js | 82 +-- .../app/vault/vaultEditLoginController.js | 18 +- .../app/vault/vaultViewLoginController.js | 58 +- src/services/apiService.js | 68 ++- src/services/autofillService.js | 2 +- src/services/cryptoService.js | 12 +- src/services/folderService.js | 31 +- src/services/loginService.js | 565 ++++++++---------- src/services/settingsService.js | 17 +- src/services/syncService.js | 20 +- src/services/tokenService.js | 20 +- src/services/userService.js | 38 +- src/services/utilsService.js | 31 + 15 files changed, 526 insertions(+), 527 deletions(-) diff --git a/src/background.js b/src/background.js index 89bb411464f..b382f48ca53 100644 --- a/src/background.js +++ b/src/background.js @@ -32,14 +32,14 @@ var bg_isBackground = true, bg_i18nService = new i18nService(bg_utilsService); bg_constantsService = new ConstantsService(bg_i18nService); bg_cryptoService = new CryptoService(bg_constantsService); - bg_tokenService = new TokenService(); + bg_tokenService = new TokenService(bg_utilsService); bg_appIdService = new AppIdService(); bg_apiService = new ApiService(bg_tokenService, bg_appIdService, bg_utilsService, bg_constantsService, logout); bg_environmentService = new EnvironmentService(bg_constantsService, bg_apiService); - bg_userService = new UserService(bg_tokenService, bg_apiService, bg_cryptoService); - bg_settingsService = new SettingsService(bg_userService); - bg_loginService = new LoginService(bg_cryptoService, bg_userService, bg_apiService, bg_settingsService); - bg_folderService = new FolderService(bg_cryptoService, bg_userService, bg_apiService, bg_i18nService); + bg_userService = new UserService(bg_tokenService, bg_apiService, bg_cryptoService, bg_utilsService); + bg_settingsService = new SettingsService(bg_userService, bg_utilsService); + bg_loginService = new LoginService(bg_cryptoService, bg_userService, bg_apiService, bg_settingsService, bg_utilsService); + bg_folderService = new FolderService(bg_cryptoService, bg_userService, bg_apiService, bg_i18nService, bg_utilsService); bg_lockService = new LockService(bg_constantsService, bg_cryptoService, bg_folderService, bg_loginService, bg_utilsService, setIcon, refreshBadgeAndMenu); bg_syncService = new SyncService(bg_loginService, bg_folderService, bg_userService, bg_apiService, bg_settingsService, @@ -807,27 +807,23 @@ var bg_isBackground = true, } } - // TODO: Fix callback hell by moving to promises function logout(expired, callback) { - bg_userService.getUserId(function (userId) { - bg_syncService.setLastSync(new Date(0), function () { - bg_settingsService.clear(function () { - bg_tokenService.clearToken(function () { - bg_cryptoService.clearKeys(function () { - bg_userService.clear(function () { - bg_loginService.clear(userId, function () { - bg_folderService.clear(userId, function () { - chrome.runtime.sendMessage({ - command: 'doneLoggingOut', expired: expired - }); - setIcon(); - refreshBadgeAndMenu(); - callback(); - }); - }); - }); - }); + bg_syncService.setLastSync(new Date(0), function () { + bg_userService.getUserIdPromise().then(function (userId) { + return Q.all([ + bg_tokenService.clearToken(), + bg_cryptoService.clearKeys(), + bg_userService.clear(), + bg_settingsService.clear(userId), + bg_loginService.clear(userId), + bg_folderService.clear(userId) + ]).then(function () { + chrome.runtime.sendMessage({ + command: 'doneLoggingOut', expired: expired }); + setIcon(); + refreshBadgeAndMenu(); + callback(); }); }); }); diff --git a/src/models/api/requestModels.js b/src/models/api/requestModels.js index 9f80a12ed5d..a78c41d04f1 100644 --- a/src/models/api/requestModels.js +++ b/src/models/api/requestModels.js @@ -7,12 +7,49 @@ this.favorite = cipher.favorite; switch (type) { - case 1: // login + case 1: // cipherType.login this.login = { - uri: cipher.uri ? cipher.uri.encryptedString : null, - username: cipher.username ? cipher.username.encryptedString : null, - password: cipher.password ? cipher.password.encryptedString : null, - totp: cipher.totp ? cipher.totp.encryptedString : null + uri: cipher.login.uri ? cipher.login.uri.encryptedString : null, + username: cipher.login.username ? cipher.login.username.encryptedString : null, + password: cipher.login.password ? cipher.login.password.encryptedString : null, + totp: cipher.login.totp ? cipher.login.totp.encryptedString : null + }; + break; + case 2: // cipherType.secureNote + this.secureNote = { + type: cipher.secureNote.type + }; + break; + case 3: // cipherType.card + this.card = { + cardholderName: cipher.card.cardholderName ? cipher.card.cardholderName.encryptedString : null, + brand: cipher.card.brand ? cipher.card.brand.encryptedString : null, + number: cipher.card.number ? cipher.card.number.encryptedString : null, + expMonth: cipher.card.expMonth ? cipher.card.expMonth.encryptedString : null, + expYear: cipher.card.expYear ? cipher.card.expYear.encryptedString : null, + code: cipher.card.code ? cipher.card.code.encryptedString : null + }; + break; + case 4: // cipherType.identity + this.identity = { + title: cipher.identity.title ? cipher.identity.title.encryptedString : null, + firstName: cipher.identity.firstName ? cipher.identity.firstName.encryptedString : null, + middleName: cipher.identity.middleName ? cipher.identity.middleName.encryptedString : null, + lastName: cipher.identity.lastName ? cipher.identity.lastName.encryptedString : null, + address1: cipher.identity.address1 ? cipher.identity.address1.encryptedString : null, + address2: cipher.identity.address2 ? cipher.identity.address2.encryptedString : null, + address3: cipher.identity.address3 ? cipher.identity.address3.encryptedString : null, + city: cipher.identity.city ? cipher.identity.city.encryptedString : null, + state: cipher.identity.state ? cipher.identity.state.encryptedString : null, + postalCode: cipher.identity.postalCode ? cipher.identity.postalCode.encryptedString : null, + country: cipher.identity.country ? cipher.identity.country.encryptedString : null, + company: cipher.identity.company ? cipher.identity.company.encryptedString : null, + email: cipher.identity.email ? cipher.identity.email.encryptedString : null, + phone: cipher.identity.phone ? cipher.identity.phone.encryptedString : null, + ssn: cipher.identity.ssn ? cipher.identity.ssn.encryptedString : null, + username: cipher.identity.username ? cipher.identity.username.encryptedString : null, + passportNumber: cipher.identity.passportNumber ? cipher.identity.passportNumber.encryptedString : null, + licenseNumber: cipher.identity.licenseNumber ? cipher.identity.licenseNumber.encryptedString : null }; break; default: diff --git a/src/popup/app/vault/vaultAttachmentsController.js b/src/popup/app/vault/vaultAttachmentsController.js index 5059cf8ec18..cb7732b73b3 100644 --- a/src/popup/app/vault/vaultAttachmentsController.js +++ b/src/popup/app/vault/vaultAttachmentsController.js @@ -1,7 +1,7 @@ angular .module('bit.vault') - .controller('vaultAttachmentsController', function ($scope, $state, $stateParams, loginService, $q, toastr, + .controller('vaultAttachmentsController', function ($scope, $state, $stateParams, loginService, toastr, SweetAlert, utilsService, $analytics, i18nService, cryptoService, tokenService) { $scope.i18n = i18nService; utilsService.initListSectionItemListeners($(document), angular); @@ -10,44 +10,44 @@ angular $scope.canAccessAttachments = $scope.isPremium; $scope.hasUpdatedKey = false; - loginService.get($stateParams.id, function (login) { - $q.when(login.decrypt()).then(function (model) { - $scope.login = model; - $scope.canAccessAttachments = $scope.isPremium || !!$scope.login.organizationId; + loginService.get($stateParams.id).then(function (login) { + return login.decrypt(); + }).then(function (model) { + $scope.login = model; + $scope.canAccessAttachments = $scope.isPremium || !!$scope.login.organizationId; - if (!$scope.canAccessAttachments) { - SweetAlert.swal({ - title: i18nService.premiumRequired, - text: i18nService.premiumRequiredDesc, - showCancelButton: true, - confirmButtonText: i18nService.learnMore, - cancelButtonText: i18nService.cancel - }, function (confirmed) { - if (confirmed) { - chrome.tabs.create({ url: 'https://vault.bitwarden.com/#/?premium=purchase' }); - } - }); - return; - } - else { - cryptoService.getEncKey().then(function (key) { - $scope.hasUpdatedKey = !!key; - if (!$scope.hasUpdatedKey) { - SweetAlert.swal({ - title: i18nService.featureUnavailable, - text: i18nService.updateKey, - showCancelButton: true, - confirmButtonText: i18nService.learnMore, - cancelButtonText: i18nService.cancel - }, function (confirmed) { - if (confirmed) { - chrome.tabs.create({ url: 'https://help.bitwarden.com/article/update-encryption-key/' }); - } - }); - } - }); - } - }); + if (!$scope.canAccessAttachments) { + SweetAlert.swal({ + title: i18nService.premiumRequired, + text: i18nService.premiumRequiredDesc, + showCancelButton: true, + confirmButtonText: i18nService.learnMore, + cancelButtonText: i18nService.cancel + }, function (confirmed) { + if (confirmed) { + chrome.tabs.create({ url: 'https://vault.bitwarden.com/#/?premium=purchase' }); + } + }); + return; + } + else { + cryptoService.getEncKey().then(function (key) { + $scope.hasUpdatedKey = !!key; + if (!$scope.hasUpdatedKey) { + SweetAlert.swal({ + title: i18nService.featureUnavailable, + text: i18nService.updateKey, + showCancelButton: true, + confirmButtonText: i18nService.learnMore, + cancelButtonText: i18nService.cancel + }, function (confirmed) { + if (confirmed) { + chrome.tabs.create({ url: 'https://help.bitwarden.com/article/update-encryption-key/' }); + } + }); + } + }); + } }); $scope.submitPromise = null; @@ -69,8 +69,8 @@ angular return deferred.promise; } - $scope.submitPromise = $q.when(loginService.saveAttachmentWithServer($scope.login, files[0])).then(function (login) { - $q.when(login.decrypt()).then(function (model) { + $scope.submitPromise = loginService.saveAttachmentWithServer($scope.login, files[0]).then(function (login) { + login.decrypt().then(function (model) { $scope.login = model; }); $analytics.eventTrack('Added Attachment'); @@ -100,7 +100,7 @@ angular cancelButtonText: i18nService.no }, function (confirmed) { if (confirmed) { - $q.when(loginService.deleteAttachmentWithServer($stateParams.id, attachment.id)).then(function () { + loginService.deleteAttachmentWithServer($stateParams.id, attachment.id).then(function () { var index = $scope.login.attachments.indexOf(attachment); if (index > -1) { $scope.login.attachments.splice(index, 1); diff --git a/src/popup/app/vault/vaultEditLoginController.js b/src/popup/app/vault/vaultEditLoginController.js index e6f709f05a6..18d143a5482 100644 --- a/src/popup/app/vault/vaultEditLoginController.js +++ b/src/popup/app/vault/vaultEditLoginController.js @@ -2,7 +2,7 @@ angular .module('bit.vault') .controller('vaultEditLoginController', function ($scope, $state, $stateParams, loginService, folderService, - cryptoService, $q, toastr, SweetAlert, utilsService, $analytics, i18nService, constantsService) { + cryptoService, toastr, SweetAlert, utilsService, $analytics, i18nService, constantsService) { $scope.i18n = i18nService; $scope.constants = constantsService; $scope.showAttachments = !utilsService.isEdge(); @@ -21,14 +21,14 @@ angular angular.extend($scope.login, $stateParams.login); } else { - loginService.get(loginId, function (login) { - $q.when(login.decrypt()).then(function (model) { - $scope.login = model; - }); + loginService.get(loginId).then(function (login) { + return login.decrypt(); + }).then(function (model) { + $scope.login = model; }); } - $q.when(folderService.getAllDecrypted()).then(function (folders) { + folderService.getAllDecrypted().then(function (folders) { $scope.folders = folders; }); @@ -41,9 +41,9 @@ angular return; } - $scope.savePromise = $q.when(loginService.encrypt(model)).then(function (loginModel) { + $scope.savePromise = loginService.encrypt(model).then(function (loginModel) { var login = new Login(loginModel, true); - return $q.when(loginService.saveWithServer(login)).then(function (login) { + return loginService.saveWithServer(login).then(function (login) { $analytics.eventTrack('Edited Login'); toastr.success(i18nService.editedLogin); $scope.close(); @@ -60,7 +60,7 @@ angular cancelButtonText: i18nService.no }, function (confirmed) { if (confirmed) { - $q.when(loginService.deleteWithServer(loginId)).then(function () { + loginService.deleteWithServer(loginId).then(function () { $analytics.eventTrack('Deleted Login'); toastr.success(i18nService.deletedLogin); $state.go('tabs.vault', { diff --git a/src/popup/app/vault/vaultViewLoginController.js b/src/popup/app/vault/vaultViewLoginController.js index 4476be14794..21551268615 100644 --- a/src/popup/app/vault/vaultViewLoginController.js +++ b/src/popup/app/vault/vaultViewLoginController.js @@ -1,7 +1,7 @@ angular .module('bit.vault') - .controller('vaultViewLoginController', function ($scope, $state, $stateParams, loginService, toastr, $q, + .controller('vaultViewLoginController', function ($scope, $state, $stateParams, loginService, toastr, $analytics, i18nService, utilsService, totpService, $timeout, tokenService, $window, cryptoService, SweetAlert, constantsService) { $scope.constants = constantsService; @@ -12,45 +12,45 @@ angular $scope.isPremium = tokenService.getPremium(); $scope.login = null; - loginService.get($stateParams.loginId, function (login) { + loginService.get($stateParams.loginId).then(function (login) { if (!login) { return; } - $q.when(login.decrypt()).then(function (model) { - $scope.login = model; + return login.decrypt(); + }).then(function (model) { + $scope.login = model; - if (model.password) { - $scope.login.maskedPassword = $scope.maskValue(model.password); - } + if (model.password) { + $scope.login.maskedPassword = $scope.maskValue(model.password); + } - if (model.uri) { - $scope.login.showLaunch = model.uri.startsWith('http://') || model.uri.startsWith('https://'); - var domain = utilsService.getDomain(model.uri); - if (domain) { - $scope.login.website = domain; - } - else { - $scope.login.website = model.uri; - } + if (model.uri) { + $scope.login.showLaunch = model.uri.startsWith('http://') || model.uri.startsWith('https://'); + var domain = utilsService.getDomain(model.uri); + if (domain) { + $scope.login.website = domain; } else { - $scope.login.showLaunch = false; + $scope.login.website = model.uri; + } + } + else { + $scope.login.showLaunch = false; + } + + if (model.totp && (login.organizationUseTotp || tokenService.getPremium())) { + totpUpdateCode(); + totpTick(); + + if (totpInterval) { + clearInterval(totpInterval); } - if (model.totp && (login.organizationUseTotp || tokenService.getPremium())) { - totpUpdateCode(); + totpInterval = setInterval(function () { totpTick(); - - if (totpInterval) { - clearInterval(totpInterval); - } - - totpInterval = setInterval(function () { - totpTick(); - }, 1000); - } - }); + }, 1000); + } }); $scope.edit = function (login) { diff --git a/src/services/apiService.js b/src/services/apiService.js index 3a204367e38..fdaa109c227 100644 --- a/src/services/apiService.js +++ b/src/services/apiService.js @@ -257,8 +257,10 @@ function initApiService() { // Cipher APIs - ApiService.prototype.postCipher = function (cipherRequest, success, error) { - var self = this; + ApiService.prototype.postCipher = function (cipherRequest) { + var self = this, + deferred = Q.defer(); + handleTokenState(self).then(function (tokenHeader) { $.ajax({ type: 'POST', @@ -268,19 +270,23 @@ function initApiService() { dataType: 'json', headers: tokenHeader, success: function (response) { - success(new CipherResponse(response)); + deferred.resolve(new CipherResponse(response)); }, error: function (jqXHR, textStatus, errorThrown) { - handleError(error, jqXHR, false, self); + handleError(deferred.reject, jqXHR, false, self); } }); }, function (jqXHR) { - handleError(error, jqXHR, true, self); + handleError(deferred.reject, jqXHR, true, self); }); + + return deferred.promise; }; - ApiService.prototype.putCipher = function (id, cipherRequest, success, error) { - var self = this; + ApiService.prototype.putCipher = function (id, cipherRequest) { + var self = this, + deferred = Q.defer(); + handleTokenState(self).then(function (tokenHeader) { $.ajax({ type: 'PUT', @@ -290,19 +296,23 @@ function initApiService() { dataType: 'json', headers: tokenHeader, success: function (response) { - success(new CipherResponse(response)); + deferred.resolve(new CipherResponse(response)); }, error: function (jqXHR, textStatus, errorThrown) { - handleError(error, jqXHR, false, self); + handleError(deferred.reject, jqXHR, false, self); } }); }, function (jqXHR) { - handleError(error, jqXHR, true, self); + handleError(deferred.reject, jqXHR, true, self); }); + + return deferred.promise; }; - ApiService.prototype.deleteCipher = function (id, success, error) { - var self = this; + ApiService.prototype.deleteCipher = function (id) { + var self = this, + deferred = Q.defer(); + handleTokenState(self).then(function (tokenHeader) { $.ajax({ type: 'DELETE', @@ -310,19 +320,23 @@ function initApiService() { dataType: 'text', headers: tokenHeader, success: function (response) { - success(); + deferred.resolve(response); }, error: function (jqXHR, textStatus, errorThrown) { - handleError(error, jqXHR, false, self); + handleError(deferred.reject, jqXHR, false, self); } }); }, function (jqXHR) { - handleError(error, jqXHR, true, self); + handleError(deferred.reject, jqXHR, true, self); }); + + return deferred.promise; }; - ApiService.prototype.postCipherAttachment = function (id, formData, success, error) { - var self = this; + ApiService.prototype.postCipherAttachment = function (id, formData) { + var self = this, + deferred = Q.defer(); + handleTokenState(self).then(function (tokenHeader) { $.ajax({ type: 'POST', @@ -333,19 +347,23 @@ function initApiService() { dataType: 'json', headers: tokenHeader, success: function (response) { - success(new CipherResponse(response)); + deferred.resolve(new CipherResponse(response)); }, error: function (jqXHR, textStatus, errorThrown) { - handleError(error, jqXHR, false, self); + handleError(deferred.reject, jqXHR, false, self); } }); }, function (jqXHR) { - handleError(error, jqXHR, true, self); + handleError(deferred.reject, jqXHR, true, self); }); + + return deferred.promise; }; ApiService.prototype.deleteCipherAttachment = function (id, attachmentId, success, error) { - var self = this; + var self = this, + deferred = Q.defer(); + handleTokenState(self).then(function (tokenHeader) { $.ajax({ type: 'DELETE', @@ -353,15 +371,17 @@ function initApiService() { dataType: 'text', headers: tokenHeader, success: function (response) { - success(); + deferred.resolve(response); }, error: function (jqXHR, textStatus, errorThrown) { - handleError(error, jqXHR, false, self); + handleError(deferred.reject, jqXHR, false, self); } }); }, function (jqXHR) { - handleError(error, jqXHR, true, self); + handleError(deferred.reject, jqXHR, true, self); }); + + return deferred.promise; }; // Sync APIs diff --git a/src/services/autofillService.js b/src/services/autofillService.js index 624c7f897c6..a128678222f 100644 --- a/src/services/autofillService.js +++ b/src/services/autofillService.js @@ -226,7 +226,7 @@ function initAutofill() { didAutofill = true; if (!options.skipLastUsed) { - self.loginService.updateLastUsedDate(options.login.id, function () { }); + self.loginService.updateLastUsedDate(options.login.id); } chrome.tabs.sendMessage(tab.id, { diff --git a/src/services/cryptoService.js b/src/services/cryptoService.js index c6556900538..8e4726555b2 100644 --- a/src/services/cryptoService.js +++ b/src/services/cryptoService.js @@ -336,21 +336,15 @@ function initCryptoService(constantsService) { return deferred.promise; }; - CryptoService.prototype.clearKeys = function (callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } - + CryptoService.prototype.clearKeys = function () { var self = this; - Q.all([ + return Q.all([ self.clearKey(), self.clearKeyHash(), self.clearOrgKeys(), self.clearEncKey(), self.clearPrivateKey() - ]).then(function () { - callback(); - }); + ]); }; CryptoService.prototype.toggleKey = function (callback) { diff --git a/src/services/folderService.js b/src/services/folderService.js index e6c2d3b8f48..e1282b4e5cd 100644 --- a/src/services/folderService.js +++ b/src/services/folderService.js @@ -1,8 +1,9 @@ -function FolderService(cryptoService, userService, apiService, i18nService) { +function FolderService(cryptoService, userService, apiService, i18nService, utilsService) { this.cryptoService = cryptoService; this.userService = userService; this.apiService = apiService; this.i18nService = i18nService; + this.utilsService = utilsService; this.decryptedFolderCache = null; initFolderService(); @@ -171,33 +172,19 @@ function initFolderService() { }); }; - FolderService.prototype.replace = function (folders, callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } - + FolderService.prototype.replace = function (folders) { var self = this; - - self.userService.getUserId(function (userId) { - var obj = {}; - obj['folders_' + userId] = folders; - chrome.storage.local.set(obj, function () { - self.decryptedFolderCache = null; - callback(); - }); + self.userService.getUserIdPromise().then(function (userId) { + return self.utilsService.saveObjToStorage('folders_' + userId, folders); + }).then(function () { + self.decryptedFolderCache = null; }); }; - FolderService.prototype.clear = function (userId, callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } - + FolderService.prototype.clear = function (userId) { var self = this; - - chrome.storage.local.remove('folders_' + userId, function () { + return self.utilsService.removeFromStorage('folders_' + userId).then(function () { self.decryptedFolderCache = null; - callback(); }); }; diff --git a/src/services/loginService.js b/src/services/loginService.js index 44ba67219f3..bd5e86c6604 100644 --- a/src/services/loginService.js +++ b/src/services/loginService.js @@ -1,16 +1,18 @@ -function LoginService(cryptoService, userService, apiService, settingsService) { +function LoginService(cryptoService, userService, apiService, settingsService, utilsService) { this.cryptoService = cryptoService; this.userService = userService; this.apiService = apiService; this.settingsService = settingsService; - this.decryptedLoginCache = null; + this.utilsService = utilsService; + this.decryptedCipherCache = null; + this.localDataKey = 'sitesLocalData'; initLoginService(); } function initLoginService() { LoginService.prototype.clearCache = function () { - this.decryptedLoginCache = null; + this.decryptedCipherCache = null; }; LoginService.prototype.encrypt = function (login) { @@ -93,114 +95,98 @@ function initLoginService() { }); }; - LoginService.prototype.get = function (id, callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } + LoginService.prototype.get = function (id) { + var self = this, + key = null, + localData; - this.userService.getUserId(function (userId) { - var loginsKey = 'sites_' + userId; - var localDataKey = 'sitesLocalData'; + return self.userService.getUserIdPromise().then(function (userId) { + key = 'ciphers_' + userId; + return self.utilsService.getObjFromStorage(self.localDataKey); + }).then(function (data) { + localData = data; + if (!localData) { + localData = {}; + } + return self.utilsService.getObjFromStorage(key); + }).then(function (ciphers) { + if (ciphers && id in ciphers) { + return new Login(ciphers[id], false, localData[id]); + } - chrome.storage.local.get(localDataKey, function (localDataObj) { - var localData = localDataObj[localDataKey]; - if (!localData) { - localData = {}; - } - - chrome.storage.local.get(loginsKey, function (obj) { - var logins = obj[loginsKey]; - if (logins && id in logins) { - callback(new Login(logins[id], false, localData[id])); - return; - } - - callback(null); - }); - }); + return null; }); }; - LoginService.prototype.getAll = function (callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } + LoginService.prototype.getAll = function () { + var self = this, + key = null, + localData = null; - this.userService.getUserId(function (userId) { - var loginsKey = 'sites_' + userId; - var localDataKey = 'sitesLocalData'; - - chrome.storage.local.get(localDataKey, function (localDataObj) { - var localData = localDataObj[localDataKey]; - if (!localData) { - localData = {}; + self.userService.getUserIdPromise().then(function (userId) { + key = 'ciphers_' + userId; + return self.utilsService.getObjFromStorage(self.localDataKey); + }).then(function (data) { + localData = data; + if (!localData) { + localData = {}; + } + return self.utilsService.getObjFromStorage(key); + }).then(function (logins) { + var response = []; + for (var id in logins) { + if (id) { + response.push(new Login(logins[id], false, localData[id])); } + } - chrome.storage.local.get(loginsKey, function (obj) { - var logins = obj[loginsKey]; - var response = []; - for (var id in logins) { - if (!id) { - continue; - } - - response.push(new Login(logins[id], false, localData[id])); - } - - callback(response); - }); - }); + return response; }); }; LoginService.prototype.getAllDecrypted = function () { - var deferred = Q.defer(); - var self = this; + var self = this, + decLogins = []; - self.cryptoService.getKey().then(function (key) { + return self.cryptoService.getKey().then(function (key) { if (!key) { deferred.reject(); return; } - if (self.decryptedLoginCache) { - deferred.resolve(self.decryptedLoginCache); + if (self.decryptedCipherCache) { + deferred.resolve(self.decryptedCipherCache); return; } + return self.getAll(); + }).then(function (logins) { var promises = []; - var decLogins = []; - self.getAll(function (logins) { - for (var i = 0; i < logins.length; i++) { - /* jshint ignore:start */ - promises.push(logins[i].decrypt().then(function (login) { - decLogins.push(login); - })); - /* jshint ignore:end */ - } + for (var i = 0; i < logins.length; i++) { + /* jshint ignore:start */ + promises.push(logins[i].decrypt().then(function (login) { + decLogins.push(login); + })); + /* jshint ignore:end */ + } - Q.all(promises).then(function () { - self.decryptedLoginCache = decLogins; - deferred.resolve(self.decryptedLoginCache); - }); - }); + return Q.all(promises); + }).then(function () { + self.decryptedCipherCache = decLogins; + return self.decryptedCipherCache; }); - - return deferred.promise; }; LoginService.prototype.getAllDecryptedForFolder = function (folderId) { - var self = this; - - return self.getAllDecrypted().then(function (logins) { - var loginsToReturn = []; - for (var i = 0; i < logins.length; i++) { - if (logins[i].folderId === folderId) { - loginsToReturn.push(logins[i]); + return this.getAllDecrypted().then(function (ciphers) { + var ciphersToReturn = []; + for (var i = 0; i < ciphers.length; i++) { + if (ciphers[i].folderId === folderId) { + ciphersToReturn.push(ciphers[i]); } } - return loginsToReturn; + return ciphersToReturn; }); }; @@ -222,265 +208,222 @@ function initLoginService() { return matchingDomains; }); - var loginsPromise = self.getAllDecrypted().then(function (logins) { - return logins; - }); + return Q.all([eqDomainsPromise, self.getAllDecrypted()]).then(function (result) { + var matchingDomains = result[0], + ciphers = result[1], + ciphersToReturn = []; - return Q.all([eqDomainsPromise, loginsPromise]).then(function (result) { - var matchingDomains = result[0]; - var logins = result[1]; - var loginsToReturn = []; - for (var i = 0; i < logins.length; i++) { - if (logins[i].domain && matchingDomains.indexOf(logins[i].domain) >= 0) { - loginsToReturn.push(logins[i]); + for (var i = 0; i < ciphers.length; i++) { + if (ciphers[i].domain && matchingDomains.indexOf(ciphers[i].domain) > -1) { + ciphersToReturn.push(ciphers[i]); } } - return loginsToReturn; + return ciphersToReturn; }); }; LoginService.prototype.getLastUsedForDomain = function (domain) { - var self = this; - var deferred = Q.defer(); - self.getAllDecryptedForDomain(domain).then(function (logins) { - if (!logins.length) { + var self = this, + deferred = Q.defer(); + + self.getAllDecryptedForDomain(domain).then(function (ciphers) { + if (!ciphers.length) { deferred.reject(); return; } - var sortedLogins = logins.sort(self.sortLoginsByLastUsed); - deferred.resolve(sortedLogins[0]); + var sortedCiphers = ciphers.sort(self.sortCiphersByLastUsed); + deferred.resolve(sortedCiphers[0]); }); + return deferred.promise; }; - LoginService.prototype.saveWithServer = function (login) { + LoginService.prototype.saveWithServer = function (cipher) { var deferred = Q.defer(); var self = this, - request = new CipherRequest(login, 1); // 1 = Login + // TODO + request = new CipherRequest(cipher, 1); // 1 = Login - if (!login.id) { - self.apiService.postCipher(request, apiSuccess, function (response) { - handleError(response, deferred); + if (!cipher.id) { + self.apiService.postCipher(request).then(apiSuccess, function (response) { + deferred.reject(response); }); } else { - self.apiService.putCipher(login.id, request, apiSuccess, function (response) { - handleError(response, deferred); + self.apiService.putCipher(cipher.id, request).then(apiSuccess, function (response) { + deferred.reject(response); }); } function apiSuccess(response) { - login.id = response.id; - self.userService.getUserId(function (userId) { + cipher.id = response.id; + self.userService.getUserIdPromise().then(function (userId) { var data = new LoginData(response, userId); - self.upsert(data, function () { - deferred.resolve(login); - }); + return self.upsert(data); + }).then(function () { + deferred.resolve(cipher); }); } return deferred.promise; }; - LoginService.prototype.upsert = function (login, callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } + LoginService.prototype.upsert = function (cipher) { + var self = this, + key = null; - var self = this; + return self.userService.getUserIdPromise().then(function (userId) { + key = 'ciphers_' + userId; + return self.utilsService.getObjFromStorage(key); + }).then(function (ciphers) { + if (!ciphers) { + ciphers = {}; + } - self.userService.getUserId(function (userId) { - var loginsKey = 'sites_' + userId; - - chrome.storage.local.get(loginsKey, function (obj) { - var logins = obj[loginsKey]; - if (!logins) { - logins = {}; + if (cipher.constructor === Array) { + for (var i = 0; i < cipher.length; i++) { + ciphers[cipher[i].id] = cipher[i]; } + } + else { + ciphers[cipher.id] = cipher; + } - if (login.constructor === Array) { - for (var i = 0; i < login.length; i++) { - logins[login[i].id] = login[i]; - } - } - else { - logins[login.id] = login; - } - - obj[loginsKey] = logins; - - chrome.storage.local.set(obj, function () { - self.decryptedLoginCache = null; - callback(); - }); - }); + return self.utilsService.saveObjToStorage(key, ciphers); + }).then(function () { + self.decryptedCipherCache = null; }); }; - LoginService.prototype.updateLastUsedDate = function (id, callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } - + LoginService.prototype.updateLastUsedDate = function (id) { var self = this; - var localDataKey = 'sitesLocalData'; - chrome.storage.local.get(localDataKey, function (obj) { - var loginsLocalData = obj[localDataKey]; - if (!loginsLocalData) { - loginsLocalData = {}; + var ciphersLocalData = null; + return self.utilsService.getObjFromStorage(self.localDataKey).then(function (obj) { + ciphersLocalData = obj; + + if (!ciphersLocalData) { + ciphersLocalData = {}; } - if (loginsLocalData[id]) { - loginsLocalData[id].lastUsedDate = new Date().getTime(); + if (ciphersLocalData[id]) { + ciphersLocalData[id].lastUsedDate = new Date().getTime(); } else { - loginsLocalData[id] = { + ciphersLocalData[id] = { lastUsedDate: new Date().getTime() }; } - obj[localDataKey] = loginsLocalData; + return self.utilsService.saveObjToStorage(key, ciphersLocalData); + }).then(function () { + if (!self.decryptedCipherCache) { + return; + } - chrome.storage.local.set(obj, function () { - if (self.decryptedLoginCache) { - for (var i = 0; i < self.decryptedLoginCache.length; i++) { - if (self.decryptedLoginCache[i].id === id) { - self.decryptedLoginCache[i].localData = loginsLocalData[id]; - break; - } + for (var i = 0; i < self.decryptedCipherCache.length; i++) { + if (self.decryptedCipherCache[i].id === id) { + self.decryptedCipherCache[i].localData = ciphersLocalData[id]; + break; + } + } + }); + }; + + LoginService.prototype.replace = function (ciphers) { + var self = this; + self.userService.getUserIdPromise().then(function (userId) { + return self.utilsService.saveObjToStorage('ciphers_' + userId, ciphers); + }).then(function () { + self.decryptedCipherCache = null; + }); + }; + + LoginService.prototype.clear = function (userId) { + var self = this; + return self.utilsService.removeFromStorage('ciphers_' + userId).then(function () { + self.decryptedCipherCache = null; + }); + }; + + LoginService.prototype.delete = function (id) { + var self = this, + key = null; + + self.userService.getUserIdPromise().then(function () { + key = 'ciphers_' + userId; + return self.utilsService.getObjFromStorage(key); + }).then(function (logins) { + if (!logins) { + return null; + } + + if (id.constructor === Array) { + for (var i = 0; i < id.length; i++) { + if (id[i] in logins) { + delete logins[id[i]]; } } + } + else if (id in logins) { + delete logins[id]; + } + else { + return null; + } - callback(); - }); - }); - }; + return logins; + }).then(function (logins) { + if (!logins) { + return false; + } - LoginService.prototype.replace = function (logins, callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } - - var self = this; - - self.userService.getUserId(function (userId) { - var obj = {}; - obj['sites_' + userId] = logins; - chrome.storage.local.set(obj, function () { - self.decryptedLoginCache = null; - callback(); - }); - }); - }; - - LoginService.prototype.clear = function (userId, callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } - - var self = this; - - chrome.storage.local.remove('sites_' + userId, function () { - self.decryptedLoginCache = null; - callback(); - }); - }; - - LoginService.prototype.delete = function (id, callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } - - var self = this; - - self.userService.getUserId(function (userId) { - var loginsKey = 'sites_' + userId; - - chrome.storage.local.get(loginsKey, function (obj) { - var logins = obj[loginsKey]; - if (!logins) { - callback(); - return; - } - - if (id.constructor === Array) { - for (var i = 0; i < id.length; i++) { - if (id[i] in logins) { - delete logins[id[i]]; - } - } - } - else if (id in logins) { - delete logins[id]; - } - else { - callback(); - return; - } - - obj[loginsKey] = logins; - chrome.storage.local.set(obj, function () { - self.decryptedLoginCache = null; - callback(); - }); - }); + return self.utilsService.saveObjToStorage(key, logins); + }).then(function (clearCache) { + if (clearCache !== false) { + self.decryptedCipherCache = null; + } }); }; LoginService.prototype.deleteWithServer = function (id) { - var deferred = Q.defer(); - var self = this; - self.apiService.deleteCipher(id, function () { - self.delete(id, function () { - deferred.resolve(); - }); - }, function (response) { - handleError(response, deferred); + return self.apiService.deleteCipher(id).then(function () { + return self.delete(id); }); - - return deferred.promise; }; LoginService.prototype.saveNeverDomain = function (domain) { - var deferred = Q.defer(); - var neverKey = 'neverDomains'; - if (!domain) { - deferred.resolve(); - } - else { - chrome.storage.local.get(neverKey, function (obj) { - var domains = obj[neverKey]; - if (!domains) { - domains = {}; - } - - domains[domain] = null; - obj[neverKey] = domains; - - chrome.storage.local.set(obj, function () { - deferred.resolve(); - }); - }); + return Q.fcall(function () { }); } - return deferred.promise; + var key = 'neverDomains'; + return self.utilsService.getObjFromStorage(key).then(function (domains) { + if (!domains) { + domains = {}; + } + + domains[domain] = null; + return self.utilsService.saveObjToStorage(key, domains); + }); }; - LoginService.prototype.saveAttachmentWithServer = function (login, unencryptedFile) { - var deferred = Q.defer(); - var self = this; + LoginService.prototype.saveAttachmentWithServer = function (cipher, unencryptedFile) { + var deferred = Q.defer(), + self = this, + response = null, + data = null, + apiErrored = false; var key, encFileName; var reader = new FileReader(); reader.readAsArrayBuffer(unencryptedFile); reader.onload = function (evt) { - self.cryptoService.getOrgKey(login.organizationId).then(function (theKey) { + self.cryptoService.getOrgKey(cipher.organizationId).then(function (theKey) { key = theKey; return self.cryptoService.encrypt(unencryptedFile.name, key); }).then(function (fileName) { @@ -491,18 +434,24 @@ function initLoginService() { var blob = new Blob([encData], { type: 'application/octet-stream' }); fd.append('data', blob, encFileName.encryptedString); - self.apiService.postCipherAttachment(login.id, fd, - function (response) { - self.userService.getUserId(function (userId) { - var data = new LoginData(response, userId); - self.upsert(data, function () { - deferred.resolve(new Login(data)); - }); - }); - }, - function (response) { - handleErrorMessage(response, deferred); - }); + return self.apiService.postCipherAttachment(cipher.id, fd); + }).then(function (resp) { + response = resp; + return self.userService.getUserIdPromise(); + }, function (resp) { + apiErrored = true; + handleErrorMessage(resp, deferred); + }).then(function (userId) { + if (apiErrored === true) { + return; + } + + data = new LoginData(response, userId); + return self.upsert(data); + }).then(function () { + if (data) { + deferred.resolve(new Login(data)); + } }); }; reader.onerror = function (evt) { @@ -512,48 +461,46 @@ function initLoginService() { return deferred.promise; }; - LoginService.prototype.deleteAttachment = function (id, attachmentId, callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } + LoginService.prototype.deleteAttachment = function (id, attachmentId) { + var self = this, + key = null; - var self = this; - - self.userService.getUserId(function (userId) { - var loginsKey = 'sites_' + userId; - - chrome.storage.local.get(loginsKey, function (obj) { - var logins = obj[loginsKey]; - if (logins && id in logins && logins[id].attachments) { - for (var i = 0; i < logins[id].attachments.length; i++) { - if (logins[id].attachments[i].id === attachmentId) { - logins[id].attachments.splice(i, 1); - } + self.userService.getUserIdPromise().then(function () { + key = 'ciphers_' + userId; + return self.utilsService.getObjFromStorage(key); + }).then(function (logins) { + if (logins && id in logins && logins[id].attachments) { + for (var i = 0; i < logins[id].attachments.length; i++) { + if (logins[id].attachments[i].id === attachmentId) { + logins[id].attachments.splice(i, 1); } + } - obj[loginsKey] = logins; - chrome.storage.local.set(obj, function () { - self.decryptedLoginCache = null; - callback(); - }); - } - else { - callback(); - } - }); + return self.utilsService.saveObjToStorage(key, logins); + } + else { + return false; + } + }).then(function (clearCache) { + if (clearCache !== false) { + self.decryptedCipherCache = null; + } }); }; LoginService.prototype.deleteAttachmentWithServer = function (id, attachmentId) { - var deferred = Q.defer(); + var self = this, + deferred = Q.defer(); - var self = this; - self.apiService.deleteCipherAttachment(id, attachmentId, function () { - self.deleteAttachment(id, attachmentId, function () { - deferred.resolve(); - }); + self.apiService.deleteCipherAttachment(id, attachmentId).then(function () { + return self.deleteAttachment(id, attachmentId); }, function (response) { handleErrorMessage(response, deferred); + return false; + }).then(function (apiSuccess) { + if (apiSuccess !== false) { + deferred.resolve(); + } }); return deferred.promise; diff --git a/src/services/settingsService.js b/src/services/settingsService.js index 6055c87d7a4..a1183879f14 100644 --- a/src/services/settingsService.js +++ b/src/services/settingsService.js @@ -1,5 +1,6 @@ -function SettingsService(userService) { +function SettingsService(userService, utilsService) { this.userService = userService; + this.utilsService = utilsService; this.settingsCache = null; initSettingsService(); @@ -85,18 +86,10 @@ function initSettingsService() { }); } - SettingsService.prototype.clear = function (callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } - + SettingsService.prototype.clear = function (userId) { var self = this; - - this.userService.getUserId(function (userId) { - chrome.storage.local.remove('settings_' + userId, function () { - self.settingsCache = null; - callback(); - }); + return self.utilsService.removeFromStorage('settings_' + userId).then(function () { + self.settingsCache = null; }); }; diff --git a/src/services/syncService.js b/src/services/syncService.js index 045f182c90a..4fb6ce2f248 100644 --- a/src/services/syncService.js +++ b/src/services/syncService.js @@ -131,38 +131,22 @@ function initSyncService() { } function syncFolders(self, userId, response) { - var deferred = Q.defer(); - var folders = {}; - for (var i = 0; i < response.length; i++) { folders[response[i].id] = new FolderData(response[i], userId); } - - self.folderService.replace(folders, function () { - deferred.resolve(); - }); - - return deferred.promise; + return self.folderService.replace(folders); } function syncCiphers(self, userId, response) { - var deferred = Q.defer(); - var logins = {}; - for (var i = 0; i < response.length; i++) { var data = response[i]; if (data.type === 1) { logins[data.id] = new LoginData(data, userId); } } - - self.loginService.replace(logins, function () { - deferred.resolve(); - }); - - return deferred.promise; + return self.loginService.replace(logins); } function syncSettings(self, userId, response) { diff --git a/src/services/tokenService.js b/src/services/tokenService.js index c7291fb00a9..d492a3c24de 100644 --- a/src/services/tokenService.js +++ b/src/services/tokenService.js @@ -1,4 +1,5 @@ -function TokenService() { +function TokenService(utilsService) { + this.utilsService = utilsService; initTokenService(); } @@ -123,16 +124,13 @@ function initTokenService() { }); }; - TokenService.prototype.clearToken = function (callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } - - _token = _decodedToken = _refreshToken = null; - chrome.storage.local.remove('accessToken', function () { - chrome.storage.local.remove('refreshToken', function () { - callback(); - }); + TokenService.prototype.clearToken = function () { + var self = this; + return Q.all([ + self.utilsService.removeFromStorage('accessToken'), + self.utilsService.removeFromStorage('refreshToken') + ]).then(function () { + _token = _decodedToken = _refreshToken = null; }); }; diff --git a/src/services/userService.js b/src/services/userService.js index d7b2046acee..5603cc26e4b 100644 --- a/src/services/userService.js +++ b/src/services/userService.js @@ -1,7 +1,8 @@ -function UserService(tokenService, apiService, cryptoService) { +function UserService(tokenService, apiService, cryptoService, utilsService) { this.tokenService = tokenService; this.apiService = apiService; this.cryptoService = cryptoService; + this.utilsService = utilsService; initUserService(); } @@ -72,6 +73,21 @@ function initUserService() { }); }; + UserService.prototype.getUserIdPromise = function () { + if (_userId) { + return Q.fcall(function () { + return _userId; + }); + } + + return utilsService.getObjFromStorage(userIdKey).then(function (obj) { + if (obj) { + _userId = obj; + } + return _userId; + }); + }; + UserService.prototype.getEmail = function (callback) { if (!callback || typeof callback !== 'function') { throw 'callback function required'; @@ -108,18 +124,14 @@ function initUserService() { return deferred.promise; }; - UserService.prototype.clear = function (callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } - - _userId = _email = _stamp = null; - chrome.storage.local.remove(userIdKey, function () { - chrome.storage.local.remove(userEmailKey, function () { - chrome.storage.local.remove(stampKey, function () { - callback(); - }); - }); + UserService.prototype.clear = function () { + var self = this; + return Q.all([ + self.utilsService.removeFromStorage(userIdKey), + self.utilsService.removeFromStorage(userEmailKey), + self.utilsService.removeFromStorage(stampKey) + ]).then(function () { + _userId = _email = _stamp = null; }); }; diff --git a/src/services/utilsService.js b/src/services/utilsService.js index 097e7d63a4d..8673b3a8f2f 100644 --- a/src/services/utilsService.js +++ b/src/services/utilsService.js @@ -245,6 +245,37 @@ function initUtilsService() { theWindow.location.search.indexOf('uilocation=popup') > -1; }; + UtilsService.prototype.saveObjToStorage = function (key, obj) { + var deferred = Q.defer(); + var storedObj = {}; + storedObj[key] = obj; + chrome.storage.local.set(storedObj, function () { + deferred.resolve(); + }); + return deferred.promise; + }; + + UtilsService.prototype.removeFromStorage = function (key) { + var deferred = Q.defer(); + chrome.storage.local.remove(key, function () { + deferred.resolve(); + }); + return deferred.promise; + }; + + UtilsService.prototype.getObjFromStorage = function (key) { + var deferred = Q.defer(); + chrome.storage.local.get(key, function (obj) { + if (obj && obj[key]) { + deferred.resolve(obj[key]); + } + else { + deferred.resolve(null); + } + }); + return deferred.promise; + }; + function validIpAddress(ipString) { var ipRegex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; return ipRegex.test(ipString);