1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-12-01 13:13:36 +01:00

more services and async callthroughs for vault list/add

This commit is contained in:
Kyle Spearrin 2016-09-04 00:34:24 -04:00
parent 32f4ab4987
commit 8f0a24b1b9
14 changed files with 303 additions and 166 deletions

View File

@ -3,3 +3,4 @@ var tokenService = new TokenService();
var apiService = new ApiService(tokenService); var apiService = new ApiService(tokenService);
var userService = new UserService(tokenService, apiService); var userService = new UserService(tokenService, apiService);
var siteService = new SiteService(cryptoService, userService, apiService); var siteService = new SiteService(cryptoService, userService, apiService);
var folderService = new FolderService(cryptoService, userService, apiService);

View File

@ -24,6 +24,7 @@
"services/tokenService.js", "services/tokenService.js",
"services/apiService.js", "services/apiService.js",
"services/userService.js", "services/userService.js",
"services/folderService.js",
"services/siteService.js", "services/siteService.js",
"background.js" "background.js"
] ]

View File

@ -1,4 +1,4 @@
var CipherString = function (encryptedString) { var CipherString = function (encryptedString) {
this.encryptedString = encryptedString; this.encryptedString = encryptedString;
if (encryptedString) { if (encryptedString) {
@ -20,20 +20,35 @@
}; };
}(); }();
var Site = function (obj) { var Site = function (obj, alreadyEncrypted) {
var cryptoService = chrome.extension.getBackgroundPage().cryptoService;
this.id = obj.id; this.id = obj.id;
this.folderId = obj.folderId; this.folderId = obj.folderId;
this.name = cryptoService.encrypt(obj.name);
this.uri = cryptoService.encrypt(obj.uri); if (alreadyEncrypted === true) {
this.username = cryptoService.encrypt(obj.username); this.name = obj.name;
this.password = cryptoService.encrypt(obj.password); this.uri = obj.uri;
this.notes = cryptoService.encrypt(obj.notes); 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; this.favorite = new obj.favorite;
}; };
var Folder = function (obj) { var Folder = function (obj, alreadyEncrypted) {
this.id = obj.id; this.id = obj.id;
this.name = new CipherString(obj.name);
if (alreadyEncrypted === true) {
this.name = obj.name;
}
else {
this.name = new CipherString(obj.name);
}
}; };

View File

@ -13,6 +13,9 @@
.factory('apiService', function () { .factory('apiService', function () {
return chrome.extension.getBackgroundPage().apiService; return chrome.extension.getBackgroundPage().apiService;
}) })
.factory('folderService', function () {
return chrome.extension.getBackgroundPage().folderService;
})
.factory('siteService', function () { .factory('siteService', function () {
return chrome.extension.getBackgroundPage().siteService; return chrome.extension.getBackgroundPage().siteService;
}); });

View File

@ -1,14 +1,41 @@
angular angular
.module('bit.vault') .module('bit.vault')
.controller('vaultAddSiteController', function ($scope, siteService) { .controller('vaultAddSiteController', function ($scope, siteService, cryptoService) {
$scope.site = { $scope.site = {
folderId: null 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);
});
});
});
});
});
}
}); });

View File

@ -1,7 +1,41 @@
angular angular
.module('bit.vault') .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 () { $scope.addSite = function () {
$ionicModal.fromTemplateUrl('app/vault/views/vaultAddSite.html', { $ionicModal.fromTemplateUrl('app/vault/views/vaultAddSite.html', {
scope: $scope, scope: $scope,

View File

@ -2,9 +2,12 @@
<ion-nav-buttons side="secondary"> <ion-nav-buttons side="secondary">
<button class="button button-icon icon ion-ios-plus-empty" ng-click="addSite()"></button> <button class="button button-icon icon ion-ios-plus-empty" ng-click="addSite()"></button>
</ion-nav-buttons> </ion-nav-buttons>
<ion-content class="padding"> <ion-content>
<p> <div class="list">
Some content for your vault. <div class="item item-button-right">
</p> Site 1
<button class="button button-clear button-dark"><i class="icon ion-more"></i></button>
</div>
</div>
</ion-content> </ion-content>
</ion-view> </ion-view>

View File

@ -1,10 +1,10 @@
<ion-modal-view> <ion-modal-view ng-controller="vaultAddSiteController">
<ion-header-bar class="bar bar-header bar-positive"> <ion-header-bar class="bar bar-header bar-positive">
<button class="button button-clear button-primary" ng-click="addSiteModal.hide()">Cancel</button> <button class="button button-clear button-primary" ng-click="close()">Cancel</button>
<h1 class="title">Add Site</h1> <h1 class="title">Add Site</h1>
<button class="button button-clear button-primary" ng-click="createSite(site)">Save</button> <button class="button button-clear button-primary" ng-click="createSite(site)">Save</button>
</ion-header-bar> </ion-header-bar>
<ion-content ng-controller="vaultAddSiteController"> <ion-content>
<div class="list"> <div class="list">
<div class="item item-divider"> <div class="item item-divider">
Site Information Site Information

View File

@ -1,9 +1,11 @@
function ApiService(tokenService) { function ApiService(tokenService) {
this.baseUrl = 'https://api.bitwarden.com'; this.baseUrl = 'https://api.bitwarden.com';
this.tokenService = tokenService; this.tokenService = tokenService;
initApiService();
}; };
!function () { function initApiService() {
// Auth APIs // Auth APIs
ApiService.prototype.postToken = function (tokenRequest, success, error) { ApiService.prototype.postToken = function (tokenRequest, success, error) {
@ -235,4 +237,4 @@
function handleError(errorCallback, jqXHR, textStatus, errorThrown) { function handleError(errorCallback, jqXHR, textStatus, errorThrown) {
errorCallback(new ErrorResponse(jqXHR)); errorCallback(new ErrorResponse(jqXHR));
} }
}(); };

View File

@ -1,8 +1,8 @@
function CryptoService() { function CryptoService() {
initCryptoService();
}; };
!function () { function initCryptoService() {
var _key, var _key,
_b64Key, _b64Key,
_aes; _aes;
@ -32,9 +32,9 @@
return callback(_key); return callback(_key);
} }
chrome.storage.local.get('key', function (key) { chrome.storage.local.get('key', function (obj) {
if (key) { if (obj && obj.key) {
_key = sjcl.codec.base64.toBits(key); _key = sjcl.codec.base64.toBits(obj.key);
} }
if (b64 && b64 === true) { if (b64 && b64 === true) {
@ -80,46 +80,60 @@
return sjcl.codec.base64.fromBits(hashBits); return sjcl.codec.base64.fromBits(hashBits);
}; };
CryptoService.prototype.getAes = function () { CryptoService.prototype.getAes = function (callback) {
if (!_aes && this.getKey()) { if (!callback || typeof callback !== 'function') {
_aes = new sjcl.cipher.aes(this.getKey()); 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) { CryptoService.prototype.encrypt = function (plaintextValue, callback) {
if (!this.getKey() && !key) { if (!callback || typeof callback !== 'function') {
throw 'Encryption key unavailable.'; throw 'callback function required';
} }
if (!key) { this.getKey(false, function (key) {
key = this.getKey(); if (!key) {
} throw 'Encryption key unavailable.';
}
var response = {}; var response = {};
var params = { var params = {
mode: "cbc", mode: "cbc",
iv: sjcl.random.randomWords(4, 0) 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 ct = ctJson.match(/"ct":"([^"]*)"/)[1];
var iv = sjcl.codec.base64.fromBits(response.iv); var iv = sjcl.codec.base64.fromBits(response.iv);
return new CipherString(iv + "|" + ct); callback(new CipherString(iv + "|" + ct));
});
}; };
CryptoService.prototype.decrypt = function (cipherString) { CryptoService.prototype.decrypt = function (cipherStrin, callback) {
if (!this.getAes()) { if (!callback || typeof callback !== 'function') {
throw 'AES encryption unavailable.'; throw 'callback function required';
} }
var ivBits = sjcl.codec.base64.toBits(cipherString.initializationVector); this.getAes(function (aes) {
var ctBits = sjcl.codec.base64.toBits(cipherString.cipherText); if (!aes) {
throw 'AES encryption unavailable.';
}
var decBits = sjcl.mode.cbc.decrypt(this.getAes(), ctBits, ivBits, null); var ivBits = sjcl.codec.base64.toBits(cipherString.initializationVector);
return sjcl.codec.utf8String.fromBits(decBits); var ctBits = sjcl.codec.base64.toBits(cipherString.cipherText);
var decBits = sjcl.mode.cbc.decrypt(aes, ctBits, ivBits, null);
callback(sjcl.codec.utf8String.fromBits(decBits));
});
}; };
}(); };

View File

@ -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
}
});
};

View File

@ -2,130 +2,115 @@
this.cryptoService = cryptoService; this.cryptoService = cryptoService;
this.userService = userService; this.userService = userService;
this.apiService = apiService; this.apiService = apiService;
initSiteService();
}; };
!function () { function initSiteService() {
var ciphersKey = 'ciphers_' + this.userService.userId; this.userService.getUserId(function (userId) {
var sitesKey = 'sites_' + userId;
SiteService.prototype.get = function (id, callback) { SiteService.prototype.get = function (id, callback) {
if (!callback || typeof callback !== 'function') { if (!callback || typeof callback !== 'function') {
throw 'callback function required'; 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); callback(null);
});
};
SiteService.prototype.getAll = function (callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
} }
var sites = obj[ciphersKey]; chrome.storage.local.get(sitesKey, function (obj) {
if (id in sites) { var sites = obj[sitesKey];
callback(new Site(sites[id])); var response = [];
return; 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) { var request = new SiteRequest(site);
if (!callback || typeof callback !== 'function') { if (newRecord) {
throw 'callback function required'; self.apiService.postSite(request, apiSuccess, handleError);
} }
else {
chrome.storage.local.get(ciphersKey, function (obj) { self.apiService.putSite(site.id, request, apiSuccess, handleError);
if (!obj) {
callback([]);
} }
var sites = obj[ciphersKey]; function apiSuccess(response) {
var response = []; userService.getUserId(function (userId) {
for (var id in sites) { var data = new SiteData(response, userId);
response.push(new Site(sites[id]));
}
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) { obj[sitesKey] = sites;
if (!callback || typeof callback !== 'function') { chrome.storage.local.set(obj, function () {
throw 'callback function required'; callback(site);
} });
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);
}); });
}); });
}); }
} };
};
SiteService.prototype.delete = function (id, callback) { SiteService.prototype.delete = function (id, callback) {
if (!callback || typeof callback !== 'function') { if (!callback || typeof callback !== 'function') {
throw 'callback function required'; throw 'callback function required';
} }
self.apiService.deleteCipher(id, apiSuccess, handleError); self.apiService.deleteCipher(id, apiSuccess, handleError);
function apiSuccess(response) { function apiSuccess(response) {
userService.getUserId(function (userId) { userService.getUserId(function (userId) {
chrome.storage.local.get(ciphersKey, function (obj) { chrome.storage.local.get(sitesKey, function (obj) {
if (!obj) { var sites = obj[sitesKey];
obj = {}; if (id in sites) {
obj[ciphersKey] = []; var index = sites.indexOf(sites[id]);
} sites.splice(index, 1);
var sites = obj[ciphersKey]; obj[sitesKey] = sites;
if (id in sites) { chrome.storage.local.set(obj, function () {
var index = sites.indexOf(sites[id]); callback();
sites.splice(index, 1); });
}
obj[ciphersKey] = sites; else {
chrome.storage.local.set(obj, function () {
callback(); callback();
}); }
} });
else {
callback();
}
}); });
}); }
};
function handleError() {
// TODO: check for unauth or forbidden and logout
} }
}; });
};
function handleError() {
// TODO: check for unauth or forbidden and logout
}
}();

View File

@ -1,8 +1,8 @@
function TokenService() { function TokenService() {
initTokenService();
}; };
!function () { function initTokenService() {
var _token; var _token;
TokenService.prototype.setToken = function (token, callback) { TokenService.prototype.setToken = function (token, callback) {
@ -101,4 +101,4 @@
} }
return window.decodeURIComponent(escape(window.atob(output))); //polyfill https://github.com/davidchambers/Base64.js return window.decodeURIComponent(escape(window.atob(output))); //polyfill https://github.com/davidchambers/Base64.js
}; };
}(); };

View File

@ -1,9 +1,11 @@
function UserService(tokenService, apiService) { function UserService(tokenService, apiService) {
this.tokenService = tokenService; this.tokenService = tokenService;
this.apiService = apiService; this.apiService = apiService;
initUserService();
}; };
!function () { function initUserService() {
var _userProfile = null; var _userProfile = null;
UserService.prototype.getUserId = function (callback) { UserService.prototype.getUserId = function (callback) {
@ -90,4 +92,4 @@
callback(profile !== null && profile.twoFactor); callback(profile !== null && profile.twoFactor);
}); });
}; };
}(); };