Async SF API
This commit is contained in:
8
.babelrc
8
.babelrc
@@ -1,3 +1,9 @@
|
||||
{
|
||||
"presets": ["env"]
|
||||
"presets": ["env"],
|
||||
"plugins": [
|
||||
["transform-runtime", {
|
||||
"polyfill": false,
|
||||
"regenerator": true
|
||||
}]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class AccountMenu {
|
||||
$timeout, storageManager, $compile, archiveManager) {
|
||||
'ngInject';
|
||||
|
||||
$scope.formData = {mergeLocal: true, url: syncManager.serverURL, ephemeral: false};
|
||||
$scope.formData = {mergeLocal: true, url: syncManager.serverURL, ephemeral: false, email: "a@bitar.io", user_password: "password"};
|
||||
$scope.user = authManager.user;
|
||||
$scope.server = syncManager.serverURL;
|
||||
|
||||
@@ -262,26 +262,28 @@ class AccountMenu {
|
||||
}.bind(this)
|
||||
|
||||
if(data.auth_params) {
|
||||
SFJS.crypto.computeEncryptionKeysForUser(password, data.auth_params, (keys) => {
|
||||
SFJS.crypto.computeEncryptionKeysForUser(password, data.auth_params).then((keys) => {
|
||||
try {
|
||||
SFItemTransformer.decryptMultipleItems(data.items, keys, false); /* throws = false as we don't want to interrupt all decryption if just one fails */
|
||||
// delete items enc_item_key since the user's actually key will do the encrypting once its passed off
|
||||
data.items.forEach(function(item){
|
||||
item.enc_item_key = null;
|
||||
item.auth_hash = null;
|
||||
});
|
||||
SFJS.itemTransformer.decryptMultipleItems(data.items, keys, false) /* throws = false as we don't want to interrupt all decryption if just one fails */
|
||||
.then(() => {
|
||||
// delete items enc_item_key since the user's actually key will do the encrypting once its passed off
|
||||
data.items.forEach(function(item){
|
||||
item.enc_item_key = null;
|
||||
item.auth_hash = null;
|
||||
});
|
||||
|
||||
var errorCount = 0;
|
||||
// Don't import items that didn't decrypt properly
|
||||
data.items = data.items.filter(function(item){
|
||||
if(item.errorDecrypting) {
|
||||
errorCount++;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
var errorCount = 0;
|
||||
// Don't import items that didn't decrypt properly
|
||||
data.items = data.items.filter(function(item){
|
||||
if(item.errorDecrypting) {
|
||||
errorCount++;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
|
||||
onDataReady(errorCount);
|
||||
})
|
||||
|
||||
onDataReady(errorCount);
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Error decrypting", e);
|
||||
|
||||
@@ -174,7 +174,7 @@ class PasswordWizard {
|
||||
// Ensure value for current password matches what's saved
|
||||
let authParams = authManager.getAuthParams();
|
||||
let password = $scope.formData.currentPassword;
|
||||
SFJS.crypto.computeEncryptionKeysForUser(password, authParams, (keys) => {
|
||||
SFJS.crypto.computeEncryptionKeysForUser(password, authParams).then((keys) => {
|
||||
let success = keys.mk === authManager.keys().mk;
|
||||
if(success) {
|
||||
this.currentServerPw = keys.pw;
|
||||
@@ -202,7 +202,10 @@ class PasswordWizard {
|
||||
|
||||
let currentServerPw = this.currentServerPw;
|
||||
|
||||
SFJS.crypto.generateInitialEncryptionKeysForUser(authManager.user.email, newUserPassword, (newKeys, newAuthParams) => {
|
||||
SFJS.crypto.generateInitialEncryptionKeysForUser(authManager.user.email, newUserPassword).then((results) => {
|
||||
let newKeys = results.newKeys;
|
||||
let newAuthParams = results.newAuthParams;
|
||||
|
||||
// perform a sync beforehand to pull in any last minutes changes before we change the encryption key (and thus cant decrypt new changes)
|
||||
syncManager.sync((response) => {
|
||||
authManager.changePassword(currentServerPw, newKeys, newAuthParams, (response) => {
|
||||
|
||||
@@ -9,7 +9,7 @@ class Item {
|
||||
this.observers = [];
|
||||
|
||||
if(!this.uuid) {
|
||||
this.uuid = SFJS.crypto.generateUUID();
|
||||
this.uuid = SFJS.crypto.generateUUIDSync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,34 +3,35 @@ class ItemParams {
|
||||
constructor(item, keys, version) {
|
||||
this.item = item;
|
||||
this.keys = keys;
|
||||
this.version = version || SFJS.crypto.version();
|
||||
this.version = version || SFJS.version();
|
||||
}
|
||||
|
||||
paramsForExportFile(includeDeleted) {
|
||||
async paramsForExportFile(includeDeleted) {
|
||||
this.additionalFields = ["updated_at"];
|
||||
this.forExportFile = true;
|
||||
if(includeDeleted) {
|
||||
return this.__params();
|
||||
} else {
|
||||
return _.omit(this.__params(), ["deleted"]);
|
||||
var result = await this.__params();
|
||||
return _.omit(result, ["deleted"]);
|
||||
}
|
||||
}
|
||||
|
||||
paramsForExtension() {
|
||||
async paramsForExtension() {
|
||||
return this.paramsForExportFile();
|
||||
}
|
||||
|
||||
paramsForLocalStorage() {
|
||||
async paramsForLocalStorage() {
|
||||
this.additionalFields = ["updated_at", "dirty", "errorDecrypting"];
|
||||
this.forExportFile = true;
|
||||
return this.__params();
|
||||
}
|
||||
|
||||
paramsForSync() {
|
||||
async paramsForSync() {
|
||||
return this.__params();
|
||||
}
|
||||
|
||||
__params() {
|
||||
async __params() {
|
||||
|
||||
console.assert(!this.item.dummy, "Item is dummy, should not have gotten here.", this.item.dummy)
|
||||
|
||||
@@ -39,7 +40,7 @@ class ItemParams {
|
||||
// Items should always be encrypted for export files. Only respect item.doNotEncrypt for remote sync params.
|
||||
var doNotEncrypt = this.item.doNotEncrypt() && !this.forExportFile;
|
||||
if(this.keys && !doNotEncrypt) {
|
||||
var encryptedParams = SFItemTransformer.encryptItem(this.item, this.keys, this.version);
|
||||
var encryptedParams = await SFJS.itemTransformer.encryptItem(this.item, this.keys, this.version);
|
||||
_.merge(params, encryptedParams);
|
||||
|
||||
if(this.version !== "001") {
|
||||
@@ -47,7 +48,7 @@ class ItemParams {
|
||||
}
|
||||
}
|
||||
else {
|
||||
params.content = this.forExportFile ? this.item.createContentJSONFromProperties() : "000" + SFJS.crypto.base64(JSON.stringify(this.item.createContentJSONFromProperties()));
|
||||
params.content = this.forExportFile ? this.item.createContentJSONFromProperties() : "000" + await SFJS.crypto.base64(JSON.stringify(this.item.createContentJSONFromProperties()));
|
||||
if(!this.forExportFile) {
|
||||
params.enc_item_key = null;
|
||||
params.auth_hash = null;
|
||||
|
||||
@@ -60,17 +60,18 @@ class ActionsManager {
|
||||
switch (action.verb) {
|
||||
case "get": {
|
||||
|
||||
this.httpManager.getAbsolute(action.url, {}, function(response){
|
||||
this.httpManager.getAbsolute(action.url, {}, (response) => {
|
||||
action.error = false;
|
||||
var items = response.items || [response.item];
|
||||
SFItemTransformer.decryptMultipleItems(items, this.authManager.keys());
|
||||
items = this.modelManager.mapResponseItemsToLocalModels(items, ModelManager.MappingSourceRemoteActionRetrieved);
|
||||
for(var item of items) {
|
||||
item.setDirty(true);
|
||||
}
|
||||
this.syncManager.sync(null);
|
||||
customCallback({items: items});
|
||||
}.bind(this), function(response){
|
||||
SFJS.itemTransformer.decryptMultipleItems(items, this.authManager.keys()).then(() => {
|
||||
items = this.modelManager.mapResponseItemsToLocalModels(items, ModelManager.MappingSourceRemoteActionRetrieved);
|
||||
for(var item of items) {
|
||||
item.setDirty(true);
|
||||
}
|
||||
this.syncManager.sync(null);
|
||||
customCallback({items: items});
|
||||
})
|
||||
}, (response) => {
|
||||
action.error = true;
|
||||
customCallback(null);
|
||||
})
|
||||
@@ -80,13 +81,13 @@ class ActionsManager {
|
||||
|
||||
case "render": {
|
||||
|
||||
this.httpManager.getAbsolute(action.url, {}, function(response){
|
||||
this.httpManager.getAbsolute(action.url, {}, (response) => {
|
||||
action.error = false;
|
||||
SFItemTransformer.decryptItem(response.item, this.authManager.keys());
|
||||
var item = this.modelManager.createItem(response.item, true /* Dont notify observers */);
|
||||
customCallback({item: item});
|
||||
|
||||
}.bind(this), function(response){
|
||||
SFJS.itemTransformer.decryptItem(response.item, this.authManager.keys()).then(() => {
|
||||
var item = this.modelManager.createItem(response.item, true /* Dont notify observers */);
|
||||
customCallback({item: item});
|
||||
})
|
||||
}, (response) => {
|
||||
action.error = true;
|
||||
customCallback(null);
|
||||
})
|
||||
@@ -102,22 +103,15 @@ class ActionsManager {
|
||||
}
|
||||
|
||||
case "post": {
|
||||
var params = {};
|
||||
this.outgoingParamsForItem(item, extension, decrypted).then((itemParams) => {
|
||||
var params = {
|
||||
items: [itemParams] // Wrap it in an array
|
||||
}
|
||||
|
||||
if(action.all) {
|
||||
var items = this.modelManager.allItemsMatchingTypes(action.content_types);
|
||||
params.items = items.map(function(item){
|
||||
var params = this.outgoingParamsForItem(item, extension, decrypted);
|
||||
return params;
|
||||
}.bind(this))
|
||||
|
||||
} else {
|
||||
params.items = [this.outgoingParamsForItem(item, extension, decrypted)];
|
||||
}
|
||||
|
||||
this.performPost(action, extension, params, function(response){
|
||||
customCallback(response);
|
||||
});
|
||||
this.performPost(action, extension, params, function(response){
|
||||
customCallback(response);
|
||||
});
|
||||
})
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -130,7 +124,7 @@ class ActionsManager {
|
||||
action.lastExecuted = new Date();
|
||||
}
|
||||
|
||||
outgoingParamsForItem(item, extension, decrypted = false) {
|
||||
async outgoingParamsForItem(item, extension, decrypted = false) {
|
||||
var keys = this.authManager.keys();
|
||||
if(decrypted) {
|
||||
keys = null;
|
||||
|
||||
@@ -24,22 +24,23 @@ class ArchiveManager {
|
||||
protocolVersion = this.authManager.protocolVersion();
|
||||
}
|
||||
}
|
||||
var data = this.__itemsData(keys, authParams, protocolVersion);
|
||||
this.__downloadData(data, `SN Archive - ${new Date()}.txt`);
|
||||
this.__itemsData(keys, authParams, protocolVersion).then((data) => {
|
||||
this.__downloadData(data, `SN Archive - ${new Date()}.txt`);
|
||||
|
||||
// download as zipped plain text files
|
||||
if(!keys) {
|
||||
var notes = this.modelManager.allItemsMatchingTypes(["Note"]);
|
||||
this.__downloadZippedNotes(notes);
|
||||
}
|
||||
// download as zipped plain text files
|
||||
if(!keys) {
|
||||
var notes = this.modelManager.allItemsMatchingTypes(["Note"]);
|
||||
this.__downloadZippedNotes(notes);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
Private
|
||||
*/
|
||||
|
||||
__itemsData(keys, authParams, protocolVersion) {
|
||||
let data = this.modelManager.getAllItemsJSONData(keys, authParams, protocolVersion);
|
||||
async __itemsData(keys, authParams, protocolVersion) {
|
||||
let data = await this.modelManager.getAllItemsJSONData(keys, authParams, protocolVersion);
|
||||
let blobData = new Blob([data], {type: 'text/json'});
|
||||
return blobData;
|
||||
}
|
||||
|
||||
@@ -83,37 +83,8 @@ angular.module('app')
|
||||
}
|
||||
}
|
||||
|
||||
this.costMinimumForVersion = function(version) {
|
||||
// all current versions have a min of 3000
|
||||
// future versions will increase this
|
||||
return SFJS.crypto.costMinimumForVersion(version);
|
||||
}
|
||||
|
||||
this.isProtocolVersionSupported = function(version) {
|
||||
return SFJS.crypto.supportedVersions().includes(version);
|
||||
}
|
||||
|
||||
/* Upon sign in to an outdated version, the user will be presented with an alert requiring them to confirm
|
||||
understanding they are signing in with an older version of the protocol, and must upgrade immediately after completing sign in.
|
||||
*/
|
||||
this.isProtocolVersionOutdated = function(version) {
|
||||
// YYYY-MM-DD
|
||||
let expirationDates = {
|
||||
"001" : Date.parse("2018-01-01"),
|
||||
"002" : Date.parse("2019-06-01"),
|
||||
}
|
||||
|
||||
let date = expirationDates[version];
|
||||
if(!date) {
|
||||
// No expiration date, is active version
|
||||
return false;
|
||||
}
|
||||
let expired = new Date() > date;
|
||||
return expired;
|
||||
}
|
||||
|
||||
this.supportsPasswordDerivationCost = function(cost) {
|
||||
return SFJS.crypto.supportsPasswordDerivationCost(cost);
|
||||
return SFJS.supportedVersions().includes(version);
|
||||
}
|
||||
|
||||
this.getAuthParamsForEmail = function(url, email, extraParams, callback) {
|
||||
@@ -130,7 +101,7 @@ angular.module('app')
|
||||
}
|
||||
|
||||
this.login = function(url, email, password, ephemeral, strictSignin, extraParams, callback) {
|
||||
this.getAuthParamsForEmail(url, email, extraParams, function(authParams){
|
||||
this.getAuthParamsForEmail(url, email, extraParams, (authParams) => {
|
||||
|
||||
// SF3 requires a unique identifier in the auth params
|
||||
authParams.identifier = email;
|
||||
@@ -147,7 +118,7 @@ angular.module('app')
|
||||
|
||||
if(!this.isProtocolVersionSupported(authParams.version)) {
|
||||
var message;
|
||||
if(SFJS.crypto.isVersionNewerThanLibraryVersion(authParams.version)) {
|
||||
if(SFJS.isVersionNewerThanLibraryVersion(authParams.version)) {
|
||||
// The user has a new account type, but is signing in to an older client.
|
||||
message = "This version of the application does not support your newer account type. Please upgrade to the latest version of Standard Notes to sign in.";
|
||||
} else {
|
||||
@@ -158,14 +129,14 @@ angular.module('app')
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.isProtocolVersionOutdated(authParams.version)) {
|
||||
if(SFJS.isProtocolVersionOutdated(authParams.version)) {
|
||||
let message = `The encryption version for your account, ${authParams.version}, is outdated and requires upgrade. You may proceed with login, but are advised to follow prompts for Security Updates once inside. Please visit standardnotes.org/help/security for more information.\n\nClick 'OK' to proceed with login.`
|
||||
if(!confirm(message)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(!this.supportsPasswordDerivationCost(authParams.pw_cost)) {
|
||||
if(!SFJS.supportsPasswordDerivationCost(authParams.pw_cost)) {
|
||||
let message = "Your account was created on a platform with higher security capabilities than this browser supports. " +
|
||||
"If we attempted to generate your login keys here, it would take hours. " +
|
||||
"Please use a browser with more up to date security capabilities, like Google Chrome or Firefox, to log in."
|
||||
@@ -173,7 +144,7 @@ angular.module('app')
|
||||
return;
|
||||
}
|
||||
|
||||
var minimum = this.costMinimumForVersion(authParams.version);
|
||||
var minimum = SFJS.costMinimumForVersion(authParams.version);
|
||||
if(authParams.pw_cost < minimum) {
|
||||
let message = "Unable to login due to insecure password parameters. Please visit standardnotes.org/help/security for more information.";
|
||||
callback({error: {message: message}});
|
||||
@@ -182,7 +153,7 @@ angular.module('app')
|
||||
|
||||
if(strictSignin) {
|
||||
// Refuse sign in if authParams.version is anything but the latest version
|
||||
var latestVersion = SFJS.crypto.version();
|
||||
var latestVersion = SFJS.version();
|
||||
if(authParams.version !== latestVersion) {
|
||||
let message = `Strict sign in refused server sign in parameters. The latest security version is ${latestVersion}, but your account is reported to have version ${authParams.version}. If you'd like to proceed with sign in anyway, please disable strict sign in and try again.`;
|
||||
callback({error: {message: message}});
|
||||
@@ -190,24 +161,25 @@ angular.module('app')
|
||||
}
|
||||
}
|
||||
|
||||
SFJS.crypto.computeEncryptionKeysForUser(password, authParams, function(keys){
|
||||
SFJS.crypto.computeEncryptionKeysForUser(password, authParams).then((keys) => {
|
||||
var requestUrl = url + "/auth/sign_in";
|
||||
var params = _.merge({password: keys.pw, email: email}, extraParams);
|
||||
httpManager.postAbsolute(requestUrl, params, function(response){
|
||||
|
||||
httpManager.postAbsolute(requestUrl, params, (response) => {
|
||||
this.setEphemeral(ephemeral);
|
||||
this.handleAuthResponse(response, email, url, authParams, keys);
|
||||
this.checkForSecurityUpdate();
|
||||
$timeout(() => callback(response));
|
||||
}.bind(this), function(response){
|
||||
}, (response) => {
|
||||
console.error("Error logging in", response);
|
||||
if(typeof response !== 'object') {
|
||||
response = {error: {message: "A server error occurred while trying to sign in. Please try again."}};
|
||||
}
|
||||
$timeout(() => callback(response));
|
||||
})
|
||||
});
|
||||
|
||||
}.bind(this));
|
||||
}.bind(this))
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
this.handleAuthResponse = function(response, email, url, authParams, keys) {
|
||||
@@ -238,7 +210,10 @@ angular.module('app')
|
||||
}
|
||||
|
||||
this.register = function(url, email, password, ephemeral, callback) {
|
||||
SFJS.crypto.generateInitialEncryptionKeysForUser(email, password, (keys, authParams) => {
|
||||
SFJS.crypto.generateInitialEncryptionKeysForUser(email, password).then((results) => {
|
||||
let keys = results.keys;
|
||||
let authParams = results.authParams;
|
||||
|
||||
var requestUrl = url + "/auth";
|
||||
var params = _.merge({password: keys.pw, email: email}, authParams);
|
||||
|
||||
@@ -277,12 +252,12 @@ angular.module('app')
|
||||
this.updateAuthParams = function(authParams, callback) {
|
||||
var requestUrl = storageManager.getItem("server") + "/auth/update";
|
||||
var params = authParams;
|
||||
httpManager.postAbsolute(requestUrl, params, function(response) {
|
||||
httpManager.postAbsolute(requestUrl, params, (response) => {
|
||||
storageManager.setItem("auth_params", JSON.stringify(authParams));
|
||||
if(callback) {
|
||||
callback(response);
|
||||
}
|
||||
}.bind(this), function(response){
|
||||
}, function(response){
|
||||
var error = response;
|
||||
console.error("Update error:", response);
|
||||
if(callback) {
|
||||
@@ -297,7 +272,7 @@ angular.module('app')
|
||||
return;
|
||||
}
|
||||
|
||||
let latest = SFJS.crypto.version();
|
||||
let latest = SFJS.version();
|
||||
|
||||
if(this.protocolVersion() !== latest) {
|
||||
// Prompt user to perform security update
|
||||
|
||||
@@ -716,7 +716,7 @@ class ComponentManager {
|
||||
console.log("Web|componentManager|registerComponentWindow", component);
|
||||
}
|
||||
component.window = componentWindow;
|
||||
component.sessionKey = SFJS.crypto.generateUUID();
|
||||
component.sessionKey = SFJS.crypto.generateUUIDSync();
|
||||
this.sendMessageToComponent(component, {
|
||||
action: "component-registered",
|
||||
sessionKey: component.sessionKey,
|
||||
|
||||
@@ -36,7 +36,7 @@ class DesktopManager {
|
||||
Sending a component in its raw state is really slow for the desktop app
|
||||
Keys are not passed into ItemParams, so the result is not encrypted
|
||||
*/
|
||||
convertComponentForTransmission(component) {
|
||||
async convertComponentForTransmission(component) {
|
||||
return new ItemParams(component).paramsForExportFile(true);
|
||||
}
|
||||
|
||||
@@ -44,14 +44,15 @@ class DesktopManager {
|
||||
syncComponentsInstallation(components) {
|
||||
if(!this.isDesktop) return;
|
||||
|
||||
var data = components.map((component) => {
|
||||
Promise.all(components.map((component) => {
|
||||
return this.convertComponentForTransmission(component);
|
||||
})).then((data) => {
|
||||
this.installationSyncHandler(data);
|
||||
})
|
||||
this.installationSyncHandler(data);
|
||||
}
|
||||
|
||||
installComponent(component) {
|
||||
this.installComponentHandler(this.convertComponentForTransmission(component));
|
||||
async installComponent(component) {
|
||||
this.installComponentHandler(await this.convertComponentForTransmission(component));
|
||||
}
|
||||
|
||||
registerUpdateObserver(callback) {
|
||||
@@ -128,7 +129,7 @@ class DesktopManager {
|
||||
}
|
||||
}
|
||||
|
||||
desktop_requestBackupFile() {
|
||||
desktop_requestBackupFile(callback) {
|
||||
var keys, authParams, protocolVersion;
|
||||
if(this.authManager.offline() && this.passcodeManager.hasPasscode()) {
|
||||
keys = this.passcodeManager.keys();
|
||||
@@ -140,13 +141,14 @@ class DesktopManager {
|
||||
protocolVersion = this.authManager.protocolVersion();
|
||||
}
|
||||
|
||||
let data = this.modelManager.getAllItemsJSONData(
|
||||
this.modelManager.getAllItemsJSONData(
|
||||
keys,
|
||||
authParams,
|
||||
protocolVersion,
|
||||
true /* return null on empty */
|
||||
);
|
||||
return data;
|
||||
).then((data) => {
|
||||
callback(data);
|
||||
})
|
||||
}
|
||||
|
||||
desktop_setMajorDataChangeHandler(handler) {
|
||||
|
||||
@@ -59,7 +59,7 @@ class ModelManager {
|
||||
|
||||
var newItem = this.createItem(item);
|
||||
|
||||
newItem.uuid = SFJS.crypto.generateUUID();
|
||||
newItem.uuid = SFJS.crypto.generateUUIDSync();
|
||||
|
||||
// Update uuids of relationships
|
||||
newItem.informReferencesOfUUIDChange(item.uuid, newItem.uuid);
|
||||
@@ -455,24 +455,25 @@ class ModelManager {
|
||||
Archives
|
||||
*/
|
||||
|
||||
getAllItemsJSONData(keys, authParams, protocolVersion, returnNullIfEmpty) {
|
||||
var items = _.map(this.allItems, (item) => {
|
||||
async getAllItemsJSONData(keys, authParams, protocolVersion, returnNullIfEmpty) {
|
||||
return Promise.all(this.allItems.map((item) => {
|
||||
var itemParams = new ItemParams(item, keys, protocolVersion);
|
||||
return itemParams.paramsForExportFile();
|
||||
});
|
||||
})).then((items) => {
|
||||
if(returnNullIfEmpty && items.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if(returnNullIfEmpty && items.length == 0) {
|
||||
return null;
|
||||
}
|
||||
var data = {items: items}
|
||||
|
||||
var data = {items: items}
|
||||
if(keys) {
|
||||
// auth params are only needed when encrypted with a standard file key
|
||||
data["auth_params"] = authParams;
|
||||
}
|
||||
|
||||
if(keys) {
|
||||
// auth params are only needed when encrypted with a standard file key
|
||||
data["auth_params"] = authParams;
|
||||
}
|
||||
return JSON.stringify(data, null, 2 /* pretty print */);
|
||||
})
|
||||
|
||||
return JSON.stringify(data, null, 2 /* pretty print */);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ angular.module('app')
|
||||
|
||||
this.unlock = function(passcode, callback) {
|
||||
var params = this.passcodeAuthParams();
|
||||
SFJS.crypto.computeEncryptionKeysForUser(passcode, params, (keys) => {
|
||||
SFJS.crypto.computeEncryptionKeysForUser(passcode, params).then((keys) => {
|
||||
if(keys.pw !== params.hash) {
|
||||
callback(false);
|
||||
return;
|
||||
@@ -40,16 +40,20 @@ angular.module('app')
|
||||
|
||||
this._keys = keys;
|
||||
this._authParams = params;
|
||||
this.decryptLocalStorage(keys, params);
|
||||
this._locked = false;
|
||||
callback(true);
|
||||
this.decryptLocalStorage(keys, params).then(() => {
|
||||
this._locked = false;
|
||||
callback(true);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
this.setPasscode = (passcode, callback) => {
|
||||
var uuid = SFJS.crypto.generateUUID();
|
||||
var uuid = SFJS.crypto.generateUUIDSync();
|
||||
|
||||
SFJS.crypto.generateInitialEncryptionKeysForUser(uuid, passcode).then((results) => {
|
||||
let keys = results.keys;
|
||||
let authParams = results.authParams;
|
||||
|
||||
SFJS.crypto.generateInitialEncryptionKeysForUser(uuid, passcode, (keys, authParams) => {
|
||||
authParams.hash = keys.pw;
|
||||
this._keys = keys;
|
||||
this._hasPasscode = true;
|
||||
@@ -83,9 +87,9 @@ angular.module('app')
|
||||
storageManager.setItemsMode(authManager.isEphemeralSession() ? StorageManager.Ephemeral : StorageManager.FixedEncrypted, true);
|
||||
}
|
||||
|
||||
this.decryptLocalStorage = function(keys, authParams) {
|
||||
this.decryptLocalStorage = async function(keys, authParams) {
|
||||
storageManager.setKeys(keys, authParams);
|
||||
storageManager.decryptStorage();
|
||||
return storageManager.decryptStorage();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -154,12 +154,14 @@ class StorageManager {
|
||||
|
||||
// Save new encrypted storage in Fixed storage
|
||||
var params = new ItemParams(encryptedStorage, this.encryptedStorageKeys, this.encryptedStorageAuthParams.version);
|
||||
this.setItem("encryptedStorage", JSON.stringify(params.paramsForSync()), StorageManager.Fixed);
|
||||
params.paramsForSync().then((syncParams) => {
|
||||
this.setItem("encryptedStorage", JSON.stringify(syncParams), StorageManager.Fixed);
|
||||
})
|
||||
}
|
||||
|
||||
decryptStorage() {
|
||||
async decryptStorage() {
|
||||
var stored = JSON.parse(this.getItem("encryptedStorage", StorageManager.Fixed));
|
||||
SFItemTransformer.decryptItem(stored, this.encryptedStorageKeys);
|
||||
await SFJS.itemTransformer.decryptItem(stored, this.encryptedStorageKeys);
|
||||
var encryptedStorage = new EncryptedStorage(stored);
|
||||
|
||||
for(var key of Object.keys(encryptedStorage.storage)) {
|
||||
|
||||
@@ -29,24 +29,26 @@ class SyncManager {
|
||||
|
||||
var version = this.authManager.offline() ? this.passcodeManager.protocolVersion() : this.authManager.protocolVersion();
|
||||
var keys = this.authManager.offline() ? this.passcodeManager.keys() : this.authManager.keys();
|
||||
var params = items.map(function(item) {
|
||||
|
||||
Promise.all(items.map(async (item) => {
|
||||
var itemParams = new ItemParams(item, keys, version);
|
||||
itemParams = itemParams.paramsForLocalStorage();
|
||||
itemParams = await itemParams.paramsForLocalStorage();
|
||||
if(offlineOnly) {
|
||||
delete itemParams.dirty;
|
||||
}
|
||||
return itemParams;
|
||||
}.bind(this));
|
||||
|
||||
this.storageManager.saveModels(params, callback);
|
||||
})).then((params) => {
|
||||
this.storageManager.saveModels(params, callback);
|
||||
})
|
||||
}
|
||||
|
||||
loadLocalItems(callback) {
|
||||
var params = this.storageManager.getAllModels(function(items){
|
||||
var items = this.handleItemsResponse(items, null, ModelManager.MappingSourceLocalRetrieved);
|
||||
Item.sortItemsByDate(items);
|
||||
callback(items);
|
||||
}.bind(this))
|
||||
var params = this.storageManager.getAllModels((items) => {
|
||||
this.handleItemsResponse(items, null, ModelManager.MappingSourceLocalRetrieved).then((items) => {
|
||||
Item.sortItemsByDate(items);
|
||||
callback(items);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
syncOffline(items, callback) {
|
||||
@@ -54,7 +56,7 @@ class SyncManager {
|
||||
for(var item of items) {
|
||||
item.updated_at = new Date();
|
||||
}
|
||||
this.writeItemsToLocalStorage(items, true, function(responseItems){
|
||||
this.writeItemsToLocalStorage(items, true, (responseItems) => {
|
||||
// delete anything needing to be deleted
|
||||
for(var item of items) {
|
||||
if(item.deleted) {
|
||||
@@ -70,7 +72,7 @@ class SyncManager {
|
||||
if(callback) {
|
||||
callback({success: true});
|
||||
}
|
||||
}.bind(this))
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -201,7 +203,7 @@ class SyncManager {
|
||||
this.syncLocked = false;
|
||||
}
|
||||
|
||||
sync(callback, options = {}, source) {
|
||||
async sync(callback, options = {}, source) {
|
||||
|
||||
if(this.syncLocked) {
|
||||
console.log("Sync Locked, Returning;");
|
||||
@@ -281,11 +283,14 @@ class SyncManager {
|
||||
|
||||
var params = {};
|
||||
params.limit = 150;
|
||||
params.items = _.map(subItems, function(item){
|
||||
|
||||
await Promise.all(subItems.map((item) => {
|
||||
var itemParams = new ItemParams(item, keys, version);
|
||||
itemParams.additionalFields = options.additionalFields;
|
||||
return itemParams.paramsForSync();
|
||||
}.bind(this));
|
||||
})).then((itemsParams) => {
|
||||
params.items = itemsParams;
|
||||
})
|
||||
|
||||
for(var item of subItems) {
|
||||
// Reset dirty counter to 0, since we're about to sync it.
|
||||
@@ -300,7 +305,7 @@ class SyncManager {
|
||||
this.stopCheckingIfSyncIsTakingTooLong();
|
||||
}.bind(this);
|
||||
|
||||
var onSyncSuccess = function(response) {
|
||||
var onSyncSuccess = async function(response) {
|
||||
// Check to make sure any subItem hasn't been marked as dirty again while a sync was ongoing
|
||||
var itemsToClearAsDirty = [];
|
||||
for(var item of subItems) {
|
||||
@@ -325,8 +330,7 @@ class SyncManager {
|
||||
|
||||
// Map retrieved items to local data
|
||||
// Note that deleted items will not be returned
|
||||
var retrieved
|
||||
= this.handleItemsResponse(response.retrieved_items, null, ModelManager.MappingSourceRemoteRetrieved);
|
||||
var retrieved = await this.handleItemsResponse(response.retrieved_items, null, ModelManager.MappingSourceRemoteRetrieved);
|
||||
|
||||
// Append items to master list of retrieved items for this ongoing sync operation
|
||||
this.allRetreivedItems = this.allRetreivedItems.concat(retrieved);
|
||||
@@ -337,8 +341,7 @@ class SyncManager {
|
||||
var omitFields = ["content", "auth_hash"];
|
||||
|
||||
// Map saved items to local data
|
||||
var saved =
|
||||
this.handleItemsResponse(response.saved_items, omitFields, ModelManager.MappingSourceRemoteSaved);
|
||||
var saved = await this.handleItemsResponse(response.saved_items, omitFields, ModelManager.MappingSourceRemoteSaved);
|
||||
|
||||
// Append items to master list of saved items for this ongoing sync operation
|
||||
this.allSavedItems = this.allSavedItems.concat(saved);
|
||||
@@ -418,9 +421,9 @@ class SyncManager {
|
||||
}
|
||||
}
|
||||
|
||||
handleItemsResponse(responseItems, omitFields, source) {
|
||||
async handleItemsResponse(responseItems, omitFields, source) {
|
||||
var keys = this.authManager.keys() || this.passcodeManager.keys();
|
||||
SFItemTransformer.decryptMultipleItems(responseItems, keys);
|
||||
await SFJS.itemTransformer.decryptMultipleItems(responseItems, keys);
|
||||
var items = this.modelManager.mapResponseItemsToLocalModelsOmittingFields(responseItems, omitFields, source);
|
||||
|
||||
// During the decryption process, items may be marked as "errorDecrypting". If so, we want to be sure
|
||||
@@ -446,7 +449,7 @@ class SyncManager {
|
||||
}
|
||||
}
|
||||
|
||||
handleUnsavedItemsResponse(unsaved) {
|
||||
async handleUnsavedItemsResponse(unsaved) {
|
||||
if(unsaved.length == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -454,7 +457,7 @@ class SyncManager {
|
||||
console.log("Handle unsaved", unsaved);
|
||||
|
||||
var i = 0;
|
||||
var handleNext = () => {
|
||||
var handleNext = async () => {
|
||||
if(i >= unsaved.length) {
|
||||
// Handled all items
|
||||
this.sync(null, {additionalFields: ["created_at", "updated_at"]});
|
||||
@@ -463,7 +466,7 @@ class SyncManager {
|
||||
|
||||
var mapping = unsaved[i];
|
||||
var itemResponse = mapping.item;
|
||||
SFItemTransformer.decryptMultipleItems([itemResponse], this.authManager.keys());
|
||||
await SFJS.itemTransformer.decryptMultipleItems([itemResponse], this.authManager.keys());
|
||||
var item = this.modelManager.findItem(itemResponse.uuid);
|
||||
|
||||
if(!item) {
|
||||
|
||||
9
package-lock.json
generated
9
package-lock.json
generated
@@ -1001,6 +1001,15 @@
|
||||
"regenerator-transform": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz"
|
||||
}
|
||||
},
|
||||
"babel-plugin-transform-runtime": {
|
||||
"version": "6.23.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz",
|
||||
"integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-runtime": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz"
|
||||
}
|
||||
},
|
||||
"babel-plugin-transform-strict-mode": {
|
||||
"version": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz",
|
||||
"integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=",
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"angular": "^1.6.1",
|
||||
"angular-mocks": "^1.6.1",
|
||||
"babel-cli": "^6.18.0",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.1.1",
|
||||
"babel-preset-es2016": "^6.16.0",
|
||||
"bower": "^1.8.0",
|
||||
|
||||
Reference in New Issue
Block a user