2006-07-27 03:10:48 +04:00
|
|
|
// This file tests authentication prompt callbacks
|
|
|
|
|
2006-12-15 05:13:54 +03:00
|
|
|
do_import_script("test-harness/xpcshell-simple/httpd.js");
|
|
|
|
|
2006-08-02 01:47:04 +04:00
|
|
|
const FLAG_RETURN_FALSE = 1 << 0;
|
|
|
|
const FLAG_WRONG_PASSWORD = 1 << 1;
|
2006-07-27 03:10:48 +04:00
|
|
|
|
2006-08-27 01:42:54 +04:00
|
|
|
const nsIAuthPrompt2 = Components.interfaces.nsIAuthPrompt2;
|
|
|
|
const nsIAuthInformation = Components.interfaces.nsIAuthInformation;
|
|
|
|
|
2006-12-15 05:13:54 +03:00
|
|
|
|
2006-08-02 01:47:04 +04:00
|
|
|
function AuthPrompt1(flags) {
|
|
|
|
this.flags = flags;
|
|
|
|
}
|
2006-07-27 03:10:48 +04:00
|
|
|
|
|
|
|
AuthPrompt1.prototype = {
|
|
|
|
user: "guest",
|
|
|
|
pass: "guest",
|
|
|
|
|
|
|
|
expectedRealm: "secret",
|
|
|
|
|
|
|
|
QueryInterface: function authprompt_qi(iid) {
|
|
|
|
if (iid.equals(Components.interfaces.nsISupports) ||
|
|
|
|
iid.equals(Components.interfaces.nsIAuthPrompt))
|
|
|
|
return this;
|
|
|
|
throw Components.results.NS_ERROR_NO_INTERFACE;
|
|
|
|
},
|
|
|
|
|
|
|
|
prompt: function ap1_prompt(title, text, realm, save, defaultText, result) {
|
|
|
|
do_throw("unexpected prompt call");
|
|
|
|
},
|
|
|
|
|
|
|
|
promptUsernameAndPassword:
|
|
|
|
function ap1_promptUP(title, text, realm, savePW, user, pw)
|
|
|
|
{
|
|
|
|
// Note that the realm here isn't actually the realm. it's a pw mgr key.
|
|
|
|
do_check_eq("localhost:4444 (" + this.expectedRealm + ")", realm);
|
|
|
|
if (text.indexOf(this.expectedRealm) == -1)
|
|
|
|
do_throw("Text must indicate the realm");
|
|
|
|
if (text.indexOf("localhost") == -1)
|
|
|
|
do_throw("Text must indicate the hostname");
|
|
|
|
if (text.indexOf("4444") == -1)
|
|
|
|
do_throw("Text must indicate the port");
|
|
|
|
if (text.indexOf("-1") != -1)
|
|
|
|
do_throw("Text must contain negative numbers");
|
|
|
|
|
2006-08-02 01:47:04 +04:00
|
|
|
if (this.flags & FLAG_RETURN_FALSE)
|
|
|
|
return false;
|
|
|
|
|
2006-07-27 03:10:48 +04:00
|
|
|
user.value = this.user;
|
2006-08-02 01:47:04 +04:00
|
|
|
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 {
|
|
|
|
pw.value = this.pass;
|
|
|
|
}
|
2006-07-27 03:10:48 +04:00
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
promptPassword: function ap1_promptPW(title, text, realm, save, pwd) {
|
|
|
|
do_throw("unexpected promptPassword call");
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2006-08-27 01:42:54 +04:00
|
|
|
function AuthPrompt2(flags) {
|
2006-08-02 01:47:04 +04:00
|
|
|
this.flags = flags;
|
2006-07-27 03:10:48 +04:00
|
|
|
}
|
|
|
|
|
2006-08-27 01:42:54 +04:00
|
|
|
AuthPrompt2.prototype = {
|
|
|
|
user: "guest",
|
|
|
|
pass: "guest",
|
|
|
|
|
|
|
|
expectedRealm: "secret",
|
|
|
|
|
|
|
|
QueryInterface: function authprompt2_qi(iid) {
|
|
|
|
if (iid.equals(Components.interfaces.nsISupports) ||
|
|
|
|
iid.equals(Components.interfaces.nsIAuthPrompt2))
|
|
|
|
return this;
|
|
|
|
throw Components.results.NS_ERROR_NO_INTERFACE;
|
|
|
|
},
|
|
|
|
|
|
|
|
promptAuth:
|
|
|
|
function ap2_promptAuth(channel, level, authInfo)
|
|
|
|
{
|
|
|
|
var isNTLM = channel.URI.path.indexOf("ntlm") != -1;
|
|
|
|
|
|
|
|
if (isNTLM)
|
|
|
|
this.expectedRealm = ""; // NTLM knows no realms
|
|
|
|
|
|
|
|
do_check_eq(this.expectedRealm, authInfo.realm);
|
|
|
|
|
2006-09-06 03:47:30 +04:00
|
|
|
var expectedLevel = isNTLM ?
|
|
|
|
nsIAuthPrompt2.LEVEL_PW_ENCRYPTED :
|
|
|
|
nsIAuthPrompt2.LEVEL_NONE;
|
2006-08-27 01:42:54 +04:00
|
|
|
do_check_eq(expectedLevel, level);
|
|
|
|
|
|
|
|
var expectedFlags = nsIAuthInformation.AUTH_HOST;
|
|
|
|
|
|
|
|
if (isNTLM)
|
|
|
|
expectedFlags |= nsIAuthInformation.NEED_DOMAIN;
|
|
|
|
|
|
|
|
do_check_eq(expectedFlags, authInfo.flags);
|
|
|
|
|
|
|
|
var expectedScheme = isNTLM ? "ntlm" : "basic";
|
|
|
|
do_check_eq(expectedScheme, authInfo.authenticationScheme);
|
|
|
|
|
|
|
|
// No passwords in the URL -> nothing should be prefilled
|
|
|
|
do_check_eq(authInfo.username, "");
|
|
|
|
do_check_eq(authInfo.password, "");
|
|
|
|
do_check_eq(authInfo.domain, "");
|
|
|
|
|
|
|
|
if (this.flags & FLAG_RETURN_FALSE)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
authInfo.username = this.user;
|
|
|
|
if (this.flags & FLAG_WRONG_PASSWORD) {
|
|
|
|
authInfo.password = this.pass + ".wrong";
|
|
|
|
// Now clear the flag to avoid an infinite loop
|
|
|
|
this.flags &= ~FLAG_WRONG_PASSWORD;
|
|
|
|
} else {
|
|
|
|
authInfo.password = this.pass;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
asyncPromptAuth: function ap2_async(chan, cb, ctx, lvl, info) {
|
|
|
|
do_throw("not implemented yet")
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
function Requestor(flags, versions) {
|
|
|
|
this.flags = flags;
|
|
|
|
this.versions = versions;
|
|
|
|
}
|
|
|
|
|
2006-07-27 03:10:48 +04:00
|
|
|
Requestor.prototype = {
|
|
|
|
QueryInterface: function requestor_qi(iid) {
|
|
|
|
if (iid.equals(Components.interfaces.nsISupports) ||
|
|
|
|
iid.equals(Components.interfaces.nsIInterfaceRequestor))
|
|
|
|
return this;
|
|
|
|
throw Components.results.NS_ERROR_NO_INTERFACE;
|
|
|
|
},
|
|
|
|
|
|
|
|
getInterface: function requestor_gi(iid) {
|
2006-08-27 01:42:54 +04:00
|
|
|
if (this.versions & 1 &&
|
|
|
|
iid.equals(Components.interfaces.nsIAuthPrompt)) {
|
2006-08-02 01:47:04 +04:00
|
|
|
// Allow the prompt to store state by caching it here
|
|
|
|
if (!this.prompt1)
|
|
|
|
this.prompt1 = new AuthPrompt1(this.flags);
|
|
|
|
return this.prompt1;
|
|
|
|
}
|
2006-08-27 01:42:54 +04:00
|
|
|
if (this.versions & 2 &&
|
|
|
|
iid.equals(Components.interfaces.nsIAuthPrompt2)) {
|
|
|
|
// Allow the prompt to store state by caching it here
|
|
|
|
if (!this.prompt2)
|
|
|
|
this.prompt2 = new AuthPrompt2(this.flags);
|
|
|
|
return this.prompt2;
|
|
|
|
}
|
|
|
|
|
2006-07-27 03:10:48 +04:00
|
|
|
throw Components.results.NS_ERROR_NO_INTERFACE;
|
2006-08-02 01:47:04 +04:00
|
|
|
},
|
|
|
|
|
2006-08-27 01:42:54 +04:00
|
|
|
prompt1: null,
|
|
|
|
prompt2: null
|
2006-07-27 03:10:48 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
var listener = {
|
|
|
|
expectedCode: 401, // Unauthorized
|
|
|
|
|
|
|
|
onStartRequest: function test_onStartR(request, ctx) {
|
|
|
|
try {
|
|
|
|
if (!Components.isSuccessCode(request.status))
|
|
|
|
do_throw("Channel should have a success code!");
|
|
|
|
|
|
|
|
if (!(request instanceof Components.interfaces.nsIHttpChannel))
|
|
|
|
do_throw("Expecting an HTTP channel");
|
|
|
|
|
|
|
|
do_check_eq(request.responseStatus, this.expectedCode);
|
|
|
|
// The request should be succeeded iff we expect 200
|
|
|
|
do_check_eq(request.requestSucceeded, this.expectedCode == 200);
|
|
|
|
|
|
|
|
} catch (e) {
|
|
|
|
do_throw("Unexpected exception: " + e);
|
|
|
|
}
|
|
|
|
|
|
|
|
throw Components.results.NS_ERROR_ABORT;
|
|
|
|
},
|
|
|
|
|
|
|
|
onDataAvailable: function test_ODA() {
|
|
|
|
do_throw("Should not get any data!");
|
|
|
|
},
|
|
|
|
|
|
|
|
onStopRequest: function test_onStopR(request, ctx, status) {
|
|
|
|
do_check_eq(status, Components.results.NS_ERROR_ABORT);
|
|
|
|
|
|
|
|
if (current_test < (tests.length - 1)) {
|
2006-08-02 06:02:37 +04:00
|
|
|
// First, gotta clear the auth cache
|
|
|
|
Components.classes["@mozilla.org/network/http-auth-manager;1"]
|
|
|
|
.getService(Components.interfaces.nsIHttpAuthManager)
|
|
|
|
.clearAll();
|
|
|
|
|
2006-07-27 03:10:48 +04:00
|
|
|
current_test++;
|
|
|
|
tests[current_test]();
|
2006-08-02 06:02:37 +04:00
|
|
|
} else {
|
2006-12-15 05:13:54 +03:00
|
|
|
httpserv.stop();
|
2006-07-27 03:10:48 +04:00
|
|
|
}
|
2006-08-02 06:02:37 +04:00
|
|
|
|
2006-07-27 03:10:48 +04:00
|
|
|
do_test_finished();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
function makeChan(url) {
|
|
|
|
var ios = Components.classes["@mozilla.org/network/io-service;1"]
|
|
|
|
.getService(Components.interfaces.nsIIOService);
|
|
|
|
var chan = ios.newChannel(url, null, null)
|
|
|
|
.QueryInterface(Components.interfaces.nsIHttpChannel);
|
|
|
|
|
|
|
|
return chan;
|
|
|
|
}
|
|
|
|
|
2006-08-27 01:42:54 +04:00
|
|
|
var tests = [test_noauth, test_returnfalse1, test_wrongpw1, test_prompt1,
|
|
|
|
test_returnfalse2, test_wrongpw2, test_prompt2, test_ntlm];
|
2006-07-27 03:10:48 +04:00
|
|
|
var current_test = 0;
|
|
|
|
|
2006-08-02 06:02:37 +04:00
|
|
|
var httpserv = null;
|
|
|
|
|
2006-07-27 03:10:48 +04:00
|
|
|
function run_test() {
|
2006-12-15 05:13:54 +03:00
|
|
|
httpserv = new nsHttpServer();
|
|
|
|
|
|
|
|
httpserv.registerPathHandler("/auth", authHandler);
|
|
|
|
httpserv.registerPathHandler("/auth/ntlm/simple", authNtlmSimple);
|
|
|
|
|
|
|
|
httpserv.start(4444);
|
|
|
|
|
2006-07-27 03:10:48 +04:00
|
|
|
tests[0]();
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_noauth() {
|
|
|
|
var chan = makeChan("http://localhost:4444/auth");
|
|
|
|
|
2006-08-02 01:47:04 +04:00
|
|
|
listener.expectedCode = 401; // Unauthorized
|
|
|
|
chan.asyncOpen(listener, null);
|
|
|
|
|
|
|
|
do_test_pending();
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_returnfalse1() {
|
|
|
|
var chan = makeChan("http://localhost:4444/auth");
|
|
|
|
|
2006-08-27 01:42:54 +04:00
|
|
|
chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 1);
|
2006-08-02 01:47:04 +04:00
|
|
|
listener.expectedCode = 401; // Unauthorized
|
|
|
|
chan.asyncOpen(listener, null);
|
|
|
|
|
|
|
|
do_test_pending();
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_wrongpw1() {
|
|
|
|
var chan = makeChan("http://localhost:4444/auth");
|
|
|
|
|
2006-08-27 01:42:54 +04:00
|
|
|
chan.notificationCallbacks = new Requestor(FLAG_WRONG_PASSWORD, 1);
|
2006-08-02 01:47:04 +04:00
|
|
|
listener.expectedCode = 200; // OK
|
2006-07-27 03:10:48 +04:00
|
|
|
chan.asyncOpen(listener, null);
|
|
|
|
|
|
|
|
do_test_pending();
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_prompt1() {
|
|
|
|
var chan = makeChan("http://localhost:4444/auth");
|
|
|
|
|
2006-08-27 01:42:54 +04:00
|
|
|
chan.notificationCallbacks = new Requestor(0, 1);
|
|
|
|
listener.expectedCode = 200; // OK
|
|
|
|
chan.asyncOpen(listener, null);
|
|
|
|
|
|
|
|
do_test_pending();
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_returnfalse2() {
|
|
|
|
var chan = makeChan("http://localhost:4444/auth");
|
|
|
|
|
|
|
|
chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 2);
|
|
|
|
listener.expectedCode = 401; // Unauthorized
|
|
|
|
chan.asyncOpen(listener, null);
|
|
|
|
|
|
|
|
do_test_pending();
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_wrongpw2() {
|
|
|
|
var chan = makeChan("http://localhost:4444/auth");
|
|
|
|
|
|
|
|
chan.notificationCallbacks = new Requestor(FLAG_WRONG_PASSWORD, 2);
|
|
|
|
listener.expectedCode = 200; // OK
|
|
|
|
chan.asyncOpen(listener, null);
|
|
|
|
|
|
|
|
do_test_pending();
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_prompt2() {
|
|
|
|
var chan = makeChan("http://localhost:4444/auth");
|
|
|
|
|
|
|
|
chan.notificationCallbacks = new Requestor(0, 2);
|
2006-07-27 03:10:48 +04:00
|
|
|
listener.expectedCode = 200; // OK
|
|
|
|
chan.asyncOpen(listener, null);
|
|
|
|
|
|
|
|
do_test_pending();
|
|
|
|
}
|
|
|
|
|
2006-08-27 01:42:54 +04:00
|
|
|
function test_ntlm() {
|
|
|
|
var chan = makeChan("http://localhost:4444/auth/ntlm/simple");
|
|
|
|
|
|
|
|
chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 2);
|
|
|
|
listener.expectedCode = 401; // Unauthorized
|
|
|
|
chan.asyncOpen(listener, null);
|
|
|
|
|
|
|
|
do_test_pending();
|
|
|
|
}
|
|
|
|
|
2006-12-15 05:13:54 +03:00
|
|
|
// PATH HANDLERS
|
|
|
|
|
|
|
|
// /auth
|
|
|
|
function authHandler(metadata, response) {
|
|
|
|
// btoa("guest:guest"), but that function is not available here
|
|
|
|
var expectedHeader = "Basic Z3Vlc3Q6Z3Vlc3Q=";
|
|
|
|
|
|
|
|
var body;
|
|
|
|
if (metadata.hasHeader("Authorization") &&
|
|
|
|
metadata.getHeader("Authorization") == expectedHeader)
|
|
|
|
{
|
|
|
|
response.setStatusLine(metadata.httpVersion, 200, "OK, authorized");
|
|
|
|
response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
|
|
|
|
|
|
|
|
body = "success";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// didn't know guest:guest, failure
|
|
|
|
response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
|
|
|
|
response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
|
|
|
|
|
|
|
|
body = "failed";
|
|
|
|
}
|
|
|
|
|
|
|
|
response.bodyOutputStream.write(body, body.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
// /auth/ntlm/simple
|
|
|
|
function authNtlmSimple(metadata, response) {
|
|
|
|
response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
|
|
|
|
response.setHeader("WWW-Authenticate", "NTLM" /* + ' realm="secret"' */);
|
|
|
|
|
|
|
|
var body = "NOTE: This just sends an NTLM challenge, it never\n" +
|
|
|
|
"accepts the authentication. It also closes\n" +
|
|
|
|
"the connection after sending the challenge\n";
|
|
|
|
|
|
|
|
|
|
|
|
response.bodyOutputStream.write(body, body.length);
|
|
|
|
}
|