/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests the case where there are logins that cannot be decrypted. */ "use strict"; // Globals /** * Resets the token used to decrypt logins. This is equivalent to resetting the * primary password when it is not known. */ function resetPrimaryPassword() { let token = Cc["@mozilla.org/security/pk11tokendb;1"] .getService(Ci.nsIPK11TokenDB) .getInternalKeyToken(); token.reset(); token.initPassword(""); } // Tests /** * Resets the primary password after some logins were added to the database. */ add_task(async function test_logins_decrypt_failure() { let logins = TestData.loginList(); await Services.logins.addLogins(logins); // This makes the existing logins non-decryptable. resetPrimaryPassword(); // These functions don't see the non-decryptable entries anymore. let savedLogins = await Services.logins.getAllLogins(); Assert.equal(savedLogins.length, 0, "getAllLogins length"); await Assert.rejects(Services.logins.searchLoginsAsync({}), /is required/); Assert.equal(Services.logins.searchLogins(newPropertyBag()).length, 0); Assert.throws( () => Services.logins.modifyLogin(logins[0], newPropertyBag()), /No matching logins/ ); Assert.throws( () => Services.logins.removeLogin(logins[0]), /No matching logins/ ); // The function that counts logins sees the non-decryptable entries also. Assert.equal(Services.logins.countLogins("", "", ""), logins.length); // Equivalent logins can be added. await Services.logins.addLogins(logins); await LoginTestUtils.checkLogins(logins); Assert.equal( (await Services.logins.getAllLogins()).length, logins.length, "getAllLogins length" ); Assert.equal(Services.logins.countLogins("", "", ""), logins.length * 2); // Finding logins doesn't return the non-decryptable duplicates. Assert.equal( ( await Services.logins.searchLoginsAsync({ origin: "http://www.example.com", }) ).length, 1 ); let matchData = newPropertyBag({ origin: "http://www.example.com" }); Assert.equal(Services.logins.searchLogins(matchData).length, 1); // Removing single logins does not remove non-decryptable logins. for (let loginInfo of TestData.loginList()) { Services.logins.removeLogin(loginInfo); } Assert.equal((await Services.logins.getAllLogins()).length, 0); Assert.equal(Services.logins.countLogins("", "", ""), logins.length); // Removing all logins removes the non-decryptable entries also. Services.logins.removeAllUserFacingLogins(); Assert.equal((await Services.logins.getAllLogins()).length, 0); Assert.equal(Services.logins.countLogins("", "", ""), 0); }); // Bug 621846 - If a login has a GUID but can't be decrypted, a search for // that GUID will (correctly) fail. Ensure we can add a new login with that // same GUID. add_task(async function test_add_logins_with_decrypt_failure() { // a login with a GUID. let login = new LoginInfo( "http://www.example2.com", "http://www.example2.com", null, "the username", "the password for www.example.com", "form_field_username", "form_field_password" ); login.QueryInterface(Ci.nsILoginMetaInfo); login.guid = "{4bc50d2f-dbb6-4aa3-807c-c4c2065a2c35}"; // A different login but with the same GUID. let loginDupeGuid = new LoginInfo( "http://www.example3.com", "http://www.example3.com", null, "the username", "the password", "form_field_username", "form_field_password" ); loginDupeGuid.QueryInterface(Ci.nsILoginMetaInfo); loginDupeGuid.guid = login.guid; await Services.logins.addLoginAsync(login); // We can search for this login by GUID. let searchProp = Cc["@mozilla.org/hash-property-bag;1"].createInstance( Ci.nsIWritablePropertyBag2 ); searchProp.setPropertyAsAUTF8String("guid", login.guid); equal(Services.logins.searchLogins(searchProp).length, 1); // We should fail to re-add it as it remains good. await Assert.rejects( Services.logins.addLoginAsync(login), /This login already exists./ ); // We should fail to re-add a different login with the same GUID. await Assert.rejects( Services.logins.addLoginAsync(loginDupeGuid), /specified GUID already exists/ ); // This makes the existing login non-decryptable. resetPrimaryPassword(); // We can no longer find it in our search. equal(Services.logins.searchLogins(searchProp).length, 0); // So we should be able to re-add a login with that same GUID. await Services.logins.addLoginAsync(login); equal(Services.logins.searchLogins(searchProp).length, 1); Services.logins.removeAllUserFacingLogins(); }); // Test the "syncID" metadata works as expected on decryption failure. add_task(async function test_sync_metadata_with_decrypt_failure() { // And some sync metadata await Services.logins.setSyncID("sync-id"); await Services.logins.setLastSync(123); equal(await Services.logins.getSyncID(), "sync-id"); equal(await Services.logins.getLastSync(), 123); // This makes the existing login and syncID non-decryptable. resetPrimaryPassword(); // The syncID is now null. equal(await Services.logins.getSyncID(), null); // The sync timestamp isn't impacted. equal(await Services.logins.getLastSync(), 123); // But we should be able to set it again. await Services.logins.setSyncID("new-id"); equal(await Services.logins.getSyncID(), "new-id"); });