more services and async callthroughs for vault list/add
This commit is contained in:
parent
32f4ab4987
commit
8f0a24b1b9
|
@ -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);
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
"services/tokenService.js",
|
||||
"services/apiService.js",
|
||||
"services/userService.js",
|
||||
"services/folderService.js",
|
||||
"services/siteService.js",
|
||||
"background.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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -2,9 +2,12 @@
|
|||
<ion-nav-buttons side="secondary">
|
||||
<button class="button button-icon icon ion-ios-plus-empty" ng-click="addSite()"></button>
|
||||
</ion-nav-buttons>
|
||||
<ion-content class="padding">
|
||||
<p>
|
||||
Some content for your vault.
|
||||
</p>
|
||||
<ion-content>
|
||||
<div class="list">
|
||||
<div class="item item-button-right">
|
||||
Site 1
|
||||
<button class="button button-clear button-dark"><i class="icon ion-more"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-view>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<ion-modal-view>
|
||||
<ion-modal-view ng-controller="vaultAddSiteController">
|
||||
<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>
|
||||
<button class="button button-clear button-primary" ng-click="createSite(site)">Save</button>
|
||||
</ion-header-bar>
|
||||
<ion-content ng-controller="vaultAddSiteController">
|
||||
<ion-content>
|
||||
<div class="list">
|
||||
<div class="item item-divider">
|
||||
Site Information
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}();
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
});
|
||||
};
|
||||
}();
|
||||
};
|
||||
|
|
|
@ -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
|
||||
}
|
||||
});
|
||||
};
|
|
@ -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
|
||||
}
|
||||
}();
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}();
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
};
|
||||
}();
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue