/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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/. */
/**
* This tests the automatic login to the proxy with password,
* if the password is stored and the browser is restarted.
*
* <copied from="test_authentication.js"/>
*/
"use strict";
const { HttpServer } = ChromeUtils.importESModule(
"resource://testing-common/httpd.sys.mjs"
);
const FLAG_RETURN_FALSE = 1 << 0;
const FLAG_WRONG_PASSWORD = 1 << 1;
const FLAG_PREVIOUS_FAILED = 1 << 2;
function AuthPrompt2(proxyFlags, hostFlags) {
this.proxyCred.flags = proxyFlags;
this.hostCred.flags = hostFlags;
}
AuthPrompt2.prototype = {
proxyCred: {
user:
"proxy",
pass:
"guest",
realmExpected:
"intern",
flags: 0,
},
hostCred: { user:
"host", pass:
"guest", realmExpected:
"extern", flags: 0 },
QueryInterface: ChromeUtils.generateQI([
"nsIAuthPrompt2"]),
promptAuth:
function ap2_promptAuth(channel, encryptionLevel, authInfo) {
try {
// never HOST and PROXY set at the same time in prompt
Assert.equal(
(authInfo.flags & Ci.nsIAuthInformation.AUTH_HOST) != 0,
(authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) == 0
);
var isProxy = (authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) != 0;
var cred = isProxy ?
this.proxyCred :
this.hostCred;
dump(
"with flags: " +
((cred.flags & FLAG_WRONG_PASSWORD) != 0 ?
"wrong password" :
"") +
" " +
((cred.flags & FLAG_PREVIOUS_FAILED) != 0 ?
"previous failed" :
"") +
" " +
((cred.flags & FLAG_RETURN_FALSE) != 0 ?
"return false" :
"") +
"\n"
);
// PROXY properly set by necko (checked using realm)
Assert.equal(cred.realmExpected, authInfo.realm);
// PREVIOUS_FAILED properly set by necko
Assert.equal(
(cred.flags & FLAG_PREVIOUS_FAILED) != 0,
(authInfo.flags & Ci.nsIAuthInformation.PREVIOUS_FAILED) != 0
);
if (cred.flags & FLAG_RETURN_FALSE) {
cred.flags |= FLAG_PREVIOUS_FAILED;
cred.flags &= ~FLAG_RETURN_FALSE;
return false;
}
authInfo.username = cred.user;
if (cred.flags & FLAG_WRONG_PASSWORD) {
authInfo.password = cred.pass +
".wrong";
cred.flags |= FLAG_PREVIOUS_FAILED;
// Now clear the flag to avoid an infinite loop
cred.flags &= ~FLAG_WRONG_PASSWORD;
}
else {
authInfo.password = cred.pass;
cred.flags &= ~FLAG_PREVIOUS_FAILED;
}
}
catch (e) {
do_throw(e);
}
return true;
},
asyncPromptAuth:
function ap2_async(
channel,
callback,
context,
encryptionLevel,
authInfo
) {
var me =
this;
var allOverAndDead =
false;
executeSoon(
function () {
try {
if (allOverAndDead) {
throw new Error(
"already canceled");
}
var ret = me.promptAuth(channel, encryptionLevel, authInfo);
if (!ret) {
callback.onAuthCancelled(context,
true);
}
else {
callback.onAuthAvailable(context, authInfo);
}
allOverAndDead =
true;
}
catch (e) {
do_throw(e);
}
});
return new Cancelable(
function () {
if (allOverAndDead) {
throw new Error(
"can't cancel, already ran");
}
callback.onAuthAvailable(context, authInfo);
allOverAndDead =
true;
});
},
};
function Cancelable(onCancelFunc) {
this.onCancelFunc = onCancelFunc;
}
Cancelable.prototype = {
QueryInterface: ChromeUtils.generateQI([
"nsICancelable"]),
cancel:
function cancel() {
try {
this.onCancelFunc();
}
catch (e) {
do_throw(e);
}
},
};
function Requestor(proxyFlags, hostFlags) {
this.proxyFlags = proxyFlags;
this.hostFlags = hostFlags;
}
Requestor.prototype = {
QueryInterface: ChromeUtils.generateQI([
"nsIInterfaceRequestor"]),
getInterface:
function requestor_gi(iid) {
if (iid.equals(Ci.nsIAuthPrompt)) {
dump(
"authprompt1 not implemented\n");
throw Components.Exception(
"", Cr.NS_ERROR_NO_INTERFACE);
}
if (iid.equals(Ci.nsIAuthPrompt2)) {
try {
// Allow the prompt to store state by caching it here
if (!
this.prompt2) {
this.prompt2 =
new AuthPrompt2(
this.proxyFlags,
this.hostFlags);
}
return this.prompt2;
}
catch (e) {
do_throw(e);
}
}
throw Components.Exception(
"", Cr.NS_ERROR_NO_INTERFACE);
},
prompt2:
null,
};
var listener = {
expectedCode: -1,
// uninitialized
onStartRequest:
function test_onStartR(request) {
try {
// Proxy auth cancellation return failures to avoid spoofing
if (
!Components.isSuccessCode(request.status) &&
this.expectedCode != 407
) {
do_throw(
"Channel should have a success code!");
}
if (!(request
instanceof Ci.nsIHttpChannel)) {
do_throw(
"Expecting an HTTP channel");
}
Assert.equal(
this.expectedCode, request.responseStatus);
// If we expect 200, the request should have succeeded
Assert.equal(
this.expectedCode == 200, request.requestSucceeded);
var cookie =
"";
try {
cookie = request.getRequestHeader(
"Cookie");
}
catch (e) {}
Assert.equal(cookie,
"");
}
catch (e) {
do_throw(
"Unexpected exception: " + e);
}
throw Components.Exception(
"", Cr.NS_ERROR_ABORT);
},
onDataAvailable:
function test_ODA() {
do_throw(
"Should not get any data!");
},
onStopRequest:
function test_onStopR(request, status) {
Assert.equal(status, Cr.NS_ERROR_ABORT);
if (current_test < tests.length - 1) {
// First, need to clear the auth cache
Cc[
"@mozilla.org/network/http-auth-manager;1"]
.getService(Ci.nsIHttpAuthManager)
.clearAll();
current_test++;
tests[current_test]();
}
else {
do_test_pending();
httpserv.stop(do_test_finished);
}
do_test_finished();
},
};
function makeChan(url) {
if (!url) {
url =
"http://somesite/";
}
return NetUtil.newChannel({
uri: url,
loadUsingSystemPrincipal:
true,
}).QueryInterface(Ci.nsIHttpChannel);
}
var current_test = 0;
var httpserv =
null;
function run_test() {
httpserv =
new HttpServer();
httpserv.registerPathHandler(
"/", proxyAuthHandler);
httpserv.identity.add(
"http",
"somesite", 80);
httpserv.start(-1);
Services.prefs.setCharPref(
"network.proxy.http",
"localhost");
Services.prefs.setIntPref(
"network.proxy.http_port",
httpserv.identity.primaryPort
);
Services.prefs.setCharPref(
"network.proxy.no_proxies_on",
"");
Services.prefs.setIntPref(
"network.proxy.type", 1);
// Turn off the authentication dialog blocking for this test.
Services.prefs.setIntPref(
"network.auth.subresource-http-auth-allow", 2);
Services.prefs.setBoolPref(
"network.auth.non-web-content-triggered-resources-http-auth-allow",
true
);
registerCleanupFunction(() => {
Services.prefs.clearUserPref(
"network.proxy.http");
Services.prefs.clearUserPref(
"network.proxy.http_port");
Services.prefs.clearUserPref(
"network.proxy.no_proxies_on");
Services.prefs.clearUserPref(
"network.proxy.type");
Services.prefs.clearUserPref(
"network.auth.subresource-http-auth-allow");
Services.prefs.clearUserPref(
"network.auth.non-web-content-triggered-resources-http-auth-allow"
);
});
tests[current_test]();
}
function test_proxy_returnfalse() {
dump(
"\ntest: proxy returnfalse\n");
var chan = makeChan();
chan.notificationCallbacks =
new Requestor(FLAG_RETURN_FALSE, 0);
listener.expectedCode = 407;
// Proxy Unauthorized
chan.asyncOpen(listener);
do_test_pending();
}
function test_proxy_wrongpw() {
dump(
"\ntest: proxy wrongpw\n");
var chan = makeChan();
chan.notificationCallbacks =
new Requestor(FLAG_WRONG_PASSWORD, 0);
listener.expectedCode = 200;
// Eventually OK
chan.asyncOpen(listener);
do_test_pending();
}
function test_all_ok() {
dump(
"\ntest: all ok\n");
var chan = makeChan();
chan.notificationCallbacks =
new Requestor(0, 0);
listener.expectedCode = 200;
// OK
chan.asyncOpen(listener);
do_test_pending();
}
function test_proxy_407_cookie() {
var chan = makeChan();
chan.notificationCallbacks =
new Requestor(FLAG_RETURN_FALSE, 0);
chan.setRequestHeader(
"X-Set-407-Cookie",
"1",
false);
listener.expectedCode = 407;
// Proxy Unauthorized
chan.asyncOpen(listener);
do_test_pending();
}
function test_proxy_200_cookie() {
var chan = makeChan();
chan.notificationCallbacks =
new Requestor(0, 0);
chan.setRequestHeader(
"X-Set-407-Cookie",
"1",
false);
listener.expectedCode = 200;
// OK
chan.asyncOpen(listener);
do_test_pending();
}
function test_host_returnfalse() {
dump(
"\ntest: host returnfalse\n");
var chan = makeChan();
chan.notificationCallbacks =
new Requestor(0, FLAG_RETURN_FALSE);
listener.expectedCode = 401;
// Host Unauthorized
chan.asyncOpen(listener);
do_test_pending();
}
function test_host_wrongpw() {
dump(
"\ntest: host wrongpw\n");
var chan = makeChan();
chan.notificationCallbacks =
new Requestor(0, FLAG_WRONG_PASSWORD);
listener.expectedCode = 200;
// Eventually OK
chan.asyncOpen(listener);
do_test_pending();
}
function test_proxy_wrongpw_host_wrongpw() {
dump(
"\ntest: proxy wrongpw, host wrongpw\n");
var chan = makeChan();
chan.notificationCallbacks =
new Requestor(
FLAG_WRONG_PASSWORD,
FLAG_WRONG_PASSWORD
);
listener.expectedCode = 200;
// OK
chan.asyncOpen(listener);
do_test_pending();
}
function test_proxy_wrongpw_host_returnfalse() {
dump(
"\ntest: proxy wrongpw, host return false\n");
var chan = makeChan();
chan.notificationCallbacks =
new Requestor(
FLAG_WRONG_PASSWORD,
FLAG_RETURN_FALSE
);
listener.expectedCode = 401;
// Host Unauthorized
chan.asyncOpen(listener);
do_test_pending();
}
var tests = [
test_proxy_returnfalse,
test_proxy_wrongpw,
test_all_ok,
test_proxy_407_cookie,
test_proxy_200_cookie,
test_host_returnfalse,
test_host_wrongpw,
test_proxy_wrongpw_host_wrongpw,
test_proxy_wrongpw_host_returnfalse,
];
// PATH HANDLERS
// Proxy
function proxyAuthHandler(metadata, response) {
try {
var realm =
"intern";
// btoa("proxy:guest"), but that function is not available here
var expectedHeader =
"Basic cHJveHk6Z3Vlc3Q=";
var body;
if (
metadata.hasHeader(
"Proxy-Authorization") &&
metadata.getHeader(
"Proxy-Authorization") == expectedHeader
) {
dump(
"proxy password ok\n");
response.setHeader(
"Proxy-Authenticate",
'Basic realm="' + realm +
'"',
false
);
hostAuthHandler(metadata, response);
}
else {
dump(
"proxy password required\n");
response.setStatusLine(
metadata.httpVersion,
407,
"Unauthorized by HTTP proxy"
);
response.setHeader(
"Proxy-Authenticate",
'Basic realm="' + realm +
'"',
false
);
if (metadata.hasHeader(
"X-Set-407-Cookie")) {
response.setHeader(
"Set-Cookie",
"chewy",
false);
}
body =
"failed";
response.bodyOutputStream.write(body, body.length);
}
}
catch (e) {
do_throw(e);
}
}
// Host /auth
function hostAuthHandler(metadata, response) {
try {
var realm =
"extern";
// btoa("host:guest"), but that function is not available here
var expectedHeader =
"Basic aG9zdDpndWVzdA==";
var body;
if (
metadata.hasHeader(
"Authorization") &&
metadata.getHeader(
"Authorization") == expectedHeader
) {
dump(
"host password ok\n");
response.setStatusLine(
metadata.httpVersion,
200,
"OK, authorized for host"
);
response.setHeader(
"WWW-Authenticate",
'Basic realm="' + realm +
'"',
false
);
body =
"success";
}
else {
dump(
"host password required\n");
response.setStatusLine(
metadata.httpVersion,
401,
"Unauthorized by HTTP server host"
);
response.setHeader(
"WWW-Authenticate",
'Basic realm="' + realm +
'"',
false
);
body =
"failed";
}
response.bodyOutputStream.write(body, body.length);
}
catch (e) {
do_throw(e);
}
}