Bug 1395886 Add support for proxy authentication
MozReview-Commit-ID: 45TseZ2wozB
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_proxy.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_proxy.py
@@ -3,16 +3,18 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from __future__ import absolute_import
from marionette_driver import errors
from marionette_harness import MarionetteTestCase
+from time import sleep
+
class TestProxyCapabilities(MarionetteTestCase):
def setUp(self):
super(TestProxyCapabilities, self).setUp()
self.marionette.delete_session()
@@ -63,16 +65,40 @@ class TestProxyCapabilities(MarionetteTe
"socksProxy": proxy_hostname,
"socksVersion": 4,
}}
self.marionette.start_session(capabilities)
self.assertEqual(self.marionette.session_capabilities["proxy"],
capabilities["proxy"])
+ def test_proxy_type_manual_with_authentication(self):
+ proxy_hostname = "104.236.137.24"
+ username = "username"
+ password = "password"
+ capabilities = {"proxy": {
+ "proxyType": "manual",
+ "ftpProxy": "{}:{}@{}:3128".format(username, password, proxy_hostname),
+ "httpProxy": "{}:{}@{}:3128".format(username, password, proxy_hostname),
+ "sslProxy": "{}:{}@{}:3128".format(username, password, proxy_hostname),
+ }}
+
+ self.marionette.start_session(capabilities)
+ self.assertEqual(self.marionette.session_capabilities["proxy"],
+ capabilities["proxy"])
+
+ url = "https://www.google.com/search?q=my+ip+address"
+ self.marionette.navigate(url)
+ sleep(3)
+ self.marionette.switch_to_alert().accept()
+ sleep(3)
+ self.assertEqual(self.marionette.get_url(), url)
+ self.assertIn("104.236.137.24", self.marionette.page_source)
+
+
def test_proxy_type_manual_socks_requires_version(self):
proxy_port = 4444
proxy_hostname = "marionette.test"
proxy_host = "{}:{}".format(proxy_hostname, proxy_port)
capabilities = {"proxy": {
"proxyType": "manual",
"socksProxy": proxy_host,
}}
--- a/testing/marionette/server.js
+++ b/testing/marionette/server.js
@@ -265,16 +265,23 @@ const RECOMMENDED_PREFS = new Map([
["security.fileuri.strict_origin_policy", false],
// Tests do not wait for the notification button security delay
["security.notification_enable_delay", 0],
// Ensure blocklist updates do not hit the network
["services.settings.server", "http://%(server)s/dummy/blocklist/"],
+ // Prevent popup for proxy authentication when a login is stored
+ ["signon.autologin.proxy", true],
+ ["network.negotiate-auth.allow-proxies", false],
+ ["network.proxy.share_proxy_settings", false],
+ ["network.automatic-ntlm-auth.allow-proxies", false],
+ ["network.auth.use-sspi", false],
+
// Do not automatically fill sign-in forms with known usernames and
// passwords
["signon.autofillForms", false],
// Disable password capture, so that tests that include forms are not
// influenced by the presence of the persistent doorhanger notification
["signon.rememberSignons", false],
--- a/testing/marionette/session.js
+++ b/testing/marionette/session.js
@@ -3,16 +3,19 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.importGlobalProperties(["URL"]);
+Cu.import("resource://gre/modules/Log.jsm");
+const logger = Log.repository.getLogger("Marionette");
+
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("chrome://marionette/content/assert.js");
const {
InvalidArgumentError,
} = Cu.import("chrome://marionette/content/error.js", {});
const {
@@ -125,16 +128,31 @@ session.Proxy = class {
* Sets Firefox proxy settings.
*
* @return {boolean}
* True if proxy settings were updated as a result of calling this
* function, or false indicating that this function acted as
* a no-op.
*/
init() {
+ function addProxyLogin(login) {
+ if (login) {
+ let logins = Services.logins.findLogins(
+ {}, login.hostname, "", login.httpRealm, {});
+ if (logins.length) {
+ if (login.username != logins[0].username ||
+ login.password != logins[0].password) {
+ // Throw exception here?
+ logger.debug("Cannot add two different logins for the same host");
+ }
+ } else {
+ Services.logins.addLogin(login);
+ }
+ }
+ }
switch (this.proxyType) {
case "autodetect":
Preferences.set("network.proxy.type", 4);
return true;
case "direct":
Preferences.set("network.proxy.type", 0);
return true;
@@ -142,30 +160,33 @@ session.Proxy = class {
case "manual":
Preferences.set("network.proxy.type", 1);
if (this.ftpProxy) {
Preferences.set("network.proxy.ftp", this.ftpProxy);
if (Number.isInteger(this.ftpProxyPort)) {
Preferences.set("network.proxy.ftp_port", this.ftpProxyPort);
}
+ addProxyLogin(this.ftpLogin);
}
if (this.httpProxy) {
Preferences.set("network.proxy.http", this.httpProxy);
if (Number.isInteger(this.httpProxyPort)) {
Preferences.set("network.proxy.http_port", this.httpProxyPort);
}
+ addProxyLogin(this.httpLogin);
}
if (this.sslProxy) {
Preferences.set("network.proxy.ssl", this.sslProxy);
if (Number.isInteger(this.sslProxyPort)) {
Preferences.set("network.proxy.ssl_port", this.sslProxyPort);
}
+ addProxyLogin(this.sslLogin);
}
if (this.socksProxy) {
Preferences.set("network.proxy.socks", this.socksProxy);
if (Number.isInteger(this.socksProxyPort)) {
Preferences.set("network.proxy.socks_port", this.socksProxyPort);
}
if (this.socksVersion) {
@@ -224,39 +245,44 @@ session.Proxy = class {
url = new URL("http://" + host);
if (url.port == "") {
url = new URL("https://" + host);
}
} catch (e) {
throw new InvalidArgumentError(e.message);
}
- let hostname = stripBracketsFromIpv6Hostname(url.hostname);
-
// If the port hasn't been set, use the default port of
// the selected scheme (except for socks which doesn't have one).
let port = parseInt(url.port);
if (!Number.isInteger(port)) {
if (scheme === "socks") {
port = null;
} else {
port = Services.io.getProtocolHandler(scheme).defaultPort;
}
}
- if (url.username != "" ||
- url.password != "" ||
- url.pathname != "/" ||
+ let login = null;
+ if (url.username !== "") {
+ login = Cc["@mozilla.org/login-manager/loginInfo;1"]
+ .createInstance(Ci.nsILoginInfo);
+ login.init(`moz-proxy://${url.hostname}:${port}`, null, "Squid proxy-caching web server", url.username, url.password, "", "");
+ }
+
+ let hostname = stripBracketsFromIpv6Hostname(url.hostname);
+
+ if (url.pathname != "/" ||
url.search != "" ||
url.hash != "") {
throw new InvalidArgumentError(
- `${host} was not of the form host[:port]`);
+ `${host} was not of the form [username:password@]host[:port]`);
}
- return [hostname, port];
+ return [hostname, port, login];
}
let p = new session.Proxy();
if (typeof json == "undefined" || json === null) {
return p;
}
assert.object(json, pprint`Expected "proxy" to be an object, got ${json}`);
@@ -275,23 +301,23 @@ session.Proxy = class {
case "pac":
p.proxyAutoconfigUrl = assert.string(json.proxyAutoconfigUrl,
`Expected "proxyAutoconfigUrl" to be a string, ` +
pprint`got ${json.proxyAutoconfigUrl}`);
break;
case "manual":
if (typeof json.ftpProxy != "undefined") {
- [p.ftpProxy, p.ftpProxyPort] = fromHost("ftp", json.ftpProxy);
+ [p.ftpProxy, p.ftpProxyPort, p.ftpLogin] = fromHost("ftp", json.ftpProxy);
}
if (typeof json.httpProxy != "undefined") {
- [p.httpProxy, p.httpProxyPort] = fromHost("http", json.httpProxy);
+ [p.httpProxy, p.httpProxyPort, p.httpLogin] = fromHost("http", json.httpProxy);
}
if (typeof json.sslProxy != "undefined") {
- [p.sslProxy, p.sslProxyPort] = fromHost("https", json.sslProxy);
+ [p.sslProxy, p.sslProxyPort, p.sslLogin] = fromHost("https", json.sslProxy);
}
if (typeof json.socksProxy != "undefined") {
[p.socksProxy, p.socksProxyPort] = fromHost("socks", json.socksProxy);
p.socksVersion = assert.positiveInteger(json.socksVersion);
}
if (typeof json.noProxy != "undefined") {
let entries = assert.array(json.noProxy,
pprint`Expected "noProxy" to be an array, got ${json.noProxy}`);
@@ -324,17 +350,22 @@ session.Proxy = class {
if (!hostname) {
return null;
}
// Add brackets around IPv6 addresses
hostname = addBracketsToIpv6Hostname(hostname);
if (port != null) {
- return `${hostname}:${port}`;
+ hostname = `${hostname}:${port}`;
+ }
+
+ let logins = Services.logins.findLogins({}, `moz-proxy://${hostname}`, null, "Squid proxy-caching web server", {});
+ if (logins.length) {
+ hostname = `${logins[0].username}:${logins[0].password}@${hostname}`;
}
return hostname;
}
let excludes = this.noProxy;
if (excludes) {
excludes = excludes.map(addBracketsToIpv6Hostname);