michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: * http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: Cu.import("resource://gre/modules/Log.jsm"); michael@0: Cu.import("resource://services-sync/constants.js"); michael@0: Cu.import("resource://services-sync/keys.js"); michael@0: Cu.import("resource://services-sync/engines/tabs.js"); michael@0: Cu.import("resource://services-sync/engines.js"); michael@0: Cu.import("resource://services-sync/record.js"); michael@0: Cu.import("resource://services-sync/service.js"); michael@0: Cu.import("resource://services-sync/util.js"); michael@0: Cu.import("resource://testing-common/services/sync/utils.js"); michael@0: michael@0: Service.engineManager.register(TabEngine); michael@0: michael@0: add_test(function v4_upgrade() { michael@0: let passphrase = "abcdeabcdeabcdeabcdeabcdea"; michael@0: michael@0: let clients = new ServerCollection(); michael@0: let meta_global = new ServerWBO('global'); michael@0: michael@0: // Tracking info/collections. michael@0: let collectionsHelper = track_collections_helper(); michael@0: let upd = collectionsHelper.with_updated_collection; michael@0: let collections = collectionsHelper.collections; michael@0: michael@0: let keysWBO = new ServerWBO("keys"); michael@0: let server = httpd_setup({ michael@0: // Special. michael@0: "/1.1/johndoe/info/collections": collectionsHelper.handler, michael@0: "/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()), michael@0: "/1.1/johndoe/storage/meta/global": upd("meta", meta_global.handler()), michael@0: michael@0: // Track modified times. michael@0: "/1.1/johndoe/storage/clients": upd("clients", clients.handler()), michael@0: "/1.1/johndoe/storage/tabs": upd("tabs", new ServerCollection().handler()), michael@0: michael@0: // Just so we don't get 404s in the logs. michael@0: "/1.1/johndoe/storage/bookmarks": new ServerCollection().handler(), michael@0: "/1.1/johndoe/storage/forms": new ServerCollection().handler(), michael@0: "/1.1/johndoe/storage/history": new ServerCollection().handler(), michael@0: "/1.1/johndoe/storage/passwords": new ServerCollection().handler(), michael@0: "/1.1/johndoe/storage/prefs": new ServerCollection().handler() michael@0: }); michael@0: michael@0: ensureLegacyIdentityManager(); michael@0: michael@0: try { michael@0: michael@0: _("Set up some tabs."); michael@0: let myTabs = michael@0: {windows: [{tabs: [{index: 1, michael@0: entries: [{ michael@0: url: "http://foo.com/", michael@0: title: "Title" michael@0: }], michael@0: attributes: { michael@0: image: "image" michael@0: } michael@0: }]}]}; michael@0: delete Svc.Session; michael@0: Svc.Session = { michael@0: getBrowserState: function () JSON.stringify(myTabs) michael@0: }; michael@0: michael@0: Service.status.resetSync(); michael@0: michael@0: _("Logging in."); michael@0: Service.serverURL = server.baseURI; michael@0: michael@0: Service.login("johndoe", "ilovejane", passphrase); michael@0: do_check_true(Service.isLoggedIn); michael@0: Service.verifyAndFetchSymmetricKeys(); michael@0: do_check_true(Service._remoteSetup()); michael@0: michael@0: function test_out_of_date() { michael@0: _("Old meta/global: " + JSON.stringify(meta_global)); michael@0: meta_global.payload = JSON.stringify({"syncID": "foooooooooooooooooooooooooo", michael@0: "storageVersion": STORAGE_VERSION + 1}); michael@0: collections.meta = Date.now() / 1000; michael@0: _("New meta/global: " + JSON.stringify(meta_global)); michael@0: Service.recordManager.set(Service.metaURL, meta_global); michael@0: try { michael@0: Service.sync(); michael@0: } michael@0: catch (ex) { michael@0: } michael@0: do_check_eq(Service.status.sync, VERSION_OUT_OF_DATE); michael@0: } michael@0: michael@0: // See what happens when we bump the storage version. michael@0: _("Syncing after server has been upgraded."); michael@0: test_out_of_date(); michael@0: michael@0: // Same should happen after a wipe. michael@0: _("Syncing after server has been upgraded and wiped."); michael@0: Service.wipeServer(); michael@0: test_out_of_date(); michael@0: michael@0: // Now's a great time to test what happens when keys get replaced. michael@0: _("Syncing afresh..."); michael@0: Service.logout(); michael@0: Service.collectionKeys.clear(); michael@0: Service.serverURL = server.baseURI; michael@0: meta_global.payload = JSON.stringify({"syncID": "foooooooooooooobbbbbbbbbbbb", michael@0: "storageVersion": STORAGE_VERSION}); michael@0: collections.meta = Date.now() / 1000; michael@0: Service.recordManager.set(Service.metaURL, meta_global); michael@0: Service.login("johndoe", "ilovejane", passphrase); michael@0: do_check_true(Service.isLoggedIn); michael@0: Service.sync(); michael@0: do_check_true(Service.isLoggedIn); michael@0: michael@0: let serverDecrypted; michael@0: let serverKeys; michael@0: let serverResp; michael@0: michael@0: michael@0: function retrieve_server_default() { michael@0: serverKeys = serverResp = serverDecrypted = null; michael@0: michael@0: serverKeys = new CryptoWrapper("crypto", "keys"); michael@0: serverResp = serverKeys.fetch(Service.resource(Service.cryptoKeysURL)).response; michael@0: do_check_true(serverResp.success); michael@0: michael@0: serverDecrypted = serverKeys.decrypt(Service.identity.syncKeyBundle); michael@0: _("Retrieved WBO: " + JSON.stringify(serverDecrypted)); michael@0: _("serverKeys: " + JSON.stringify(serverKeys)); michael@0: michael@0: return serverDecrypted.default; michael@0: } michael@0: michael@0: function retrieve_and_compare_default(should_succeed) { michael@0: let serverDefault = retrieve_server_default(); michael@0: let localDefault = Service.collectionKeys.keyForCollection().keyPairB64; michael@0: michael@0: _("Retrieved keyBundle: " + JSON.stringify(serverDefault)); michael@0: _("Local keyBundle: " + JSON.stringify(localDefault)); michael@0: michael@0: if (should_succeed) michael@0: do_check_eq(JSON.stringify(serverDefault), JSON.stringify(localDefault)); michael@0: else michael@0: do_check_neq(JSON.stringify(serverDefault), JSON.stringify(localDefault)); michael@0: } michael@0: michael@0: // Uses the objects set above. michael@0: function set_server_keys(pair) { michael@0: serverDecrypted.default = pair; michael@0: serverKeys.cleartext = serverDecrypted; michael@0: serverKeys.encrypt(Service.identity.syncKeyBundle); michael@0: serverKeys.upload(Service.resource(Service.cryptoKeysURL)); michael@0: } michael@0: michael@0: _("Checking we have the latest keys."); michael@0: retrieve_and_compare_default(true); michael@0: michael@0: _("Update keys on server."); michael@0: set_server_keys(["KaaaaaaaaaaaHAtfmuRY0XEJ7LXfFuqvF7opFdBD/MY=", michael@0: "aaaaaaaaaaaapxMO6TEWtLIOv9dj6kBAJdzhWDkkkis="]); michael@0: michael@0: _("Checking that we no longer have the latest keys."); michael@0: retrieve_and_compare_default(false); michael@0: michael@0: _("Indeed, they're what we set them to..."); michael@0: do_check_eq("KaaaaaaaaaaaHAtfmuRY0XEJ7LXfFuqvF7opFdBD/MY=", michael@0: retrieve_server_default()[0]); michael@0: michael@0: _("Sync. Should download changed keys automatically."); michael@0: let oldClientsModified = collections.clients; michael@0: let oldTabsModified = collections.tabs; michael@0: michael@0: Service.login("johndoe", "ilovejane", passphrase); michael@0: Service.sync(); michael@0: _("New key should have forced upload of data."); michael@0: _("Tabs: " + oldTabsModified + " < " + collections.tabs); michael@0: _("Clients: " + oldClientsModified + " < " + collections.clients); michael@0: do_check_true(collections.clients > oldClientsModified); michael@0: do_check_true(collections.tabs > oldTabsModified); michael@0: michael@0: _("... and keys will now match."); michael@0: retrieve_and_compare_default(true); michael@0: michael@0: // Clean up. michael@0: Service.startOver(); michael@0: michael@0: } finally { michael@0: Svc.Prefs.resetBranch(""); michael@0: server.stop(run_next_test); michael@0: } michael@0: }); michael@0: michael@0: add_test(function v5_upgrade() { michael@0: let passphrase = "abcdeabcdeabcdeabcdeabcdea"; michael@0: michael@0: // Tracking info/collections. michael@0: let collectionsHelper = track_collections_helper(); michael@0: let upd = collectionsHelper.with_updated_collection; michael@0: let collections = collectionsHelper.collections; michael@0: michael@0: let keysWBO = new ServerWBO("keys"); michael@0: let bulkWBO = new ServerWBO("bulk"); michael@0: let clients = new ServerCollection(); michael@0: let meta_global = new ServerWBO('global'); michael@0: michael@0: let server = httpd_setup({ michael@0: // Special. michael@0: "/1.1/johndoe/storage/meta/global": upd("meta", meta_global.handler()), michael@0: "/1.1/johndoe/info/collections": collectionsHelper.handler, michael@0: "/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()), michael@0: "/1.1/johndoe/storage/crypto/bulk": upd("crypto", bulkWBO.handler()), michael@0: michael@0: // Track modified times. michael@0: "/1.1/johndoe/storage/clients": upd("clients", clients.handler()), michael@0: "/1.1/johndoe/storage/tabs": upd("tabs", new ServerCollection().handler()), michael@0: }); michael@0: michael@0: try { michael@0: michael@0: _("Set up some tabs."); michael@0: let myTabs = michael@0: {windows: [{tabs: [{index: 1, michael@0: entries: [{ michael@0: url: "http://foo.com/", michael@0: title: "Title" michael@0: }], michael@0: attributes: { michael@0: image: "image" michael@0: } michael@0: }]}]}; michael@0: delete Svc.Session; michael@0: Svc.Session = { michael@0: getBrowserState: function () JSON.stringify(myTabs) michael@0: }; michael@0: michael@0: Service.status.resetSync(); michael@0: michael@0: setBasicCredentials("johndoe", "ilovejane", passphrase); michael@0: Service.serverURL = server.baseURI + "/"; michael@0: Service.clusterURL = server.baseURI + "/"; michael@0: michael@0: // Test an upgrade where the contents of the server would cause us to error michael@0: // -- keys decrypted with a different sync key, for example. michael@0: _("Testing v4 -> v5 (or similar) upgrade."); michael@0: function update_server_keys(syncKeyBundle, wboName, collWBO) { michael@0: generateNewKeys(Service.collectionKeys); michael@0: serverKeys = Service.collectionKeys.asWBO("crypto", wboName); michael@0: serverKeys.encrypt(syncKeyBundle); michael@0: let res = Service.resource(Service.storageURL + collWBO); michael@0: do_check_true(serverKeys.upload(res).success); michael@0: } michael@0: michael@0: _("Bumping version."); michael@0: // Bump version on the server. michael@0: let m = new WBORecord("meta", "global"); michael@0: m.payload = {"syncID": "foooooooooooooooooooooooooo", michael@0: "storageVersion": STORAGE_VERSION + 1}; michael@0: m.upload(Service.resource(Service.metaURL)); michael@0: michael@0: _("New meta/global: " + JSON.stringify(meta_global)); michael@0: michael@0: // Fill the keys with bad data. michael@0: let badKeys = new SyncKeyBundle("foobar", "aaaaaaaaaaaaaaaaaaaaaaaaaa"); michael@0: update_server_keys(badKeys, "keys", "crypto/keys"); // v4 michael@0: update_server_keys(badKeys, "bulk", "crypto/bulk"); // v5 michael@0: michael@0: _("Generating new keys."); michael@0: generateNewKeys(Service.collectionKeys); michael@0: michael@0: // Now sync and see what happens. It should be a version fail, not a crypto michael@0: // fail. michael@0: michael@0: _("Logging in."); michael@0: try { michael@0: Service.login("johndoe", "ilovejane", passphrase); michael@0: } michael@0: catch (e) { michael@0: _("Exception: " + e); michael@0: } michael@0: _("Status: " + Service.status); michael@0: do_check_false(Service.isLoggedIn); michael@0: do_check_eq(VERSION_OUT_OF_DATE, Service.status.sync); michael@0: michael@0: // Clean up. michael@0: Service.startOver(); michael@0: michael@0: } finally { michael@0: Svc.Prefs.resetBranch(""); michael@0: server.stop(run_next_test); michael@0: } michael@0: }); michael@0: michael@0: function run_test() { michael@0: let logger = Log.repository.rootLogger; michael@0: Log.repository.rootLogger.addAppender(new Log.DumpAppender()); michael@0: michael@0: run_next_test(); michael@0: }