// This file tests authentication prompt callbacks
// TODO NIT use do_check_eq(expected, actual) consistently, not sometimes eq(actual, expected)
"use strict";
const { HttpServer } = ChromeUtils.importESModule(
"resource://testing-common/httpd.sys.mjs"
);
// Turn off the authentication dialog blocking for this test.
var prefs = Services.prefs;
prefs.setIntPref(
"network.auth.subresource-http-auth-allow", 2);
ChromeUtils.defineLazyGetter(
this,
"URL",
function () {
return "http://localhost:" + httpserv.identity.primaryPort;
});
ChromeUtils.defineLazyGetter(
this,
"PORT",
function () {
return httpserv.identity.primaryPort;
});
const FLAG_RETURN_FALSE = 1 << 0;
const FLAG_WRONG_PASSWORD = 1 << 1;
const FLAG_BOGUS_USER = 1 << 2;
// const FLAG_PREVIOUS_FAILED = 1 << 3;
const CROSS_ORIGIN = 1 << 4;
const FLAG_NO_REALM = 1 << 5;
const FLAG_NON_ASCII_USER_PASSWORD = 1 << 6;
function AuthPrompt1(flags) {
this.flags = flags;
}
AuthPrompt1.prototype = {
user:
"guest",
pass:
"guest",
expectedRealm:
"secret",
QueryInterface: ChromeUtils.generateQI([
"nsIAuthPrompt"]),
prompt:
function ap1_prompt() {
do_throw(
"unexpected prompt call");
},
promptUsernameAndPassword:
function ap1_promptUP(
title,
text,
realm,
savePW,
user,
pw
) {
if (
this.flags & FLAG_NO_REALM) {
// Note that the realm here isn't actually the realm. it's a pw mgr key.
Assert.equal(URL +
" (" +
this.expectedRealm +
")", realm);
}
if (!(
this.flags & CROSS_ORIGIN)) {
if (!text.includes(
this.expectedRealm)) {
do_throw(
"Text must indicate the realm");
}
}
else if (text.includes(
this.expectedRealm)) {
do_throw(
"There should not be realm for cross origin");
}
if (!text.includes(
"localhost")) {
do_throw(
"Text must indicate the hostname");
}
if (!text.includes(String(PORT))) {
do_throw(
"Text must indicate the port");
}
if (text.includes(
"-1")) {
do_throw(
"Text must contain negative numbers");
}
if (
this.flags & FLAG_RETURN_FALSE) {
return false;
}
if (
this.flags & FLAG_BOGUS_USER) {
this.user =
"foo\nbar";
}
else if (
this.flags & FLAG_NON_ASCII_USER_PASSWORD) {
this.user =
"é";
}
user.value =
this.user;
if (
this.flags & FLAG_WRONG_PASSWORD) {
pw.value =
this.pass +
".wrong";
// Now clear the flag to avoid an infinite loop
this.flags &= ~FLAG_WRONG_PASSWORD;
}
else if (
this.flags & FLAG_NON_ASCII_USER_PASSWORD) {
pw.value =
"é";
}
else {
pw.value =
this.pass;
}
return true;
},
promptPassword:
function ap1_promptPW() {
do_throw(
"unexpected promptPassword call");
},
};
function AuthPrompt2(flags) {
this.flags = flags;
}
AuthPrompt2.prototype = {
user:
"guest",
pass:
"guest",
expectedRealm:
"secret",
QueryInterface: ChromeUtils.generateQI([
"nsIAuthPrompt2"]),
promptAuth:
function ap2_promptAuth(channel, level, authInfo) {
authInfo.username =
this.user;
authInfo.password =
this.pass;
return true;
},
asyncPromptAuth:
function ap2_async() {
throw Components.Exception(
"", Cr.NS_ERROR_NOT_IMPLEMENTED);
},
};
function Requestor(flags, versions) {
this.flags = flags;
this.versions = versions;
}
Requestor.prototype = {
QueryInterface: ChromeUtils.generateQI([
"nsIInterfaceRequestor"]),
getInterface:
function requestor_gi(iid) {
if (
this.versions & 1 && iid.equals(Ci.nsIAuthPrompt)) {
// Allow the prompt to store state by caching it here
if (!
this.prompt1) {
this.prompt1 =
new AuthPrompt1(
this.flags);
}
return this.prompt1;
}
if (
this.versions & 2 && iid.equals(Ci.nsIAuthPrompt2)) {
// Allow the prompt to store state by caching it here
if (!
this.prompt2) {
this.prompt2 =
new AuthPrompt2(
this.flags);
}
return this.prompt2;
}
throw Components.Exception(
"", Cr.NS_ERROR_NO_INTERFACE);
},
prompt1:
null,
prompt2:
null,
};
function RealmTestRequestor() {}
RealmTestRequestor.prototype = {
QueryInterface: ChromeUtils.generateQI([
"nsIInterfaceRequestor",
"nsIAuthPrompt2",
]),
getInterface:
function realmtest_interface(iid) {
if (iid.equals(Ci.nsIAuthPrompt2)) {
return this;
}
throw Components.Exception(
"", Cr.NS_ERROR_NO_INTERFACE);
},
promptAuth:
function realmtest_checkAuth(channel, level, authInfo) {
Assert.equal(authInfo.realm,
'"foo_bar');
return false;
},
asyncPromptAuth:
function realmtest_async() {
throw Components.Exception(
"", Cr.NS_ERROR_NOT_IMPLEMENTED);
},
};
function makeChan(url, loadingUrl) {
var principal = Services.scriptSecurityManager.createContentPrincipal(
Services.io.newURI(loadingUrl),
{}
);
return NetUtil.newChannel({
uri: url,
loadingPrincipal: principal,
securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
});
}
// /auth/ntlm/simple
function authNtlmSimple(metadata, response) {
if (!metadata.hasHeader(
"Authorization")) {
response.setStatusLine(metadata.httpVersion, 401,
"Unauthorized");
response.setHeader(
"WWW-Authenticate",
"NTLM",
false);
return;
}
let challenge = metadata.getHeader(
"Authorization");
if (!challenge.startsWith(
"NTLM ")) {
response.setStatusLine(metadata.httpVersion, 401,
"Unauthorized");
return;
}
let decoded = atob(challenge.substring(5));
info(decoded);
if (!decoded.startsWith(
"NTLMSSP\0")) {
response.setStatusLine(metadata.httpVersion, 401,
"Unauthorized");
return;
}
let isNegotiate = decoded.substring(8).startsWith(
"\x01\x00\x00\x00");
let isAuthenticate = decoded.substring(8).startsWith(
"\x03\x00\x00\x00");
if (isNegotiate) {
response.setStatusLine(metadata.httpVersion, 401,
"Unauthorized");
response.setHeader(
"WWW-Authenticate",
"NTLM TlRMTVNTUAACAAAAAAAAAAAoAAABggAAASNFZ4mrze8AAAAAAAAAAAAAAAAAAAAA",
false
);
return;
}
if (isAuthenticate) {
let body =
"OK";
response.bodyOutputStream.write(body, body.length);
return;
}
// Something else went wrong.
response.setStatusLine(metadata.httpVersion, 401,
"Unauthorized");
}
let httpserv;
add_task(async
function test_ntlm() {
Services.prefs.setBoolPref(
"network.auth.force-generic-ntlm",
true);
Services.prefs.setBoolPref(
"network.auth.force-generic-ntlm-v1",
true);
httpserv =
new HttpServer();
httpserv.registerPathHandler(
"/auth/ntlm/simple", authNtlmSimple);
httpserv.start(-1);
registerCleanupFunction(async () => {
Services.prefs.clearUserPref(
"network.auth.force-generic-ntlm");
Services.prefs.clearUserPref(
"network.auth.force-generic-ntlm-v1");
await httpserv.stop();
});
var chan = makeChan(URL +
"/auth/ntlm/simple", URL);
chan.notificationCallbacks =
new Requestor(FLAG_RETURN_FALSE, 2);
let [req, buf] = await
new Promise(resolve => {
chan.asyncOpen(
new ChannelListener((req1, buf1) => resolve([req1, buf1]),
null)
);
});
Assert.ok(buf);
Assert.equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200);
});