Bug 1807268 - Re-enable verifyShowClipboardSuggestionsToggleTest UI test r=jajohnson
[gecko.git] / netwerk / test / unit / test_ntlm_authentication.js
blob1517a65df6473edb217ff1ee7bef9d7df57b6f42
1 // This file tests authentication prompt callbacks
2 // TODO NIT use do_check_eq(expected, actual) consistently, not sometimes eq(actual, expected)
4 "use strict";
6 const { HttpServer } = ChromeUtils.importESModule(
7   "resource://testing-common/httpd.sys.mjs"
8 );
10 // Turn off the authentication dialog blocking for this test.
11 var prefs = Services.prefs;
12 prefs.setIntPref("network.auth.subresource-http-auth-allow", 2);
14 ChromeUtils.defineLazyGetter(this, "URL", function () {
15   return "http://localhost:" + httpserv.identity.primaryPort;
16 });
18 ChromeUtils.defineLazyGetter(this, "PORT", function () {
19   return httpserv.identity.primaryPort;
20 });
22 const FLAG_RETURN_FALSE = 1 << 0;
23 const FLAG_WRONG_PASSWORD = 1 << 1;
24 const FLAG_BOGUS_USER = 1 << 2;
25 // const FLAG_PREVIOUS_FAILED = 1 << 3;
26 const CROSS_ORIGIN = 1 << 4;
27 const FLAG_NO_REALM = 1 << 5;
28 const FLAG_NON_ASCII_USER_PASSWORD = 1 << 6;
30 function AuthPrompt1(flags) {
31   this.flags = flags;
34 AuthPrompt1.prototype = {
35   user: "guest",
36   pass: "guest",
38   expectedRealm: "secret",
40   QueryInterface: ChromeUtils.generateQI(["nsIAuthPrompt"]),
42   prompt: function ap1_prompt() {
43     do_throw("unexpected prompt call");
44   },
46   promptUsernameAndPassword: function ap1_promptUP(
47     title,
48     text,
49     realm,
50     savePW,
51     user,
52     pw
53   ) {
54     if (this.flags & FLAG_NO_REALM) {
55       // Note that the realm here isn't actually the realm. it's a pw mgr key.
56       Assert.equal(URL + " (" + this.expectedRealm + ")", realm);
57     }
58     if (!(this.flags & CROSS_ORIGIN)) {
59       if (!text.includes(this.expectedRealm)) {
60         do_throw("Text must indicate the realm");
61       }
62     } else if (text.includes(this.expectedRealm)) {
63       do_throw("There should not be realm for cross origin");
64     }
65     if (!text.includes("localhost")) {
66       do_throw("Text must indicate the hostname");
67     }
68     if (!text.includes(String(PORT))) {
69       do_throw("Text must indicate the port");
70     }
71     if (text.includes("-1")) {
72       do_throw("Text must contain negative numbers");
73     }
75     if (this.flags & FLAG_RETURN_FALSE) {
76       return false;
77     }
79     if (this.flags & FLAG_BOGUS_USER) {
80       this.user = "foo\nbar";
81     } else if (this.flags & FLAG_NON_ASCII_USER_PASSWORD) {
82       this.user = "é";
83     }
85     user.value = this.user;
86     if (this.flags & FLAG_WRONG_PASSWORD) {
87       pw.value = this.pass + ".wrong";
88       // Now clear the flag to avoid an infinite loop
89       this.flags &= ~FLAG_WRONG_PASSWORD;
90     } else if (this.flags & FLAG_NON_ASCII_USER_PASSWORD) {
91       pw.value = "é";
92     } else {
93       pw.value = this.pass;
94     }
95     return true;
96   },
98   promptPassword: function ap1_promptPW() {
99     do_throw("unexpected promptPassword call");
100   },
103 function AuthPrompt2(flags) {
104   this.flags = flags;
107 AuthPrompt2.prototype = {
108   user: "guest",
109   pass: "guest",
111   expectedRealm: "secret",
113   QueryInterface: ChromeUtils.generateQI(["nsIAuthPrompt2"]),
115   promptAuth: function ap2_promptAuth(channel, level, authInfo) {
116     authInfo.username = this.user;
117     authInfo.password = this.pass;
118     return true;
119   },
121   asyncPromptAuth: function ap2_async() {
122     throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
123   },
126 function Requestor(flags, versions) {
127   this.flags = flags;
128   this.versions = versions;
131 Requestor.prototype = {
132   QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]),
134   getInterface: function requestor_gi(iid) {
135     if (this.versions & 1 && iid.equals(Ci.nsIAuthPrompt)) {
136       // Allow the prompt to store state by caching it here
137       if (!this.prompt1) {
138         this.prompt1 = new AuthPrompt1(this.flags);
139       }
140       return this.prompt1;
141     }
142     if (this.versions & 2 && iid.equals(Ci.nsIAuthPrompt2)) {
143       // Allow the prompt to store state by caching it here
144       if (!this.prompt2) {
145         this.prompt2 = new AuthPrompt2(this.flags);
146       }
147       return this.prompt2;
148     }
150     throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
151   },
153   prompt1: null,
154   prompt2: null,
157 function RealmTestRequestor() {}
159 RealmTestRequestor.prototype = {
160   QueryInterface: ChromeUtils.generateQI([
161     "nsIInterfaceRequestor",
162     "nsIAuthPrompt2",
163   ]),
165   getInterface: function realmtest_interface(iid) {
166     if (iid.equals(Ci.nsIAuthPrompt2)) {
167       return this;
168     }
170     throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
171   },
173   promptAuth: function realmtest_checkAuth(channel, level, authInfo) {
174     Assert.equal(authInfo.realm, '"foo_bar');
176     return false;
177   },
179   asyncPromptAuth: function realmtest_async() {
180     throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
181   },
184 function makeChan(url, loadingUrl) {
185   var principal = Services.scriptSecurityManager.createContentPrincipal(
186     Services.io.newURI(loadingUrl),
187     {}
188   );
189   return NetUtil.newChannel({
190     uri: url,
191     loadingPrincipal: principal,
192     securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
193     contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
194   });
197 // /auth/ntlm/simple
198 function authNtlmSimple(metadata, response) {
199   if (!metadata.hasHeader("Authorization")) {
200     response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
201     response.setHeader("WWW-Authenticate", "NTLM", false);
202     return;
203   }
205   let challenge = metadata.getHeader("Authorization");
206   if (!challenge.startsWith("NTLM ")) {
207     response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
208     return;
209   }
211   let decoded = atob(challenge.substring(5));
212   info(decoded);
214   if (!decoded.startsWith("NTLMSSP\0")) {
215     response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
216     return;
217   }
219   let isNegotiate = decoded.substring(8).startsWith("\x01\x00\x00\x00");
220   let isAuthenticate = decoded.substring(8).startsWith("\x03\x00\x00\x00");
222   if (isNegotiate) {
223     response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
224     response.setHeader(
225       "WWW-Authenticate",
226       "NTLM TlRMTVNTUAACAAAAAAAAAAAoAAABggAAASNFZ4mrze8AAAAAAAAAAAAAAAAAAAAA",
227       false
228     );
229     return;
230   }
232   if (isAuthenticate) {
233     let body = "OK";
234     response.bodyOutputStream.write(body, body.length);
235     return;
236   }
238   // Something else went wrong.
239   response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
242 let httpserv;
243 add_task(async function test_ntlm() {
244   Services.prefs.setBoolPref("network.auth.force-generic-ntlm", true);
245   Services.prefs.setBoolPref("network.auth.force-generic-ntlm-v1", true);
247   httpserv = new HttpServer();
248   httpserv.registerPathHandler("/auth/ntlm/simple", authNtlmSimple);
249   httpserv.start(-1);
251   registerCleanupFunction(async () => {
252     Services.prefs.clearUserPref("network.auth.force-generic-ntlm");
253     Services.prefs.clearUserPref("network.auth.force-generic-ntlm-v1");
255     await httpserv.stop();
256   });
258   var chan = makeChan(URL + "/auth/ntlm/simple", URL);
260   chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 2);
261   let [req, buf] = await new Promise(resolve => {
262     chan.asyncOpen(
263       new ChannelListener((req1, buf1) => resolve([req1, buf1]), null)
264     );
265   });
266   Assert.ok(buf);
267   Assert.equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200);