start of sync refactor with tests
This commit is contained in:
Родитель
2c980a76f1
Коммит
2c63bd2286
|
@ -1,4 +1,6 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
|
<script src="https://cdn.firebase.com/v0/firebase.js"></script>
|
||||||
|
<script src='https://cdn.firebase.com/v0/firebase-auth-client.js'></script>
|
||||||
<script src="lib/jquery.js"></script>
|
<script src="lib/jquery.js"></script>
|
||||||
<script src="lib/jsuri.js"></script>
|
<script src="lib/jsuri.js"></script>
|
||||||
<script src="lib/tldjs.js"></script>
|
<script src="lib/tldjs.js"></script>
|
||||||
|
@ -22,6 +24,8 @@
|
||||||
<script src="captured_credential_storage.js"></script>
|
<script src="captured_credential_storage.js"></script>
|
||||||
<script src="site_configs.js"></script>
|
<script src="site_configs.js"></script>
|
||||||
<script src="gombot_sync.js"></script>
|
<script src="gombot_sync.js"></script>
|
||||||
|
<script src="sync_adapter.js"></script>
|
||||||
|
<script src="firebase_sync.js"></script>
|
||||||
<script src="models/login_credential.js"></script>
|
<script src="models/login_credential.js"></script>
|
||||||
<script src="collections/login_credential_collection.js"></script>
|
<script src="collections/login_credential_collection.js"></script>
|
||||||
<script src="models/user.js"></script>
|
<script src="models/user.js"></script>
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
var FirebaseSync = function() {
|
||||||
|
|
||||||
|
var dataRef = new Firebase('https://gombot.firebaseIO.com');
|
||||||
|
|
||||||
|
var authClient = new FirebaseAuthClient(dataRef, authClientCallback);
|
||||||
|
|
||||||
|
var usersRef = dataRef.child('users');
|
||||||
|
|
||||||
|
var currentUser;
|
||||||
|
|
||||||
|
chrome.webRequest.onBeforeSendHeaders.addListener(
|
||||||
|
function(details) {
|
||||||
|
// Remove Origin header if it exists
|
||||||
|
for (var i = 0; i < details.requestHeaders.length; ++i) {
|
||||||
|
if (details.requestHeaders[i].name === 'Origin') {
|
||||||
|
details.requestHeaders.splice(i, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remove Referer header if it exists
|
||||||
|
for (var i = 0; i < details.requestHeaders.length; ++i) {
|
||||||
|
if (details.requestHeaders[i].name === 'Referer') {
|
||||||
|
details.requestHeaders.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add a Referer header for gombot.org
|
||||||
|
details.requestHeaders.push({ name: 'Referer', value: 'https://gombot.org/'});
|
||||||
|
return {requestHeaders: details.requestHeaders};
|
||||||
|
},
|
||||||
|
{ urls: ["https://auth.firebase.com/*"] },
|
||||||
|
["blocking", "requestHeaders"]
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
function authClientCallback(error, user) {
|
||||||
|
if (error) {
|
||||||
|
// an error occurred while attempting login
|
||||||
|
console.log(error);
|
||||||
|
} else if (user) {
|
||||||
|
currentUser = user;
|
||||||
|
usersRef.child(user.id).on('value', getUserData);
|
||||||
|
// user authenticated with Firebase
|
||||||
|
console.log('User ID: ' + user.id + ', Provider: ' + user.provider);
|
||||||
|
} else {
|
||||||
|
if (currentUser) usersRef.child(currentUser.id).off('value', getUserData);
|
||||||
|
currentUser = null;
|
||||||
|
// user is logged out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function create(email, password, options) {
|
||||||
|
authClient.createUser(email, password, function(error, user) {
|
||||||
|
if (!error) {
|
||||||
|
console.log('User Id: ' + user.id + ', Email: ' + user.email);
|
||||||
|
} else {
|
||||||
|
console.log("FirebaseSync.create error:", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function login(email, password, options) {
|
||||||
|
authClient.login('password', {
|
||||||
|
email: email,
|
||||||
|
password: password,
|
||||||
|
rememberMe: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserData(data) {
|
||||||
|
console.log("User data:", data.val());
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
create: create,
|
||||||
|
login: login,
|
||||||
|
getUserData: getUserData
|
||||||
|
};
|
||||||
|
};
|
|
@ -45,9 +45,10 @@ var _Gombot = function(importedModules, Gombot) {
|
||||||
Gombot.TldService = getModule("TldService")(getModule("Tld"), getModule("Uri"));
|
Gombot.TldService = getModule("TldService")(getModule("Tld"), getModule("Uri"));
|
||||||
Gombot.SiteConfigs = getModule("SiteConfigs");
|
Gombot.SiteConfigs = getModule("SiteConfigs");
|
||||||
Gombot.Realms = getModule("Realms")(Gombot, Gombot.SiteConfigs, getModule("Uri"));
|
Gombot.Realms = getModule("Realms")(Gombot, Gombot.SiteConfigs, getModule("Uri"));
|
||||||
Gombot.Storage = getModule("Storage")(Backbone, _, Gombot.LocalStorage); // defined by backbone.localStorage.js
|
Gombot.Storage = getModule("Storage")(Backbone, _, Gombot.LocalStorage); // local sync; defined by backbone.localStorage.js
|
||||||
Gombot.GombotClient = getModule("GombotClient");
|
//Gombot.GombotClient = getModule("GombotClient");
|
||||||
Gombot.Sync = getModule("GombotSync")(Gombot, Backbone, _);
|
//Gombot.Sync = getModule("GombotSync")(Gombot, Backbone, _); // original sync using our api
|
||||||
|
//Gombot.FirebaseSync = getModule("FirebaseSync")(Gombot); // sync using firebase
|
||||||
Gombot.LoginCredential = getModule("LoginCredential")(Gombot, Backbone, _);
|
Gombot.LoginCredential = getModule("LoginCredential")(Gombot, Backbone, _);
|
||||||
Gombot.LoginCredentialCollection = getModule("LoginCredentialCollection")(Backbone, _, Gombot.LoginCredential); // LoginCredential need to be initialized
|
Gombot.LoginCredentialCollection = getModule("LoginCredentialCollection")(Backbone, _, Gombot.LoginCredential); // LoginCredential need to be initialized
|
||||||
Gombot.CapturedCredentialStorage = getModule("CapturedCredentialStorage")(Gombot, getModule("Uri"));
|
Gombot.CapturedCredentialStorage = getModule("CapturedCredentialStorage")(Gombot, getModule("Uri"));
|
||||||
|
@ -55,8 +56,12 @@ var _Gombot = function(importedModules, Gombot) {
|
||||||
Gombot.AccountManager = getModule("AccountManager")(Gombot, _);
|
Gombot.AccountManager = getModule("AccountManager")(Gombot, _);
|
||||||
Gombot.CommandHandler = getModule("CommandHandler")(Gombot, Gombot.Messaging, _);
|
Gombot.CommandHandler = getModule("CommandHandler")(Gombot, Gombot.Messaging, _);
|
||||||
Gombot.Pages = getModule("Pages")(Gombot);
|
Gombot.Pages = getModule("Pages")(Gombot);
|
||||||
Gombot.InfobarManager = getModule("InfobarManager");
|
Gombot.Crypto = getModule("GombotCrypto");
|
||||||
Gombot.Infobars = getModule("Infobars")(Gombot);
|
Gombot.User = getModule("User")(Backbone, _, Gombot);
|
||||||
|
if (typeof chrome !== "undefined") {
|
||||||
|
Gombot.InfobarManager = getModule("InfobarManager");
|
||||||
|
Gombot.Infobars = getModule("Infobars")(Gombot);
|
||||||
|
}
|
||||||
|
|
||||||
var currentUser = null;
|
var currentUser = null;
|
||||||
Gombot.getCurrentUser = function() {
|
Gombot.getCurrentUser = function() {
|
||||||
|
@ -73,19 +78,24 @@ var _Gombot = function(importedModules, Gombot) {
|
||||||
currentUser.destroy({ localOnly: true, success: function() { currentUser = null; callback(); }});
|
currentUser.destroy({ localOnly: true, success: function() { currentUser = null; callback(); }});
|
||||||
};
|
};
|
||||||
|
|
||||||
new Gombot.Storage("users", function(store) {
|
Gombot.init = function(options) {
|
||||||
Gombot.User = getModule("User")(Backbone, _, Gombot, store);
|
options = options || {};
|
||||||
Gombot.UserCollection = getModule("UserCollection")(Backbone, _, Gombot, store);
|
options.storeName = options.storeName || "users";
|
||||||
checkFirstRun();
|
options.callback = options.callback || checkFirstRun;
|
||||||
});
|
new Gombot.Storage(options.storeName, function(store) {
|
||||||
|
Gombot.SyncAdapter = getModule("SyncAdapter")(Gombot, Gombot.Crypto, store, _);
|
||||||
function checkFirstRun() {
|
Gombot.UserCollection = getModule("UserCollection")(Backbone, _, Gombot, store);
|
||||||
Gombot.LocalStorage.getItem("firstRun", function(firstRun) {
|
options.callback();
|
||||||
initGombot(firstRun);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function initGombot(firstRun) {
|
function checkFirstRun() {
|
||||||
|
Gombot.LocalStorage.getItem("firstRun", function(firstRun) {
|
||||||
|
fetchUsers(firstRun);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchUsers(firstRun) {
|
||||||
Gombot.users = new Gombot.UserCollection();
|
Gombot.users = new Gombot.UserCollection();
|
||||||
Gombot.users.fetch({
|
Gombot.users.fetch({
|
||||||
success: function() {
|
success: function() {
|
||||||
|
@ -102,6 +112,7 @@ var _Gombot = function(importedModules, Gombot) {
|
||||||
|
|
||||||
if (typeof module !== "undefined" && module.exports) {
|
if (typeof module !== "undefined" && module.exports) {
|
||||||
module.exports = _Gombot; // export namespace constructor, for Firefox
|
module.exports = _Gombot; // export namespace constructor, for Firefox
|
||||||
} else { // otherwise, just create the global Gombot namespace
|
} else { // otherwise, just create the global Gombot namespace and init
|
||||||
var Gombot = _Gombot({});
|
var Gombot = _Gombot({});
|
||||||
|
Gombot.init();
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,33 +47,9 @@ windows.on('open', function(window) {
|
||||||
addToolbarButton();
|
addToolbarButton();
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Load all Gombot modules **/
|
var GombotModules = require("./modules");
|
||||||
|
var Gombot = require("./gombot")(GombotModules);
|
||||||
var gombotModules = {
|
Gombot.init();
|
||||||
Backbone: require("./lib/backbone"),
|
|
||||||
_ : require("./lib/underscore"),
|
|
||||||
Messaging: require("./messaging"),
|
|
||||||
LocalStorage: require("./local_storage"),
|
|
||||||
Tld: require("./lib/tld.js"),
|
|
||||||
Uri: require("./lib/jsuri"),
|
|
||||||
TldService: require("./tld_service"),
|
|
||||||
SiteConfigs: require("./site_configs"),
|
|
||||||
Realms: require("./realms"),
|
|
||||||
Storage: require("./storage"),
|
|
||||||
GombotClient: require("./client/client"),
|
|
||||||
GombotSync: require("./gombot_sync"),
|
|
||||||
LoginCredential: require("./models/login_credential"),
|
|
||||||
LoginCredentialCollection: require("./collections/login_credential_collection"),
|
|
||||||
CapturedCredentialStorage: require("./captured_credential_storage"),
|
|
||||||
Linker: require("./linker"),
|
|
||||||
CommandHandler: require("./command_handler"),
|
|
||||||
User: require("./models/user"),
|
|
||||||
UserCollection: require("./collections/user_collection"),
|
|
||||||
AccountManager: require("./account_manager"),
|
|
||||||
Pages: require("./pages")
|
|
||||||
};
|
|
||||||
|
|
||||||
var Gombot = require("./gombot")(gombotModules);
|
|
||||||
|
|
||||||
/** Tpp panel stuff **/
|
/** Tpp panel stuff **/
|
||||||
|
|
||||||
|
@ -109,3 +85,5 @@ pageMod.PageMod({
|
||||||
Gombot.Messaging.registerPageModWorker(worker);
|
Gombot.Messaging.registerPageModWorker(worker);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
exports.gombot = Gombot;
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
var User = function(Backbone, _, Gombot, LocalStorage) {
|
var User = function(Backbone, _, Gombot) {
|
||||||
|
|
||||||
const USER_DATA_VERSIONS = [
|
const USER_DATA_VERSIONS = [
|
||||||
"identity.mozilla.com/gombot/v1/userData"
|
"identity.mozilla.com/gombot/v1/userData"
|
||||||
];
|
];
|
||||||
|
|
||||||
var GombotSync = Gombot.Sync,
|
|
||||||
LoginCredentialCollection = Gombot.LoginCredentialCollection;
|
|
||||||
|
|
||||||
// attributes should be something like:
|
// attributes should be something like:
|
||||||
// {
|
// {
|
||||||
// "version": "identity.mozilla.com/gombot/v1/userData",
|
// "version": "identity.mozilla.com/gombot/v1/userData",
|
||||||
|
@ -38,8 +35,6 @@ var User = function(Backbone, _, Gombot, LocalStorage) {
|
||||||
disabledSites: {}
|
disabledSites: {}
|
||||||
},
|
},
|
||||||
|
|
||||||
localStorage: LocalStorage,
|
|
||||||
|
|
||||||
initialize: function() {
|
initialize: function() {
|
||||||
Backbone.Model.prototype.initialize.apply(this, arguments);
|
Backbone.Model.prototype.initialize.apply(this, arguments);
|
||||||
this.addSyncListener(this.get("logins"));
|
this.addSyncListener(this.get("logins"));
|
||||||
|
@ -64,6 +59,7 @@ var User = function(Backbone, _, Gombot, LocalStorage) {
|
||||||
},
|
},
|
||||||
|
|
||||||
isAuthenticated: function() {
|
isAuthenticated: function() {
|
||||||
|
return false;
|
||||||
return this.client && ((this.client.isAuthenticated && this.client.isAuthenticated()) || (this.client.keys && this.client.user));
|
return this.client && ((this.client.isAuthenticated && this.client.isAuthenticated()) || (this.client.keys && this.client.user));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -71,60 +67,61 @@ var User = function(Backbone, _, Gombot, LocalStorage) {
|
||||||
// call model.toJSON({ encrypted: true, ciphertext: <ciphertext> })
|
// call model.toJSON({ encrypted: true, ciphertext: <ciphertext> })
|
||||||
// Other toJSON() creates a standard plaintext representation of a User object
|
// Other toJSON() creates a standard plaintext representation of a User object
|
||||||
toJSON: function(args) {
|
toJSON: function(args) {
|
||||||
var result;
|
var result = Backbone.Model.prototype.toJSON.apply(this, arguments);
|
||||||
args = args || {};
|
return _.extend(result, { logins: this.get("logins").toJSON() });
|
||||||
if (args.ciphertext) {
|
},
|
||||||
result = { ciphertext: args.ciphertext, updated: this.updated, id: this.id, email: this.get("email"), version: this.get("version") };
|
|
||||||
if (this.isAuthenticated()) _.extend(result, { client: this.client.toJSON() });
|
// Returns an object containing key/values of data that will be
|
||||||
return result;
|
// stored in plaintext with an encrypted copy of this model's data.
|
||||||
}
|
// The metadata should not contain any information that is intended
|
||||||
else {
|
// to be stored encrypted at rest.
|
||||||
result = Backbone.Model.prototype.toJSON.apply(this, arguments);
|
getMetadata: function() {
|
||||||
return _.extend(result, { logins: this.get("logins").toJSON() });
|
return {
|
||||||
|
id: this.id,
|
||||||
|
email: this.get("email"),
|
||||||
|
version: this.get("version"),
|
||||||
|
updated: this.updated
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
parse: function(resp) {
|
parse: function(resp) {
|
||||||
if (resp.ciphertext) this.ciphertext = resp.ciphertext;
|
|
||||||
if (resp.updated) this.updated = resp.updated;
|
if (resp.updated) this.updated = resp.updated;
|
||||||
if (resp.client) this.client = resp.client;
|
|
||||||
delete resp.ciphertext;
|
|
||||||
delete resp.updated;
|
delete resp.updated;
|
||||||
delete resp.client;
|
|
||||||
return resp;
|
return resp;
|
||||||
},
|
},
|
||||||
|
|
||||||
sync: function(method, model, options) {
|
sync: function(method, model, options) {
|
||||||
var self = this;
|
Gombot.SyncAdapter.sync(method, model, options);
|
||||||
var success = function(resp) {
|
// var self = this;
|
||||||
var s = options.success;
|
// var success = function(resp) {
|
||||||
options.success = function(model, resp, options) {
|
// var s = options.success;
|
||||||
console.log("User.sync finished method="+method+" resp="+JSON.stringify(resp)+" model="+JSON.stringify(model));
|
// options.success = function(model, resp, options) {
|
||||||
// resp.data is returned by GombotSync calls with plaintext user data
|
// console.log("User.sync finished method="+method+" resp="+JSON.stringify(resp)+" model="+JSON.stringify(model));
|
||||||
if (s) s(model, resp.data || {}, options);
|
// // resp.data is returned by GombotSync calls with plaintext user data
|
||||||
}
|
// if (s) s(model, resp.data || {}, options);
|
||||||
if (resp.updated) self.updated = resp.updated;
|
// }
|
||||||
// ciphertext in resp indicates we need to write it out to local storage
|
// if (resp.updated) self.updated = resp.updated;
|
||||||
if (resp.ciphertext) {
|
// // ciphertext in resp indicates we need to write it out to local storage
|
||||||
if (method === "read") {
|
// if (resp.ciphertext) {
|
||||||
self.save(resp.data, _.extend(options, { localOnly: true, ciphertext: resp.ciphertext }));
|
// if (method === "read") {
|
||||||
} else {
|
// self.save(resp.data, _.extend(options, { localOnly: true, ciphertext: resp.ciphertext }));
|
||||||
console.log("localSync method="+method);
|
// } else {
|
||||||
Backbone.localSync(method, model, _.extend(options, { ciphertext: resp.ciphertext }));
|
// console.log("localSync method="+method);
|
||||||
}
|
// Backbone.localSync(method, model, _.extend(options, { ciphertext: resp.ciphertext }));
|
||||||
} else if (options.success) {
|
// }
|
||||||
options.success(model, resp, options);
|
// } else if (options.success) {
|
||||||
}
|
// options.success(model, resp, options);
|
||||||
};
|
// }
|
||||||
var error = function(args) {
|
// };
|
||||||
if (options.error) options.error(args);
|
// var error = function(args) {
|
||||||
};
|
// if (options.error) options.error(args);
|
||||||
var o = _.clone(options);
|
// };
|
||||||
if (options.localOnly) {
|
// var o = _.clone(options);
|
||||||
Backbone.localSync(method, model, options);
|
// if (options.localOnly) {
|
||||||
} else {
|
// Backbone.localSync(method, model, options);
|
||||||
GombotSync.sync(method, model, _.extend(o,{ success: success, error: error }));
|
// } else {
|
||||||
}
|
// GombotSync.sync(method, model, _.extend(o,{ success: success, error: error }));
|
||||||
|
// }
|
||||||
},
|
},
|
||||||
|
|
||||||
set: function(key, val, options) {
|
set: function(key, val, options) {
|
||||||
|
@ -137,9 +134,9 @@ var User = function(Backbone, _, Gombot, LocalStorage) {
|
||||||
} else {
|
} else {
|
||||||
(attributes = {})[key] = val;
|
(attributes = {})[key] = val;
|
||||||
}
|
}
|
||||||
if (attributes.logins !== undefined && !(attributes.logins instanceof LoginCredentialCollection)) {
|
if (attributes.logins !== undefined && !(attributes.logins instanceof Gombot.LoginCredentialCollection)) {
|
||||||
logins = attributes.logins;
|
logins = attributes.logins;
|
||||||
attributes.logins = this.get("logins") || new LoginCredentialCollection();
|
attributes.logins = this.get("logins") || new Gombot.LoginCredentialCollection();
|
||||||
}
|
}
|
||||||
result = Backbone.Model.prototype.set.call(this, attributes, options);
|
result = Backbone.Model.prototype.set.call(this, attributes, options);
|
||||||
if (result && logins) {
|
if (result && logins) {
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/** Load all Gombot modules **/
|
||||||
|
|
||||||
|
var GombotModules = {
|
||||||
|
Backbone: require("./lib/backbone"),
|
||||||
|
_ : require("./lib/underscore"),
|
||||||
|
Messaging: require("./messaging"),
|
||||||
|
LocalStorage: require("./local_storage"),
|
||||||
|
Tld: require("./lib/tld.js"),
|
||||||
|
Uri: require("./lib/jsuri"),
|
||||||
|
TldService: require("./tld_service"),
|
||||||
|
SiteConfigs: require("./site_configs"),
|
||||||
|
Realms: require("./realms"),
|
||||||
|
Storage: require("./storage"),
|
||||||
|
//GombotClient: require("./client/client"),
|
||||||
|
//GombotSync: require("./gombot_sync"),
|
||||||
|
LoginCredential: require("./models/login_credential"),
|
||||||
|
LoginCredentialCollection: require("./collections/login_credential_collection"),
|
||||||
|
CapturedCredentialStorage: require("./captured_credential_storage"),
|
||||||
|
Linker: require("./linker"),
|
||||||
|
CommandHandler: require("./command_handler"),
|
||||||
|
User: require("./models/user"),
|
||||||
|
UserCollection: require("./collections/user_collection"),
|
||||||
|
AccountManager: require("./account_manager"),
|
||||||
|
Pages: require("./pages"),
|
||||||
|
GombotCrypto: require("./client/crypto"),
|
||||||
|
SyncAdapter: require("./sync_adapter")
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = GombotModules;
|
|
@ -0,0 +1,121 @@
|
||||||
|
var SyncAdapter = function(Gombot, GombotCrypto, SyncStrategy, _) {
|
||||||
|
|
||||||
|
// TODO: seed from an actual entropy source
|
||||||
|
GombotCrypto.seed("oiqwjeciouqh3c89cnkjasdcnasjf84u9jcuqwiench734fhujhwuqhf73f73fhsdjfhasdf734fhdkcnuf"+(new Date().toString()) ,function(err) {
|
||||||
|
if (err) console.log("GombotCrypto.seed error:", err);
|
||||||
|
});
|
||||||
|
|
||||||
|
function maybeHandleError(handler, err) {
|
||||||
|
if (err) {
|
||||||
|
console.log("SyncMediator error", err);
|
||||||
|
if (handler) handler(err);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function encryptModel(model, keys, options) {
|
||||||
|
GombotCrypto.encrypt(keys, JSON.stringify(model), function(err, ciphertext) {
|
||||||
|
if (err) return maybeHandleError(options.error, err);
|
||||||
|
options.success(ciphertext);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function decryptModelData(ciphertext, keys, options) {
|
||||||
|
GombotCrypto.decrypt(keys, ciphertext, function(err, json) {
|
||||||
|
var modelData;
|
||||||
|
try {
|
||||||
|
if (!err) {
|
||||||
|
modelData = JSON.parse(json);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
err = new Error("Could not parse decrypted JSON:", json);
|
||||||
|
}
|
||||||
|
if (err) return maybeHandleError(options.error, err);
|
||||||
|
options.success(modelData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCryptoProxyForModel(model, keys) {
|
||||||
|
var clone = _.clone(model);
|
||||||
|
return _.extend(clone, {
|
||||||
|
toJSON: function(options) {
|
||||||
|
// missing options means synchronous response to underyling object
|
||||||
|
if (!options || !options.success) return model.toJSON();
|
||||||
|
var o = _.clone(options);
|
||||||
|
encryptModel(model, keys, _.extend(o, { success: function(ciphertext) {
|
||||||
|
options.success(_.extend(model.getMetadata(), {
|
||||||
|
ciphertext: ciphertext // encrypted plaintext model
|
||||||
|
}));
|
||||||
|
}}));
|
||||||
|
},
|
||||||
|
parse: function(resp, options) {
|
||||||
|
var o = _.clone(options),
|
||||||
|
ciphertext = resp.ciphertext;
|
||||||
|
decryptModelData(ciphertext, keys, _.extend(o, { success: function(modelData) {
|
||||||
|
delete resp.ciphertext;
|
||||||
|
options.success(_.extend(resp, modelData));
|
||||||
|
}}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCryptoProxyForModel(model, options) {
|
||||||
|
if (model.cryptoProxy) return options.success(model.cryptoProxy);
|
||||||
|
var o = _.clone(options);
|
||||||
|
deriveKeysForModel(model, _.extend(o, { success: function(keys) {
|
||||||
|
model.cryptoProxy = createCryptoProxyForModel(model, keys);
|
||||||
|
options.success(model.cryptoProxy);
|
||||||
|
}}));
|
||||||
|
}
|
||||||
|
|
||||||
|
var kdf = GombotCrypto.derive;
|
||||||
|
// Special kdf derivation function we'll pass to GombotClient to handle FX slowness bug
|
||||||
|
if (typeof require !== "undefined") {
|
||||||
|
kdf = function (args, callback) {
|
||||||
|
console.log("in derive")
|
||||||
|
require("gombot-crypto-jetpack").kdf(args.email, args.password).then(function(keys) {
|
||||||
|
callback(null, keys);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// options.password must be present
|
||||||
|
function deriveKeysForModel(model, options) {
|
||||||
|
kdf({
|
||||||
|
email: model.get("email"),
|
||||||
|
password: options.password
|
||||||
|
}, function(err, keys) {
|
||||||
|
if (err) return maybeHandleError(options.error, err);
|
||||||
|
options.success(keys);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSyncStrategy(strategy) {
|
||||||
|
SyncStrategy = strategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sync(method, model, options) {
|
||||||
|
if (!(model instanceof Gombot.User)) {
|
||||||
|
if (options.error) options.error("sync only supports syncing instances of Gombot.User");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var o = _.clone(options);
|
||||||
|
getCryptoProxyForModel(model, _.extend(o, { success: function(cryptoProxyForModel) {
|
||||||
|
var o = _.clone(options);
|
||||||
|
// translate model proxy back to original model
|
||||||
|
SyncStrategy.sync(method, cryptoProxyForModel, _.extend(o, { success: function(modelProxy, resp, modifiedOptions) {
|
||||||
|
if (options.success) options.success(model, resp, options);
|
||||||
|
}}));
|
||||||
|
}}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
sync: sync,
|
||||||
|
setSyncStrategy: setSyncStrategy
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof module !== "undefined" && module.exports) {
|
||||||
|
module.exports = SyncAdapter;
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* https://github.com/jeromegn/Backbone.localStorage
|
* https://github.com/jeromegn/Backbone.localStorage
|
||||||
*/
|
*/
|
||||||
var Storage = function(Backbone, _, LocalStorage) {
|
var Storage = function(Backbone, _, LocalStorage, store) {
|
||||||
return (function (root, factory) {
|
return (function (root, factory) {
|
||||||
// if (typeof define === "function" && define.amd) {
|
// if (typeof define === "function" && define.amd) {
|
||||||
// // AMD. Register as an anonymous module.
|
// // AMD. Register as an anonymous module.
|
||||||
|
@ -62,9 +62,12 @@ _.extend(Backbone.LocalStorage.prototype, {
|
||||||
var cb = _.after(2, function() {
|
var cb = _.after(2, function() {
|
||||||
callback(model.toJSON());
|
callback(model.toJSON());
|
||||||
});
|
});
|
||||||
this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(model.toJSON({ ciphertext: options.ciphertext })), cb);
|
var o = _.clone(options);
|
||||||
this.records.push(model.id.toString());
|
model.toJSON(_.extend(o, { success: (function(jsonObj) {
|
||||||
this.save(cb);
|
this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(jsonObj), cb);
|
||||||
|
this.records.push(model.id.toString());
|
||||||
|
this.save(cb);
|
||||||
|
}).bind(this)}));
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -116,6 +119,12 @@ _.extend(Backbone.LocalStorage.prototype, {
|
||||||
// fix for "illegal access" error on Android when JSON.parse is passed null
|
// fix for "illegal access" error on Android when JSON.parse is passed null
|
||||||
jsonData: function (data) {
|
jsonData: function (data) {
|
||||||
return data && JSON.parse(data);
|
return data && JSON.parse(data);
|
||||||
|
},
|
||||||
|
|
||||||
|
sync: function(method, model, options) {
|
||||||
|
var o = _.clone(options);
|
||||||
|
o.store = this;
|
||||||
|
Backbone.LocalStorage.sync(method, model, o);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -124,7 +133,7 @@ _.extend(Backbone.LocalStorage.prototype, {
|
||||||
// *localStorage* property, which should be an instance of `Store`.
|
// *localStorage* property, which should be an instance of `Store`.
|
||||||
// window.Store.sync and Backbone.localSync is deprectated, use Backbone.LocalStorage.sync instead
|
// window.Store.sync and Backbone.localSync is deprectated, use Backbone.LocalStorage.sync instead
|
||||||
Backbone.LocalStorage.sync = Backbone.localSync = function(method, model, options) {
|
Backbone.LocalStorage.sync = Backbone.localSync = function(method, model, options) {
|
||||||
var store = model.localStorage || model.collection.localStorage;
|
var store = options.store || model.localStorage || model.collection.localStorage;
|
||||||
|
|
||||||
var syncDfd = (typeof $ !== "undefined") && $.Deferred && $.Deferred(); //If $ is having Deferred - use it.
|
var syncDfd = (typeof $ !== "undefined") && $.Deferred && $.Deferred(); //If $ is having Deferred - use it.
|
||||||
|
|
||||||
|
@ -149,6 +158,8 @@ Backbone.LocalStorage.sync = Backbone.localSync = function(method, model, option
|
||||||
if (options && options.complete) options.complete(resp);
|
if (options && options.complete) options.complete(resp);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log("Backbone.LocalStorage.sync", method, model, options);
|
||||||
|
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case "read": model.id != undefined ? store.find(model, callback, options) : store.findAll(callback, options); break;
|
case "read": model.id != undefined ? store.find(model, callback, options) : store.findAll(callback, options); break;
|
||||||
case "create": store.create(model, callback, options); break;
|
case "create": store.create(model, callback, options); break;
|
||||||
|
|
|
@ -8,12 +8,14 @@
|
||||||
"notifications",
|
"notifications",
|
||||||
"storage",
|
"storage",
|
||||||
"http://*/",
|
"http://*/",
|
||||||
"https://*/"
|
"https://*/",
|
||||||
|
"webRequest",
|
||||||
|
"webRequestBlocking"
|
||||||
],
|
],
|
||||||
"icons": {
|
"icons": {
|
||||||
"128": "images/gombot-icon-128.png"
|
"128": "images/gombot-icon-128.png"
|
||||||
},
|
},
|
||||||
|
"content_security_policy": "script-src 'self' https://cdn.firebase.com https://auth.firebase.com https://*.firebaseio.com; object-src 'self'",
|
||||||
"background": {
|
"background": {
|
||||||
"page": "background/background.html"
|
"page": "background/background.html"
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
exports.testCreate = function(test) {
|
||||||
|
var GombotModules = require("./modules");
|
||||||
|
var Gombot = require("./gombot")(GombotModules);
|
||||||
|
|
||||||
|
Gombot.init({ storeName: "testUsers", callback: function() {
|
||||||
|
var email = "test+"+Math.floor((1+Math.random())*10000)+"@test.com";
|
||||||
|
var u = new Gombot.User({ email: email });
|
||||||
|
u.save(null, { success: function() {
|
||||||
|
console.log("user saved", u);
|
||||||
|
test.pass();
|
||||||
|
test.done();
|
||||||
|
},
|
||||||
|
error: function(err) {
|
||||||
|
console.log("error:", err);
|
||||||
|
test.fail();
|
||||||
|
test.done();
|
||||||
|
},
|
||||||
|
password: "foobar"
|
||||||
|
});
|
||||||
|
}});
|
||||||
|
test.waitUntilDone();
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче