From 8f0a24b1b903536e3e9dca11cdc033881e492e99 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Sun, 4 Sep 2016 00:34:24 -0400 Subject: [PATCH] more services and async callthroughs for vault list/add --- src/background.js | 1 + src/manifest.json | 1 + src/models/domainModels.js | 37 +++- src/popup/app/services/backgroundService.js | 3 + src/popup/app/vault/vaultAddSiteController.js | 35 +++- src/popup/app/vault/vaultController.js | 36 +++- src/popup/app/vault/views/vault.html | 11 +- src/popup/app/vault/views/vaultAddSite.html | 6 +- src/services/apiService.js | 6 +- src/services/cryptoService.js | 78 ++++--- src/services/folderService.js | 50 +++++ src/services/siteService.js | 193 ++++++++---------- src/services/tokenService.js | 6 +- src/services/userService.js | 6 +- 14 files changed, 303 insertions(+), 166 deletions(-) create mode 100644 src/services/folderService.js diff --git a/src/background.js b/src/background.js index 7988c831..909c33af 100644 --- a/src/background.js +++ b/src/background.js @@ -3,3 +3,4 @@ var tokenService = new TokenService(); var apiService = new ApiService(tokenService); var userService = new UserService(tokenService, apiService); var siteService = new SiteService(cryptoService, userService, apiService); +var folderService = new FolderService(cryptoService, userService, apiService); diff --git a/src/manifest.json b/src/manifest.json index 95e14869..e2ac4fa2 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -24,6 +24,7 @@ "services/tokenService.js", "services/apiService.js", "services/userService.js", + "services/folderService.js", "services/siteService.js", "background.js" ] diff --git a/src/models/domainModels.js b/src/models/domainModels.js index ed09723b..992cde91 100644 --- a/src/models/domainModels.js +++ b/src/models/domainModels.js @@ -1,4 +1,4 @@ -var CipherString = function (encryptedString) { +var CipherString = function (encryptedString) { this.encryptedString = encryptedString; if (encryptedString) { @@ -20,20 +20,35 @@ }; }(); -var Site = function (obj) { - var cryptoService = chrome.extension.getBackgroundPage().cryptoService; - +var Site = function (obj, alreadyEncrypted) { this.id = obj.id; this.folderId = obj.folderId; - this.name = cryptoService.encrypt(obj.name); - this.uri = cryptoService.encrypt(obj.uri); - this.username = cryptoService.encrypt(obj.username); - this.password = cryptoService.encrypt(obj.password); - this.notes = cryptoService.encrypt(obj.notes); + + if (alreadyEncrypted === true) { + this.name = obj.name; + this.uri = obj.uri; + this.username = obj.username; + this.password = obj.password; + this.notes = obj.notes; + } + else { + this.name = new CipherString(obj.name); + this.uri = new CipherString(obj.uri); + this.username = new CipherString(obj.username); + this.password = new CipherString(obj.password); + this.notes = new CipherString(obj.notes); + } + this.favorite = new obj.favorite; }; -var Folder = function (obj) { +var Folder = function (obj, alreadyEncrypted) { this.id = obj.id; - this.name = new CipherString(obj.name); + + if (alreadyEncrypted === true) { + this.name = obj.name; + } + else { + this.name = new CipherString(obj.name); + } }; diff --git a/src/popup/app/services/backgroundService.js b/src/popup/app/services/backgroundService.js index 71ac01ed..456cb3cb 100644 --- a/src/popup/app/services/backgroundService.js +++ b/src/popup/app/services/backgroundService.js @@ -13,6 +13,9 @@ .factory('apiService', function () { return chrome.extension.getBackgroundPage().apiService; }) + .factory('folderService', function () { + return chrome.extension.getBackgroundPage().folderService; + }) .factory('siteService', function () { return chrome.extension.getBackgroundPage().siteService; }); diff --git a/src/popup/app/vault/vaultAddSiteController.js b/src/popup/app/vault/vaultAddSiteController.js index 478dc592..2505f8d5 100644 --- a/src/popup/app/vault/vaultAddSiteController.js +++ b/src/popup/app/vault/vaultAddSiteController.js @@ -1,14 +1,41 @@ angular .module('bit.vault') - .controller('vaultAddSiteController', function ($scope, siteService) { + .controller('vaultAddSiteController', function ($scope, siteService, cryptoService) { $scope.site = { folderId: null }; - $scope.createSite = function (model) { - var site = new Site(model); - siteService.save(model, function () { + $scope.createSite = function (model) { + var newModel = model; + encryptSite(newModel, function (siteModel) { + var site = new Site(siteModel, true); + siteService.save(site, function () { + $scope.close(); + }); }); }; + + $scope.close = function () { + $scope.parentScope.closeAddSite(); + }; + + function encryptSite(siteModel, callback) { + cryptoService.encrypt(siteModel.name, function (nameCipherString) { + siteModel.name = nameCipherString; + cryptoService.encrypt(siteModel.uri, function (uriCipherString) { + siteModel.uri = uriCipherString; + cryptoService.encrypt(siteModel.username, function (usernameCipherString) { + siteModel.username = usernameCipherString; + cryptoService.encrypt(siteModel.password, function (passwordCipherString) { + siteModel.password = passwordCipherString; + cryptoService.encrypt(siteModel.notes, function (notesCipherString) { + siteModel.notes = notesCipherString; + callback(siteModel); + }); + }); + }); + }); + }); + } }); diff --git a/src/popup/app/vault/vaultController.js b/src/popup/app/vault/vaultController.js index d0c8a1e0..583b3aaf 100644 --- a/src/popup/app/vault/vaultController.js +++ b/src/popup/app/vault/vaultController.js @@ -1,7 +1,41 @@ angular .module('bit.vault') - .controller('vaultController', function ($scope, $ionicModal) { + .controller('vaultController', function ($scope, $ionicModal, siteService, folderService) { + $scope.parentScope = $scope; + $scope.sites = []; + $scope.folders = []; + + var decSites = []; + var decFolders = [{ + id: null, + name: '(none)' + }]; + + folderService.getAll(function (folders) { + siteService.getAll(function (sites) { + for (var i = 0; i < folders.length; i++) { + decFolders.push({ + id: folders[i].id, + name: folders[i].name.decrypt() + }); + } + + for (var j = 0; j < sites.length; j++) { + decSites.push({ + id: sites[j].id, + folderId: sites[j].folderId, + favorite: sites[j].favorite, + name: sites[j].name.decrypt(), + username: sites[j].username.decrypt() + }); + } + + $scope.sites = decSites; + $scope.folders = decFolders; + }); + }); + $scope.addSite = function () { $ionicModal.fromTemplateUrl('app/vault/views/vaultAddSite.html', { scope: $scope, diff --git a/src/popup/app/vault/views/vault.html b/src/popup/app/vault/views/vault.html index 425bc919..285039ca 100644 --- a/src/popup/app/vault/views/vault.html +++ b/src/popup/app/vault/views/vault.html @@ -2,9 +2,12 @@ - -

- Some content for your vault. -

+ +
+
+ Site 1 + +
+
diff --git a/src/popup/app/vault/views/vaultAddSite.html b/src/popup/app/vault/views/vaultAddSite.html index 5ffb7efa..cc8328ca 100644 --- a/src/popup/app/vault/views/vaultAddSite.html +++ b/src/popup/app/vault/views/vaultAddSite.html @@ -1,10 +1,10 @@ - + - +

Add Site

- +
Site Information diff --git a/src/services/apiService.js b/src/services/apiService.js index 6301f2ad..c6e6b2f3 100644 --- a/src/services/apiService.js +++ b/src/services/apiService.js @@ -1,9 +1,11 @@ function ApiService(tokenService) { this.baseUrl = 'https://api.bitwarden.com'; this.tokenService = tokenService; + + initApiService(); }; -!function () { +function initApiService() { // Auth APIs ApiService.prototype.postToken = function (tokenRequest, success, error) { @@ -235,4 +237,4 @@ function handleError(errorCallback, jqXHR, textStatus, errorThrown) { errorCallback(new ErrorResponse(jqXHR)); } -}(); +}; diff --git a/src/services/cryptoService.js b/src/services/cryptoService.js index 4696c3c6..4649f17a 100644 --- a/src/services/cryptoService.js +++ b/src/services/cryptoService.js @@ -1,8 +1,8 @@ function CryptoService() { - + initCryptoService(); }; -!function () { +function initCryptoService() { var _key, _b64Key, _aes; @@ -32,9 +32,9 @@ return callback(_key); } - chrome.storage.local.get('key', function (key) { - if (key) { - _key = sjcl.codec.base64.toBits(key); + chrome.storage.local.get('key', function (obj) { + if (obj && obj.key) { + _key = sjcl.codec.base64.toBits(obj.key); } if (b64 && b64 === true) { @@ -80,46 +80,60 @@ return sjcl.codec.base64.fromBits(hashBits); }; - CryptoService.prototype.getAes = function () { - if (!_aes && this.getKey()) { - _aes = new sjcl.cipher.aes(this.getKey()); + CryptoService.prototype.getAes = function (callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; } - return _aes; + this.getKey(false, function (key) { + if (!_aes && key) { + _aes = new sjcl.cipher.aes(key); + } + + callback(_aes); + }); }; - CryptoService.prototype.encrypt = function (plaintextValue, key) { - if (!this.getKey() && !key) { - throw 'Encryption key unavailable.'; + CryptoService.prototype.encrypt = function (plaintextValue, callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; } - if (!key) { - key = this.getKey(); - } + this.getKey(false, function (key) { + if (!key) { + throw 'Encryption key unavailable.'; + } - var response = {}; - var params = { - mode: "cbc", - iv: sjcl.random.randomWords(4, 0) - }; + var response = {}; + var params = { + mode: "cbc", + iv: sjcl.random.randomWords(4, 0) + }; - var ctJson = sjcl.encrypt(key, plaintextValue, params, response); + var ctJson = sjcl.encrypt(key, plaintextValue, params, response); - var ct = ctJson.match(/"ct":"([^"]*)"/)[1]; - var iv = sjcl.codec.base64.fromBits(response.iv); + var ct = ctJson.match(/"ct":"([^"]*)"/)[1]; + var iv = sjcl.codec.base64.fromBits(response.iv); - return new CipherString(iv + "|" + ct); + callback(new CipherString(iv + "|" + ct)); + }); }; - CryptoService.prototype.decrypt = function (cipherString) { - if (!this.getAes()) { - throw 'AES encryption unavailable.'; + CryptoService.prototype.decrypt = function (cipherStrin, callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; } - var ivBits = sjcl.codec.base64.toBits(cipherString.initializationVector); - var ctBits = sjcl.codec.base64.toBits(cipherString.cipherText); + this.getAes(function (aes) { + if (!aes) { + throw 'AES encryption unavailable.'; + } - var decBits = sjcl.mode.cbc.decrypt(this.getAes(), ctBits, ivBits, null); - return sjcl.codec.utf8String.fromBits(decBits); + var ivBits = sjcl.codec.base64.toBits(cipherString.initializationVector); + var ctBits = sjcl.codec.base64.toBits(cipherString.cipherText); + + var decBits = sjcl.mode.cbc.decrypt(aes, ctBits, ivBits, null); + callback(sjcl.codec.utf8String.fromBits(decBits)); + }); }; -}(); +}; diff --git a/src/services/folderService.js b/src/services/folderService.js new file mode 100644 index 00000000..70141d9d --- /dev/null +++ b/src/services/folderService.js @@ -0,0 +1,50 @@ +function FolderService(cryptoService, userService, apiService) { + this.cryptoService = cryptoService; + this.userService = userService; + this.apiService = apiService; + + initFolderService(); +}; + +function initFolderService() { + this.userService.getUserId(function (userId) { + var foldersKey = 'folders_' + userId; + + FolderService.prototype.get = function (id, callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; + } + + chrome.storage.local.get(foldersKey, function (obj) { + var folders = obj[foldersKey]; + if (id in folders) { + callback(new Folder(folders[id])); + return; + } + + callback(null); + }); + }; + + FolderService.prototype.getAll = function (callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; + } + + chrome.storage.local.get(foldersKey, function (obj) { + var folders = obj[foldersKey]; + var response = []; + for (var id in folders) { + var folder = folders[id]; + response.push(new Folder(folder)); + } + + callback(response); + }); + }; + + function handleError() { + // TODO: check for unauth or forbidden and logout + } + }); +}; diff --git a/src/services/siteService.js b/src/services/siteService.js index dbf7fcb8..3cff741f 100644 --- a/src/services/siteService.js +++ b/src/services/siteService.js @@ -2,130 +2,115 @@ this.cryptoService = cryptoService; this.userService = userService; this.apiService = apiService; + + initSiteService(); }; -!function () { - var ciphersKey = 'ciphers_' + this.userService.userId; +function initSiteService() { + this.userService.getUserId(function (userId) { + var sitesKey = 'sites_' + userId; - SiteService.prototype.get = function (id, callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } + SiteService.prototype.get = function (id, callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; + } + + chrome.storage.local.get(sitesKey, function (obj) { + var sites = obj[sitesKey]; + if (id in sites) { + callback(new Site(sites[id])); + return; + } - chrome.storage.local.get(ciphersKey, function (obj) { - if (!obj) { callback(null); + }); + }; + + SiteService.prototype.getAll = function (callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; } - var sites = obj[ciphersKey]; - if (id in sites) { - callback(new Site(sites[id])); - return; + chrome.storage.local.get(sitesKey, function (obj) { + var sites = obj[sitesKey]; + var response = []; + for (var id in sites) { + response.push(new Site(sites[id])); + } + + callback(response); + }); + }; + + SiteService.prototype.save = function (site, callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; } - callback(null); - }); - }; + var newRecord = site.id === null, + self = this; - SiteService.prototype.getAll = function (callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } - - chrome.storage.local.get(ciphersKey, function (obj) { - if (!obj) { - callback([]); + var request = new SiteRequest(site); + if (newRecord) { + self.apiService.postSite(request, apiSuccess, handleError); + } + else { + self.apiService.putSite(site.id, request, apiSuccess, handleError); } - var sites = obj[ciphersKey]; - var response = []; - for (var id in sites) { - response.push(new Site(sites[id])); - } + function apiSuccess(response) { + userService.getUserId(function (userId) { + var data = new SiteData(response, userId); - callback(response); - }); - }; + chrome.storage.local.get(sitesKey, function (obj) { + var sites = obj[sitesKey]; + if (!newRecord && site.id in sites) { + sites[site.id] = data; + } + else { + sites.push(data); + site.id = data.id; + } - SiteService.prototype.save = function (site, callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } - - var newRecord = site.id === null, - self = this; - - var request = new SiteRequest(site); - if (newRecord) { - self.apiService.postSite(request, apiSuccess, handleError); - } - else { - self.apiService.putSite(site.id, request, apiSuccess, handleError); - } - - function apiSuccess(response) { - userService.getUserId(function (userId) { - var data = new SiteData(response, userId); - - chrome.storage.local.get(ciphersKey, function (obj) { - if (!obj) { - obj = {}; - obj[ciphersKey] = []; - } - - var sites = obj[ciphersKey]; - if (!newRecord && site.id in sites) { - sites[site.id] = data; - } - else { - sites.push(data); - site.id = data.id; - } - - obj[ciphersKey] = sites; - chrome.storage.local.set(obj, function () { - callback(site); + obj[sitesKey] = sites; + chrome.storage.local.set(obj, function () { + callback(site); + }); }); }); - }); - } - }; + } + }; - SiteService.prototype.delete = function (id, callback) { - if (!callback || typeof callback !== 'function') { - throw 'callback function required'; - } + SiteService.prototype.delete = function (id, callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; + } - self.apiService.deleteCipher(id, apiSuccess, handleError); + self.apiService.deleteCipher(id, apiSuccess, handleError); - function apiSuccess(response) { - userService.getUserId(function (userId) { - chrome.storage.local.get(ciphersKey, function (obj) { - if (!obj) { - obj = {}; - obj[ciphersKey] = []; - } + function apiSuccess(response) { + userService.getUserId(function (userId) { + chrome.storage.local.get(sitesKey, function (obj) { + var sites = obj[sitesKey]; + if (id in sites) { + var index = sites.indexOf(sites[id]); + sites.splice(index, 1); - var sites = obj[ciphersKey]; - if (id in sites) { - var index = sites.indexOf(sites[id]); - sites.splice(index, 1); - - obj[ciphersKey] = sites; - chrome.storage.local.set(obj, function () { + obj[sitesKey] = sites; + chrome.storage.local.set(obj, function () { + callback(); + }); + } + else { callback(); - }); - } - else { - callback(); - } + } + }); }); - }); + } + }; + + function handleError() { + // TODO: check for unauth or forbidden and logout } - }; - - - function handleError() { - // TODO: check for unauth or forbidden and logout - } -}(); + }); +}; diff --git a/src/services/tokenService.js b/src/services/tokenService.js index 13122504..0d447caa 100644 --- a/src/services/tokenService.js +++ b/src/services/tokenService.js @@ -1,8 +1,8 @@ function TokenService() { - + initTokenService(); }; -!function () { +function initTokenService() { var _token; TokenService.prototype.setToken = function (token, callback) { @@ -101,4 +101,4 @@ } return window.decodeURIComponent(escape(window.atob(output))); //polyfill https://github.com/davidchambers/Base64.js }; -}(); +}; diff --git a/src/services/userService.js b/src/services/userService.js index 9509d684..85c289a2 100644 --- a/src/services/userService.js +++ b/src/services/userService.js @@ -1,9 +1,11 @@ function UserService(tokenService, apiService) { this.tokenService = tokenService; this.apiService = apiService; + + initUserService(); }; -!function () { +function initUserService() { var _userProfile = null; UserService.prototype.getUserId = function (callback) { @@ -90,4 +92,4 @@ callback(profile !== null && profile.twoFactor); }); }; -}(); +};