Bug 1419102 - Implement the Enterprise Policies feature to provide enterprise users with easier control and setup of deployments of Firefox. r=Mossop

This feature is currently disabled behind a pref, but this pref will be removed when we're green to release it.

MozReview-Commit-ID: 3ZH2UJVdtC0

--HG--
rename : browser/components/newtab/tests/browser/.eslintrc.js => browser/components/enterprisepolicies/tests/browser/.eslintrc.js
This commit is contained in:
Felipe Gomes 2018-01-22 17:01:48 -02:00
Родитель 89c4589def
Коммит fcb1b42e8d
30 изменённых файлов: 1218 добавлений и 0 удалений

Просмотреть файл

@ -77,6 +77,10 @@ browser/base/content/newtab/**
# Test files that are really json not js, and don't need to be linted.
browser/components/sessionstore/test/unit/data/sessionstore_valid.js
browser/components/sessionstore/test/unit/data/sessionstore_invalid.js
# This file is split into two in order to keep it as a valid json file
# for documentation purposes (policies.json) but to be accessed by the
# code as a .jsm (schema.jsm)
browser/components/enterprisepolicies/schemas/schema.jsm
# generated & special files in cld2
browser/components/translation/cld2/**
# Screenshots and Follow-on search are imported as a system add-on and have

Просмотреть файл

@ -0,0 +1,358 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
NetUtil: "resource://gre/modules/NetUtil.jsm",
Policies: "resource:///modules/policies/Policies.jsm",
PoliciesValidator: "resource:///modules/policies/PoliciesValidator.jsm",
});
// This is the file that will be searched for in the
// ${InstallDir}/distribution folder.
const POLICIES_FILENAME = "policies.json";
// For easy testing, modify the helpers/sample.json file,
// and set PREF_ALTERNATE_PATH in firefox.js as:
// /your/repo/browser/components/enterprisepolicies/helpers/sample.json
const PREF_ALTERNATE_PATH = "browser.policies.alternatePath";
// This pref is meant to be temporary: it will only be used while we're
// testing this feature without rolling it out officially. When the
// policy engine is released, this pref should be removed.
const PREF_ENABLED = "browser.policies.enabled";
const PREF_LOGLEVEL = "browser.policies.loglevel";
XPCOMUtils.defineLazyGetter(this, "log", () => {
let { ConsoleAPI } = Cu.import("resource://gre/modules/Console.jsm", {});
return new ConsoleAPI({
prefix: "Enterprise Policies",
// tip: set maxLogLevel to "debug" and use log.debug() to create detailed
// messages during development. See LOG_LEVELS in Console.jsm for details.
maxLogLevel: "error",
maxLogLevelPref: PREF_LOGLEVEL,
});
});
// ==== Start XPCOM Boilerplate ==== \\
// Factory object
const EnterprisePoliciesFactory = {
_instance: null,
createInstance: function BGSF_createInstance(outer, iid) {
if (outer != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return this._instance == null ?
this._instance = new EnterprisePoliciesManager() : this._instance;
}
};
// ==== End XPCOM Boilerplate ==== //
// Constructor
function EnterprisePoliciesManager() {
Services.obs.addObserver(this, "profile-after-change", true);
Services.obs.addObserver(this, "final-ui-startup", true);
Services.obs.addObserver(this, "sessionstore-windows-restored", true);
Services.obs.addObserver(this, "EnterprisePolicies:Restart", true);
}
EnterprisePoliciesManager.prototype = {
// for XPCOM
classID: Components.ID("{ea4e1414-779b-458b-9d1f-d18e8efbc145}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISupportsWeakReference,
Ci.nsIEnterprisePolicies]),
// redefine the default factory for XPCOMUtils
_xpcom_factory: EnterprisePoliciesFactory,
_initialize() {
if (!Services.prefs.getBoolPref(PREF_ENABLED, false)) {
this.status = Ci.nsIEnterprisePolicies.INACTIVE;
return;
}
this._file = new JSONFileReader(getConfigurationFile());
this._file.readData();
if (!this._file.exists) {
this.status = Ci.nsIEnterprisePolicies.INACTIVE;
return;
}
if (this._file.failed) {
this.status = Ci.nsIEnterprisePolicies.FAILED;
return;
}
this.status = Ci.nsIEnterprisePolicies.ACTIVE;
this._activatePolicies();
},
_activatePolicies() {
let { schema } = Cu.import("resource:///modules/policies/schema.jsm", {});
let json = this._file.json;
for (let policyName of Object.keys(json.policies)) {
let policySchema = schema.properties[policyName];
let policyParameters = json.policies[policyName];
if (!policySchema) {
log.error(`Unknown policy: ${policyName}`);
continue;
}
let [parametersAreValid, parsedParameters] =
PoliciesValidator.validateAndParseParameters(policyParameters,
policySchema);
if (!parametersAreValid) {
log.error(`Invalid parameters specified for ${policyName}.`);
continue;
}
let policyImpl = Policies[policyName];
for (let timing of Object.keys(this._callbacks)) {
let policyCallback = policyImpl["on" + timing];
if (policyCallback) {
this._schedulePolicyCallback(
timing,
policyCallback.bind(null,
this, /* the EnterprisePoliciesManager */
parsedParameters));
}
}
}
},
_callbacks: {
BeforeAddons: [],
ProfileAfterChange: [],
BeforeUIStartup: [],
AllWindowsRestored: [],
},
_schedulePolicyCallback(timing, callback) {
this._callbacks[timing].push(callback);
},
_runPoliciesCallbacks(timing) {
let callbacks = this._callbacks[timing];
while (callbacks.length > 0) {
let callback = callbacks.shift();
try {
callback();
} catch (ex) {
log.error("Error running ", callback, `for ${timing}:`, ex);
}
}
},
async _restart() {
if (!Cu.isInAutomation) {
return;
}
DisallowedFeatures = {};
this._status = Ci.nsIEnterprisePolicies.UNINITIALIZED;
for (let timing of Object.keys(this._callbacks)) {
this._callbacks[timing] = [];
}
delete Services.ppmm.initialProcessData.policies;
Services.ppmm.broadcastAsyncMessage("EnterprisePolicies:Restart", null);
let { PromiseUtils } = Cu.import("resource://gre/modules/PromiseUtils.jsm",
{});
// Simulate the startup process. This step-by-step is a bit ugly but it
// tries to emulate the same behavior as of a normal startup.
await PromiseUtils.idleDispatch(() => {
this.observe(null, "policies-startup", null);
});
await PromiseUtils.idleDispatch(() => {
this.observe(null, "profile-after-change", null);
});
await PromiseUtils.idleDispatch(() => {
this.observe(null, "final-ui-startup", null);
});
await PromiseUtils.idleDispatch(() => {
this.observe(null, "sessionstore-windows-restored", null);
});
},
// nsIObserver implementation
observe: function BG_observe(subject, topic, data) {
switch (topic) {
case "policies-startup":
this._initialize();
this._runPoliciesCallbacks("BeforeAddons");
break;
case "profile-after-change":
// Before the first set of policy callbacks runs, we must
// initialize the service.
this._runPoliciesCallbacks("ProfileAfterChange");
break;
case "final-ui-startup":
this._runPoliciesCallbacks("BeforeUIStartup");
break;
case "sessionstore-windows-restored":
this._runPoliciesCallbacks("AllWindowsRestored");
// After the last set of policy callbacks ran, notify the test observer.
Services.obs.notifyObservers(null,
"EnterprisePolicies:AllPoliciesApplied");
break;
case "EnterprisePolicies:Restart":
this._restart().then(null, Cu.reportError);
break;
}
},
disallowFeature(feature, neededOnContentProcess = false) {
DisallowedFeatures[feature] = true;
// NOTE: For optimization purposes, only features marked as needed
// on content process will be passed onto the child processes.
if (neededOnContentProcess) {
Services.ppmm.initialProcessData.policies
.disallowedFeatures.push(feature);
if (Services.ppmm.childCount > 1) {
// If there has been a content process already initialized, let's
// broadcast the newly disallowed feature.
Services.ppmm.broadcastAsyncMessage(
"EnterprisePolicies:DisallowFeature", {feature}
);
}
}
},
// ------------------------------
// public nsIEnterprisePolicies members
// ------------------------------
_status: Ci.nsIEnterprisePolicies.UNINITIALIZED,
set status(val) {
this._status = val;
if (val != Ci.nsIEnterprisePolicies.INACTIVE) {
Services.ppmm.initialProcessData.policies = {
status: val,
disallowedFeatures: [],
};
}
return val;
},
get status() {
return this._status;
},
isAllowed: function BG_sanitize(feature) {
return !(feature in DisallowedFeatures);
},
};
let DisallowedFeatures = {};
function JSONFileReader(file) {
this._file = file;
this._data = {
exists: null,
failed: false,
json: null,
};
}
JSONFileReader.prototype = {
get exists() {
if (this._data.exists === null) {
this.readData();
}
return this._data.exists;
},
get failed() {
return this._data.failed;
},
get json() {
if (this._data.failed) {
return null;
}
if (this._data.json === null) {
this.readData();
}
return this._data.json;
},
readData() {
try {
let data = Cu.readUTF8File(this._file);
if (data) {
this._data.exists = true;
this._data.json = JSON.parse(data);
} else {
this._data.exists = false;
}
} catch (ex) {
if (ex instanceof Components.Exception &&
ex.result == Cr.NS_ERROR_FILE_NOT_FOUND) {
this._data.exists = false;
} else if (ex instanceof SyntaxError) {
log.error("Error parsing JSON file");
this._data.failed = true;
} else {
log.error("Error reading file");
this._data.failed = true;
}
}
}
};
function getConfigurationFile() {
let configFile = Services.dirsvc.get("XREAppDist", Ci.nsIFile);
configFile.append(POLICIES_FILENAME);
let prefType = Services.prefs.getPrefType(PREF_ALTERNATE_PATH);
if ((prefType == Services.prefs.PREF_STRING) && !configFile.exists()) {
// We only want to use the alternate file path if the file on the install
// folder doesn't exist. Otherwise it'd be possible for a user to override
// the admin-provided policies by changing the user-controlled prefs.
// This pref is only meant for tests, so it's fine to use this extra
// synchronous configFile.exists() above.
configFile = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsIFile);
let alternatePath = Services.prefs.getStringPref(PREF_ALTERNATE_PATH);
configFile.initWithPath(alternatePath);
}
return configFile;
}
var components = [EnterprisePoliciesManager];
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

Просмотреть файл

@ -0,0 +1,5 @@
component {ea4e1414-779b-458b-9d1f-d18e8efbc145} EnterprisePolicies.js process=main
contract @mozilla.org/browser/enterprisepolicies;1 {ea4e1414-779b-458b-9d1f-d18e8efbc145} process=main
component {dc6358f8-d167-4566-bf5b-4350b5e6a7a2} EnterprisePoliciesContent.js process=content
contract @mozilla.org/browser/enterprisepolicies;1 {dc6358f8-d167-4566-bf5b-4350b5e6a7a2} process=content

Просмотреть файл

@ -0,0 +1,91 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
const PREF_LOGLEVEL = "browser.policies.loglevel";
XPCOMUtils.defineLazyGetter(this, "log", () => {
let { ConsoleAPI } = Cu.import("resource://gre/modules/Console.jsm", {});
return new ConsoleAPI({
prefix: "Enterprise Policies Child",
// tip: set maxLogLevel to "debug" and use log.debug() to create detailed
// messages during development. See LOG_LEVELS in Console.jsm for details.
maxLogLevel: "error",
maxLogLevelPref: PREF_LOGLEVEL,
});
});
// ==== Start XPCOM Boilerplate ==== \\
// Factory object
const EnterprisePoliciesFactory = {
_instance: null,
createInstance: function BGSF_createInstance(outer, iid) {
if (outer != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return this._instance == null ?
this._instance = new EnterprisePoliciesManagerContent() : this._instance;
}
};
// ==== End XPCOM Boilerplate ==== //
function EnterprisePoliciesManagerContent() {
let policies = Services.cpmm.initialProcessData.policies;
if (policies) {
this._status = policies.status;
// make a copy of the array so that we can keep adding to it
// in a way that is not confusing.
this._disallowedFeatures = policies.disallowedFeatures.slice();
}
Services.cpmm.addMessageListener("EnterprisePolicies:DisallowFeature", this);
Services.cpmm.addMessageListener("EnterprisePolicies:Restart", this);
}
EnterprisePoliciesManagerContent.prototype = {
// for XPCOM
classID: Components.ID("{dc6358f8-d167-4566-bf5b-4350b5e6a7a2}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
Ci.nsIEnterprisePolicies]),
// redefine the default factory for XPCOMUtils
_xpcom_factory: EnterprisePoliciesFactory,
_status: Ci.nsIEnterprisePolicies.INACTIVE,
_disallowedFeatures: [],
receiveMessage({name, data}) {
switch (name) {
case "EnterprisePolicies:DisallowFeature":
this._disallowedFeatures.push(data.feature);
break;
case "EnterprisePolicies:Restart":
this._disallowedFeatures = [];
break;
}
},
get status() {
return this._status;
},
isAllowed(feature) {
return !this._disallowedFeatures.includes(feature);
}
};
var components = [EnterprisePoliciesManagerContent];
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

Просмотреть файл

@ -0,0 +1,38 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
const PREF_LOGLEVEL = "browser.policies.loglevel";
XPCOMUtils.defineLazyGetter(this, "log", () => {
let { ConsoleAPI } = Cu.import("resource://gre/modules/Console.jsm", {});
return new ConsoleAPI({
prefix: "Policies.jsm",
// tip: set maxLogLevel to "debug" and use log.debug() to create detailed
// messages during development. See LOG_LEVELS in Console.jsm for details.
maxLogLevel: "error",
maxLogLevelPref: PREF_LOGLEVEL,
});
});
this.EXPORTED_SYMBOLS = ["Policies"];
this.Policies = {
"block_about_config": {
onBeforeUIStartup(manager, param) {
if (param == true) {
manager.disallowFeature("about:config", true);
}
}
},
};

Просмотреть файл

@ -0,0 +1,148 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
const PREF_LOGLEVEL = "browser.policies.loglevel";
XPCOMUtils.defineLazyGetter(this, "log", () => {
let { ConsoleAPI } = Cu.import("resource://gre/modules/Console.jsm", {});
return new ConsoleAPI({
prefix: "PoliciesValidator.jsm",
// tip: set maxLogLevel to "debug" and use log.debug() to create detailed
// messages during development. See LOG_LEVELS in Console.jsm for details.
maxLogLevel: "error",
maxLogLevelPref: PREF_LOGLEVEL,
});
});
this.EXPORTED_SYMBOLS = ["PoliciesValidator"];
this.PoliciesValidator = {
validateAndParseParameters(param, properties) {
return validateAndParseParamRecursive(param, properties);
}
};
function validateAndParseParamRecursive(param, properties) {
if (properties.enum) {
if (properties.enum.includes(param)) {
return [true, param];
}
return [false, null];
}
log.debug(`checking @${param}@ for type ${properties.type}`);
switch (properties.type) {
case "boolean":
case "number":
case "integer":
case "string":
case "URL":
case "origin":
return validateAndParseSimpleParam(param, properties.type);
case "array":
if (!Array.isArray(param)) {
log.error("Array expected but not received");
return [false, null];
}
let parsedArray = [];
for (let item of param) {
log.debug(`in array, checking @${item}@ for type ${properties.items.type}`);
let [valid, parsedValue] = validateAndParseParamRecursive(item, properties.items);
if (!valid) {
return [false, null];
}
parsedArray.push(parsedValue);
}
return [true, parsedArray];
case "object": {
if (typeof(param) != "object") {
log.error("Object expected but not received");
return [false, null];
}
let parsedObj = {};
for (let property of Object.keys(properties.properties)) {
log.debug(`in object, for property ${property} checking @${param[property]}@ for type ${properties.properties[property].type}`);
let [valid, parsedValue] = validateAndParseParamRecursive(param[property], properties.properties[property]);
if (!valid) {
return [false, null];
}
parsedObj[property] = parsedValue;
}
return [true, parsedObj];
}
}
return [false, null];
}
function validateAndParseSimpleParam(param, type) {
let valid = false;
let parsedParam = param;
switch (type) {
case "boolean":
case "number":
case "string":
valid = (typeof(param) == type);
break;
// integer is an alias to "number" that some JSON schema tools use
case "integer":
valid = (typeof(param) == "number");
break;
case "origin":
if (typeof(param) != "string") {
break;
}
try {
parsedParam = Services.io.newURI(param);
let pathQueryRef = parsedParam.pathQueryRef;
// Make sure that "origin" types won't accept full URLs.
if (pathQueryRef != "/" && pathQueryRef != "") {
valid = false;
} else {
valid = true;
}
} catch (ex) {
valid = false;
}
break;
case "URL":
if (typeof(param) != "string") {
break;
}
try {
parsedParam = Services.io.newURI(param);
valid = true;
} catch (ex) {
valid = false;
}
break;
}
return [valid, parsedParam];
}

Просмотреть файл

@ -0,0 +1,8 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Firefox", "Enterprise Policies")

Просмотреть файл

@ -0,0 +1,18 @@
{
"policies": {
"block_about_config": true,
"dont_check_default_browser": true,
"flash_plugin": {
"allow": [
"https://www.example.com"
],
"block": [
"https://www.example.org"
]
},
"block_about_profiles": true
}
}

Просмотреть файл

@ -0,0 +1,30 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Firefox", "Enterprise Policies")
DIRS += [
'helpers',
'schemas',
]
TEST_DIRS += [
'tests'
]
EXTRA_COMPONENTS += [
'EnterprisePolicies.js',
'EnterprisePolicies.manifest',
'EnterprisePoliciesContent.js',
]
EXTRA_JS_MODULES.policies += [
'Policies.jsm',
'PoliciesValidator.jsm',
]
FINAL_LIBRARY = 'browsercomps'

Просмотреть файл

@ -0,0 +1,10 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"policies": {
"$ref": "policies.json"
}
},
"required": ["policies"]
}

Просмотреть файл

@ -0,0 +1,12 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Firefox", "Enterprise Policies")
EXTRA_PP_JS_MODULES.policies += [
'schema.jsm',
]

Просмотреть файл

@ -0,0 +1,13 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"block_about_config": {
"description": "Blocks access to the about:config page.",
"first_available": "60.0",
"type": "boolean",
"enum": [true]
}
}
}

Просмотреть файл

@ -0,0 +1,10 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.EXPORTED_SYMBOLS = ["schema"];
this.schema =
#include policies-schema.json

Просмотреть файл

@ -0,0 +1,7 @@
"use strict";
module.exports = {
"extends": [
"plugin:mozilla/browser-test"
]
};

Просмотреть файл

@ -0,0 +1,11 @@
[DEFAULT]
prefs =
browser.policies.enabled=true
support-files =
head.js
config_simple_policies.json
config_broken_json.json
[browser_policies_broken_json.js]
[browser_policies_simple_policies.js]
[browser_policies_validate_and_parse_API.js]

Просмотреть файл

@ -0,0 +1,15 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
add_task(async function test_clean_slate() {
await startWithCleanSlate();
});
add_task(async function test_broken_json() {
await setupPolicyEngineWithJson("config_broken_json.json");
is(Services.policies.status, Ci.nsIEnterprisePolicies.FAILED, "Engine was correctly set to the error state");
});

Просмотреть файл

@ -0,0 +1,101 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
add_task(async function test_clean_slate() {
await startWithCleanSlate();
});
add_task(async function test_simple_policies() {
await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
// Initialize the service in the content process, in case it hasn't
// already started.
Services.policies;
});
let { Policies } = Cu.import("resource:///modules/policies/Policies.jsm", {});
let policy0Ran = false, policy1Ran = false, policy2Ran = false, policy3Ran = false;
// Implement functions to handle the four simple policies that will be added
// to the schema.
Policies.simple_policy0 = {
onProfileAfterChange(manager, param) {
is(param, true, "Param matches what was passed in config file");
policy0Ran = true;
}
};
Policies.simple_policy1 = {
onProfileAfterChange(manager, param) {
is(param, true, "Param matches what was passed in config file");
manager.disallowFeature("feature1", /* needed in content process */ true);
policy1Ran = true;
}
};
Policies.simple_policy2 = {
onBeforeUIStartup(manager, param) {
is(param, true, "Param matches what was passed in config file");
manager.disallowFeature("feature2", /* needed in content process */ false);
policy2Ran = true;
}
};
Policies.simple_policy3 = {
onAllWindowsRestored(manager, param) {
is(param, false, "Param matches what was passed in config file");
policy3Ran = true;
}
};
await setupPolicyEngineWithJson(
"config_simple_policies.json",
/* custom schema */
{
properties: {
"simple_policy0": {
"type": "boolean"
},
"simple_policy1": {
"type": "boolean"
},
"simple_policy2": {
"type": "boolean"
},
"simple_policy3": {
"type": "boolean"
}
}
}
);
is(Services.policies.status, Ci.nsIEnterprisePolicies.ACTIVE, "Engine is active");
is(Services.policies.isAllowed("feature1"), false, "Dummy feature was disallowed");
is(Services.policies.isAllowed("feature2"), false, "Dummy feature was disallowed");
ok(policy0Ran, "Policy 0 ran correctly through BeforeAddons");
ok(policy1Ran, "Policy 1 ran correctly through onProfileAfterChange");
ok(policy2Ran, "Policy 2 ran correctly through onBeforeUIStartup");
ok(policy3Ran, "Policy 3 ran correctly through onAllWindowsRestored");
await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
is(Services.policies.isAllowed("feature1"), false, "Correctly disallowed in the content process");
// Feature 2 wasn't explictly marked as needed in the content process, so it is not marked
// as disallowed there.
is(Services.policies.isAllowed("feature2"), true, "Correctly missing in the content process");
}
});
delete Policies.simple_policy0;
delete Policies.simple_policy1;
delete Policies.simple_policy2;
delete Policies.simple_policy3;
});

Просмотреть файл

@ -0,0 +1,231 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/* This file will test the parameters parsing and validation directly through
the PoliciesValidator API.
*/
const { PoliciesValidator } = Cu.import("resource:///modules/policies/PoliciesValidator.jsm", {});
add_task(async function test_boolean_values() {
let schema = {
type: "boolean"
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters(true, schema);
ok(valid && parsed === true, "Parsed boolean value correctly");
[valid, parsed] = PoliciesValidator.validateAndParseParameters(false, schema);
ok(valid && parsed === false, "Parsed boolean value correctly");
// Invalid values:
ok(!PoliciesValidator.validateAndParseParameters("0", schema)[0], "No type coercion");
ok(!PoliciesValidator.validateAndParseParameters("true", schema)[0], "No type coercion");
ok(!PoliciesValidator.validateAndParseParameters(undefined, schema)[0], "Invalid value");
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
ok(!PoliciesValidator.validateAndParseParameters(null, schema)[0], "Invalid value");
});
add_task(async function test_number_values() {
let schema = {
type: "number"
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters(1, schema);
ok(valid && parsed === 1, "Parsed number value correctly");
// Invalid values:
ok(!PoliciesValidator.validateAndParseParameters("1", schema)[0], "No type coercion");
ok(!PoliciesValidator.validateAndParseParameters(true, schema)[0], "Invalid value");
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
ok(!PoliciesValidator.validateAndParseParameters(null, schema)[0], "Invalid value");
});
add_task(async function test_integer_values() {
// Integer is an alias for number
let schema = {
type: "integer"
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters(1, schema);
ok(valid && parsed == 1, "Parsed integer value correctly");
// Invalid values:
ok(!PoliciesValidator.validateAndParseParameters("1", schema)[0], "No type coercion");
ok(!PoliciesValidator.validateAndParseParameters(true, schema)[0], "Invalid value");
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
ok(!PoliciesValidator.validateAndParseParameters(null, schema)[0], "Invalid value");
});
add_task(async function test_string_values() {
let schema = {
type: "string"
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters("foobar", schema);
ok(valid && parsed == "foobar", "Parsed string value correctly");
// Invalid values:
ok(!PoliciesValidator.validateAndParseParameters(1, schema)[0], "No type coercion");
ok(!PoliciesValidator.validateAndParseParameters(true, schema)[0], "No type coercion");
ok(!PoliciesValidator.validateAndParseParameters(undefined, schema)[0], "Invalid value");
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
ok(!PoliciesValidator.validateAndParseParameters(null, schema)[0], "Invalid value");
});
add_task(async function test_URL_values() {
let schema = {
type: "URL"
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters("https://www.example.com/foo#bar", schema);
ok(valid, "URL is valid");
ok(parsed instanceof Ci.nsIURI, "parsed is a nsIURI");
is(parsed.prePath, "https://www.example.com", "prePath is correct");
is(parsed.pathQueryRef, "/foo#bar", "pathQueryRef is correct");
// Invalid values:
ok(!PoliciesValidator.validateAndParseParameters("www.example.com", schema)[0], "Scheme is required for URL");
ok(!PoliciesValidator.validateAndParseParameters("https://:!$%", schema)[0], "Invalid URL");
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
});
add_task(async function test_origin_values() {
// Origin is a URL that doesn't contain a path/query string (i.e., it's only scheme + host + port)
let schema = {
type: "origin"
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters("https://www.example.com", schema);
ok(valid, "Origin is valid");
ok(parsed instanceof Ci.nsIURI, "parsed is a nsIURI");
is(parsed.prePath, "https://www.example.com", "prePath is correct");
is(parsed.pathQueryRef, "/", "pathQueryRef is corect");
// Invalid values:
ok(!PoliciesValidator.validateAndParseParameters("https://www.example.com/foobar", schema)[0], "Origin cannot contain a path part");
ok(!PoliciesValidator.validateAndParseParameters("https://:!$%", schema)[0], "Invalid origin");
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
});
add_task(async function test_array_values() {
// The types inside an array object must all be the same
let schema = {
type: "array",
items: {
type: "number"
}
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters([1, 2, 3], schema);
ok(valid, "Array is valid");
ok(Array.isArray(parsed), "parsed is an array");
is(parsed.length, 3, "array is correct");
// An empty array is also valid
[valid, parsed] = PoliciesValidator.validateAndParseParameters([], schema);
ok(valid, "Array is valid");
ok(Array.isArray(parsed), "parsed is an array");
is(parsed.length, 0, "array is correct");
// Invalid values:
ok(!PoliciesValidator.validateAndParseParameters([1, true, 3], schema)[0], "Mixed types");
ok(!PoliciesValidator.validateAndParseParameters(2, schema)[0], "Type is correct but not in an array");
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Object is not an array");
});
add_task(async function test_object_values() {
let schema = {
type: "object",
properties: {
url: {
type: "URL"
},
title: {
type: "string"
}
}
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters(
{
url: "https://www.example.com/foo#bar",
title: "Foo",
alias: "Bar"
},
schema);
ok(valid, "Object is valid");
ok(typeof(parsed) == "object", "parsed in an object");
ok(parsed.url instanceof Ci.nsIURI, "types inside the object are also parsed");
is(parsed.url.spec, "https://www.example.com/foo#bar", "URL was correctly parsed");
is(parsed.title, "Foo", "title was correctly parsed");
is(parsed.alias, undefined, "property not described in the schema is not present in the parsed object");
// Invalid values:
ok(!PoliciesValidator.validateAndParseParameters(
{
url: "https://www.example.com/foo#bar",
title: 3,
},
schema)[0], "Mismatched type for title");
ok(!PoliciesValidator.validateAndParseParameters(
{
url: "www.example.com",
title: 3,
},
schema)[0], "Invalid URL inside the object");
});
add_task(async function test_array_of_objects() {
// This schema is used, for example, for bookmarks
let schema = {
type: "array",
items: {
type: "object",
properties: {
url: {
type: "URL",
},
title: {
type: "string"
}
}
}
};
let valid, parsed;
[valid, parsed] = PoliciesValidator.validateAndParseParameters(
[{
url: "https://www.example.com/bookmark1",
title: "Foo",
},
{
url: "https://www.example.com/bookmark2",
title: "Bar",
}],
schema);
ok(valid, "Array is valid");
is(parsed.length, 2, "Correct number of items");
ok(typeof(parsed[0]) == "object" && typeof(parsed[1]) == "object", "Correct objects inside array");
is(parsed[0].url.spec, "https://www.example.com/bookmark1", "Correct URL for bookmark 1");
is(parsed[1].url.spec, "https://www.example.com/bookmark2", "Correct URL for bookmark 2");
is(parsed[0].title, "Foo", "Correct title for bookmark 1");
is(parsed[1].title, "Bar", "Correct title for bookmark 2");
});

Просмотреть файл

@ -0,0 +1,3 @@
{
"policies
}

Просмотреть файл

@ -0,0 +1,8 @@
{
"policies": {
"simple_policy0": true,
"simple_policy1": true,
"simple_policy2": true,
"simple_policy3": false
}
}

Просмотреть файл

@ -0,0 +1,34 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
async function setupPolicyEngineWithJson(jsonName, customSchema) {
let filePath = getTestFilePath(jsonName ? jsonName : "non-existing-file.json");
Services.prefs.setStringPref("browser.policies.alternatePath", filePath);
let resolve = null;
let promise = new Promise((r) => resolve = r);
Services.obs.addObserver(function observer() {
Services.obs.removeObserver(observer, "EnterprisePolicies:AllPoliciesApplied");
resolve();
}, "EnterprisePolicies:AllPoliciesApplied");
// Clear any previously used custom schema
Cu.unload("resource:///modules/policies/schema.jsm");
if (customSchema) {
let schemaModule = Cu.import("resource:///modules/policies/schema.jsm", {});
schemaModule.schema = customSchema;
}
Services.obs.notifyObservers(null, "EnterprisePolicies:Restart");
return promise;
}
async function startWithCleanSlate() {
await setupPolicyEngineWithJson("");
is(Services.policies.status, Ci.nsIEnterprisePolicies.INACTIVE, "Engine is inactive");
}

Просмотреть файл

@ -0,0 +1,12 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Firefox", "General")
BROWSER_CHROME_MANIFESTS += [
'browser/browser.ini'
]

Просмотреть файл

@ -38,6 +38,7 @@ DIRS += [
'customizableui',
'dirprovider',
'downloads',
'enterprisepolicies',
'extensions',
'feeds',
'migration',

Просмотреть файл

@ -229,6 +229,7 @@
@RESPATH@/components/dom_presentation.xpt
@RESPATH@/components/downloads.xpt
@RESPATH@/components/editor.xpt
@RESPATH@/components/enterprisepolicies.xpt
@RESPATH@/components/extensions.xpt
@RESPATH@/components/exthandler.xpt
@RESPATH@/components/fastfind.xpt
@ -379,6 +380,9 @@
@RESPATH@/browser/components/browser-newtab.xpt
@RESPATH@/browser/components/aboutNewTabService.js
@RESPATH@/browser/components/NewTabComponents.manifest
@RESPATH@/browser/components/EnterprisePolicies.js
@RESPATH@/browser/components/EnterprisePoliciesContent.js
@RESPATH@/browser/components/EnterprisePolicies.manifest
@RESPATH@/components/Downloads.manifest
@RESPATH@/components/DownloadLegacy.js
@RESPATH@/components/thumbnails.xpt

Просмотреть файл

@ -0,0 +1,14 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
with Files("**"):
BUG_COMPONENT = ("Firefox", "Enterprise Policies")
XPIDL_SOURCES += [
'nsIEnterprisePolicies.idl',
]
XPIDL_MODULE = 'enterprisepolicies'

Просмотреть файл

@ -0,0 +1,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
[scriptable, uuid(6a568972-cc91-4bf5-963e-3768f3319b8a)]
interface nsIEnterprisePolicies : nsISupports
{
const unsigned short UNINITIALIZED = 0;
const unsigned short INACTIVE = 1;
const unsigned short ACTIVE = 2;
const unsigned short FAILED = 3;
readonly attribute short status;
bool isAllowed(in ACString feature);
};

Просмотреть файл

@ -30,6 +30,7 @@ DIRS += [
'crashmonitor',
'diskspacewatcher',
'downloads',
'enterprisepolicies',
'extensions',
'filewatcher',
'finalizationwitness',

Просмотреть файл

@ -122,6 +122,9 @@ if (AppConstants.MOZ_GECKO_PROFILER) {
if (AppConstants.MOZ_TOOLKIT_SEARCH) {
initTable.search = ["@mozilla.org/browser/search-service;1", "nsIBrowserSearchService"];
}
if (AppConstants.MOZ_BUILD_APP == "browser") {
initTable.policies = ["@mozilla.org/browser/enterprisepolicies;1", "nsIEnterprisePolicies"];
}
XPCOMUtils.defineLazyServiceGetters(Services, initTable);

Просмотреть файл

@ -70,6 +70,10 @@ function run_test() {
if ("nsIAndroidBridge" in Ci) {
checkService("androidBridge", Ci.nsIAndroidBridge);
}
if ("@mozilla.org/browser/enterprisepolicies;1" in Cc) {
checkService("policies", Ci.nsIEnterprisePolicies);
}
// In xpcshell tests, the "@mozilla.org/xre/app-info;1" component implements
// only the nsIXULRuntime interface, but not nsIXULAppInfo. To test the

Просмотреть файл

@ -1009,6 +1009,12 @@ nsXREDirProvider::DoStartup()
static const char16_t kStartup[] = {'s','t','a','r','t','u','p','\0'};
obsSvc->NotifyObservers(nullptr, "profile-do-change", kStartup);
// Initialize the Enterprise Policies service
nsCOMPtr<nsIObserver> policies(do_GetService("@mozilla.org/browser/enterprisepolicies;1"));
if (policies) {
policies->Observe(nullptr, "policies-startup", nullptr);
}
// Init the Extension Manager
nsCOMPtr<nsIObserver> em = do_GetService("@mozilla.org/addons/integration;1");
if (em) {