Commit 1fb3ac97 authored by Felipe Gomes's avatar Felipe Gomes
Browse files

Bug 1429177 - Policy: Set network proxy settings. r=mixedpuppy

parent 98bed8bd
......@@ -12,6 +12,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "gXulStore",
XPCOMUtils.defineLazyModuleGetters(this, {
BookmarksPolicies: "resource:///modules/policies/BookmarksPolicies.jsm",
ProxyPolicies: "resource:///modules/policies/ProxyPolicies.jsm",
});
const PREF_LOGLEVEL = "browser.policies.loglevel";
......@@ -325,6 +326,17 @@ var Policies = {
}
},
"Proxy": {
onBeforeAddons(manager, param) {
if (param.Locked) {
manager.disallowFeature("changeProxySettings");
ProxyPolicies.configureProxySettings(param, setAndLockPref);
} else {
ProxyPolicies.configureProxySettings(param, setDefaultPref);
}
}
},
"RememberPasswords": {
onBeforeUIStartup(manager, param) {
setAndLockPref("signon.rememberSignons", param);
......
/* 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";
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.importGlobalProperties(["URL"]);
const PREF_LOGLEVEL = "browser.policies.loglevel";
XPCOMUtils.defineLazyGetter(this, "log", () => {
let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
return new ConsoleAPI({
prefix: "ProxyPolicies.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,
});
});
// Don't use const here because this is acessed by
// tests through the BackstagePass object.
var PROXY_TYPES_MAP = new Map([
["none", Ci.nsIProtocolProxyService.PROXYCONFIG_DIRECT],
["system", Ci.nsIProtocolProxyService.PROXYCONFIG_SYSTEM],
["manual", Ci.nsIProtocolProxyService.PROXYCONFIG_MANUAL],
["autoDetect", Ci.nsIProtocolProxyService.PROXYCONFIG_WPAD],
["autoConfig", Ci.nsIProtocolProxyService.PROXYCONFIG_PAC],
]);
var EXPORTED_SYMBOLS = [ "ProxyPolicies" ];
var ProxyPolicies = {
configureProxySettings(param, setPref) {
if (param.Mode) {
setPref("network.proxy.type", PROXY_TYPES_MAP.get(param.Mode));
}
if (param.AutoConfigURL) {
setPref("network.proxy.autoconfig_url", param.AutoConfigURL.spec);
}
if (param.UseProxyForDNS !== undefined) {
setPref("network.proxy.socks_remote_dns", param.UseProxyForDNS);
}
if (param.AutoLogin !== undefined) {
setPref("signon.autologin.proxy", param.AutoLogin);
}
if (param.SOCKSVersion !== undefined) {
if (param.SOCKSVersion != 4 && param.SOCKSVersion != 5) {
log.error("Invalid SOCKS version");
} else {
setPref("network.proxy.socks_version", param.SOCKSVersion);
}
}
if (param.Passthrough !== undefined) {
setPref("network.proxy.no_proxies_on", param.Passthrough);
}
if (param.UseHTTPProxyForAllProtocols !== undefined) {
setPref("network.proxy.share_proxy_settings", param.UseHTTPProxyForAllProtocols);
}
function setProxyHostAndPort(type, address) {
let url;
try {
// Prepend https just so we can use the URL parser
// instead of parsing manually.
url = new URL(`https://${address}`);
} catch (e) {
log.error(`Invalid address for ${type} proxy: ${address}`);
return;
}
setPref(`network.proxy.${type}`, url.hostname);
if (url.port) {
setPref(`network.proxy.${type}_port`, Number(url.port));
}
}
if (param.HTTPProxy) {
setProxyHostAndPort("http", param.HTTPProxy);
// network.proxy.share_proxy_settings is a UI feature, not handled by the
// network code. That pref only controls if the checkbox is checked, and
// then we must manually set the other values.
if (param.UseHTTPProxyForAllProtocols) {
param.FTPProxy = param.SSLProxy = param.SOCKSProxy = param.HTTPProxy;
}
}
if (param.FTPProxy) {
setProxyHostAndPort("ftp", param.FTPProxy);
}
if (param.SSLProxy) {
setProxyHostAndPort("ssl", param.SSLProxy);
}
if (param.SOCKSProxy) {
setProxyHostAndPort("socks", param.SOCKSProxy);
}
}
};
......@@ -9,4 +9,5 @@ with Files("**"):
EXTRA_JS_MODULES.policies += [
'BookmarksPolicies.jsm',
'ProxyPolicies.jsm',
]
{
"policies": {
"Proxy": {
"Mode": "manual",
"Locked": true,
"HTTPProxy": "www.example.com:42",
"UseHTTPProxyForAllProtocols": true,
"Passthrough": "foo, bar, baz",
"SOCKSVersion": 4,
"UseProxyForDNS": true
}
}
}
......@@ -306,6 +306,64 @@
}
},
"Proxy": {
"description": "Configure Proxy settings.",
"first_available": "60.0",
"type": "object",
"properties": {
"Mode": {
"type": "string",
"enum": ["none", "system", "manual", "autoDetect", "autoConfig"]
},
"Locked": {
"type": "boolean"
},
"AutoConfigURL": {
"type": "URLorEmpty"
},
"FTPProxy": {
"type": "string"
},
"HTTPProxy": {
"type": "string"
},
"SSLProxy": {
"type": "string"
},
"SOCKSProxy": {
"type": "string"
},
"SOCKSVersion": {
"type": "number",
"enum": [4, 5]
},
"UseHTTPProxyForAllProtocols": {
"type": "boolean"
},
"Passthrough": {
"type": "string"
},
"UseProxyForDNS": {
"type": "boolean"
},
"AutoLogin": {
"type": "boolean"
}
}
},
"RememberPasswords": {
"description": "Enforces the setting to allow Firefox to remember saved logins and passwords. Both true and false values are accepted.",
"first_available": "60.0",
......
......@@ -37,5 +37,6 @@ support-files =
[browser_policy_display_bookmarks.js]
[browser_policy_display_menu.js]
[browser_policy_enable_tracking_protection.js]
[browser_policy_proxy.js]
[browser_policy_remember_passwords.js]
[browser_policy_set_homepage.js]
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
function checkPref(prefName, expectedValue, expectedLockedness) {
let prefType, prefValue;
switch (typeof(expectedValue)) {
case "boolean":
prefType = Services.prefs.PREF_BOOL;
prefValue = Services.prefs.getBoolPref(prefName);
break;
case "number":
prefType = Services.prefs.PREF_INT;
prefValue = Services.prefs.getIntPref(prefName);
break;
case "string":
prefType = Services.prefs.PREF_STRING;
prefValue = Services.prefs.getStringPref(prefName);
break;
}
if (expectedLockedness !== undefined) {
is(Services.prefs.prefIsLocked(prefName), expectedLockedness, `Pref ${prefName} is correctly locked`);
}
is(Services.prefs.getPrefType(prefName), prefType, `Pref ${prefName} has the correct type`);
is(prefValue, expectedValue, `Pref ${prefName} has the correct value`);
}
add_task(async function test_proxy_modes_and_autoconfig() {
// Directly test the proxy Mode and AutoconfigURL parameters through
// the API instead of the policy engine, because the test harness
// uses these prefs, and changing them interfere with the harness.
// Checks that every Mode value translates correctly to the expected pref value
let { ProxyPolicies, PROXY_TYPES_MAP } = ChromeUtils.import("resource:///modules/policies/ProxyPolicies.jsm", {});
for (let [mode, expectedValue] of PROXY_TYPES_MAP) {
ProxyPolicies.configureProxySettings({Mode: mode}, (_, value) => {
is(value, expectedValue, "Correct proxy mode");
});
}
let autoconfigURL = Services.io.newURI("data:text/plain,test");
ProxyPolicies.configureProxySettings({AutoConfigURL: autoconfigURL}, (_, value) => {
is(value, autoconfigURL.spec, "AutoconfigURL correctly set");
});
});
add_task(async function test_proxy_boolean_settings() {
// Tests that both false and true values are correctly set and locked
await setupPolicyEngineWithJson({
"policies": {
"Proxy": {
"UseProxyForDNS": false,
"AutoLogin": false,
}
}
});
checkPref("network.proxy.socks_remote_dns", false);
checkPref("signon.autologin.proxy", false);
await setupPolicyEngineWithJson({
"policies": {
"Proxy": {
"UseProxyForDNS": true,
"AutoLogin": true,
}
}
});
checkPref("network.proxy.socks_remote_dns", true);
checkPref("signon.autologin.proxy", true);
});
add_task(async function test_proxy_socks_and_passthrough() {
await setupPolicyEngineWithJson({
"policies": {
"Proxy": {
"SOCKSVersion": 4,
"Passthrough": "a, b, c"
}
}
});
checkPref("network.proxy.socks_version", 4);
checkPref("network.proxy.no_proxies_on", "a, b, c");
});
add_task(async function test_proxy_addresses() {
function checkProxyPref(proxytype, address, port) {
checkPref(`network.proxy.${proxytype}`, address);
checkPref(`network.proxy.${proxytype}_port`, port);
}
await setupPolicyEngineWithJson({
"policies": {
"Proxy": {
"HTTPProxy": "http.proxy.example.com:10",
"FTPProxy": "ftp.proxy.example.com:20",
"SSLProxy": "ssl.proxy.example.com:30",
"SOCKSProxy": "socks.proxy.example.com:40",
}
}
});
checkProxyPref("http", "http.proxy.example.com", 10);
checkProxyPref("ftp", "ftp.proxy.example.com", 20);
checkProxyPref("ssl", "ssl.proxy.example.com", 30);
checkProxyPref("socks", "socks.proxy.example.com", 40);
// Do the same, but now use the UseHTTPProxyForAllProtocols option
// and check that it takes effect.
await setupPolicyEngineWithJson({
"policies": {
"Proxy": {
"HTTPProxy": "http.proxy.example.com:10",
"FTPProxy": "ftp.proxy.example.com:20",
"SSLProxy": "ssl.proxy.example.com:30",
"SOCKSProxy": "socks.proxy.example.com:40",
"UseHTTPProxyForAllProtocols": true
}
}
});
checkProxyPref("http", "http.proxy.example.com", 10);
checkProxyPref("ftp", "http.proxy.example.com", 10);
checkProxyPref("ssl", "http.proxy.example.com", 10);
checkProxyPref("socks", "http.proxy.example.com", 10);
});
......@@ -264,7 +264,7 @@ var gConnectionsDialog = {
for (let element of gConnectionsDialog.getProxyControls()) {
element.disabled = disabled;
}
if (!isControlled) {
if (!isLocked) {
gConnectionsDialog.proxyTypeChanged();
}
}
......
......@@ -310,6 +310,11 @@ this.browserSettings = class extends ExtensionAPI {
"proxyConfig is not supported on android.");
}
if (!Services.policies.isAllowed("changeProxySettings")) {
throw new ExtensionError(
"Proxy settings are being managed by the Policies manager.");
}
let value = details.value;
if (!PROXY_TYPES_MAP.has(value.proxyType)) {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment