Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/services/sync/tests/unit/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 37 kB image not shown  

Quellcode-Bibliothek test_password_engine.js   Sprache: JAVA

 
const { FXA_PWDMGR_HOST, FXA_PWDMGR_REALM } = ChromeUtils.importESModule(
  "resource://gre/modules/FxAccountsCommon.sys.mjs"
);
const { LoginRec } = ChromeUtils.importESModule(
  "resource://services-sync/engines/passwords.sys.mjs"
);
const { Service } = ChromeUtils.importESModule(
  "resource://services-sync/service.sys.mjs"
);

const LoginInfo = Components.Constructor(
  "@mozilla.org/login-manager/loginInfo;1",
  Ci.nsILoginInfo,
  "init"
);

const { LoginCSVImport } = ChromeUtils.importESModule(
  "resource://gre/modules/LoginCSVImport.sys.mjs"
);

const { FileTestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/FileTestUtils.sys.mjs"
);

const PropertyBag = Components.Constructor(
  "@mozilla.org/hash-property-bag;1",
  Ci.nsIWritablePropertyBag
);

async function cleanup(engine, server) {
  await engine._tracker.stop();
  await engine.wipeClient();
  engine.lastModified = null;
  for (const pref of Svc.PrefBranch.getChildList("")) {
    Svc.PrefBranch.clearUserPref(pref);
  }
  Service.recordManager.clearCache();
  if (server) {
    await promiseStopServer(server);
  }
}

add_task(async function setup() {
  // Disable addon sync because AddonManager won't be initialized here.
  await Service.engineManager.unregister("addons");
  await Service.engineManager.unregister("extension-storage");
});

add_task(async function test_ignored_fields() {
  _("Only changes to syncable fields should be tracked");

  let engine = Service.engineManager.get("passwords");

  let server = await serverForFoo(engine);
  await SyncTestingInfrastructure(server);

  enableValidationPrefs();

  let loginInfo = new LoginInfo(
    "https://example.com",
    "",
    null,
    "username",
    "password",
    "",
    ""
  );

  // Setting syncCounter to -1 so that it will be incremented to 0 when added.
  loginInfo.syncCounter = -1;
  let login = await Services.logins.addLoginAsync(loginInfo);
  login.QueryInterface(Ci.nsILoginMetaInfo); // For `guid`.

  engine._tracker.start();

  try {
    let nonSyncableProps = new PropertyBag();
    nonSyncableProps.setProperty("timeLastUsed", Date.now());
    nonSyncableProps.setProperty("timesUsed", 3);
    Services.logins.modifyLogin(login, nonSyncableProps);

    let noChanges = await engine.pullNewChanges();
    deepEqual(noChanges, {}, "Should not track non-syncable fields");

    let syncableProps = new PropertyBag();
    syncableProps.setProperty("username""newuser");
    Services.logins.modifyLogin(login, syncableProps);

    let changes = await engine.pullNewChanges();
    deepEqual(
      Object.keys(changes),
      [login.guid],
      "Should track syncable fields"
    );
  } finally {
    await cleanup(engine, server);
  }
});

add_task(async function test_ignored_sync_credentials() {
  _("Sync credentials in login manager should be ignored");

  let engine = Service.engineManager.get("passwords");

  let server = await serverForFoo(engine);
  await SyncTestingInfrastructure(server);

  enableValidationPrefs();

  engine._tracker.start();

  try {
    let login = await Services.logins.addLoginAsync(
      new LoginInfo(
        FXA_PWDMGR_HOST,
        null,
        FXA_PWDMGR_REALM,
        "fxa-uid",
        "creds",
        "",
        ""
      )
    );

    let noChanges = await engine.pullNewChanges();
    deepEqual(noChanges, {}, "Should not track new FxA credentials");

    let props = new PropertyBag();
    props.setProperty("password""newcreds");
    Services.logins.modifyLogin(login, props);

    noChanges = await engine.pullNewChanges();
    deepEqual(noChanges, {}, "Should not track changes to FxA credentials");

    let foundLogins = await Services.logins.searchLoginsAsync({
      origin: FXA_PWDMGR_HOST,
    });
    equal(foundLogins.length, 1);
    equal(foundLogins[0].syncCounter, 0);
    equal(foundLogins[0].everSynced, false);
  } finally {
    await cleanup(engine, server);
  }
});

add_task(async function test_password_engine() {
  _("Basic password sync test");

  let engine = Service.engineManager.get("passwords");

  let server = await serverForFoo(engine);
  await SyncTestingInfrastructure(server);
  let collection = server.user("foo").collection("passwords");

  enableValidationPrefs();

  _("Add new login to upload during first sync");
  let newLogin;
  {
    let login = new LoginInfo(
      "https://example.com",
      "",
      null,
      "username",
      "password",
      "",
      ""
    );
    await Services.logins.addLoginAsync(login);

    let logins = await Services.logins.searchLoginsAsync({
      origin: "https://example.com",
    });
    equal(logins.length, 1, "Should find new login in login manager");
    newLogin = logins[0].QueryInterface(Ci.nsILoginMetaInfo);

    // Insert a server record that's older, so that we prefer the local one.
    let rec = new LoginRec("passwords", newLogin.guid);
    rec.formSubmitURL = newLogin.formActionOrigin;
    rec.httpRealm = newLogin.httpRealm;
    rec.hostname = newLogin.origin;
    rec.username = newLogin.username;
    rec.password = "sekrit";
    let remotePasswordChangeTime = Date.now() - 1 * 60 * 60 * 24 * 1000;
    rec.timeCreated = remotePasswordChangeTime;
    rec.timePasswordChanged = remotePasswordChangeTime;
    collection.insert(
      newLogin.guid,
      encryptPayload(rec.cleartext),
      remotePasswordChangeTime / 1000
    );
  }

  _("Add login with older password change time to replace during first sync");
  let oldLogin;
  {
    let login = new LoginInfo(
      "https://mozilla.com",
      "",
      null,
      "us3r",
      "0ldpa55",
      "",
      ""
    );
    await Services.logins.addLoginAsync(login);

    let props = new PropertyBag();
    let localPasswordChangeTime = Date.now() - 1 * 60 * 60 * 24 * 1000;
    props.setProperty("timePasswordChanged", localPasswordChangeTime);
    Services.logins.modifyLogin(login, props);

    let logins = await Services.logins.searchLoginsAsync({
      origin: "https://mozilla.com",
    });
    equal(logins.length, 1, "Should find old login in login manager");
    oldLogin = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
    equal(oldLogin.timePasswordChanged, localPasswordChangeTime);

    let rec = new LoginRec("passwords", oldLogin.guid);
    rec.hostname = oldLogin.origin;
    rec.formSubmitURL = oldLogin.formActionOrigin;
    rec.httpRealm = oldLogin.httpRealm;
    rec.username = oldLogin.username;
    // Change the password and bump the password change time to ensure we prefer
    // the remote one during reconciliation.
    rec.password = "n3wpa55";
    rec.usernameField = oldLogin.usernameField;
    rec.passwordField = oldLogin.usernameField;
    rec.timeCreated = oldLogin.timeCreated;
    rec.timePasswordChanged = Date.now();
    collection.insert(oldLogin.guid, encryptPayload(rec.cleartext));
  }

  await engine._tracker.stop();

  try {
    await sync_engine_and_validate_telem(engine, false);

    let newRec = collection.cleartext(newLogin.guid);
    equal(
      newRec.password,
      "password",
      "Should update remote password for newer login"
    );

    let logins = await Services.logins.searchLoginsAsync({
      origin: "https://mozilla.com",
    });
    equal(
      logins[0].password,
      "n3wpa55",
      "Should update local password for older login"
    );
  } finally {
    await cleanup(engine, server);
  }
});

add_task(async function test_sync_outgoing() {
  _("Test syncing outgoing records");

  let engine = Service.engineManager.get("passwords");

  let server = await serverForFoo(engine);
  await SyncTestingInfrastructure(server);

  let collection = server.user("foo").collection("passwords");

  let loginInfo = new LoginInfo(
    "http://mozilla.com",
    "http://mozilla.com",
    null,
    "theuser",
    "thepassword",
    "username",
    "password"
  );
  let login = await Services.logins.addLoginAsync(loginInfo);

  engine._tracker.start();

  try {
    let foundLogins = await Services.logins.searchLoginsAsync({
      origin: "http://mozilla.com",
    });
    equal(foundLogins.length, 1);
    equal(foundLogins[0].syncCounter, 1);
    equal(foundLogins[0].everSynced, false);
    equal(collection.count(), 0);

    let guid = foundLogins[0].QueryInterface(Ci.nsILoginMetaInfo).guid;

    let changes = await engine.getChangedIDs();
    let change = changes[guid];
    equal(Object.keys(changes).length, 1);
    equal(change.counter, 1);
    ok(!change.deleted);

    // This test modifies the password and then performs a sync and
    // then ensures that the synced record is correct. This is done twice
    // to ensure that syncing occurs correctly when the server record does not
    // yet exist and when it does already exist.
    for (let i = 1; i <= 2; i++) {
      _("Modify the password iteration " + i);
      foundLogins[0].password = "newpassword" + i;
      Services.logins.modifyLogin(login, foundLogins[0]);
      foundLogins = await Services.logins.searchLoginsAsync({
        origin: "http://mozilla.com",
      });
      equal(foundLogins.length, 1);
      // On the first pass, the counter should be 2, one for the add and one for the modify.
      // No sync has occurred yet so everSynced should be false.
      // On the second pass, the counter will only be 1 for the modify. The everSynced
      // property should be true as the sync happened on the last iteration.
      equal(foundLogins[0].syncCounter, i == 2 ? 1 : 2);
      equal(foundLogins[0].everSynced, i == 2);

      changes = await engine.getChangedIDs();
      change = changes[guid];
      equal(Object.keys(changes).length, 1);
      equal(change.counter, i == 2 ? 1 : 2);
      ok(!change.deleted);

      _("Perform sync after modifying the password");
      await sync_engine_and_validate_telem(engine, false);

      equal(Object.keys(await engine.getChangedIDs()), 0);

      // The remote login should have the updated password.
      let newRec = collection.cleartext(guid);
      equal(
        newRec.password,
        "newpassword" + i,
        "Should update remote password for login"
      );

      foundLogins = await Services.logins.searchLoginsAsync({
        origin: "http://mozilla.com",
      });
      equal(foundLogins.length, 1);
      equal(foundLogins[0].syncCounter, 0);
      equal(foundLogins[0].everSynced, true);

      login.password = "newpassword" + i;
    }

    // Next, modify the username and sync.
    _("Modify the username");
    foundLogins[0].username = "newuser";
    Services.logins.modifyLogin(login, foundLogins[0]);
    foundLogins = await Services.logins.searchLoginsAsync({
      origin: "http://mozilla.com",
    });
    equal(foundLogins.length, 1);
    equal(foundLogins[0].syncCounter, 1);
    equal(foundLogins[0].everSynced, true);

    _("Perform sync after modifying the username");
    await sync_engine_and_validate_telem(engine, false);

    // The remote login should have the updated password.
    let newRec = collection.cleartext(guid);
    equal(
      newRec.username,
      "newuser",
      "Should update remote username for login"
    );

    foundLogins = await Services.logins.searchLoginsAsync({
      origin: "http://mozilla.com",
    });
    equal(foundLogins.length, 1);
    equal(foundLogins[0].syncCounter, 0);
    equal(foundLogins[0].everSynced, true);

    // Finally, remove the login. The server record should be marked as deleted.
    _("Remove the login");
    equal(collection.count(), 1);
    equal(Services.logins.countLogins(""""""), 2);
    equal((await Services.logins.getAllLogins()).length, 2);
    ok(await engine._store.itemExists(guid));

    ok((await engine._store.getAllIDs())[guid]);

    Services.logins.removeLogin(foundLogins[0]);
    foundLogins = await Services.logins.searchLoginsAsync({
      origin: "http://mozilla.com",
    });
    equal(foundLogins.length, 0);

    changes = await engine.getChangedIDs();
    change = changes[guid];
    equal(Object.keys(changes).length, 1);
    equal(change.counter, 1);
    ok(change.deleted);

    _("Perform sync after removing the login");
    await sync_engine_and_validate_telem(engine, false);

    equal(collection.count(), 1);
    let payload = collection.payloads()[0];
    ok(payload.deleted);

    equal(Object.keys(await engine.getChangedIDs()), 0);

    // All of these should not include the deleted login. Only the FxA password should exist.
    equal(Services.logins.countLogins(""""""), 1);
    equal((await Services.logins.getAllLogins()).length, 1);
    ok(!(await engine._store.itemExists(guid)));

    // getAllIDs includes deleted items but skips the FxA login.
    ok((await engine._store.getAllIDs())[guid]);
    let deletedLogin = await engine._store._getLoginFromGUID(guid);

    equal(deletedLogin.hostname, null"deleted login hostname");
    equal(
      deletedLogin.formActionOrigin,
      null,
      "deleted login formActionOrigin"
    );
    equal(deletedLogin.formSubmitURL, null"deleted login formSubmitURL");
    equal(deletedLogin.httpRealm, null"deleted login httpRealm");
    equal(deletedLogin.username, null"deleted login username");
    equal(deletedLogin.password, null"deleted login password");
    equal(deletedLogin.usernameField, """deleted login usernameField");
    equal(deletedLogin.passwordField, """deleted login passwordField");
    equal(deletedLogin.unknownFields, null"deleted login unknownFields");
    equal(deletedLogin.timeCreated, 0, "deleted login timeCreated");
    equal(deletedLogin.timeLastUsed, 0, "deleted login timeLastUsed");
    equal(deletedLogin.timesUsed, 0, "deleted login timesUsed");

    // These fields are not reset when the login is removed.
    equal(deletedLogin.guid, guid, "deleted login guid");
    equal(deletedLogin.everSynced, true"deleted login everSynced");
    equal(deletedLogin.syncCounter, 0, "deleted login syncCounter");
    Assert.greater(
      deletedLogin.timePasswordChanged,
      0,
      "deleted login timePasswordChanged"
    );
  } finally {
    await engine._tracker.stop();

    await cleanup(engine, server);
  }
});

add_task(async function test_sync_incoming() {
  _("Test syncing incoming records");

  let engine = Service.engineManager.get("passwords");

  let server = await serverForFoo(engine);
  await SyncTestingInfrastructure(server);

  let collection = server.user("foo").collection("passwords");

  const checkFields = [
    "formSubmitURL",
    "hostname",
    "httpRealm",
    "username",
    "password",
    "usernameField",
    "passwordField",
    "timeCreated",
  ];

  let guid1 = Utils.makeGUID();
  let details = {
    formSubmitURL: "https://www.example.com",
    hostname: "https://www.example.com",
    httpRealm: null,
    username: "camel",
    password: "llama",
    usernameField: "username-field",
    passwordField: "password-field",
    timeCreated: Date.now(),
    timePasswordChanged: Date.now(),
  };

  try {
    // This test creates a remote server record and then verifies that the login
    // has been added locally after the sync occurs.
    _("Create remote login");
    collection.insertRecord(Object.assign({}, details, { id: guid1 }));

    _("Perform sync when remote login has been added");
    await sync_engine_and_validate_telem(engine, false);

    let logins = await Services.logins.searchLoginsAsync({
      origin: "https://www.example.com",
    });
    equal(logins.length, 1);

    equal(logins[0].QueryInterface(Ci.nsILoginMetaInfo).guid, guid1);
    checkFields.forEach(field => {
      equal(logins[0][field], details[field]);
    });
    equal(logins[0].timePasswordChanged, details.timePasswordChanged);
    equal(logins[0].syncCounter, 0);
    equal(logins[0].everSynced, true);

    // Modify the password within the remote record and then sync again.
    _("Perform sync when remote login's password has been modified");
    let newTime = Date.now();
    collection.updateRecord(
      guid1,
      cleartext => {
        cleartext.password = "alpaca";
      },
      newTime / 1000 + 10
    );

    await engine.setLastSync(newTime / 1000 - 30);
    await sync_engine_and_validate_telem(engine, false);

    logins = await Services.logins.searchLoginsAsync({
      origin: "https://www.example.com",
    });
    equal(logins.length, 1);

    details.password = "alpaca";
    equal(logins[0].QueryInterface(Ci.nsILoginMetaInfo).guid, guid1);
    checkFields.forEach(field => {
      equal(logins[0][field], details[field]);
    });
    Assert.greater(logins[0].timePasswordChanged, details.timePasswordChanged);
    equal(logins[0].syncCounter, 0);
    equal(logins[0].everSynced, true);

    // Modify the username within the remote record and then sync again.
    _("Perform sync when remote login's username has been modified");
    newTime = Date.now();
    collection.updateRecord(
      guid1,
      cleartext => {
        cleartext.username = "guanaco";
      },
      newTime / 1000 + 10
    );

    await engine.setLastSync(newTime / 1000 - 30);
    await sync_engine_and_validate_telem(engine, false);

    logins = await Services.logins.searchLoginsAsync({
      origin: "https://www.example.com",
    });
    equal(logins.length, 1);

    details.username = "guanaco";
    equal(logins[0].QueryInterface(Ci.nsILoginMetaInfo).guid, guid1);
    checkFields.forEach(field => {
      equal(logins[0][field], details[field]);
    });
    Assert.greater(logins[0].timePasswordChanged, details.timePasswordChanged);
    equal(logins[0].syncCounter, 0);
    equal(logins[0].everSynced, true);

    // Mark the remote record as deleted and then sync again.
    _("Perform sync when remote login has been marked for deletion");
    newTime = Date.now();
    collection.updateRecord(
      guid1,
      cleartext => {
        cleartext.deleted = true;
      },
      newTime / 1000 + 10
    );

    await engine.setLastSync(newTime / 1000 - 30);
    await sync_engine_and_validate_telem(engine, false);

    logins = await Services.logins.searchLoginsAsync({
      origin: "https://www.example.com",
    });
    equal(logins.length, 0);
  } finally {
    await cleanup(engine, server);
  }
});

add_task(async function test_sync_incoming_deleted() {
  _("Test syncing incoming deleted records");

  let engine = Service.engineManager.get("passwords");

  let server = await serverForFoo(engine);
  await SyncTestingInfrastructure(server);

  let collection = server.user("foo").collection("passwords");

  let guid1 = Utils.makeGUID();
  let details2 = {
    formSubmitURL: "https://www.example.org",
    hostname: "https://www.example.org",
    httpRealm: null,
    username: "capybara",
    password: "beaver",
    usernameField: "username-field",
    passwordField: "password-field",
    timeCreated: Date.now(),
    timePasswordChanged: Date.now(),
    deleted: true,
  };

  try {
    // This test creates a remote server record that has been deleted
    // and then verifies that the login is not imported locally.
    _("Create remote login");
    collection.insertRecord(Object.assign({}, details2, { id: guid1 }));

    _("Perform sync when remote login has been deleted");
    await sync_engine_and_validate_telem(engine, false);

    let logins = await Services.logins.searchLoginsAsync({
      origin: "https://www.example.com",
    });
    equal(logins.length, 0);
    ok(!(await engine._store.getAllIDs())[guid1]);
    ok(!(await engine._store.itemExists(guid1)));
  } finally {
    await cleanup(engine, server);
  }
});

add_task(async function test_sync_incoming_deleted_localchanged_remotenewer() {
  _(
    "Test syncing incoming deleted records where the local login has been changed but the remote record is newer"
  );

  let engine = Service.engineManager.get("passwords");

  let server = await serverForFoo(engine);
  await SyncTestingInfrastructure(server);

  let collection = server.user("foo").collection("passwords");

  let loginInfo = new LoginInfo(
    "http://mozilla.com",
    "http://mozilla.com",
    null,
    "kangaroo",
    "kaola",
    "username",
    "password"
  );
  let login = await Services.logins.addLoginAsync(loginInfo);
  let guid = login.QueryInterface(Ci.nsILoginMetaInfo).guid;

  try {
    _("Perform sync on new login");
    await sync_engine_and_validate_telem(engine, false);

    let foundLogins = await Services.logins.searchLoginsAsync({
      origin: "http://mozilla.com",
    });
    foundLogins[0].password = "wallaby";
    Services.logins.modifyLogin(login, foundLogins[0]);

    // Use a time in the future to ensure that the remote record is newer.
    collection.updateRecord(
      guid,
      cleartext => {
        cleartext.deleted = true;
      },
      Date.now() / 1000 + 1000
    );

    _(
      "Perform sync when remote login has been deleted and local login has been changed"
    );
    await sync_engine_and_validate_telem(engine, false);

    let logins = await Services.logins.searchLoginsAsync({
      origin: "https://mozilla.com",
    });
    equal(logins.length, 0);
    ok(await engine._store.getAllIDs());
  } finally {
    await cleanup(engine, server);
  }
});

add_task(async function test_sync_incoming_deleted_localchanged_localnewer() {
  _(
    "Test syncing incoming deleted records where the local login has been changed but the local record is newer"
  );

  let engine = Service.engineManager.get("passwords");

  let server = await serverForFoo(engine);
  await SyncTestingInfrastructure(server);

  let collection = server.user("foo").collection("passwords");

  let loginInfo = new LoginInfo(
    "http://www.mozilla.com",
    "http://www.mozilla.com",
    null,
    "lion",
    "tiger",
    "username",
    "password"
  );
  let login = await Services.logins.addLoginAsync(loginInfo);
  let guid = login.QueryInterface(Ci.nsILoginMetaInfo).guid;

  try {
    _("Perform sync on new login");
    await sync_engine_and_validate_telem(engine, false);

    let foundLogins = await Services.logins.searchLoginsAsync({
      origin: "http://www.mozilla.com",
    });
    foundLogins[0].password = "cheetah";
    Services.logins.modifyLogin(login, foundLogins[0]);

    // Use a time in the past to ensure that the local record is newer.
    collection.updateRecord(
      guid,
      cleartext => {
        cleartext.deleted = true;
      },
      Date.now() / 1000 - 1000
    );

    _(
      "Perform sync when remote login has been deleted and local login has been changed"
    );
    await sync_engine_and_validate_telem(engine, false);

    let logins = await Services.logins.searchLoginsAsync({
      origin: "http://www.mozilla.com",
    });
    equal(logins.length, 1);
    equal(logins[0].password, "cheetah");
    equal(logins[0].syncCounter, 0);
    equal(logins[0].everSynced, true);
    ok(await engine._store.getAllIDs());
  } finally {
    await cleanup(engine, server);
  }
});

add_task(async function test_sync_incoming_no_formactionorigin() {
  _("Test syncing incoming a record where there is no formActionOrigin");

  let engine = Service.engineManager.get("passwords");

  let server = await serverForFoo(engine);
  await SyncTestingInfrastructure(server);

  let collection = server.user("foo").collection("passwords");

  const checkFields = [
    "formSubmitURL",
    "hostname",
    "httpRealm",
    "username",
    "password",
    "usernameField",
    "passwordField",
    "timeCreated",
  ];

  let guid1 = Utils.makeGUID();
  let details = {
    formSubmitURL: "",
    hostname: "https://www.example.com",
    httpRealm: null,
    username: "rabbit",
    password: "squirrel",
    usernameField: "username-field",
    passwordField: "password-field",
    timeCreated: Date.now(),
    timePasswordChanged: Date.now(),
  };

  try {
    // This test creates a remote server record and then verifies that the login
    // has been added locally after the sync occurs.
    _("Create remote login");
    collection.insertRecord(Object.assign({}, details, { id: guid1 }));

    _("Perform sync when remote login has been added");
    await sync_engine_and_validate_telem(engine, false);

    let logins = await Services.logins.searchLoginsAsync({
      origin: "https://www.example.com",
      formActionOrigin: "",
    });
    equal(logins.length, 1);

    equal(logins[0].QueryInterface(Ci.nsILoginMetaInfo).guid, guid1);
    checkFields.forEach(field => {
      equal(logins[0][field], details[field]);
    });
    equal(logins[0].timePasswordChanged, details.timePasswordChanged);
    equal(logins[0].syncCounter, 0);
    equal(logins[0].everSynced, true);
  } finally {
    await cleanup(engine, server);
  }
});

add_task(async function test_password_dupe() {
  let engine = Service.engineManager.get("passwords");

  let server = await serverForFoo(engine);
  await SyncTestingInfrastructure(server);
  let collection = server.user("foo").collection("passwords");

  let guid1 = Utils.makeGUID();
  let rec1 = new LoginRec("passwords", guid1);
  let guid2 = Utils.makeGUID();
  let cleartext = {
    formSubmitURL: "https://www.example.com",
    hostname: "https://www.example.com",
    httpRealm: null,
    username: "foo",
    password: "bar",
    usernameField: "username-field",
    passwordField: "password-field",
    timeCreated: Math.round(Date.now()),
    timePasswordChanged: Math.round(Date.now()),
  };
  rec1.cleartext = cleartext;

  _("Create remote record with same details and guid1");
  collection.insert(guid1, encryptPayload(rec1.cleartext));

  _("Create remote record with guid2");
  collection.insert(guid2, encryptPayload(cleartext));

  _("Create local record with same details and guid1");
  await engine._store.create(rec1);

  try {
    _("Perform sync");
    await sync_engine_and_validate_telem(engine, true);

    let logins = await Services.logins.searchLoginsAsync({
      origin: "https://www.example.com",
    });

    equal(logins.length, 1);
    equal(logins[0].QueryInterface(Ci.nsILoginMetaInfo).guid, guid2);
    equal(null, collection.payload(guid1));
  } finally {
    await cleanup(engine, server);
  }
});

add_task(async function test_updated_null_password_sync() {
  _("Ensure updated null login username is converted to a string");

  let engine = Service.engineManager.get("passwords");

  let server = await serverForFoo(engine);
  await SyncTestingInfrastructure(server);
  let collection = server.user("foo").collection("passwords");

  let guid1 = Utils.makeGUID();
  let guid2 = Utils.makeGUID();
  let remoteDetails = {
    formSubmitURL: "https://www.nullupdateexample.com",
    hostname: "https://www.nullupdateexample.com",
    httpRealm: null,
    username: null,
    password: "bar",
    usernameField: "username-field",
    passwordField: "password-field",
    timeCreated: Date.now(),
    timePasswordChanged: Date.now(),
  };
  let localDetails = {
    formSubmitURL: "https://www.nullupdateexample.com",
    hostname: "https://www.nullupdateexample.com",
    httpRealm: null,
    username: "foo",
    password: "foobar",
    usernameField: "username-field",
    passwordField: "password-field",
    timeCreated: Date.now(),
    timePasswordChanged: Date.now(),
  };

  _("Create remote record with same details and guid1");
  collection.insertRecord(Object.assign({}, remoteDetails, { id: guid1 }));

  try {
    _("Create local updated login with null password");
    await engine._store.update(Object.assign({}, localDetails, { id: guid2 }));

    _("Perform sync");
    await sync_engine_and_validate_telem(engine, false);

    let logins = await Services.logins.searchLoginsAsync({
      origin: "https://www.nullupdateexample.com",
    });

    equal(logins.length, 1);
    equal(logins[0].QueryInterface(Ci.nsILoginMetaInfo).guid, guid1);
  } finally {
    await cleanup(engine, server);
  }
});

add_task(async function test_updated_undefined_password_sync() {
  _("Ensure updated undefined login username is converted to a string");

  let engine = Service.engineManager.get("passwords");

  let server = await serverForFoo(engine);
  await SyncTestingInfrastructure(server);
  let collection = server.user("foo").collection("passwords");

  let guid1 = Utils.makeGUID();
  let guid2 = Utils.makeGUID();
  let remoteDetails = {
    formSubmitURL: "https://www.undefinedupdateexample.com",
    hostname: "https://www.undefinedupdateexample.com",
    httpRealm: null,
    username: undefined,
    password: "bar",
    usernameField: "username-field",
    passwordField: "password-field",
    timeCreated: Date.now(),
    timePasswordChanged: Date.now(),
  };
  let localDetails = {
    formSubmitURL: "https://www.undefinedupdateexample.com",
    hostname: "https://www.undefinedupdateexample.com",
    httpRealm: null,
    username: "foo",
    password: "foobar",
    usernameField: "username-field",
    passwordField: "password-field",
    timeCreated: Date.now(),
    timePasswordChanged: Date.now(),
  };

  _("Create remote record with same details and guid1");
  collection.insertRecord(Object.assign({}, remoteDetails, { id: guid1 }));

  try {
    _("Create local updated login with undefined password");
    await engine._store.update(Object.assign({}, localDetails, { id: guid2 }));

    _("Perform sync");
    await sync_engine_and_validate_telem(engine, false);

    let logins = await Services.logins.searchLoginsAsync({
      origin: "https://www.undefinedupdateexample.com",
    });

    equal(logins.length, 1);
    equal(logins[0].QueryInterface(Ci.nsILoginMetaInfo).guid, guid1);
  } finally {
    await cleanup(engine, server);
  }
});

add_task(async function test_new_null_password_sync() {
  _("Ensure new null login username is converted to a string");

  let engine = Service.engineManager.get("passwords");

  let server = await serverForFoo(engine);
  await SyncTestingInfrastructure(server);

  let guid1 = Utils.makeGUID();
  let rec1 = new LoginRec("passwords", guid1);
  rec1.cleartext = {
    formSubmitURL: "https://www.example.com",
    hostname: "https://www.example.com",
    httpRealm: null,
    username: null,
    password: "bar",
    usernameField: "username-field",
    passwordField: "password-field",
    timeCreated: Date.now(),
    timePasswordChanged: Date.now(),
  };

  try {
    _("Create local login with null password");
    await engine._store.create(rec1);

    _("Perform sync");
    await sync_engine_and_validate_telem(engine, false);

    let logins = await Services.logins.searchLoginsAsync({
      origin: "https://www.example.com",
    });

    equal(logins.length, 1);
    notEqual(logins[0].QueryInterface(Ci.nsILoginMetaInfo).username, null);
    notEqual(logins[0].QueryInterface(Ci.nsILoginMetaInfo).username, undefined);
    equal(logins[0].QueryInterface(Ci.nsILoginMetaInfo).username, "");
  } finally {
    await cleanup(engine, server);
  }
});

add_task(async function test_new_undefined_password_sync() {
  _("Ensure new undefined login username is converted to a string");

  let engine = Service.engineManager.get("passwords");

  let server = await serverForFoo(engine);
  await SyncTestingInfrastructure(server);

  let guid1 = Utils.makeGUID();
  let rec1 = new LoginRec("passwords", guid1);
  rec1.cleartext = {
    formSubmitURL: "https://www.example.com",
    hostname: "https://www.example.com",
    httpRealm: null,
    username: undefined,
    password: "bar",
    usernameField: "username-field",
    passwordField: "password-field",
    timeCreated: Date.now(),
    timePasswordChanged: Date.now(),
  };

  try {
    _("Create local login with undefined password");
    await engine._store.create(rec1);

    _("Perform sync");
    await sync_engine_and_validate_telem(engine, false);

    let logins = await Services.logins.searchLoginsAsync({
      origin: "https://www.example.com",
    });

    equal(logins.length, 1);
    notEqual(logins[0].QueryInterface(Ci.nsILoginMetaInfo).username, null);
    notEqual(logins[0].QueryInterface(Ci.nsILoginMetaInfo).username, undefined);
    equal(logins[0].QueryInterface(Ci.nsILoginMetaInfo).username, "");
  } finally {
    await cleanup(engine, server);
  }
});

add_task(async function test_sync_password_validation() {
  // This test isn't in test_password_validator to avoid duplicating cleanup.
  _("Ensure that if a password validation happens, it ends up in the ping");

  let engine = Service.engineManager.get("passwords");

  let server = await serverForFoo(engine);
  await SyncTestingInfrastructure(server);

  Svc.PrefBranch.setIntPref("engine.passwords.validation.interval", 0);
  Svc.PrefBranch.setIntPref(
    "engine.passwords.validation.percentageChance",
    100
  );
  Svc.PrefBranch.setIntPref("engine.passwords.validation.maxRecords", -1);
  Svc.PrefBranch.setBoolPref("engine.passwords.validation.enabled"true);

  try {
    let ping = await wait_for_ping(() => Service.sync());

    let engineInfo = ping.engines.find(e => e.name == "passwords");
    ok(engineInfo, "Engine should be in ping");

    let validation = engineInfo.validation;
    ok(validation, "Engine should have validation info");
  } finally {
    await cleanup(engine, server);
  }
});

add_task(async function test_roundtrip_unknown_fields() {
  _(
    "Testing that unknown fields from other clients get roundtripped back to server"
  );

  let engine = Service.engineManager.get("passwords");

  let server = await serverForFoo(engine);
  await SyncTestingInfrastructure(server);
  let collection = server.user("foo").collection("passwords");

  enableValidationPrefs();

  _("Add login with older password change time to replace during first sync");
  let oldLogin;
  {
    let login = new LoginInfo(
      "https://mozilla.com",
      "",
      null,
      "us3r",
      "0ldpa55",
      "",
      ""
    );
    await Services.logins.addLoginAsync(login);

    let props = new PropertyBag();
    let localPasswordChangeTime = Math.round(
      Date.now() - 1 * 60 * 60 * 24 * 1000
    );
    props.setProperty("timePasswordChanged", localPasswordChangeTime);
    Services.logins.modifyLogin(login, props);

    let logins = await Services.logins.searchLoginsAsync({
      origin: "https://mozilla.com",
    });
    equal(logins.length, 1, "Should find old login in login manager");
    oldLogin = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
    equal(oldLogin.timePasswordChanged, localPasswordChangeTime);

    let rec = new LoginRec("passwords", oldLogin.guid);
    rec.hostname = oldLogin.origin;
    rec.formSubmitURL = oldLogin.formActionOrigin;
    rec.httpRealm = oldLogin.httpRealm;
    rec.username = oldLogin.username;
    // Change the password and bump the password change time to ensure we prefer
    // the remote one during reconciliation.
    rec.password = "n3wpa55";
    rec.usernameField = oldLogin.usernameField;
    rec.passwordField = oldLogin.usernameField;
    rec.timeCreated = oldLogin.timeCreated;
    rec.timePasswordChanged = Math.round(Date.now());

    // pretend other clients have some snazzy new fields
    // we don't quite understand yet
    rec.cleartext.someStrField = "I am a str";
    rec.cleartext.someObjField = { newField: "I am a new field" };
    collection.insert(oldLogin.guid, encryptPayload(rec.cleartext));
  }

  await engine._tracker.stop();

  try {
    await sync_engine_and_validate_telem(engine, false);

    let logins = await Services.logins.searchLoginsAsync({
      origin: "https://mozilla.com",
    });
    equal(
      logins[0].password,
      "n3wpa55",
      "Should update local password for older login"
    );
    let expectedUnknowns = JSON.stringify({
      someStrField: "I am a str",
      someObjField: { newField: "I am a new field" },
    });
    // Check that the local record has all unknown fields properly
    // stringified
    equal(logins[0].unknownFields, expectedUnknowns);

    // Check that the server has the unknown fields unfurled and on the
    // top-level record
    let serverRec = collection.cleartext(oldLogin.guid);
    equal(serverRec.someStrField, "I am a str");
    equal(serverRec.someObjField.newField, "I am a new field");
  } finally {
    await cleanup(engine, server);
  }
});

add_task(async function test_new_passwords_from_csv() {
  _("Test syncing records imported from a csv file");

  let engine = Service.engineManager.get("passwords");

  let server = await serverForFoo(engine);
  await SyncTestingInfrastructure(server);

  let collection = server.user("foo").collection("passwords");

  engine._tracker.start();

  let data = [
    {
      hostname: "https://example.com",
      url: "https://example.com/path",
      username: "exampleuser",
      password: "examplepassword",
    },
    {
      hostname: "https://mozilla.org",
      url: "https://mozilla.org",
      username: "mozillauser",
      password: "mozillapassword",
    },
    {
      hostname: "https://www.example.org",
      url: "https://www.example.org/example1/example2",
      username: "person",
      password: "mypassword",
    },
  ];

  let csvData = ["url,username,login_password"];
  for (let row of data) {
    csvData.push(row.url + "," + row.username + "," + row.password);
  }

  let csvFile = FileTestUtils.getTempFile(`firefox_logins.csv`);
  await IOUtils.writeUTF8(csvFile.path, csvData.join("\r\n"));

  await LoginCSVImport.importFromCSV(csvFile.path);

  equal(
    engine._tracker.score,
    SCORE_INCREMENT_XLARGE,
    "Should only get one update notification for import"
  );

  _("Ensure that the csv import is correct");
  for (let item of data) {
    let foundLogins = await Services.logins.searchLoginsAsync({
      origin: item.hostname,
    });
    equal(foundLogins.length, 1);
    equal(foundLogins[0].syncCounter, 1);
    equal(foundLogins[0].everSynced, false);
    equal(foundLogins[0].username, item.username);
    equal(foundLogins[0].password, item.password);
  }

  _("Perform sync after modifying the password");
  await sync_engine_and_validate_telem(engine, false);

  _("Verify that the sync counter and status are updated");
  for (let item of data) {
    let foundLogins = await Services.logins.searchLoginsAsync({
      origin: item.hostname,
    });
    equal(foundLogins.length, 1);
    equal(foundLogins[0].syncCounter, 0);
    equal(foundLogins[0].everSynced, true);
    equal(foundLogins[0].username, item.username);
    equal(foundLogins[0].password, item.password);
    item.guid = foundLogins[0].guid;
  }

  equal(Object.keys(await engine.getChangedIDs()), 0);
  equal(collection.count(), 3);

  for (let item of data) {
    // The remote login should have the imported username and password.
    let newRec = collection.cleartext(item.guid);
    equal(newRec.username, item.username);
    equal(newRec.password, item.password);
  }
});

Messung V0.5
C=95 H=98 G=96

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.10Bemerkung:  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.