Bug 1429169 - Add enterprise policy to change the global cookie settings draft
authorKirk Steuber <ksteuber@mozilla.com>
Mon, 19 Mar 2018 10:30:11 -0700
changeset 775295 50587d42afc024ed190179ae45642b4bad2cabde
parent 775294 11eebf289689b25ca19afeba0067facf893284d3
child 775296 69d1f591fadfba49d088b1f4865db6e727452a17
push id104688
push userksteuber@mozilla.com
push dateFri, 30 Mar 2018 20:38:45 +0000
bugs1429169
milestone61.0a1
Bug 1429169 - Add enterprise policy to change the global cookie settings Additionally fixes a minor bug in PoliciesPrefTracker.restoreDefaultValues that causes prefs with falsey values not to be reset MozReview-Commit-ID: 2OG4tHsjTTb
browser/components/enterprisepolicies/Policies.jsm
browser/components/enterprisepolicies/schemas/policies-schema.json
browser/components/enterprisepolicies/tests/EnterprisePolicyTesting.jsm
browser/components/enterprisepolicies/tests/browser/browser.ini
browser/components/enterprisepolicies/tests/browser/browser_policy_cookie_settings.js
--- a/browser/components/enterprisepolicies/Policies.jsm
+++ b/browser/components/enterprisepolicies/Policies.jsm
@@ -113,16 +113,58 @@ var Policies = {
       if (param.Block) {
         const hosts = param.Block.map(uri => uri.host).sort().join("\n");
         runOncePerModification("clearCookiesForBlockedHosts", hosts, () => {
           for (let blocked of param.Block) {
             Services.cookies.removeCookiesWithOriginAttributes("{}", blocked.host);
           }
         });
       }
+
+      if (param.Default !== undefined ||
+          param.AcceptThirdParty !== undefined ||
+          param.Locked) {
+        const ACCEPT_COOKIES = 0;
+        const REJECT_THIRD_PARTY_COOKIES = 1;
+        const REJECT_ALL_COOKIES = 2;
+        const REJECT_UNVISITED_THIRD_PARTY = 3;
+
+        let newCookieBehavior = ACCEPT_COOKIES;
+        if (param.Default !== undefined && !param.Default) {
+          newCookieBehavior = REJECT_ALL_COOKIES;
+        } else if (param.AcceptThirdParty) {
+          if (param.AcceptThirdParty == "none") {
+            newCookieBehavior = REJECT_THIRD_PARTY_COOKIES;
+          } else if (param.AcceptThirdParty == "from-visited") {
+            newCookieBehavior = REJECT_UNVISITED_THIRD_PARTY;
+          }
+        }
+
+        if (param.Locked) {
+          setAndLockPref("network.cookie.cookieBehavior", newCookieBehavior);
+        } else {
+          setDefaultPref("network.cookie.cookieBehavior", newCookieBehavior);
+        }
+      }
+
+      const KEEP_COOKIES_UNTIL_EXPIRATION = 0;
+      const KEEP_COOKIES_UNTIL_END_OF_SESSION = 2;
+
+      if (param.ExpireAtSessionEnd !== undefined || param.Locked) {
+        let newLifetimePolicy = KEEP_COOKIES_UNTIL_EXPIRATION;
+        if (param.ExpireAtSessionEnd) {
+          newLifetimePolicy = KEEP_COOKIES_UNTIL_END_OF_SESSION;
+        }
+
+        if (param.Locked) {
+          setAndLockPref("network.cookie.lifetimePolicy", newLifetimePolicy);
+        } else {
+          setDefaultPref("network.cookie.lifetimePolicy", newLifetimePolicy);
+        }
+      }
     }
   },
 
   "CreateMasterPassword": {
     onBeforeUIStartup(manager, param) {
       if (!param) {
         manager.disallowFeature("createMasterPassword");
       }
--- a/browser/components/enterprisepolicies/schemas/policies-schema.json
+++ b/browser/components/enterprisepolicies/schemas/policies-schema.json
@@ -83,16 +83,33 @@
           }
         },
 
         "Block": {
           "type": "array",
           "items": {
             "type": "origin"
           }
+        },
+
+        "Default": {
+          "type": "boolean"
+        },
+
+        "AcceptThirdParty": {
+          "type": "string",
+          "enum": ["all", "none", "from-visited"]
+        },
+
+        "ExpireAtSessionEnd": {
+          "type": "boolean"
+        },
+
+        "Locked": {
+          "type": "boolean"
         }
       }
     },
 
     "CreateMasterPassword": {
       "description": "If false, removes access to create a master password.",
       "first_available": "60.0",
 
--- a/browser/components/enterprisepolicies/tests/EnterprisePolicyTesting.jsm
+++ b/browser/components/enterprisepolicies/tests/EnterprisePolicyTesting.jsm
@@ -130,20 +130,20 @@ var PoliciesPrefTracker = {
     let defaults = new Preferences({defaultBranch: true});
 
     for (let [prefName, stored] of this._originalValues) {
       // If a pref was used through setDefaultPref instead
       // of setAndLockPref, it wasn't locked, but calling
       // unlockPref is harmless
       Preferences.unlock(prefName);
 
-      if (stored.originalDefaultValue) {
+      if (stored.originalDefaultValue !== undefined) {
         defaults.set(prefName, stored.originalDefaultValue);
       }
 
-      if (stored.originalUserValue) {
+      if (stored.originalUserValue !== undefined) {
         Preferences.set(prefName, stored.originalUserValue);
       }
     }
 
     this._originalValues.clear();
   },
 };
--- a/browser/components/enterprisepolicies/tests/browser/browser.ini
+++ b/browser/components/enterprisepolicies/tests/browser/browser.ini
@@ -20,16 +20,17 @@ support-files =
 [browser_policy_app_update.js]
 [browser_policy_block_about_addons.js]
 [browser_policy_block_about_config.js]
 [browser_policy_block_about_profiles.js]
 [browser_policy_block_about_support.js]
 [browser_policy_block_set_desktop_background.js]
 [browser_policy_bookmarks.js]
 [browser_policy_clear_blocked_cookies.js]
+[browser_policy_cookie_settings.js]
 [browser_policy_default_browser_check.js]
 [browser_policy_disable_feedback_commands.js]
 [browser_policy_disable_fxaccounts.js]
 [browser_policy_disable_masterpassword.js]
 [browser_policy_disable_pdfjs.js]
 [browser_policy_disable_pocket.js]
 [browser_policy_disable_privatebrowsing.js]
 [browser_policy_disable_safemode.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_cookie_settings.js
@@ -0,0 +1,262 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm", {});
+XPCOMUtils.defineLazyServiceGetter(Services, "cookies",
+                                   "@mozilla.org/cookieService;1",
+                                   "nsICookieService");
+XPCOMUtils.defineLazyServiceGetter(Services, "cookiemgr",
+                                   "@mozilla.org/cookiemanager;1",
+                                   "nsICookieManager");
+
+function restore_prefs() {
+  Services.prefs.clearUserPref("network.cookie.cookieBehavior");
+  Services.prefs.clearUserPref("network.cookie.lifetimePolicy");
+}
+registerCleanupFunction(restore_prefs);
+
+async function fake_profile_change() {
+  await new Promise(resolve => {
+    Services.obs.addObserver(function waitForDBClose() {
+      Services.obs.removeObserver(waitForDBClose, "cookie-db-closed");
+      resolve();
+    }, "cookie-db-closed");
+    Services.cookies.QueryInterface(Ci.nsIObserver).observe(null, "profile-before-change", "shutdown-persist");
+  });
+  await new Promise(resolve => {
+    Services.obs.addObserver(function waitForDBOpen() {
+      Services.obs.removeObserver(waitForDBOpen, "cookie-db-read");
+      resolve();
+    }, "cookie-db-read");
+    Services.cookies.QueryInterface(Ci.nsIObserver).observe(null, "profile-do-change", "");
+  });
+}
+
+async function test_cookie_settings({
+                                      cookiesEnabled,
+                                      thirdPartyCookiesEnabled,
+                                      cookiesExpireAfterSession,
+                                      cookieSettingsLocked
+                                    }) {
+  let firstPartyURI = NetUtil.newURI("http://example.com/");
+  let thirdPartyURI = NetUtil.newURI("http://example.org/");
+  let channel = NetUtil.newChannel({uri: firstPartyURI,
+                                    loadUsingSystemPrincipal: true});
+  channel.QueryInterface(Ci.nsIHttpChannelInternal).forceAllowThirdPartyCookie = true;
+  Services.cookies.removeAll();
+  Services.cookies.setCookieString(firstPartyURI, null, "key=value", channel);
+  Services.cookies.setCookieString(thirdPartyURI, null, "key=value", channel);
+
+  let expectedFirstPartyCookies = 1;
+  let expectedThirdPartyCookies = 1;
+  if (!cookiesEnabled) {
+    expectedFirstPartyCookies = 0;
+  }
+  if (!cookiesEnabled || !thirdPartyCookiesEnabled) {
+    expectedThirdPartyCookies = 0;
+  }
+  is(Services.cookiemgr.countCookiesFromHost(firstPartyURI.host),
+     expectedFirstPartyCookies,
+     "Number of first-party cookies should match expected");
+  is(Services.cookiemgr.countCookiesFromHost(thirdPartyURI.host),
+     expectedThirdPartyCookies,
+     "Number of third-party cookies should match expected");
+
+  // Add a cookie so we can check if it persists past the end of the session
+  // but, first remove existing cookies set by this host to put us in a known state
+  Services.cookies.removeAll();
+  Services.cookies.setCookieString(firstPartyURI, null, "key=value; max-age=1000", channel);
+
+  await fake_profile_change();
+
+  // Now check if the cookie persisted or not
+  let expectedCookieCount = 1;
+  if (cookiesExpireAfterSession || !cookiesEnabled) {
+    expectedCookieCount = 0;
+  }
+  is(Services.cookies.countCookiesFromHost(firstPartyURI.host), expectedCookieCount,
+     "Number of cookies was not what expected after restarting session");
+
+  is(Services.prefs.prefIsLocked("network.cookie.cookieBehavior"), cookieSettingsLocked,
+     "Cookie behavior pref lock status should be what is expected");
+  is(Services.prefs.prefIsLocked("network.cookie.lifetimePolicy"), cookieSettingsLocked,
+     "Cookie lifetime pref lock status should be what is expected");
+}
+
+add_task(async function test_initial_state() {
+  await test_cookie_settings({
+    cookiesEnabled: true,
+    thirdPartyCookiesEnabled: true,
+    cookiesExpireAfterSession: false,
+    cookieSettingsLocked: false
+  });
+  restore_prefs();
+});
+
+add_task(async function test_undefined_unlocked() {
+  Services.prefs.setIntPref("network.cookie.cookieBehavior", 3);
+  Services.prefs.setIntPref("network.cookie.lifetimePolicy", 2);
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "Cookies": {
+      }
+    }
+  });
+  is(Services.prefs.getIntPref("network.cookie.cookieBehavior", undefined), 3,
+     "An empty cookie policy should not have changed the cookieBehavior preference");
+  is(Services.prefs.getIntPref("network.cookie.lifetimePolicy", undefined), 2,
+     "An empty cookie policy should not have changed the lifetimePolicy preference");
+  restore_prefs();
+});
+
+add_task(async function test_disabled() {
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "Cookies": {
+        "Default": false
+      }
+    }
+  });
+
+  await test_cookie_settings({
+    cookiesEnabled: false,
+    thirdPartyCookiesEnabled: true,
+    cookiesExpireAfterSession: false,
+    cookieSettingsLocked: false
+  });
+  restore_prefs();
+});
+
+add_task(async function test_third_party_disabled() {
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "Cookies": {
+        "AcceptThirdParty": "none"
+      }
+    }
+  });
+
+  await test_cookie_settings({
+    cookiesEnabled: true,
+    thirdPartyCookiesEnabled: false,
+    cookiesExpireAfterSession: false,
+    cookieSettingsLocked: false
+  });
+  restore_prefs();
+});
+
+add_task(async function test_disabled_and_third_party_disabled() {
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "Cookies": {
+        "Default": false,
+        "AcceptThirdParty": "none"
+      }
+    }
+  });
+
+  await test_cookie_settings({
+    cookiesEnabled: false,
+    thirdPartyCookiesEnabled: false,
+    cookiesExpireAfterSession: false,
+    cookieSettingsLocked: false
+  });
+  restore_prefs();
+});
+
+add_task(async function test_disabled_and_third_party_disabled_locked() {
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "Cookies": {
+        "Default": false,
+        "AcceptThirdParty": "none",
+        "Locked": true
+      }
+    }
+  });
+
+  await test_cookie_settings({
+    cookiesEnabled: false,
+    thirdPartyCookiesEnabled: false,
+    cookiesExpireAfterSession: false,
+    cookieSettingsLocked: true
+  });
+  restore_prefs();
+});
+
+add_task(async function test_undefined_locked() {
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "Cookies": {
+        "Locked": true
+      }
+    }
+  });
+
+  await test_cookie_settings({
+    cookiesEnabled: true,
+    thirdPartyCookiesEnabled: true,
+    cookiesExpireAfterSession: false,
+    cookieSettingsLocked: true
+  });
+  restore_prefs();
+});
+
+add_task(async function test_cookie_expire() {
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "Cookies": {
+        "ExpireAtSessionEnd": true
+      }
+    }
+  });
+
+  await test_cookie_settings({
+    cookiesEnabled: true,
+    thirdPartyCookiesEnabled: true,
+    cookiesExpireAfterSession: true,
+    cookieSettingsLocked: false
+  });
+  restore_prefs();
+});
+
+add_task(async function test_cookie_expire_locked() {
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "Cookies": {
+        "ExpireAtSessionEnd": true,
+        "Locked": true
+      }
+    }
+  });
+
+  await test_cookie_settings({
+    cookiesEnabled: true,
+    thirdPartyCookiesEnabled: true,
+    cookiesExpireAfterSession: true,
+    cookieSettingsLocked: true
+  });
+  restore_prefs();
+});
+
+add_task(async function test_disabled_cookie_expire_locked() {
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "Cookies": {
+        "Default": false,
+        "AcceptThirdParty": "none",
+        "ExpireAtSessionEnd": true,
+        "Locked": true
+      }
+    }
+  });
+
+  await test_cookie_settings({
+    cookiesEnabled: false,
+    thirdPartyCookiesEnabled: false,
+    cookiesExpireAfterSession: true,
+    cookieSettingsLocked: true
+  });
+  restore_prefs();
+});