diff options
Diffstat (limited to 'toolkit/components/passwordmgr/test/mochitest/test_prompt_async.html')
-rw-r--r-- | toolkit/components/passwordmgr/test/mochitest/test_prompt_async.html | 621 |
1 files changed, 621 insertions, 0 deletions
diff --git a/toolkit/components/passwordmgr/test/mochitest/test_prompt_async.html b/toolkit/components/passwordmgr/test/mochitest/test_prompt_async.html new file mode 100644 index 0000000000..41a58cb416 --- /dev/null +++ b/toolkit/components/passwordmgr/test/mochitest/test_prompt_async.html @@ -0,0 +1,621 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test for Async Auth Prompt</title> + <script type="text/javascript" src="/MochiKit/MochiKit.js"></script> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="pwmgr_common.js"></script> + <script type="text/javascript" src="../../../prompts/test/prompt_common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + + <script class="testbody" type="text/javascript"> + const { NetUtil } = SpecialPowers.ChromeUtils.importESModule( + "resource://gre/modules/NetUtil.sys.mjs" + ); + const { TestUtils } = SpecialPowers.ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" + ); + const EXAMPLE_COM = "http://example.com/tests/toolkit/components/passwordmgr/test/mochitest/"; + const EXAMPLE_ORG = "http://example.org/tests/toolkit/components/passwordmgr/test/mochitest/"; + let mozproxyOrigin; + + // Let prompt_common know what kind of modal type is enabled for auth prompts. + modalType = authPromptModalType; + + // These are magically defined on the window due to the iframe IDs + /* global iframe1, iframe2a, iframe2b */ + + /** + * Add a listener to add some logins to be autofilled in the HTTP/proxy auth. prompts later. + */ + let pwmgrParent = runInParent(() => { + Services.prefs.setIntPref("network.auth.subresource-http-auth-allow", 2); + Services.prefs.setIntPref("prompts.authentication_dialog_abuse_limit", -1); + + addMessageListener("initLogins", async function onMessage(msg) { + const loginsData = [ + [msg.mozproxyOrigin, "proxy_realm", "proxy_user", "proxy_pass"], + [msg.mozproxyOrigin, "proxy_realm2", "proxy_user2", "proxy_pass2"], + [msg.mozproxyOrigin, "proxy_realm3", "proxy_user3", "proxy_pass3"], + [msg.mozproxyOrigin, "proxy_realm4", "proxy_user4", "proxy_pass4"], + [msg.mozproxyOrigin, "proxy_realm5", "proxy_user5", "proxy_pass5"], + ["http://example.com", "mochirealm", "user1name", "user1pass"], + ["http://example.org", "mochirealm2", "user2name", "user2pass"], + ["http://example.com", "mochirealm3", "user3name", "user3pass"], + ["http://example.com", "mochirealm4", "user4name", "user4pass"], + ["http://example.com", "mochirealm5", "user5name", "user5pass"], + ["http://example.com", "mochirealm6", "user6name", "user6pass"] + ]; + const logins = loginsData.map(([host, realm, user, pass]) => { + const login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo); + login.init(host, null, realm, user, pass, "", ""); + return login + }) + await Services.logins.addLogins(logins); + }); + }); // end runInParent + + function promiseLoadedContentDoc(frame) { + return new Promise(resolve => { + frame.addEventListener("load", function onLoad(evt) { + resolve(SpecialPowers.wrap(frame).contentDocument); + }, { once: true }); + }); + } + + function promiseProxyErrorLoad(frame) { + return TestUtils.waitForCondition(async function checkForProxyConnectFailure() { + try { + return await SpecialPowers.spawn(frame, [], function() { + return this.content.document.documentURI.includes("proxyConnectFailure"); + }) + } catch (e) { + // The frame may not be ready for the 'spawn' task right after setting + // iframe.src, which will throw an exception when that happens. + // Since this test is testing error load, we can't wait until the iframe + // is 'loaded' either. So we simply catch the exception here and retry the task + // later since we are in the waitForCondition loop. + return false; + } + }, "Waiting for proxyConnectFailure documentURI"); + } + + /** + * Make a channel to get the ProxyInfo used by the test harness so that we + * can add logins for the correct proxy origin. + */ + add_task(async function setup_getProxyInfoForHarness() { + await new Promise(resolve => { + let resolveCallback = SpecialPowers.wrapCallbackObject({ + // eslint-disable-next-line mozilla/use-chromeutils-generateqi + QueryInterface(iid) { + const interfaces = [Ci.nsIProtocolProxyCallback, Ci.nsISupports]; + + if (!interfaces.some(v => iid.equals(v))) { + throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE; + } + return this; + }, + + onProxyAvailable(req, uri, pi, status) { + // Add logins using the proxy host and port used by the mochitest harness. + mozproxyOrigin = "moz-proxy://" + SpecialPowers.wrap(pi).host + ":" + + SpecialPowers.wrap(pi).port; + + pwmgrParent.sendQuery("initLogins", {mozproxyOrigin}).then(resolve) + }, + }); + + // Need to allow for arbitrary network servers defined in PAC instead of a hardcoded moz-proxy. + let channel = NetUtil.newChannel({ + uri: "http://example.com", + loadUsingSystemPrincipal: true, + }); + + let pps = SpecialPowers.Cc["@mozilla.org/network/protocol-proxy-service;1"] + .getService(); + + pps.asyncResolve(channel, 0, resolveCallback); + }); + }); + + add_task(async function test_proxyAuthThenTwoHTTPAuth() { + // Load through a single proxy with authentication required 3 different + // pages, first with one login, other two with their own different login. + // We expect to show just a single dialog for proxy authentication and + // then two dialogs to authenticate to login 1 and then login 2. + + let iframe1DocPromise = promiseLoadedContentDoc(iframe1); + let iframe2aDocPromise = promiseLoadedContentDoc(iframe2a); + let iframe2bDocPromise = promiseLoadedContentDoc(iframe2b); + + iframe1.src = EXAMPLE_COM + "authenticate.sjs?" + + "r=1&" + + "user=user1name&" + + "pass=user1pass&" + + "realm=mochirealm&" + + "proxy_user=proxy_user&" + + "proxy_pass=proxy_pass&" + + "proxy_realm=proxy_realm"; + iframe2a.src = EXAMPLE_ORG + "authenticate.sjs?" + + "r=2&" + + "user=user2name&" + + "pass=user2pass&" + + "realm=mochirealm2&" + + "proxy_user=proxy_user&" + + "proxy_pass=proxy_pass&" + + "proxy_realm=proxy_realm"; + iframe2b.src = EXAMPLE_ORG + "authenticate.sjs?" + + "r=3&" + + "user=user2name&" + + "pass=user2pass&" + + "realm=mochirealm2&" + + "proxy_user=proxy_user&" + + "proxy_pass=proxy_pass&" + + "proxy_realm=proxy_realm"; + + let state = { + msg: `The proxy ${mozproxyOrigin} is requesting a username and password. The site says: “proxy_realm”`, + title: "Authentication Required", + textValue: "proxy_user", + passValue: "proxy_pass", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: false, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + focused: "textField", + defButton: "button0", + }; + let action = { + buttonClick: "ok", + }; + await handlePrompt(state, action); + + // We don't know what order these prompts appear in so get both states and check them. + // We can't use Promise.all here since we can't start the 2nd timer in chromeScript.js until + // the first timer is done since the timer variable gets clobbered, plus we don't want + // different actions racing each other. + let promptStates = [ + await handlePromptWithoutChecks(action), + await handlePromptWithoutChecks(action), + ]; + + let expected1 = Object.assign({}, state, { + msg: "This site is asking you to sign in. Warning: Your login information will be shared with example.com, not the website you are currently visiting.", + textValue: "user1name", + passValue: "user1pass", + }); + + let expected2 = Object.assign({}, state, { + msg: "This site is asking you to sign in. Warning: Your login information will be shared with example.org, not the website you are currently visiting.", + textValue: "user2name", + passValue: "user2pass", + }); + + // The order isn't important. + let expectedPromptStates = [ + expected1, + expected2, + ]; + + is(promptStates.length, expectedPromptStates.length, + "Check we handled the right number of prompts"); + for (let promptState of promptStates) { + let expectedStateIndexForMessage = expectedPromptStates.findIndex(eps => { + return eps.msg == promptState.msg; + }); + isnot(expectedStateIndexForMessage, -1, "Check state message was found in expected array"); + let expectedPromptState = expectedPromptStates.splice(expectedStateIndexForMessage, 1)[0]; + checkPromptState(promptState, expectedPromptState); + } + + await iframe1DocPromise; + await iframe2aDocPromise; + await iframe2bDocPromise; + + await SpecialPowers.spawn(getIframeBrowsingContext(window, 0), [], function() { + let doc = this.content.document; + let authok1 = doc.getElementById("ok").textContent; + let proxyok1 = doc.getElementById("proxy").textContent; + Assert.equal(authok1, "PASS", "WWW Authorization OK, frame1"); + Assert.equal(proxyok1, "PASS", "Proxy Authorization OK, frame1"); + }); + + await SpecialPowers.spawn(getIframeBrowsingContext(window, 1), [], function() { + let doc = this.content.document; + let authok2a = doc.getElementById("ok").textContent; + let proxyok2a = doc.getElementById("proxy").textContent; + Assert.equal(authok2a, "PASS", "WWW Authorization OK, frame2a"); + Assert.equal(proxyok2a, "PASS", "Proxy Authorization OK, frame2a"); + }); + + await SpecialPowers.spawn(getIframeBrowsingContext(window, 2), [], function() { + let doc = this.content.document; + let authok2b = doc.getElementById("ok").textContent; + let proxyok2b = doc.getElementById("proxy").textContent; + Assert.equal(authok2b, "PASS", "WWW Authorization OK, frame2b"); + Assert.equal(proxyok2b, "PASS", "Proxy Authorization OK, frame2b"); + }); + }); + + add_task(async function test_threeSubframesWithSameProxyAndHTTPAuth() { + // Load an iframe with 3 subpages all requiring the same login through + // an authenticated proxy. We expect 2 dialogs, proxy authentication + // and web authentication. + + let iframe1DocPromise = promiseLoadedContentDoc(iframe1); + + iframe1.src = EXAMPLE_COM + "subtst_prompt_async.html"; + iframe2a.src = "about:blank"; + iframe2b.src = "about:blank"; + + let state = { + msg: `The proxy ${mozproxyOrigin} is requesting a username and password. The site says: “proxy_realm2”`, + title: "Authentication Required", + textValue: "proxy_user2", + passValue: "proxy_pass2", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: false, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + focused: "textField", + defButton: "button0", + }; + let action = { + buttonClick: "ok", + }; + await handlePrompt(state, action); + + Object.assign(state, { + msg: "This site is asking you to sign in. Warning: Your login information will be shared with example.com, not the website you are currently visiting.", + textValue: "user3name", + passValue: "user3pass", + }); + await handlePrompt(state, action); + + await iframe1DocPromise; + + function checkIframe(frameid) { + let doc = this.content.document; + let authok = doc.getElementById("ok").textContent; + let proxyok = doc.getElementById("proxy").textContent; + + Assert.equal(authok, "PASS", "WWW Authorization OK, " + frameid); + Assert.equal(proxyok, "PASS", "Proxy Authorization OK, " + frameid); + } + + let parentIFrameBC = SpecialPowers.wrap(window).windowGlobalChild + .browsingContext.children[0]; + + let childIFrame = SpecialPowers.unwrap(parentIFrameBC.children[0]); + await SpecialPowers.spawn(childIFrame, ["iframe1"], checkIframe); + childIFrame = SpecialPowers.unwrap(parentIFrameBC.children[1]); + await SpecialPowers.spawn(childIFrame, ["iframe2"], checkIframe); + childIFrame = SpecialPowers.unwrap(parentIFrameBC.children[2]); + await SpecialPowers.spawn(childIFrame, ["iframe3"], checkIframe); + }); + + add_task(async function test_oneFrameWithUnauthenticatedProxy() { + // Load in the iframe page through unauthenticated proxy + // and discard the proxy authentication. We expect to see + // unauthenticated page content and just a single dialog. + + iframe1.src = EXAMPLE_COM + "authenticate.sjs?" + + "user=user4name&" + + "pass=user4pass&" + + "realm=mochirealm4&" + + "proxy_user=proxy_user3&" + + "proxy_pass=proxy_pass3&" + + "proxy_realm=proxy_realm3"; + + let state = { + msg: `The proxy ${mozproxyOrigin} is requesting a username and password. The site says: “proxy_realm3”`, + title: "Authentication Required", + textValue: "proxy_user3", + passValue: "proxy_pass3", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: false, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + focused: "textField", + defButton: "button0", + }; + let action = { + buttonClick: "cancel", + }; + await handlePrompt(state, action); + + await promiseProxyErrorLoad(iframe1); + }); + + add_task(async function test_reloadReusingProxyAuthButCancellingHTTPAuth() { + // Reload the frame from previous step and pass the proxy authentication + // but cancel the WWW authentication. We should get the proxy=ok and WWW=fail + // content as a result. + let iframe1DocPromise = promiseLoadedContentDoc(iframe1); + + iframe1.src = EXAMPLE_COM + "authenticate.sjs?" + + "user=user4name&" + + "pass=user4pass&" + + "realm=mochirealm4&" + + "proxy_user=proxy_user3&" + + "proxy_pass=proxy_pass3&" + + "proxy_realm=proxy_realm3"; + + let state = { + msg: `The proxy ${mozproxyOrigin} is requesting a username and password. The site says: “proxy_realm3”`, + title: "Authentication Required", + textValue: "proxy_user3", + passValue: "proxy_pass3", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: false, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + focused: "textField", + defButton: "button0", + }; + let action = { + buttonClick: "ok", + }; + await handlePrompt(state, action); + + Object.assign(state, { + msg: "This site is asking you to sign in. Warning: Your login information will be shared with example.com, not the website you are currently visiting.", + textValue: "user4name", + passValue: "user4pass", + }); + action = { + buttonClick: "cancel", + }; + await handlePrompt(state, action); + + await iframe1DocPromise; + + await SpecialPowers.spawn(getIframeBrowsingContext(window, 0), [], function() { + let doc = this.content.document; + let authok1 = doc.getElementById("ok").textContent; + let proxyok1 = doc.getElementById("proxy").textContent; + + Assert.equal(authok1, "FAIL", "WWW Authorization FAILED, frame1"); + Assert.equal(proxyok1, "PASS", "Proxy Authorization OK, frame1"); + }); + }); + + add_task(async function test_hugePayloadCancelled() { + // Same as the previous two steps but let the server generate + // huge content load to check http channel is capable to handle + // case when auth dialog is canceled or accepted before unauthenticated + // content data is load from the server. (This would be better to + // implement using delay of server response). + iframe1.src = EXAMPLE_COM + "authenticate.sjs?" + + "user=user5name&" + + "pass=user5pass&" + + "realm=mochirealm5&" + + "proxy_user=proxy_user4&" + + "proxy_pass=proxy_pass4&" + + "proxy_realm=proxy_realm4&" + + "huge=1"; + + let state = { + msg: `The proxy ${mozproxyOrigin} is requesting a username and password. The site says: “proxy_realm4”`, + title: "Authentication Required", + textValue: "proxy_user4", + passValue: "proxy_pass4", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: false, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + focused: "textField", + defButton: "button0", + }; + let action = { + buttonClick: "cancel", + }; + await handlePrompt(state, action); + + await promiseProxyErrorLoad(iframe1); + }); + + add_task(async function test_hugeProxySuccessWWWFail() { + // Reload the frame from the previous step and let the proxy + // authentication pass but WWW fail. We expect two dialogs + // and an unauthenticated page content load. + + let iframe1DocPromise = promiseLoadedContentDoc(iframe1); + iframe1.src = EXAMPLE_COM + "authenticate.sjs?" + + "user=user5name&" + + "pass=user5pass&" + + "realm=mochirealm5&" + + "proxy_user=proxy_user4&" + + "proxy_pass=proxy_pass4&" + + "proxy_realm=proxy_realm4&" + + "huge=1"; + + let state = { + msg: `The proxy ${mozproxyOrigin} is requesting a username and password. The site says: “proxy_realm4”`, + title: "Authentication Required", + textValue: "proxy_user4", + passValue: "proxy_pass4", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: false, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + focused: "textField", + defButton: "button0", + }; + let action = { + buttonClick: "ok", + }; + await handlePrompt(state, action); + + Object.assign(state, { + msg: "This site is asking you to sign in. Warning: Your login information will be shared with example.com, not the website you are currently visiting.", + textValue: "user5name", + passValue: "user5pass", + }); + action = { + buttonClick: "cancel", + }; + await handlePrompt(state, action); + + await iframe1DocPromise; + + await SpecialPowers.spawn(getIframeBrowsingContext(window, 0), [], function() { + let doc = this.content.document; + let authok1 = doc.getElementById("ok").textContent; + let proxyok1 = doc.getElementById("proxy").textContent; + let footnote = doc.getElementById("footnote").textContent; + + Assert.equal(authok1, "FAIL", "WWW Authorization FAILED, frame1"); + Assert.equal(proxyok1, "PASS", "Proxy Authorization OK, frame1"); + Assert.equal(footnote, "This is a footnote after the huge content fill", + "Footnote present and loaded completely"); + }); + }); + + add_task(async function test_hugeProxySuccessWWWSuccess() { + // Reload again and let pass all authentication dialogs. + // Check we get the authenticated content not broken by + // the unauthenticated content. + + let iframe1DocPromise = promiseLoadedContentDoc(iframe1); + await SpecialPowers.spawn(iframe1, [], function() { + this.content.document.location.reload(); + }); + + let state = { + msg: "This site is asking you to sign in. Warning: Your login information will be shared with example.com, not the website you are currently visiting.", + title: "Authentication Required", + textValue: "user5name", + passValue: "user5pass", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: false, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + focused: "textField", + defButton: "button0", + }; + let action = { + buttonClick: "ok", + }; + await handlePrompt(state, action); + + await iframe1DocPromise; + + await SpecialPowers.spawn(getIframeBrowsingContext(window, 0), [], function() { + let doc = this.content.document; + let authok1 = doc.getElementById("ok").textContent; + let proxyok1 = doc.getElementById("proxy").textContent; + let footnote = doc.getElementById("footnote").textContent; + + Assert.equal(authok1, "PASS", "WWW Authorization OK, frame1"); + Assert.equal(proxyok1, "PASS", "Proxy Authorization OK, frame1"); + Assert.equal(footnote, "This is a footnote after the huge content fill", + "Footnote present and loaded completely"); + }); + }); + + add_task(async function test_cancelSome() { + // Check we process all challenges sent by server when + // user cancels prompts + let iframe1DocPromise = promiseLoadedContentDoc(iframe1); + iframe1.src = EXAMPLE_COM + "authenticate.sjs?" + + "user=user6name&" + + "pass=user6pass&" + + "realm=mochirealm6&" + + "proxy_user=proxy_user5&" + + "proxy_pass=proxy_pass5&" + + "proxy_realm=proxy_realm5&" + + "huge=1&" + + "multiple=3"; + + let state = { + msg: `The proxy ${mozproxyOrigin} is requesting a username and password. The site says: “proxy_realm5”`, + title: "Authentication Required", + textValue: "proxy_user5", + passValue: "proxy_pass5", + iconClass: "authentication-icon question-icon", + titleHidden: true, + textHidden: false, + passHidden: false, + checkHidden: true, + checkMsg: "", + checked: false, + focused: "textField", + defButton: "button0", + }; + let action = { + buttonClick: "cancel", + }; + await handlePrompt(state, action); + + action = { + buttonClick: "cancel", + }; + await handlePrompt(state, action); + + action = { + buttonClick: "ok", + }; + await handlePrompt(state, action); + + Object.assign(state, { + msg: "This site is asking you to sign in. Warning: Your login information will be shared with example.com, not the website you are currently visiting.", + textValue: "user6name", + passValue: "user6pass", + }); + + action = { + buttonClick: "cancel", + }; + await handlePrompt(state, action); + + action = { + buttonClick: "ok", + }; + await handlePrompt(state, action); + + await iframe1DocPromise; + await SpecialPowers.spawn(getIframeBrowsingContext(window, 0), [], function() { + let doc = this.content.document; + let authok1 = doc.getElementById("ok").textContent; + let proxyok1 = doc.getElementById("proxy").textContent; + let footnote = doc.getElementById("footnote").textContent; + + Assert.equal(authok1, "PASS", "WWW Authorization OK, frame1"); + Assert.equal(proxyok1, "PASS", "Proxy Authorization OK, frame1"); + Assert.equal(footnote, "This is a footnote after the huge content fill", + "Footnote present and loaded completely"); + }); + + }); + </script> +</head> +<body> + <iframe id="iframe1"></iframe> + <iframe id="iframe2a"></iframe> + <iframe id="iframe2b"></iframe> +</body> +</html> |