1
0
mirror of https://github.com/bitwarden/desktop.git synced 2024-11-14 10:16:02 +01:00

setting up more models and services

This commit is contained in:
Kyle Spearrin 2016-09-03 12:07:30 -04:00
parent c39aab4ee7
commit c3053ea3a7
7 changed files with 323 additions and 95 deletions

View File

@ -1,15 +1,15 @@
var SiteRequest = function () { var SiteRequest = function (site) {
this.folderId = null; this.folderId = site.folderId;
this.name = null; this.name = site.name ? site.name.encryptedString : null;
this.uri = null; this.uri = site.uri ? site.uri.encryptedString : null;
this.username = null; this.username = site.username ? site.username.encryptedString : null;
this.password = null; this.password = site.password ? site.password.encryptedString : null;
this.notes = null; this.notes = site.notes ? site.notes.encryptedString : null;
this.favorite = false; this.favorite = site.favorite;
}; };
var FolderRequest = function () { var FolderRequest = function (folder) {
this.name = null; this.name = folder.name ? folder.name.encryptedString : null;
}; };
var TokenRequest = function () { var TokenRequest = function () {

41
src/models/dataModels.js Normal file
View File

@ -0,0 +1,41 @@
var FolderData = function (response, userId) {
var data = null;
if (response instanceof FolderResponse) {
data = response;
}
else if (response instanceof CipherResponse) {
data = response.Data;
}
else {
throw 'unsupported instance';
}
this.id = response.Id;
this.userId = userId;
this.name = data.Name;
this.revisionDate = response.RevisionDate;
};
var SiteData = function (response, userId) {
var data = null;
if (response instanceof SiteResponse) {
data = response;
}
else if (response instanceof CipherResponse) {
data = response.Data;
}
else {
throw 'unsupported instance';
}
this.id = response.Id;
this.folderId = response.FolderId;
this.userId = userId;
this.name = data.Name;
this.uri = data.Uri;
this.username = data.Username;
this.password = data.Password;
this.notes = data.Notes;
this.favorite = response.Favorite;
this.revisionDate = response.RevisionDate;
};

View File

@ -0,0 +1,37 @@
var CipherString = function (encryptedString) {
this.encryptedString = encryptedString;
if (encryptedString) {
this.initializationVector = this.encryptedString.split('|')[0];
this.cipherText = this.encryptedString.split('|')[1];
}
};
!function () {
var _decryptedValue = null;
CipherString.prototype.decrypt = function (callback) {
if (!_decryptedValue) {
var cryptoService = chrome.extension.getBackgroundPage().cryptoService;
_decryptedValue = cryptoService.Decrypt(this);
}
return _decryptedValue;
};
}();
var Site = function (obj) {
this.id = obj.id;
this.folderId = obj.folderId;
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) {
this.id = obj.id;
this.name = new CipherString(obj.name);
};

View File

@ -5,13 +5,13 @@
!function () { !function () {
// Account APIs // Account APIs
ApiService.prototype.getProfile = function (success, error) { ApiService.prototype.getProfile = function (success, error) {
var self = this; var self = this;
this.tokenService.getToken(function (token) { this.tokenService.getToken(function (token) {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: self.baseUrl + '/accounts/profile', url: self.baseUrl + '/accounts/profile?access_token=' + token,
data: 'access_token=' + token,
dataType: 'json', dataType: 'json',
success: function (response) { success: function (response) {
success(new ProfileResponse(response)) success(new ProfileResponse(response))
@ -25,16 +25,122 @@
// Site APIs // Site APIs
ApiService.prototype.getSite = function (id, success, error) {
var self = this;
this.tokenService.getToken(function (token) {
$.ajax({
type: 'GET',
url: self.baseUrl + '/sites/' + id + '?access_token=' + token,
dataType: 'json',
success: function (response) {
success(new SiteResponse(response))
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
}
});
});
};
ApiService.prototype.postSite = function (siteRequest, success, error) {
var self = this;
this.tokenService.getToken(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/sites?access_token=' + token,
data: siteRequest,
dataType: 'json',
success: function (response) {
success(new SiteResponse(response))
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
}
});
});
};
ApiService.prototype.putSite = function (id, siteRequest, success, error) {
var self = this;
this.tokenService.getToken(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/sites/' + id + '?access_token=' + token,
data: siteRequest,
dataType: 'json',
success: function (response) {
success(new SiteResponse(response))
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
}
});
});
};
// Folder APIs // Folder APIs
ApiService.prototype.getFolder = function (id, success, error) {
var self = this;
this.tokenService.getToken(function (token) {
$.ajax({
type: 'GET',
url: self.baseUrl + '/folders/' + id + '?access_token=' + token,
dataType: 'json',
success: function (response) {
success(new FolderResponse(response))
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
}
});
});
};
ApiService.prototype.postFolder = function (folderRequest, success, error) {
var self = this;
this.tokenService.getToken(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/folders?access_token=' + token,
data: folderRequest,
dataType: 'json',
success: function (response) {
success(new FolderResponse(response))
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
}
});
});
};
ApiService.prototype.putFolder = function (id, folderRequest, success, error) {
var self = this;
this.tokenService.getToken(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/folders/' + id + '?access_token=' + token,
data: folderRequest,
dataType: 'json',
success: function (response) {
success(new FolderResponse(response))
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
}
});
});
};
// Cipher APIs // Cipher APIs
ApiService.prototype.getCipher = function (id, success, error) { ApiService.prototype.getCipher = function (id, success, error) {
var self = this; var self = this;
this.tokenService.getToken(function (token) { this.tokenService.getToken(function (token) {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: self.baseUrl + '/ciphers/' + id, url: self.baseUrl + '/ciphers/' + id + '?access_token=' + token,
data: 'access_token=' + token,
dataType: 'json', dataType: 'json',
success: function (response) { success: function (response) {
success(new CipherResponse(response)) success(new CipherResponse(response))
@ -51,8 +157,7 @@
this.tokenService.getToken(function (token) { this.tokenService.getToken(function (token) {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: self.baseUrl + '/ciphers', url: self.baseUrl + '/ciphers?access_token=' + token,
data: 'access_token=' + token,
dataType: 'json', dataType: 'json',
success: function (response) { success: function (response) {
var data = []; var data = [];
@ -69,6 +174,23 @@
}); });
}; };
ApiService.prototype.deleteCipher = function (id, success, error) {
var self = this;
this.tokenService.getToken(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/ciphers/' + id + '/delete?access_token=' + token,
dataType: 'json',
success: success,
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, textStatus, errorThrown);
}
});
});
};
// Helpers
function handleError(errorCallback, jqXHR, textStatus, errorThrown) { function handleError(errorCallback, jqXHR, textStatus, errorThrown) {
errorCallback(new ErrorResponse(jqXHR)); errorCallback(new ErrorResponse(jqXHR));
} }

View File

@ -108,21 +108,16 @@
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 iv + "|" + ct; return new CipherString(iv + "|" + ct);
}; };
CryptoService.prototype.decrypt = function (encValue) { CryptoService.prototype.decrypt = function (cipherString) {
if (!this.getAes()) { if (!this.getAes()) {
throw 'AES encryption unavailable.'; throw 'AES encryption unavailable.';
} }
var encPieces = encValue.split('|'); var ivBits = sjcl.codec.base64.toBits(cipherString.initializationVector);
if (encPieces.length !== 2) { var ctBits = sjcl.codec.base64.toBits(cipherString.cipherText);
return '';
}
var ivBits = sjcl.codec.base64.toBits(encPieces[0]);
var ctBits = sjcl.codec.base64.toBits(encPieces[1]);
var decBits = sjcl.mode.cbc.decrypt(this.getAes(), ctBits, ivBits, null); var decBits = sjcl.mode.cbc.decrypt(this.getAes(), ctBits, ivBits, null);
return sjcl.codec.utf8String.fromBits(decBits); return sjcl.codec.utf8String.fromBits(decBits);

View File

@ -1,104 +1,131 @@
function TokenService() { function SiteService(cryptoService, userService, apiService) {
this.cryptoService = cryptoService;
this.userService = userService;
this.apiService = apiService;
}; };
!function () { !function () {
var _token; var ciphersKey = 'ciphers_' + this.userService.userId;
TokenService.prototype.setToken = function (token, 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';
} }
_token = token; chrome.storage.local.get(ciphersKey, function (obj) {
chrome.storage.local.set({ if (!obj) {
'authBearer': token callback(null);
}, function () {
callback();
});
};
TokenService.prototype.getToken = function (callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
if (_token) {
return callback(_token);
}
chrome.storage.local.get('authBearer', function (obj) {
if (obj && obj.authBearer) {
_token = obj.authBearer;
} }
return callback(_token); var sites = obj[ciphersKey];
if (id in sites) {
callback(new Site(sites[id]));
return;
}
callback(null);
}); });
}; };
TokenService.prototype.clearToken = function (callback) { SiteService.prototype.getAll = function (callback) {
if (!callback || typeof callback !== 'function') { if (!callback || typeof callback !== 'function') {
throw 'callback function required'; throw 'callback function required';
} }
_token = null; chrome.storage.local.get(ciphersKey, function (obj) {
chrome.storage.local.remove('authBearer', function () { if (!obj) {
callback(); callback([]);
}
var sites = obj[ciphersKey];
var response = [];
for (var id in sites) {
response.push(new Site(sites[id]));
}
callback(response);
}); });
}; };
// jwthelper methods SiteService.prototype.save = function (site, callback) {
// ref https://github.com/auth0/angular-jwt/blob/master/src/angularJwt/services/jwt.js if (!callback || typeof callback !== 'function') {
throw 'callback function required';
TokenService.prototype.decodeToken = function (token) {
var parts = token.split('.');
if (parts.length !== 3) {
throw new Error('JWT must have 3 parts');
} }
var decoded = urlBase64Decode(parts[1]); var newRecord = site.id === null,
if (!decoded) { self = this;
throw new Error('Cannot decode the token');
var request = new SiteRequest(site);
if (newRecord) {
self.apiService.postSite(request, apiSuccess, handleError);
}
else {
self.apiService.putSite(site.id, request, apiSuccess, handleError);
} }
return JSON.parse(decoded); 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);
});
});
});
}
}; };
TokenService.prototype.getTokenExpirationDate = function (token) { SiteService.prototype.delete = function (id, callback) {
var decoded = this.decodeToken(token); if (!callback || typeof callback !== 'function') {
throw 'callback function required';
if (typeof decoded.exp === "undefined") {
return null;
} }
var d = new Date(0); // The 0 here is the key, which sets the date to the epoch self.apiService.deleteCipher(id, apiSuccess, handleError);
d.setUTCSeconds(decoded.exp);
return d; function apiSuccess(response) {
}; userService.getUserId(function (userId) {
chrome.storage.local.get(ciphersKey, function (obj) {
if (!obj) {
obj = {};
obj[ciphersKey] = [];
}
TokenService.prototype.isTokenExpired = function (token, offsetSeconds) { var sites = obj[ciphersKey];
var d = this.getTokenExpirationDate(token); if (id in sites) {
offsetSeconds = offsetSeconds || 0; var index = sites.indexOf(sites[id]);
if (d === null) { sites.splice(index, 1);
return false;
obj[ciphersKey] = sites;
chrome.storage.local.set(obj, function () {
callback();
});
}
else {
callback();
}
});
});
} }
// Token expired?
return !(d.valueOf() > (new Date().valueOf() + (offsetSeconds * 1000)));
}; };
function urlBase64Decode(str) {
var output = str.replace(/-/g, '+').replace(/_/g, '/'); function handleError() {
switch (output.length % 4) { // TODO: check for unauth or forbidden and logout
case 0: { break; } }
case 2: { output += '=='; break; }
case 3: { output += '='; break; }
default: {
throw 'Illegal base64url string!';
}
}
return window.decodeURIComponent(escape(window.atob(output))); //polyfill https://github.com/davidchambers/Base64.js
};
}(); }();

View File

@ -6,6 +6,12 @@
!function () { !function () {
var _userProfile = null; var _userProfile = null;
UserService.prototype.getUserId = function (callback) {
this.getUserProfile(function (profile) {
callback(profile.id);
});
};
UserService.prototype.getUserProfile = function (callback) { UserService.prototype.getUserProfile = function (callback) {
if (!callback || typeof callback !== 'function') { if (!callback || typeof callback !== 'function') {
throw 'callback function required'; throw 'callback function required';