Bug 458256. Use LoadLibraryW instead of LoadLibrary (patch by DougT). r+sr=vlad
[wine-gecko.git] / netwerk / test / unit / test_authentication.js
blob5eb1b999694880ee76b707f556c7d89a6ece5a7d
1 // This file tests authentication prompt callbacks
3 do_import_script("netwerk/test/httpserver/httpd.js");
5 const FLAG_RETURN_FALSE = 1 << 0;
6 const FLAG_WRONG_PASSWORD = 1 << 1;
8 const nsIAuthPrompt2 = Components.interfaces.nsIAuthPrompt2;
9 const nsIAuthInformation = Components.interfaces.nsIAuthInformation;
12 function AuthPrompt1(flags) {
13 this.flags = flags;
16 AuthPrompt1.prototype = {
17 user: "guest",
18 pass: "guest",
20 expectedRealm: "secret",
22 QueryInterface: function authprompt_qi(iid) {
23 if (iid.equals(Components.interfaces.nsISupports) ||
24 iid.equals(Components.interfaces.nsIAuthPrompt))
25 return this;
26 throw Components.results.NS_ERROR_NO_INTERFACE;
29 prompt: function ap1_prompt(title, text, realm, save, defaultText, result) {
30 do_throw("unexpected prompt call");
33 promptUsernameAndPassword:
34 function ap1_promptUP(title, text, realm, savePW, user, pw)
36 // Note that the realm here isn't actually the realm. it's a pw mgr key.
37 do_check_eq("localhost:4444 (" + this.expectedRealm + ")", realm);
38 if (text.indexOf(this.expectedRealm) == -1)
39 do_throw("Text must indicate the realm");
40 if (text.indexOf("localhost") == -1)
41 do_throw("Text must indicate the hostname");
42 if (text.indexOf("4444") == -1)
43 do_throw("Text must indicate the port");
44 if (text.indexOf("-1") != -1)
45 do_throw("Text must contain negative numbers");
47 if (this.flags & FLAG_RETURN_FALSE)
48 return false;
50 user.value = this.user;
51 if (this.flags & FLAG_WRONG_PASSWORD) {
52 pw.value = this.pass + ".wrong";
53 // Now clear the flag to avoid an infinite loop
54 this.flags &= ~FLAG_WRONG_PASSWORD;
55 } else {
56 pw.value = this.pass;
58 return true;
61 promptPassword: function ap1_promptPW(title, text, realm, save, pwd) {
62 do_throw("unexpected promptPassword call");
67 function AuthPrompt2(flags) {
68 this.flags = flags;
71 AuthPrompt2.prototype = {
72 user: "guest",
73 pass: "guest",
75 expectedRealm: "secret",
77 QueryInterface: function authprompt2_qi(iid) {
78 if (iid.equals(Components.interfaces.nsISupports) ||
79 iid.equals(Components.interfaces.nsIAuthPrompt2))
80 return this;
81 throw Components.results.NS_ERROR_NO_INTERFACE;
84 promptAuth:
85 function ap2_promptAuth(channel, level, authInfo)
87 var isNTLM = channel.URI.path.indexOf("ntlm") != -1;
88 var isDigest = channel.URI.path.indexOf("digest") != -1;
90 if (isNTLM)
91 this.expectedRealm = ""; // NTLM knows no realms
93 do_check_eq(this.expectedRealm, authInfo.realm);
95 var expectedLevel = (isNTLM || isDigest) ?
96 nsIAuthPrompt2.LEVEL_PW_ENCRYPTED :
97 nsIAuthPrompt2.LEVEL_NONE;
98 do_check_eq(expectedLevel, level);
100 var expectedFlags = nsIAuthInformation.AUTH_HOST;
102 if (isNTLM)
103 expectedFlags |= nsIAuthInformation.NEED_DOMAIN;
105 do_check_eq(expectedFlags, authInfo.flags);
107 var expectedScheme = isNTLM ? "ntlm" : isDigest ? "digest" : "basic";
108 do_check_eq(expectedScheme, authInfo.authenticationScheme);
110 // No passwords in the URL -> nothing should be prefilled
111 do_check_eq(authInfo.username, "");
112 do_check_eq(authInfo.password, "");
113 do_check_eq(authInfo.domain, "");
115 if (this.flags & FLAG_RETURN_FALSE)
116 return false;
118 authInfo.username = this.user;
119 if (this.flags & FLAG_WRONG_PASSWORD) {
120 authInfo.password = this.pass + ".wrong";
121 // Now clear the flag to avoid an infinite loop
122 this.flags &= ~FLAG_WRONG_PASSWORD;
123 } else {
124 authInfo.password = this.pass;
126 return true;
129 asyncPromptAuth: function ap2_async(chan, cb, ctx, lvl, info) {
130 do_throw("not implemented yet")
134 function Requestor(flags, versions, username) {
135 this.flags = flags;
136 this.versions = versions;
137 this.username = username;
140 Requestor.prototype = {
141 QueryInterface: function requestor_qi(iid) {
142 if (iid.equals(Components.interfaces.nsISupports) ||
143 iid.equals(Components.interfaces.nsIInterfaceRequestor))
144 return this;
145 throw Components.results.NS_ERROR_NO_INTERFACE;
148 getInterface: function requestor_gi(iid) {
149 if (this.versions & 1 &&
150 iid.equals(Components.interfaces.nsIAuthPrompt)) {
151 // Allow the prompt to store state by caching it here
152 if (!this.prompt1)
153 this.prompt1 = new AuthPrompt1(this.flags);
154 return this.prompt1;
156 if (this.versions & 2 &&
157 iid.equals(Components.interfaces.nsIAuthPrompt2)) {
158 // Allow the prompt to store state by caching it here
159 if (!this.prompt2)
160 this.prompt2 = new AuthPrompt2(this.flags);
161 if (this.username)
162 this.prompt2.user = this.username;
163 return this.prompt2;
166 throw Components.results.NS_ERROR_NO_INTERFACE;
169 prompt1: null,
170 prompt2: null
173 function RealmTestRequestor() {}
175 RealmTestRequestor.prototype = {
176 QueryInterface: function realmtest_qi(iid) {
177 if (iid.equals(Components.interfaces.nsISupports) ||
178 iid.equals(Components.interfaces.nsIInterfaceRequestor) ||
179 iid.equals(Components.interfaces.nsIAuthPrompt2))
180 return this;
181 throw Components.results.NS_ERROR_NO_INTERFACE;
184 getInterface: function realmtest_interface(iid) {
185 if (iid.equals(Components.interfaces.nsIAuthPrompt2))
186 return this;
188 throw Components.results.NS_ERROR_NO_INTERFACE;
191 promptAuth: function realmtest_checkAuth(channel, level, authInfo) {
192 do_check_eq(authInfo.realm, '\\"foo_bar');
194 return false;
197 asyncPromptAuth: function realmtest_async(chan, cb, ctx, lvl, info) {
198 do_throw("not implemented yet");
202 var listener = {
203 expectedCode: 401, // Unauthorized
205 onStartRequest: function test_onStartR(request, ctx) {
206 try {
207 if (!Components.isSuccessCode(request.status))
208 do_throw("Channel should have a success code!");
210 if (!(request instanceof Components.interfaces.nsIHttpChannel))
211 do_throw("Expecting an HTTP channel");
213 do_check_eq(request.responseStatus, this.expectedCode);
214 // The request should be succeeded iff we expect 200
215 do_check_eq(request.requestSucceeded, this.expectedCode == 200);
217 } catch (e) {
218 do_throw("Unexpected exception: " + e);
221 throw Components.results.NS_ERROR_ABORT;
224 onDataAvailable: function test_ODA() {
225 do_throw("Should not get any data!");
228 onStopRequest: function test_onStopR(request, ctx, status) {
229 do_check_eq(status, Components.results.NS_ERROR_ABORT);
231 if (current_test < (tests.length - 1)) {
232 // First, gotta clear the auth cache
233 Components.classes["@mozilla.org/network/http-auth-manager;1"]
234 .getService(Components.interfaces.nsIHttpAuthManager)
235 .clearAll();
237 current_test++;
238 tests[current_test]();
239 } else {
240 httpserv.stop();
243 do_test_finished();
247 function makeChan(url) {
248 var ios = Components.classes["@mozilla.org/network/io-service;1"]
249 .getService(Components.interfaces.nsIIOService);
250 var chan = ios.newChannel(url, null, null)
251 .QueryInterface(Components.interfaces.nsIHttpChannel);
253 return chan;
256 var tests = [test_noauth, test_returnfalse1, test_wrongpw1, test_prompt1,
257 test_returnfalse2, test_wrongpw2, test_prompt2, test_ntlm,
258 test_auth, test_digest_noauth, test_digest,
259 test_digest_bogus_user];
261 var current_test = 0;
263 var httpserv = null;
265 function run_test() {
266 httpserv = new nsHttpServer();
268 httpserv.registerPathHandler("/auth", authHandler);
269 httpserv.registerPathHandler("/auth/ntlm/simple", authNtlmSimple);
270 httpserv.registerPathHandler("/auth/realm", authRealm);
271 httpserv.registerPathHandler("/auth/digest", authDigest);
273 httpserv.start(4444);
275 tests[0]();
278 function test_noauth() {
279 var chan = makeChan("http://localhost:4444/auth");
281 listener.expectedCode = 401; // Unauthorized
282 chan.asyncOpen(listener, null);
284 do_test_pending();
287 function test_returnfalse1() {
288 var chan = makeChan("http://localhost:4444/auth");
290 chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 1);
291 listener.expectedCode = 401; // Unauthorized
292 chan.asyncOpen(listener, null);
294 do_test_pending();
297 function test_wrongpw1() {
298 var chan = makeChan("http://localhost:4444/auth");
300 chan.notificationCallbacks = new Requestor(FLAG_WRONG_PASSWORD, 1);
301 listener.expectedCode = 200; // OK
302 chan.asyncOpen(listener, null);
304 do_test_pending();
307 function test_prompt1() {
308 var chan = makeChan("http://localhost:4444/auth");
310 chan.notificationCallbacks = new Requestor(0, 1);
311 listener.expectedCode = 200; // OK
312 chan.asyncOpen(listener, null);
314 do_test_pending();
317 function test_returnfalse2() {
318 var chan = makeChan("http://localhost:4444/auth");
320 chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 2);
321 listener.expectedCode = 401; // Unauthorized
322 chan.asyncOpen(listener, null);
324 do_test_pending();
327 function test_wrongpw2() {
328 var chan = makeChan("http://localhost:4444/auth");
330 chan.notificationCallbacks = new Requestor(FLAG_WRONG_PASSWORD, 2);
331 listener.expectedCode = 200; // OK
332 chan.asyncOpen(listener, null);
334 do_test_pending();
337 function test_prompt2() {
338 var chan = makeChan("http://localhost:4444/auth");
340 chan.notificationCallbacks = new Requestor(0, 2);
341 listener.expectedCode = 200; // OK
342 chan.asyncOpen(listener, null);
344 do_test_pending();
347 function test_ntlm() {
348 var chan = makeChan("http://localhost:4444/auth/ntlm/simple");
350 chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 2);
351 listener.expectedCode = 401; // Unauthorized
352 chan.asyncOpen(listener, null);
354 do_test_pending();
357 function test_auth() {
358 var chan = makeChan("http://localhost:4444/auth/realm");
360 chan.notificationCallbacks = new RealmTestRequestor();
361 listener.expectedCode = 401; // Unauthorized
362 chan.asyncOpen(listener, null);
364 do_test_pending();
367 function test_digest_noauth() {
368 var chan = makeChan("http://localhost:4444/auth/digest");
370 //chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 2);
371 listener.expectedCode = 401; // Unauthorized
372 chan.asyncOpen(listener, null);
374 do_test_pending();
377 function test_digest() {
378 var chan = makeChan("http://localhost:4444/auth/digest");
380 chan.notificationCallbacks = new Requestor(0, 2);
381 listener.expectedCode = 200; // OK
382 chan.asyncOpen(listener, null);
384 do_test_pending();
387 function test_digest_bogus_user() {
388 var chan = makeChan("http://localhost:4444/auth/digest");
389 chan.notificationCallbacks = new Requestor(0, 2, "foo\nbar");
390 listener.expectedCode = 401; // unauthorized
391 chan.asyncOpen(listener, null);
393 do_test_pending();
396 // PATH HANDLERS
398 // /auth
399 function authHandler(metadata, response) {
400 // btoa("guest:guest"), but that function is not available here
401 var expectedHeader = "Basic Z3Vlc3Q6Z3Vlc3Q=";
403 var body;
404 if (metadata.hasHeader("Authorization") &&
405 metadata.getHeader("Authorization") == expectedHeader)
407 response.setStatusLine(metadata.httpVersion, 200, "OK, authorized");
408 response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
410 body = "success";
412 else
414 // didn't know guest:guest, failure
415 response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
416 response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
418 body = "failed";
421 response.bodyOutputStream.write(body, body.length);
424 // /auth/ntlm/simple
425 function authNtlmSimple(metadata, response) {
426 response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
427 response.setHeader("WWW-Authenticate", "NTLM" /* + ' realm="secret"' */, false);
429 var body = "NOTE: This just sends an NTLM challenge, it never\n" +
430 "accepts the authentication. It also closes\n" +
431 "the connection after sending the challenge\n";
434 response.bodyOutputStream.write(body, body.length);
437 // /auth/realm
438 function authRealm(metadata, response) {
439 response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
440 response.setHeader("WWW-Authenticate", 'Basic realm="\\"foo_bar"', false);
441 var body = "success";
443 response.bodyOutputStream.write(body, body.length);
447 // Digest functions
449 function bytesFromString(str) {
450 var converter =
451 Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
452 .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
453 converter.charset = "UTF-8";
454 var result = {};
455 var data = converter.convertToByteArray(str, result);
456 return data;
459 // return the two-digit hexadecimal code for a byte
460 function toHexString(charCode) {
461 return ("0" + charCode.toString(16)).slice(-2);
464 function H(str) {
465 var data = bytesFromString(str);
466 var ch = Components.classes["@mozilla.org/security/hash;1"]
467 .createInstance(Components.interfaces.nsICryptoHash);
468 ch.init(Components.interfaces.nsICryptoHash.MD5);
469 ch.update(data, data.length);
470 var hash = ch.finish(false);
471 return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
475 // Digest handler
477 // /auth/digest
478 function authDigest(metadata, response) {
479 var nonce = "6f93719059cf8d568005727f3250e798";
480 var opaque = "1234opaque1234";
481 var cnonceRE = /cnonce="(\w+)"/;
482 var responseRE = /response="(\w+)"/;
483 var usernameRE = /username="(\w+)"/;
484 var authenticate = 'Digest realm="secret", domain="/", qop=auth,' +
485 'algorithm=MD5, nonce="' + nonce+ '" opaque="' +
486 opaque + '"';
487 var body;
488 // check creds if we have them
489 if (metadata.hasHeader("Authorization")) {
490 var auth = metadata.getHeader("Authorization");
491 var cnonce = (auth.match(cnonceRE))[1];
492 var clientDigest = (auth.match(responseRE))[1];
493 var username = (auth.match(usernameRE))[1];
494 var nc = "00000001";
496 if (username != "guest") {
497 response.setStatusLine(metadata.httpVersion, 400, "bad request");
498 body = "should never get here";
499 } else {
500 // see RFC2617 for the description of this calculation
501 var A1 = "guest:secret:guest";
502 var A2 = "GET:/auth/digest";
503 var noncebits = [nonce, nc, cnonce, "auth", H(A2)].join(":");
504 var digest = H([H(A1), noncebits].join(":"));
506 if (clientDigest == digest) {
507 response.setStatusLine(metadata.httpVersion, 200, "OK, authorized");
508 body = "success";
509 } else {
510 response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
511 response.setHeader("WWW-Authenticate", authenticate, false);
512 body = "auth failed";
515 } else {
516 // no header, send one
517 response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
518 response.setHeader("WWW-Authenticate", authenticate, false);
519 body = "failed, no header";
522 response.bodyOutputStream.write(body, body.length);