tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_service_sync_remoteSetup.js (8406B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 const { Service } = ChromeUtils.importESModule(
      5  "resource://services-sync/service.sys.mjs"
      6 );
      7 
      8 // This sucks, but this test fails if this engine is enabled, due to dumb
      9 // things that aren't related to this engine. In short:
     10 // * Because the addon manager isn't initialized, the addons engine fails to
     11 //   initialize. So we end up writing a meta/global with `extension-storage`
     12 //   but not addons.
     13 // * After we sync, we discover 'addons' is locally enabled, but because it's
     14 //   not in m/g, we decide it's been remotely declined (and it decides this
     15 //   without even considering `declined`). So we disable 'addons'.
     16 // * Disabling 'addons' means 'extension-storage' is disabled - but because
     17 //   that *is* in meta/global we re-update meta/global to remove it.
     18 // * This test fails due to the extra, unexpected update of m/g.
     19 //
     20 // Another option would be to ensure the addons manager is initialized, but
     21 // that's a larger patch and still isn't strictly relevant to what's being
     22 // tested here, so...
     23 Services.prefs.setBoolPref(
     24  "services.sync.engine.extension-storage.force",
     25  false
     26 );
     27 
     28 add_task(async function run_test() {
     29  enableValidationPrefs();
     30 
     31  validate_all_future_pings();
     32  Log.repository.rootLogger.addAppender(new Log.DumpAppender());
     33 
     34  let clients = new ServerCollection();
     35  let meta_global = new ServerWBO("global");
     36 
     37  let collectionsHelper = track_collections_helper();
     38  let upd = collectionsHelper.with_updated_collection;
     39  let collections = collectionsHelper.collections;
     40 
     41  function wasCalledHandler(wbo) {
     42    let handler = wbo.handler();
     43    return function () {
     44      wbo.wasCalled = true;
     45      handler.apply(this, arguments);
     46    };
     47  }
     48 
     49  let keysWBO = new ServerWBO("keys");
     50  let cryptoColl = new ServerCollection({ keys: keysWBO });
     51  let metaColl = new ServerCollection({ global: meta_global });
     52  do_test_pending();
     53 
     54  /**
     55   * Handle the bulk DELETE request sent by wipeServer.
     56   */
     57  function storageHandler(request, response) {
     58    Assert.equal("DELETE", request.method);
     59    Assert.ok(request.hasHeader("X-Confirm-Delete"));
     60 
     61    _("Wiping out all collections.");
     62    cryptoColl.delete({});
     63    clients.delete({});
     64    metaColl.delete({});
     65 
     66    let ts = new_timestamp();
     67    collectionsHelper.update_collection("crypto", ts);
     68    collectionsHelper.update_collection("clients", ts);
     69    collectionsHelper.update_collection("meta", ts);
     70    return_timestamp(request, response, ts);
     71  }
     72 
     73  const GLOBAL_PATH = "/1.1/johndoe/storage/meta/global";
     74 
     75  let handlers = {
     76    "/1.1/johndoe/storage": storageHandler,
     77    "/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
     78    "/1.1/johndoe/storage/crypto": upd("crypto", cryptoColl.handler()),
     79    "/1.1/johndoe/storage/clients": upd("clients", clients.handler()),
     80    "/1.1/johndoe/storage/meta": upd("meta", wasCalledHandler(metaColl)),
     81    "/1.1/johndoe/storage/meta/global": upd(
     82      "meta",
     83      wasCalledHandler(meta_global)
     84    ),
     85    "/1.1/johndoe/info/collections": collectionsHelper.handler,
     86  };
     87 
     88  function mockHandler(path, mock) {
     89    server.registerPathHandler(path, mock(handlers[path]));
     90    return {
     91      restore() {
     92        server.registerPathHandler(path, handlers[path]);
     93      },
     94    };
     95  }
     96 
     97  let server = httpd_setup(handlers);
     98 
     99  try {
    100    _("Checking Status.sync with no credentials.");
    101    await Service.verifyAndFetchSymmetricKeys();
    102    Assert.equal(Service.status.sync, CREDENTIALS_CHANGED);
    103    Assert.equal(Service.status.login, LOGIN_FAILED_NO_PASSPHRASE);
    104 
    105    await configureIdentity({ username: "johndoe" }, server);
    106 
    107    await Service.login();
    108    _("Checking that remoteSetup returns true when credentials have changed.");
    109    (await Service.recordManager.get(Service.metaURL)).payload.syncID =
    110      "foobar";
    111    Assert.ok(await Service._remoteSetup());
    112 
    113    let returnStatusCode = (method, code) => oldMethod => (req, res) => {
    114      if (req.method === method) {
    115        res.setStatusLine(req.httpVersion, code, "");
    116      } else {
    117        oldMethod(req, res);
    118      }
    119    };
    120 
    121    let mock = mockHandler(GLOBAL_PATH, returnStatusCode("GET", 401));
    122    Service.recordManager.del(Service.metaURL);
    123    _(
    124      "Checking that remoteSetup returns false on 401 on first get /meta/global."
    125    );
    126    Assert.equal(false, await Service._remoteSetup());
    127    mock.restore();
    128 
    129    await Service.login();
    130    mock = mockHandler(GLOBAL_PATH, returnStatusCode("GET", 503));
    131    Service.recordManager.del(Service.metaURL);
    132    _(
    133      "Checking that remoteSetup returns false on 503 on first get /meta/global."
    134    );
    135    Assert.equal(false, await Service._remoteSetup());
    136    Assert.equal(Service.status.sync, METARECORD_DOWNLOAD_FAIL);
    137    mock.restore();
    138 
    139    await Service.login();
    140    mock = mockHandler(GLOBAL_PATH, returnStatusCode("GET", 404));
    141    Service.recordManager.del(Service.metaURL);
    142    _("Checking that remoteSetup recovers on 404 on first get /meta/global.");
    143    Assert.ok(await Service._remoteSetup());
    144    mock.restore();
    145 
    146    let makeOutdatedMeta = async () => {
    147      Service.metaModified = 0;
    148      let infoResponse = await Service._fetchInfo();
    149      return {
    150        status: infoResponse.status,
    151        obj: {
    152          crypto: infoResponse.obj.crypto,
    153          clients: infoResponse.obj.clients,
    154          meta: 1,
    155        },
    156      };
    157    };
    158 
    159    _(
    160      "Checking that remoteSetup recovers on 404 on get /meta/global after clear cached one."
    161    );
    162    mock = mockHandler(GLOBAL_PATH, returnStatusCode("GET", 404));
    163    Service.recordManager.set(Service.metaURL, { isNew: false });
    164    Assert.ok(await Service._remoteSetup(await makeOutdatedMeta()));
    165    mock.restore();
    166 
    167    _(
    168      "Checking that remoteSetup returns false on 503 on get /meta/global after clear cached one."
    169    );
    170    mock = mockHandler(GLOBAL_PATH, returnStatusCode("GET", 503));
    171    Service.status.sync = "";
    172    Service.recordManager.set(Service.metaURL, { isNew: false });
    173    Assert.equal(false, await Service._remoteSetup(await makeOutdatedMeta()));
    174    Assert.equal(Service.status.sync, "");
    175    mock.restore();
    176 
    177    metaColl.delete({});
    178 
    179    _("Do an initial sync.");
    180    await Service.sync();
    181 
    182    _("Checking that remoteSetup returns true.");
    183    Assert.ok(await Service._remoteSetup());
    184 
    185    _("Verify that the meta record was uploaded.");
    186    Assert.equal(meta_global.data.syncID, Service.syncID);
    187    Assert.equal(meta_global.data.storageVersion, STORAGE_VERSION);
    188    Assert.equal(
    189      meta_global.data.engines.clients.version,
    190      Service.clientsEngine.version
    191    );
    192    Assert.equal(
    193      meta_global.data.engines.clients.syncID,
    194      await Service.clientsEngine.getSyncID()
    195    );
    196 
    197    _(
    198      "Set the collection info hash so that sync() will remember the modified times for future runs."
    199    );
    200    let lastSync = await Service.clientsEngine.getLastSync();
    201    collections.meta = lastSync;
    202    collections.clients = lastSync;
    203    await Service.sync();
    204 
    205    _("Sync again and verify that meta/global wasn't downloaded again");
    206    meta_global.wasCalled = false;
    207    await Service.sync();
    208    Assert.ok(!meta_global.wasCalled);
    209 
    210    _(
    211      "Fake modified records. This will cause a redownload, but not reupload since it hasn't changed."
    212    );
    213    collections.meta += 42;
    214    meta_global.wasCalled = false;
    215 
    216    let metaModified = meta_global.modified;
    217 
    218    await Service.sync();
    219    Assert.ok(meta_global.wasCalled);
    220    Assert.equal(metaModified, meta_global.modified);
    221 
    222    // Try to screw up HMAC calculation.
    223    // Re-encrypt keys with a new random keybundle, and upload them to the
    224    // server, just as might happen with a second client.
    225    _("Attempting to screw up HMAC by re-encrypting keys.");
    226    let keys = Service.collectionKeys.asWBO();
    227    let b = new BulkKeyBundle("hmacerror");
    228    await b.generateRandom();
    229    collections.crypto = keys.modified = 100 + Date.now() / 1000; // Future modification time.
    230    await keys.encrypt(b);
    231    await keys.upload(Service.resource(Service.cryptoKeysURL));
    232 
    233    Assert.equal(false, await Service.verifyAndFetchSymmetricKeys());
    234    Assert.equal(Service.status.login, LOGIN_FAILED_INVALID_PASSPHRASE);
    235  } finally {
    236    for (const pref of Svc.PrefBranch.getChildList("")) {
    237      Svc.PrefBranch.clearUserPref(pref);
    238    }
    239    server.stop(do_test_finished);
    240  }
    241 });