michael@0: /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /** michael@0: * This tests the automatic login to the proxy with password, michael@0: * if the password is stored and the browser is restarted. michael@0: * michael@0: * michael@0: */ michael@0: michael@0: Cu.import("resource://testing-common/httpd.js"); michael@0: michael@0: const FLAG_RETURN_FALSE = 1 << 0; michael@0: const FLAG_WRONG_PASSWORD = 1 << 1; michael@0: const FLAG_PREVIOUS_FAILED = 1 << 2; michael@0: michael@0: function AuthPrompt2(proxyFlags, hostFlags) { michael@0: this.proxyCred.flags = proxyFlags; michael@0: this.hostCred.flags = hostFlags; michael@0: } michael@0: AuthPrompt2.prototype = { michael@0: proxyCred : { user: "proxy", pass: "guest", michael@0: realmExpected: "intern", flags : 0 }, michael@0: hostCred : { user: "host", pass: "guest", michael@0: realmExpected: "extern", flags : 0 }, michael@0: michael@0: QueryInterface: function authprompt2_qi(iid) { michael@0: if (iid.equals(Ci.nsISupports) || michael@0: iid.equals(Ci.nsIAuthPrompt2)) michael@0: return this; michael@0: throw Cr.NS_ERROR_NO_INTERFACE; michael@0: }, michael@0: michael@0: promptAuth: michael@0: function ap2_promptAuth(channel, encryptionLevel, authInfo) michael@0: { michael@0: try { michael@0: michael@0: // never HOST and PROXY set at the same time in prompt michael@0: do_check_eq((authInfo.flags & Ci.nsIAuthInformation.AUTH_HOST) != 0, michael@0: (authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) == 0); michael@0: michael@0: var isProxy = (authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) != 0; michael@0: var cred = isProxy ? this.proxyCred : this.hostCred; michael@0: michael@0: dump("with flags: " + michael@0: ((cred.flags & FLAG_WRONG_PASSWORD) !=0 ? "wrong password" : "")+" "+ michael@0: ((cred.flags & FLAG_PREVIOUS_FAILED) !=0 ? "previous failed" : "")+" "+ michael@0: ((cred.flags & FLAG_RETURN_FALSE) !=0 ? "return false" : "") + "\n"); michael@0: michael@0: // PROXY properly set by necko (checked using realm) michael@0: do_check_eq(cred.realmExpected, authInfo.realm); michael@0: michael@0: // PREVIOUS_FAILED properly set by necko michael@0: do_check_eq((cred.flags & FLAG_PREVIOUS_FAILED) != 0, michael@0: (authInfo.flags & Ci.nsIAuthInformation.PREVIOUS_FAILED) != 0); michael@0: michael@0: if (cred.flags & FLAG_RETURN_FALSE) michael@0: { michael@0: cred.flags |= FLAG_PREVIOUS_FAILED; michael@0: cred.flags &= ~FLAG_RETURN_FALSE; michael@0: return false; michael@0: } michael@0: michael@0: authInfo.username = cred.user; michael@0: if (cred.flags & FLAG_WRONG_PASSWORD) { michael@0: authInfo.password = cred.pass + ".wrong"; michael@0: cred.flags |= FLAG_PREVIOUS_FAILED; michael@0: // Now clear the flag to avoid an infinite loop michael@0: cred.flags &= ~FLAG_WRONG_PASSWORD; michael@0: } else { michael@0: authInfo.password = cred.pass; michael@0: cred.flags &= ~FLAG_PREVIOUS_FAILED; michael@0: } michael@0: return true; michael@0: michael@0: } catch (e) { do_throw(e); } michael@0: }, michael@0: michael@0: asyncPromptAuth: michael@0: function ap2_async(channel, callback, context, encryptionLevel, authInfo) michael@0: { michael@0: try { michael@0: var me = this; michael@0: var allOverAndDead = false; michael@0: do_execute_soon(function() { michael@0: try { michael@0: if (allOverAndDead) michael@0: throw "already canceled"; michael@0: var ret = me.promptAuth(channel, encryptionLevel, authInfo); michael@0: if (!ret) michael@0: callback.onAuthCancelled(context, true); michael@0: else michael@0: callback.onAuthAvailable(context, authInfo); michael@0: allOverAndDead = true; michael@0: } catch (e) { do_throw(e); } michael@0: }); michael@0: return new Cancelable(function() { michael@0: if (allOverAndDead) michael@0: throw "can't cancel, already ran"; michael@0: callback.onAuthAvailable(context, authInfo); michael@0: allOverAndDead = true; michael@0: }); michael@0: } catch (e) { do_throw(e); } michael@0: } michael@0: }; michael@0: michael@0: function Cancelable(onCancelFunc) { michael@0: this.onCancelFunc = onCancelFunc; michael@0: } michael@0: Cancelable.prototype = { michael@0: QueryInterface: function cancelable_qi(iid) { michael@0: if (iid.equals(Ci.nsISupports) || michael@0: iid.equals(Ci.nsICancelable)) michael@0: return this; michael@0: throw Cr.NS_ERROR_NO_INTERFACE; michael@0: }, michael@0: cancel: function cancel() { michael@0: try { michael@0: this.onCancelFunc(); michael@0: } catch (e) { do_throw(e); } michael@0: } michael@0: }; michael@0: michael@0: function Requestor(proxyFlags, hostFlags) { michael@0: this.proxyFlags = proxyFlags; michael@0: this.hostFlags = hostFlags; michael@0: } michael@0: Requestor.prototype = { michael@0: QueryInterface: function requestor_qi(iid) { michael@0: if (iid.equals(Ci.nsISupports) || michael@0: iid.equals(Ci.nsIInterfaceRequestor)) michael@0: return this; michael@0: throw Cr.NS_ERROR_NO_INTERFACE; michael@0: }, michael@0: michael@0: getInterface: function requestor_gi(iid) { michael@0: if (iid.equals(Ci.nsIAuthPrompt)) { michael@0: dump("authprompt1 not implemented\n"); michael@0: throw Cr.NS_ERROR_NO_INTERFACE; michael@0: } michael@0: if (iid.equals(Ci.nsIAuthPrompt2)) { michael@0: try { michael@0: // Allow the prompt to store state by caching it here michael@0: if (!this.prompt2) michael@0: this.prompt2 = new AuthPrompt2(this.proxyFlags, this.hostFlags); michael@0: return this.prompt2; michael@0: } catch (e) { do_throw(e); } michael@0: } michael@0: throw Cr.NS_ERROR_NO_INTERFACE; michael@0: }, michael@0: michael@0: prompt2: null michael@0: }; michael@0: michael@0: var listener = { michael@0: expectedCode: -1, // uninitialized michael@0: michael@0: onStartRequest: function test_onStartR(request, ctx) { michael@0: try { michael@0: // Proxy auth cancellation return failures to avoid spoofing michael@0: if (!Components.isSuccessCode(request.status) && michael@0: (this.expectedCode != 407)) michael@0: do_throw("Channel should have a success code!"); michael@0: michael@0: if (!(request instanceof Ci.nsIHttpChannel)) michael@0: do_throw("Expecting an HTTP channel"); michael@0: michael@0: do_check_eq(this.expectedCode, request.responseStatus); michael@0: // If we expect 200, the request should have succeeded michael@0: do_check_eq(this.expectedCode == 200, request.requestSucceeded); michael@0: michael@0: } catch (e) { michael@0: do_throw("Unexpected exception: " + e); michael@0: } michael@0: michael@0: throw Cr.NS_ERROR_ABORT; michael@0: }, michael@0: michael@0: onDataAvailable: function test_ODA() { michael@0: do_throw("Should not get any data!"); michael@0: }, michael@0: michael@0: onStopRequest: function test_onStopR(request, ctx, status) { michael@0: do_check_eq(status, Cr.NS_ERROR_ABORT); michael@0: michael@0: if (current_test < (tests.length - 1)) { michael@0: // First, need to clear the auth cache michael@0: Cc["@mozilla.org/network/http-auth-manager;1"] michael@0: .getService(Ci.nsIHttpAuthManager) michael@0: .clearAll(); michael@0: michael@0: current_test++; michael@0: tests[current_test](); michael@0: } else { michael@0: do_test_pending(); michael@0: httpserv.stop(do_test_finished); michael@0: } michael@0: michael@0: do_test_finished(); michael@0: } michael@0: }; michael@0: michael@0: function makeChan(url) { michael@0: if (!url) michael@0: url = "http://somesite/"; michael@0: var ios = Cc["@mozilla.org/network/io-service;1"] michael@0: .getService(Ci.nsIIOService); michael@0: var chan = ios.newChannel(url, null, null) michael@0: .QueryInterface(Ci.nsIHttpChannel); michael@0: michael@0: return chan; michael@0: } michael@0: michael@0: var current_test = 0; michael@0: var httpserv = null; michael@0: michael@0: function run_test() { michael@0: httpserv = new HttpServer(); michael@0: httpserv.registerPathHandler("/", proxyAuthHandler); michael@0: httpserv.identity.add("http", "somesite", 80); michael@0: httpserv.start(-1); michael@0: michael@0: const prefs = Cc["@mozilla.org/preferences-service;1"] michael@0: .getService(Ci.nsIPrefBranch); michael@0: prefs.setCharPref("network.proxy.http", "localhost"); michael@0: prefs.setIntPref("network.proxy.http_port", httpserv.identity.primaryPort); michael@0: prefs.setCharPref("network.proxy.no_proxies_on", ""); michael@0: prefs.setIntPref("network.proxy.type", 1); michael@0: michael@0: tests[current_test](); michael@0: } michael@0: michael@0: function test_proxy_returnfalse() { michael@0: dump("\ntest: proxy returnfalse\n"); michael@0: var chan = makeChan(); michael@0: chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 0); michael@0: listener.expectedCode = 407; // Proxy Unauthorized michael@0: chan.asyncOpen(listener, null); michael@0: michael@0: do_test_pending(); michael@0: } michael@0: michael@0: function test_proxy_wrongpw() { michael@0: dump("\ntest: proxy wrongpw\n"); michael@0: var chan = makeChan(); michael@0: chan.notificationCallbacks = new Requestor(FLAG_WRONG_PASSWORD, 0); michael@0: listener.expectedCode = 200; // Eventually OK michael@0: chan.asyncOpen(listener, null); michael@0: do_test_pending(); michael@0: } michael@0: michael@0: function test_all_ok() { michael@0: dump("\ntest: all ok\n"); michael@0: var chan = makeChan(); michael@0: chan.notificationCallbacks = new Requestor(0, 0); michael@0: listener.expectedCode = 200; // OK michael@0: chan.asyncOpen(listener, null); michael@0: do_test_pending(); michael@0: } michael@0: michael@0: function test_host_returnfalse() { michael@0: dump("\ntest: host returnfalse\n"); michael@0: var chan = makeChan(); michael@0: chan.notificationCallbacks = new Requestor(0, FLAG_RETURN_FALSE); michael@0: listener.expectedCode = 401; // Host Unauthorized michael@0: chan.asyncOpen(listener, null); michael@0: michael@0: do_test_pending(); michael@0: } michael@0: michael@0: function test_host_wrongpw() { michael@0: dump("\ntest: host wrongpw\n"); michael@0: var chan = makeChan(); michael@0: chan.notificationCallbacks = new Requestor(0, FLAG_WRONG_PASSWORD); michael@0: listener.expectedCode = 200; // Eventually OK michael@0: chan.asyncOpen(listener, null); michael@0: do_test_pending(); michael@0: } michael@0: michael@0: function test_proxy_wrongpw_host_wrongpw() { michael@0: dump("\ntest: proxy wrongpw, host wrongpw\n"); michael@0: var chan = makeChan(); michael@0: chan.notificationCallbacks = michael@0: new Requestor(FLAG_WRONG_PASSWORD, FLAG_WRONG_PASSWORD); michael@0: listener.expectedCode = 200; // OK michael@0: chan.asyncOpen(listener, null); michael@0: do_test_pending(); michael@0: } michael@0: michael@0: function test_proxy_wrongpw_host_returnfalse() { michael@0: dump("\ntest: proxy wrongpw, host return false\n"); michael@0: var chan = makeChan(); michael@0: chan.notificationCallbacks = michael@0: new Requestor(FLAG_WRONG_PASSWORD, FLAG_RETURN_FALSE); michael@0: listener.expectedCode = 401; // Host Unauthorized michael@0: chan.asyncOpen(listener, null); michael@0: do_test_pending(); michael@0: } michael@0: michael@0: var tests = [test_proxy_returnfalse, test_proxy_wrongpw, test_all_ok, michael@0: test_host_returnfalse, test_host_wrongpw, michael@0: test_proxy_wrongpw_host_wrongpw, test_proxy_wrongpw_host_returnfalse]; michael@0: michael@0: michael@0: // PATH HANDLERS michael@0: michael@0: // Proxy michael@0: function proxyAuthHandler(metadata, response) { michael@0: try { michael@0: var realm = "intern"; michael@0: // btoa("proxy:guest"), but that function is not available here michael@0: var expectedHeader = "Basic cHJveHk6Z3Vlc3Q="; michael@0: michael@0: var body; michael@0: if (metadata.hasHeader("Proxy-Authorization") && michael@0: metadata.getHeader("Proxy-Authorization") == expectedHeader) michael@0: { michael@0: dump("proxy password ok\n"); michael@0: response.setHeader("Proxy-Authenticate", michael@0: 'Basic realm="' + realm + '"', false); michael@0: michael@0: hostAuthHandler(metadata, response); michael@0: } michael@0: else michael@0: { michael@0: dump("proxy password required\n"); michael@0: response.setStatusLine(metadata.httpVersion, 407, michael@0: "Unauthorized by HTTP proxy"); michael@0: response.setHeader("Proxy-Authenticate", michael@0: 'Basic realm="' + realm + '"', false); michael@0: body = "failed"; michael@0: response.bodyOutputStream.write(body, body.length); michael@0: } michael@0: } catch (e) { do_throw(e); } michael@0: } michael@0: michael@0: // Host /auth michael@0: function hostAuthHandler(metadata, response) { michael@0: try { michael@0: var realm = "extern"; michael@0: // btoa("host:guest"), but that function is not available here michael@0: var expectedHeader = "Basic aG9zdDpndWVzdA=="; michael@0: michael@0: var body; michael@0: if (metadata.hasHeader("Authorization") && michael@0: metadata.getHeader("Authorization") == expectedHeader) michael@0: { michael@0: dump("host password ok\n"); michael@0: response.setStatusLine(metadata.httpVersion, 200, michael@0: "OK, authorized for host"); michael@0: response.setHeader("WWW-Authenticate", michael@0: 'Basic realm="' + realm + '"', false); michael@0: body = "success"; michael@0: } michael@0: else michael@0: { michael@0: dump("host password required\n"); michael@0: response.setStatusLine(metadata.httpVersion, 401, michael@0: "Unauthorized by HTTP server host"); michael@0: response.setHeader("WWW-Authenticate", michael@0: 'Basic realm="' + realm + '"', false); michael@0: body = "failed"; michael@0: } michael@0: response.bodyOutputStream.write(body, body.length); michael@0: } catch (e) { do_throw(e); } michael@0: }