Bug 1466512 - Add a policy for forcing a master password. r=fluent-reviewers,MattN,flod
authorMichael Kaply <mozilla@kaply.com>
Thu, 04 Jun 2020 20:01:54 +0000
changeset 534010 44c03736a07abaafbd87fbc8b0e4cb34d797f923
parent 534009 1c927e612ddacfdb8150dd92465075aa1b8b4b9a
child 534011 dfc7af8d8ed6aae98566bd7a990d316ea8455175
push id37481
push userncsoregi@mozilla.com
push dateFri, 05 Jun 2020 04:39:26 +0000
treeherdermozilla-central@fecffba489bd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfluent-reviewers, MattN, flod
bugs1466512
milestone79.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1466512 - Add a policy for forcing a master password. r=fluent-reviewers,MattN,flod Differential Revision: https://phabricator.services.mozilla.com/D70935
browser/components/enterprisepolicies/Policies.jsm
browser/components/enterprisepolicies/schemas/policies-schema.json
browser/components/enterprisepolicies/tests/browser/browser.ini
browser/components/enterprisepolicies/tests/browser/browser_policy_masterpassword.js
browser/components/preferences/privacy.js
browser/locales/en-US/browser/policies/policies-descriptions.ftl
toolkit/locales/en-US/toolkit/preferences/preferences.ftl
toolkit/mozapps/preferences/changemp.js
toolkit/mozapps/preferences/changemp.xhtml
--- a/browser/components/enterprisepolicies/Policies.jsm
+++ b/browser/components/enterprisepolicies/Policies.jsm
@@ -1181,16 +1181,26 @@ var Policies = {
       );
       setAndLockPref(
         "capability.policy.localfilelinks_policy.sites",
         param.join(" ")
       );
     },
   },
 
+  MasterPassword: {
+    onAllWindowsRestored(manager, param) {
+      if (param) {
+        manager.disallowFeature("removeMasterPassword");
+      } else {
+        manager.disallowFeature("createMasterPassword");
+      }
+    },
+  },
+
   NetworkPrediction: {
     onBeforeAddons(manager, param) {
       setAndLockPref("network.dns.disablePrefetch", !param);
       setAndLockPref("network.dns.disablePrefetchFromHTTPS", !param);
     },
   },
 
   NewTabPage: {
--- a/browser/components/enterprisepolicies/schemas/policies-schema.json
+++ b/browser/components/enterprisepolicies/schemas/policies-schema.json
@@ -626,16 +626,20 @@
 
     "LocalFileLinks": {
       "type": "array",
       "items": {
         "type": "string"
       }
     },
 
+    "MasterPassword": {
+      "type": "boolean"
+    },
+
     "NetworkPrediction": {
       "type": "boolean"
     },
 
     "NewTabPage": {
       "type": "boolean"
     },
 
--- a/browser/components/enterprisepolicies/tests/browser/browser.ini
+++ b/browser/components/enterprisepolicies/tests/browser/browser.ini
@@ -39,16 +39,17 @@ skip-if = (verify && debug && (os == 'ma
 [browser_policy_disable_telemetry.js]
 [browser_policy_display_bookmarks.js]
 [browser_policy_display_menu.js]
 [browser_policy_extensions.js]
 [browser_policy_downloads.js]
 [browser_policy_extensionsettings.js]
 [browser_policy_firefoxhome.js]
 [browser_policy_handlers.js]
+[browser_policy_masterpassword.js]
 [browser_policy_override_postupdatepage.js]
 [browser_policy_pageinfo_permissions.js]
 [browser_policy_passwordmanager.js]
 [browser_policy_search_engine.js]
 [browser_policy_searchbar.js]
 [browser_policy_set_homepage.js]
 [browser_policy_set_startpage.js]
 [browser_policy_support_menu.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_masterpassword.js
@@ -0,0 +1,95 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let { LoginTestUtils } = ChromeUtils.import(
+  "resource://testing-common/LoginTestUtils.jsm"
+);
+
+// Test that once a password is set, you can't unset it
+add_task(async function test_policy_masterpassword_set() {
+  await setupPolicyEngineWithJson({
+    policies: {
+      MasterPassword: true,
+    },
+  });
+
+  LoginTestUtils.masterPassword.enable();
+
+  await BrowserTestUtils.withNewTab(
+    "about:preferences#privacy",
+    async browser => {
+      is(
+        browser.contentDocument.getElementById("useMasterPassword").disabled,
+        true,
+        "Master Password checkbox should be disabled"
+      );
+    }
+  );
+
+  LoginTestUtils.masterPassword.disable();
+});
+
+// Test that password can't be removed in changemp.xhtml
+add_task(async function test_policy_nochangemp() {
+  await setupPolicyEngineWithJson({
+    policies: {
+      MasterPassword: true,
+    },
+  });
+
+  LoginTestUtils.masterPassword.enable();
+
+  let changeMPWindow = window.openDialog(
+    "chrome://mozapps/content/preferences/changemp.xhtml",
+    "",
+    ""
+  );
+  await BrowserTestUtils.waitForEvent(changeMPWindow, "load");
+
+  is(
+    changeMPWindow.document.getElementById("admin").hidden,
+    true,
+    "Admin message should not be visible because there is a password."
+  );
+
+  changeMPWindow.document.getElementById("oldpw").value =
+    LoginTestUtils.masterPassword.masterPassword;
+
+  is(
+    changeMPWindow.document.getElementById("changemp").getButton("accept")
+      .disabled,
+    true,
+    "OK button should not be enabled if there is an old password."
+  );
+
+  await BrowserTestUtils.closeWindow(changeMPWindow);
+
+  LoginTestUtils.masterPassword.disable();
+});
+
+// Test that admin message shows
+add_task(async function test_policy_admin() {
+  await setupPolicyEngineWithJson({
+    policies: {
+      MasterPassword: true,
+    },
+  });
+
+  let changeMPWindow = window.openDialog(
+    "chrome://mozapps/content/preferences/changemp.xhtml",
+    "",
+    ""
+  );
+  await BrowserTestUtils.waitForEvent(changeMPWindow, "load");
+
+  is(
+    changeMPWindow.document.getElementById("admin").hidden,
+    false,
+    true,
+    "Admin message should not be hidden because there is not a password."
+  );
+
+  await BrowserTestUtils.closeWindow(changeMPWindow);
+});
--- a/browser/components/preferences/privacy.js
+++ b/browser/components/preferences/privacy.js
@@ -1908,17 +1908,18 @@ var gPrivacyPane = {
     var noMP = !LoginHelper.isMasterPasswordSet();
 
     var button = document.getElementById("changeMasterPassword");
     button.disabled = noMP;
 
     var checkbox = document.getElementById("useMasterPassword");
     checkbox.checked = !noMP;
     checkbox.disabled =
-      noMP && !Services.policies.isAllowed("createMasterPassword");
+      (noMP && !Services.policies.isAllowed("createMasterPassword")) ||
+      (!noMP && !Services.policies.isAllowed("removeMasterPassword"));
   },
 
   /**
    * Enables/disables the master password button depending on the state of the
    * "use master password" checkbox, and prompts for master password removal if
    * one is set.
    */
   async updateMasterPasswordButton() {
--- a/browser/locales/en-US/browser/policies/policies-descriptions.ftl
+++ b/browser/locales/en-US/browser/policies/policies-descriptions.ftl
@@ -125,16 +125,18 @@ policy-LegacyProfiles = Disable the feat
 policy-LegacySameSiteCookieBehaviorEnabled = Enable default legacy SameSite cookie behavior setting.
 
 policy-LegacySameSiteCookieBehaviorEnabledForDomainList = Revert to legacy SameSite behavior for cookies on specified sites.
 
 ##
 
 policy-LocalFileLinks = Allow specific websites to link to local files.
 
+policy-MasterPassword = Require or prevent using a master password.
+
 policy-NetworkPrediction = Enable or disable network prediction (DNS prefetching).
 
 policy-NewTabPage = Enable or disable the New Tab page.
 
 policy-NoDefaultBookmarks = Disable creation of the default bookmarks bundled with { -brand-short-name }, and the Smart Bookmarks (Most Visited, Recent Tags). Note: this policy is only effective if used before the first run of the profile.
 
 policy-OfferToSaveLogins = Enforce the setting to allow { -brand-short-name } to offer to remember saved logins and passwords. Both true and false values are accepted.
 
--- a/toolkit/locales/en-US/toolkit/preferences/preferences.ftl
+++ b/toolkit/locales/en-US/toolkit/preferences/preferences.ftl
@@ -21,16 +21,17 @@ pw-remove-button =
 
 set-password =
     .title = Change Master Password
 set-password-old-password = Current password:
 set-password-new-password = Enter new password:
 set-password-reenter-password = Re-enter password:
 set-password-meter = Password quality meter
 set-password-meter-loading = Loading
+master-password-admin = Your administrator requires that you have a Master Password set in order to save passwords.
 master-password-description = A Master Password is used to protect sensitive information like site passwords. If you create a Master Password you will be asked to enter it once per session when { -brand-short-name } retrieves saved information protected by the password.
 master-password-warning = Please make sure you remember the Master Password you have set. If you forget your Master Password, you will be unable to access any of the information protected by it.
 
 remove-password =
     .title = Remove Master Password
 remove-info =
     .value = You must enter your current password to proceed:
 remove-warning1 = Your Master Password is used to protect sensitive information like site passwords.
--- a/toolkit/mozapps/preferences/changemp.js
+++ b/toolkit/mozapps/preferences/changemp.js
@@ -51,16 +51,23 @@ function process() {
       // Select old password field
       oldpwbox.removeAttribute("hidden");
       msgBox.setAttribute("hidden", "true");
       oldpwbox.setAttribute("inited", "false");
       oldpwbox.focus();
     }
   }
 
+  if (
+    !token.hasPassword &&
+    !Services.policies.isAllowed("removeMasterPassword")
+  ) {
+    document.getElementById("admin").hidden = false;
+  }
+
   if (params) {
     // Return value 0 means "canceled"
     params.SetInt(1, 0);
   }
 
   checkPasswords();
 }
 
@@ -199,14 +206,17 @@ function checkPasswords() {
       // was called with the intention to change the password.
       // The token currently uses an empty password.
       // We will not allow changing the password from empty to empty.
       ok.setAttribute("disabled", "true");
       return;
     }
   }
 
-  if (pw1 == pw2) {
+  if (
+    pw1 == pw2 &&
+    (pw1 != "" || Services.policies.isAllowed("removeMasterPassword"))
+  ) {
     ok.setAttribute("disabled", "false");
   } else {
     ok.setAttribute("disabled", "true");
   }
 }
--- a/toolkit/mozapps/preferences/changemp.xhtml
+++ b/toolkit/mozapps/preferences/changemp.xhtml
@@ -15,16 +15,17 @@
 
   <script src="chrome://mozapps/content/preferences/changemp.js"/>
 
   <linkset>
     <html:link rel="localization" href="branding/brand.ftl"/>
     <html:link rel="localization" href="toolkit/preferences/preferences.ftl"/>
   </linkset>
 
+  <description id="admin" class="header" data-l10n-id="master-password-admin" hidden="true"></description>
   <description control="pw1" data-l10n-id="master-password-description"></description>
 
   <vbox>
     <hbox>
       <label flex="1" control="oldpw" data-l10n-id="set-password-old-password"></label>
       <html:input id="oldpw" type="password"/>
       <!-- This textbox is inserted as a workaround to the fact that making the 'type'
             & 'disabled' property of the 'oldpw' textbox toggle between ['password' &