summaryrefslogtreecommitdiffstats
path: root/services/sync/tests/unit/test_service_sync_remoteSetup.js
diff options
context:
space:
mode:
Diffstat (limited to 'services/sync/tests/unit/test_service_sync_remoteSetup.js')
-rw-r--r--services/sync/tests/unit/test_service_sync_remoteSetup.js237
1 files changed, 237 insertions, 0 deletions
diff --git a/services/sync/tests/unit/test_service_sync_remoteSetup.js b/services/sync/tests/unit/test_service_sync_remoteSetup.js
new file mode 100644
index 000000000..83dbf3cd7
--- /dev/null
+++ b/services/sync/tests/unit/test_service_sync_remoteSetup.js
@@ -0,0 +1,237 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Cu.import("resource://gre/modules/Log.jsm");
+Cu.import("resource://services-sync/constants.js");
+Cu.import("resource://services-sync/keys.js");
+Cu.import("resource://services-sync/service.js");
+Cu.import("resource://services-sync/util.js");
+Cu.import("resource://testing-common/services/sync/fakeservices.js");
+Cu.import("resource://testing-common/services/sync/utils.js");
+
+function run_test() {
+ validate_all_future_pings();
+ let logger = Log.repository.rootLogger;
+ Log.repository.rootLogger.addAppender(new Log.DumpAppender());
+
+ let guidSvc = new FakeGUIDService();
+ let clients = new ServerCollection();
+ let meta_global = new ServerWBO('global');
+
+ let collectionsHelper = track_collections_helper();
+ let upd = collectionsHelper.with_updated_collection;
+ let collections = collectionsHelper.collections;
+
+ function wasCalledHandler(wbo) {
+ let handler = wbo.handler();
+ return function() {
+ wbo.wasCalled = true;
+ handler.apply(this, arguments);
+ };
+ }
+
+ let keysWBO = new ServerWBO("keys");
+ let cryptoColl = new ServerCollection({keys: keysWBO});
+ let metaColl = new ServerCollection({global: meta_global});
+ do_test_pending();
+
+ /**
+ * Handle the bulk DELETE request sent by wipeServer.
+ */
+ function storageHandler(request, response) {
+ do_check_eq("DELETE", request.method);
+ do_check_true(request.hasHeader("X-Confirm-Delete"));
+
+ _("Wiping out all collections.");
+ cryptoColl.delete({});
+ clients.delete({});
+ metaColl.delete({});
+
+ let ts = new_timestamp();
+ collectionsHelper.update_collection("crypto", ts);
+ collectionsHelper.update_collection("clients", ts);
+ collectionsHelper.update_collection("meta", ts);
+ return_timestamp(request, response, ts);
+ }
+
+ const GLOBAL_PATH = "/1.1/johndoe/storage/meta/global";
+ const INFO_PATH = "/1.1/johndoe/info/collections";
+
+ let handlers = {
+ "/1.1/johndoe/storage": storageHandler,
+ "/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
+ "/1.1/johndoe/storage/crypto": upd("crypto", cryptoColl.handler()),
+ "/1.1/johndoe/storage/clients": upd("clients", clients.handler()),
+ "/1.1/johndoe/storage/meta": upd("meta", wasCalledHandler(metaColl)),
+ "/1.1/johndoe/storage/meta/global": upd("meta", wasCalledHandler(meta_global)),
+ "/1.1/johndoe/info/collections": collectionsHelper.handler
+ };
+
+ function mockHandler(path, mock) {
+ server.registerPathHandler(path, mock(handlers[path]));
+ return {
+ restore() { server.registerPathHandler(path, handlers[path]); }
+ }
+ }
+
+ let server = httpd_setup(handlers);
+
+ try {
+ _("Log in.");
+ ensureLegacyIdentityManager();
+ Service.serverURL = server.baseURI;
+
+ _("Checking Status.sync with no credentials.");
+ Service.verifyAndFetchSymmetricKeys();
+ do_check_eq(Service.status.sync, CREDENTIALS_CHANGED);
+ do_check_eq(Service.status.login, LOGIN_FAILED_NO_PASSPHRASE);
+
+ _("Log in with an old secret phrase, is upgraded to Sync Key.");
+ Service.login("johndoe", "ilovejane", "my old secret phrase!!1!");
+ _("End of login");
+ do_check_true(Service.isLoggedIn);
+ do_check_true(Utils.isPassphrase(Service.identity.syncKey));
+ let syncKey = Service.identity.syncKey;
+ Service.startOver();
+
+ Service.serverURL = server.baseURI;
+ Service.login("johndoe", "ilovejane", syncKey);
+ do_check_true(Service.isLoggedIn);
+
+ _("Checking that remoteSetup returns true when credentials have changed.");
+ Service.recordManager.get(Service.metaURL).payload.syncID = "foobar";
+ do_check_true(Service._remoteSetup());
+
+ let returnStatusCode = (method, code) => (oldMethod) => (req, res) => {
+ if (req.method === method) {
+ res.setStatusLine(req.httpVersion, code, "");
+ } else {
+ oldMethod(req, res);
+ }
+ };
+
+ let mock = mockHandler(GLOBAL_PATH, returnStatusCode("GET", 401));
+ Service.recordManager.del(Service.metaURL);
+ _("Checking that remoteSetup returns false on 401 on first get /meta/global.");
+ do_check_false(Service._remoteSetup());
+ mock.restore();
+
+ Service.login("johndoe", "ilovejane", syncKey);
+ mock = mockHandler(GLOBAL_PATH, returnStatusCode("GET", 503));
+ Service.recordManager.del(Service.metaURL);
+ _("Checking that remoteSetup returns false on 503 on first get /meta/global.");
+ do_check_false(Service._remoteSetup());
+ do_check_eq(Service.status.sync, METARECORD_DOWNLOAD_FAIL);
+ mock.restore();
+
+ mock = mockHandler(GLOBAL_PATH, returnStatusCode("GET", 404));
+ Service.recordManager.del(Service.metaURL);
+ _("Checking that remoteSetup recovers on 404 on first get /meta/global.");
+ do_check_true(Service._remoteSetup());
+ mock.restore();
+
+ let makeOutdatedMeta = () => {
+ Service.metaModified = 0;
+ let infoResponse = Service._fetchInfo();
+ return {
+ status: infoResponse.status,
+ obj: {
+ crypto: infoResponse.obj.crypto,
+ clients: infoResponse.obj.clients,
+ meta: 1
+ }
+ };
+ }
+
+ _("Checking that remoteSetup recovers on 404 on get /meta/global after clear cached one.");
+ mock = mockHandler(GLOBAL_PATH, returnStatusCode("GET", 404));
+ Service.recordManager.set(Service.metaURL, { isNew: false });
+ do_check_true(Service._remoteSetup(makeOutdatedMeta()));
+ mock.restore();
+
+ _("Checking that remoteSetup returns false on 503 on get /meta/global after clear cached one.");
+ mock = mockHandler(GLOBAL_PATH, returnStatusCode("GET", 503));
+ Service.status.sync = "";
+ Service.recordManager.set(Service.metaURL, { isNew: false });
+ do_check_false(Service._remoteSetup(makeOutdatedMeta()));
+ do_check_eq(Service.status.sync, "");
+ mock.restore();
+
+ metaColl.delete({});
+
+ _("Do an initial sync.");
+ let beforeSync = Date.now()/1000;
+ Service.sync();
+
+ _("Checking that remoteSetup returns true.");
+ do_check_true(Service._remoteSetup());
+
+ _("Verify that the meta record was uploaded.");
+ do_check_eq(meta_global.data.syncID, Service.syncID);
+ do_check_eq(meta_global.data.storageVersion, STORAGE_VERSION);
+ do_check_eq(meta_global.data.engines.clients.version, Service.clientsEngine.version);
+ do_check_eq(meta_global.data.engines.clients.syncID, Service.clientsEngine.syncID);
+
+ _("Set the collection info hash so that sync() will remember the modified times for future runs.");
+ collections.meta = Service.clientsEngine.lastSync;
+ collections.clients = Service.clientsEngine.lastSync;
+ Service.sync();
+
+ _("Sync again and verify that meta/global wasn't downloaded again");
+ meta_global.wasCalled = false;
+ Service.sync();
+ do_check_false(meta_global.wasCalled);
+
+ _("Fake modified records. This will cause a redownload, but not reupload since it hasn't changed.");
+ collections.meta += 42;
+ meta_global.wasCalled = false;
+
+ let metaModified = meta_global.modified;
+
+ Service.sync();
+ do_check_true(meta_global.wasCalled);
+ do_check_eq(metaModified, meta_global.modified);
+
+ _("Checking bad passphrases.");
+ let pp = Service.identity.syncKey;
+ Service.identity.syncKey = "notvalid";
+ do_check_false(Service.verifyAndFetchSymmetricKeys());
+ do_check_eq(Service.status.sync, CREDENTIALS_CHANGED);
+ do_check_eq(Service.status.login, LOGIN_FAILED_INVALID_PASSPHRASE);
+ Service.identity.syncKey = pp;
+ do_check_true(Service.verifyAndFetchSymmetricKeys());
+
+ // changePassphrase wipes our keys, and they're regenerated on next sync.
+ _("Checking changed passphrase.");
+ let existingDefault = Service.collectionKeys.keyForCollection();
+ let existingKeysPayload = keysWBO.payload;
+ let newPassphrase = "bbbbbabcdeabcdeabcdeabcdea";
+ Service.changePassphrase(newPassphrase);
+
+ _("Local key cache is full, but different.");
+ do_check_true(!!Service.collectionKeys._default);
+ do_check_false(Service.collectionKeys._default.equals(existingDefault));
+
+ _("Server has new keys.");
+ do_check_true(!!keysWBO.payload);
+ do_check_true(!!keysWBO.modified);
+ do_check_neq(keysWBO.payload, existingKeysPayload);
+
+ // Try to screw up HMAC calculation.
+ // Re-encrypt keys with a new random keybundle, and upload them to the
+ // server, just as might happen with a second client.
+ _("Attempting to screw up HMAC by re-encrypting keys.");
+ let keys = Service.collectionKeys.asWBO();
+ let b = new BulkKeyBundle("hmacerror");
+ b.generateRandom();
+ collections.crypto = keys.modified = 100 + (Date.now()/1000); // Future modification time.
+ keys.encrypt(b);
+ keys.upload(Service.resource(Service.cryptoKeysURL));
+
+ do_check_false(Service.verifyAndFetchSymmetricKeys());
+ do_check_eq(Service.status.login, LOGIN_FAILED_INVALID_PASSPHRASE);
+ } finally {
+ Svc.Prefs.resetBranch("");
+ server.stop(do_test_finished);
+ }
+}