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);
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));
}
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);
// The remote login should have the updated password.
let newRec = collection.cleartext(guid);
equal(
newRec.password, "newpassword" + i, "Should update remote password for login"
);
// 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"
);
// 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));
// 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);
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);
// 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
);
// 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
);
// 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
);
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);
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);
// 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);
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);
// 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);
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);
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);
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));
}
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");
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;
}
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);
}
});
¤ 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.21Bemerkung:
(vorverarbeitet)
¤