Bug 1567423 - Allow for searching passwords in about:logins when MP is disabled. r=MattN
authorJared Wein <jwein@mozilla.com>
Wed, 18 Sep 2019 03:47:22 +0000
changeset 555194 d625054db0cd549d82544631f2fec04bb619849e
parent 555193 23b2c24e9c80bc0ea77657504919b181a8b6da6d
child 555195 9f00e78551f6c0c34ddf76379fdbf710764eb0c2
push id2165
push userffxbld-merge
push dateMon, 14 Oct 2019 16:30:58 +0000
treeherdermozilla-release@0eae18af659f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN
bugs1567423
milestone70.0
Bug 1567423 - Allow for searching passwords in about:logins when MP is disabled. r=MattN Differential Revision: https://phabricator.services.mozilla.com/D45016
browser/components/aboutlogins/AboutLoginsChild.jsm
browser/components/aboutlogins/AboutLoginsParent.jsm
browser/components/aboutlogins/content/components/login-list.js
browser/components/aboutlogins/tests/browser/browser_masterPassword.js
browser/components/aboutlogins/tests/chrome/aboutlogins_common.js
browser/components/aboutlogins/tests/chrome/test_login_filter.html
browser/components/aboutlogins/tests/chrome/test_login_list.html
toolkit/components/passwordmgr/content/passwordManager.js
--- a/browser/components/aboutlogins/AboutLoginsChild.jsm
+++ b/browser/components/aboutlogins/AboutLoginsChild.jsm
@@ -59,16 +59,18 @@ class AboutLoginsChild extends ActorChil
             masterPasswordPromise = {
               resolve,
             };
 
             messageManager.sendAsyncMessage(
               "AboutLogins:MasterPasswordRequest"
             );
           },
+          // Default to enabled just in case a search is attempted before we get a response.
+          masterPasswordEnabled: true,
         };
         waivedContent.AboutLoginsUtils = Cu.cloneInto(
           AboutLoginsUtils,
           waivedContent,
           {
             cloneFunctions: true,
           }
         );
@@ -191,16 +193,18 @@ class AboutLoginsChild extends ActorChil
           masterPasswordPromise.resolve(message.data);
         }
         break;
       case "AboutLogins:SendFavicons":
         this.sendToContent("SendFavicons", message.data);
         break;
       case "AboutLogins:Setup":
         this.sendToContent("Setup", message.data);
+        Cu.waiveXrays(this.content).AboutLoginsUtils.masterPasswordEnabled =
+          message.data.masterPasswordEnabled;
         break;
       case "AboutLogins:ShowLoginItemError":
         this.sendToContent("ShowLoginItemError", message.data);
         break;
       case "AboutLogins:SyncState":
         this.sendToContent("SyncState", message.data);
         break;
       case "AboutLogins:UpdateBreaches":
--- a/browser/components/aboutlogins/AboutLoginsParent.jsm
+++ b/browser/components/aboutlogins/AboutLoginsParent.jsm
@@ -321,17 +321,17 @@ var AboutLoginsParent = {
         }
 
         message.target.ownerGlobal.openWebLinkIn(logins[0].origin, "tab", {
           relatedToCurrent: true,
         });
         break;
       }
       case "AboutLogins:MasterPasswordRequest": {
-        // This doesn't harm if passwords are not encrypted
+        // This does no harm if master password isn't set.
         let tokendb = Cc["@mozilla.org/security/pk11tokendb;1"].createInstance(
           Ci.nsIPK11TokenDB
         );
         let token = tokendb.getInternalKeyToken();
 
         let messageManager = message.target.messageManager;
 
         // If there is no master password, return as-if authentication succeeded.
@@ -407,16 +407,17 @@ var AboutLoginsParent = {
             appStoreBadgeLanguage,
             playStoreBadgeLanguage,
           };
 
           messageManager.sendAsyncMessage("AboutLogins:Setup", {
             logins,
             syncState,
             selectedBadgeLanguages,
+            masterPasswordEnabled: LoginHelper.isMasterPasswordSet(),
           });
 
           if (BREACH_ALERTS_ENABLED) {
             const breachesByLoginGUID = await LoginBreaches.getPotentialBreachesByLoginGUID(
               logins
             );
             messageManager.sendAsyncMessage(
               "AboutLogins:UpdateBreaches",
@@ -434,17 +435,16 @@ var AboutLoginsParent = {
           }
 
           // The message manager may be destroyed before the replies can be sent.
           log.debug(
             "AboutLogins:Subscribe: exception when replying with logins",
             ex
           );
         }
-
         break;
       }
       case "AboutLogins:UpdateLogin": {
         let loginUpdates = message.data.login;
         let logins = LoginHelper.searchLoginsWithObject({
           guid: loginUpdates.guid,
         });
         if (logins.length != 1) {
--- a/browser/components/aboutlogins/content/components/login-list.js
+++ b/browser/components/aboutlogins/content/components/login-list.js
@@ -388,17 +388,19 @@ export default class LoginList extends H
     if (this._filter) {
       matchingLoginGuids = new Set(
         this._loginGuidsSortedOrder.filter(guid => {
           let { login } = this._logins[guid];
           return (
             login.origin.toLocaleLowerCase().includes(this._filter) ||
             (!!login.httpRealm &&
               login.httpRealm.toLocaleLowerCase().includes(this._filter)) ||
-            login.username.toLocaleLowerCase().includes(this._filter)
+            login.username.toLocaleLowerCase().includes(this._filter) ||
+            (!window.AboutLoginsUtils.masterPasswordEnabled &&
+              login.password.toLocaleLowerCase().includes(this._filter))
           );
         })
       );
     } else {
       matchingLoginGuids = new Set([...this._loginGuidsSortedOrder]);
     }
 
     return matchingLoginGuids;
--- a/browser/components/aboutlogins/tests/browser/browser_masterPassword.js
+++ b/browser/components/aboutlogins/tests/browser/browser_masterPassword.js
@@ -46,17 +46,16 @@ add_task(async function test() {
   let mpDialogShown = waitForMPDialog("cancel");
   await BrowserTestUtils.openNewForegroundTab({
     gBrowser,
     url: "about:logins",
   });
   await mpDialogShown;
 
   registerCleanupFunction(function() {
-    LoginTestUtils.masterPassword.disable();
     Services.logins.removeAllLogins();
     BrowserTestUtils.removeTab(gBrowser.selectedTab);
   });
 
   let browser = gBrowser.selectedBrowser;
   let logins = await waitForLoginCountToReach(browser, 0);
   is(
     logins,
@@ -164,9 +163,48 @@ add_task(async function test() {
     let revealCheckbox = loginItem.shadowRoot.querySelector(
       ".reveal-password-checkbox"
     );
     ok(
       revealCheckbox.checked,
       "reveal checkbox should be checked if MP dialog authenticated"
     );
   });
+
+  await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
+    let loginFilter = Cu.waiveXrays(
+      content.document.querySelector("login-filter")
+    );
+    loginFilter.value = "pass1";
+    let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
+    is(
+      loginList._list.querySelectorAll(
+        ".login-list-item[data-guid]:not([hidden])"
+      ).length,
+      0,
+      "login-list should not show any results since the filter won't search passwords when MP is enabled"
+    );
+    loginFilter.value = "";
+    is(
+      loginList._list.querySelectorAll(
+        ".login-list-item[data-guid]:not([hidden])"
+      ).length,
+      1,
+      "login-list should show all results since the filter is empty"
+    );
+  });
+  LoginTestUtils.masterPassword.disable();
+  await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
+    Cu.waiveXrays(content).AboutLoginsUtils.masterPasswordEnabled = false;
+    let loginFilter = Cu.waiveXrays(
+      content.document.querySelector("login-filter")
+    );
+    loginFilter.value = "pass1";
+    let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
+    is(
+      loginList._list.querySelectorAll(
+        ".login-list-item[data-guid]:not([hidden])"
+      ).length,
+      1,
+      "login-list should show login with matching password since MP is disabled"
+    );
+  });
 });
--- a/browser/components/aboutlogins/tests/chrome/aboutlogins_common.js
+++ b/browser/components/aboutlogins/tests/chrome/aboutlogins_common.js
@@ -64,10 +64,11 @@ Object.defineProperty(window, "AboutLogi
     },
     doLoginsMatch(login1, login2) {
       return (
         login1.origin == login2.origin &&
         login1.username == login2.username &&
         login1.password == login2.password
       );
     },
+    masterPasswordEnabled: false,
   },
 });
--- a/browser/components/aboutlogins/tests/chrome/test_login_filter.html
+++ b/browser/components/aboutlogins/tests/chrome/test_login_filter.html
@@ -69,20 +69,20 @@ add_task(async function test_list_filter
   gLoginList.setLogins(LOGINS);
 
   let tests = [
     ["", 2],
     [LOGINS[0].username, 1],
     [LOGINS[0].username + "-notfound", 0],
     [LOGINS[0].username.substr(2, 3), 1],
     ["", 2],
-    // The password is not used for search.
-    [LOGINS[0].password, 0],
+    // The password is also used for search when MP is disabled.
+    [LOGINS[0].password, 1],
     [LOGINS[0].password + "-notfound", 0],
-    [LOGINS[0].password.substr(2, 3), 0],
+    [LOGINS[0].password.substr(2, 3), 1],
     ["", 2],
     [LOGINS[0].origin, 2],
     [LOGINS[0].origin + "-notfound", 0],
     [LOGINS[0].origin.substr(2, 3), 2],
     ["", 2],
     // The guid is not used for search.
     [LOGINS[0].guid, 0],
     [LOGINS[0].guid + "-notfound", 0],
--- a/browser/components/aboutlogins/tests/chrome/test_login_list.html
+++ b/browser/components/aboutlogins/tests/chrome/test_login_list.html
@@ -446,16 +446,17 @@ add_task(async function test_login_list_
   let logins = [];
   for (let i = 0; i < 12; i++) {
     let group = i % 2 ? "BB" : "AA";
     // Create logins of the form `jared0AAa@example.com`,
     // `jared1BBb@example.com`, `jared2AAc@example.com`, etc.
     logins.push({
       guid: `${i}`,
       username: `jared${i}${group}${String.fromCharCode(97 + i)}@example.com`,
+      password: "omgsecret!!1",
       origin: "https://www.example.com",
     });
   }
 
   gLoginList.setLogins(logins);
   let visibleLogins = gLoginList.shadowRoot.querySelectorAll(".login-list-item[data-guid]:not([hidden])");
   await SimpleTest.promiseWaitForCondition(() => {
     return visibleLogins.length == 12;
--- a/toolkit/components/passwordmgr/content/passwordManager.js
+++ b/toolkit/components/passwordmgr/content/passwordManager.js
@@ -794,17 +794,17 @@ function UpdateContextMenu() {
   if (!document.getElementById("passwordCol").hidden) {
     menuItems.get("context-editpassword").removeAttribute("disabled");
   } else {
     menuItems.get("context-editpassword").setAttribute("disabled", "true");
   }
 }
 
 async function masterPasswordLogin(noPasswordCallback) {
-  // This doesn't harm if passwords are not encrypted
+  // This does no harm if master password isn't set.
   let tokendb = Cc["@mozilla.org/security/pk11tokendb;1"].createInstance(
     Ci.nsIPK11TokenDB
   );
   let token = tokendb.getInternalKeyToken();
 
   // If there is no master password, still give the user a chance to opt-out of displaying passwords
   if (token.checkPassword("")) {
     return noPasswordCallback ? noPasswordCallback() : true;