Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / netwerk / test / unit / test_ntlm_proxy_and_web_auth.js
blobeb3396770eea0cd85c7352d056f7b41ec1119582
1 // Unit tests for a NTLM authenticated proxy, proxying for a NTLM authenticated
2 // web server.
3 //
4 // Currently the tests do not determine whether the Authentication dialogs have
5 // been displayed.
6 //
8 "use strict";
10 const { HttpServer } = ChromeUtils.importESModule(
11 "resource://testing-common/httpd.sys.mjs"
14 ChromeUtils.defineLazyGetter(this, "URL", function () {
15 return "http://localhost:" + httpserver.identity.primaryPort;
16 });
18 function AuthPrompt() {}
20 AuthPrompt.prototype = {
21 user: "guest",
22 pass: "guest",
24 QueryInterface: ChromeUtils.generateQI(["nsIAuthPrompt2"]),
26 promptAuth: function ap_promptAuth(channel, level, authInfo) {
27 authInfo.username = this.user;
28 authInfo.password = this.pass;
30 return true;
33 asyncPromptAuth: function ap_async() {
34 throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
38 function Requestor() {}
40 Requestor.prototype = {
41 QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]),
43 getInterface: function requestor_gi(iid) {
44 if (iid.equals(Ci.nsIAuthPrompt2)) {
45 // Allow the prompt to store state by caching it here
46 if (!this.prompt) {
47 this.prompt = new AuthPrompt();
49 return this.prompt;
52 throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
55 prompt: null,
58 function makeChan(url, loadingUrl) {
59 var principal = Services.scriptSecurityManager.createContentPrincipal(
60 Services.io.newURI(loadingUrl),
63 return NetUtil.newChannel({
64 uri: url,
65 loadingPrincipal: principal,
66 securityFlags: Ci.nsILoadInfo.SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT,
67 contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
68 });
71 function TestListener(resolve) {
72 this.resolve = resolve;
74 TestListener.prototype.onStartRequest = function (request) {
75 // Need to do the instanceof to allow request.responseStatus
76 // to be read.
77 if (!(request instanceof Ci.nsIHttpChannel)) {
78 dump("Expecting an HTTP channel");
81 Assert.equal(expectedResponse, request.responseStatus, "HTTP Status code");
83 TestListener.prototype.onStopRequest = function () {
84 Assert.equal(expectedRequests, requestsMade, "Number of requests made ");
86 this.resolve();
88 TestListener.prototype.onDataAvaiable = function (
89 request,
90 context,
91 stream,
92 offset,
93 count
94 ) {
95 read_stream(stream, count);
98 // NTLM Messages, for the received type 1 and 3 messages only check that they
99 // are of the expected type.
100 const NTLM_TYPE1_PREFIX = "NTLM TlRMTVNTUAABAAAA";
101 const NTLM_TYPE2_PREFIX = "NTLM TlRMTVNTUAACAAAA";
102 const NTLM_TYPE3_PREFIX = "NTLM TlRMTVNTUAADAAAA";
103 const NTLM_PREFIX_LEN = 21;
105 const NTLM_CHALLENGE =
106 NTLM_TYPE2_PREFIX +
107 "DAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAAR" +
108 "ABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUg" +
109 "BWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwB" +
110 "lAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA=";
112 const PROXY_CHALLENGE =
113 NTLM_TYPE2_PREFIX +
114 "DgAOADgAAAAFgooCqLNOPe2aZOAAAAAAAAAAAFAAUABGAAAA" +
115 "BgEAAAAAAA9HAFcATAAtAE0ATwBaAAIADgBHAFcATAAtAE0A" +
116 "TwBaAAEADgBHAFcATAAtAE0ATwBaAAQAAgAAAAMAEgBsAG8A" +
117 "YwBhAGwAaABvAHMAdAAHAAgAOKEwGEZL0gEAAAAA";
119 // Proxy and Web server responses for the happy path scenario.
120 // i.e. successful proxy auth and successful web server auth
122 function authHandler(metadata, response) {
123 let authorization;
124 let authPrefix;
125 switch (requestsMade) {
126 case 0:
127 // Proxy - First request to the Proxy resppond with a 407 to start auth
128 response.setStatusLine(metadata.httpVersion, 407, "Unauthorized");
129 response.setHeader("Proxy-Authenticate", "NTLM", false);
130 break;
131 case 1:
132 // Proxy - Expecting a type 1 negotiate message from the client
133 authorization = metadata.getHeader("Proxy-Authorization");
134 authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
135 Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message");
136 response.setStatusLine(metadata.httpVersion, 407, "Unauthorized");
137 response.setHeader("Proxy-Authenticate", PROXY_CHALLENGE, false);
138 break;
139 case 2:
140 // Proxy - Expecting a type 3 Authenticate message from the client
141 // Will respond with a 401 to start web server auth sequence
142 authorization = metadata.getHeader("Proxy-Authorization");
143 authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
144 Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message");
145 response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
146 response.setHeader("WWW-Authenticate", "NTLM", false);
147 break;
148 case 3:
149 // Web Server - Expecting a type 1 negotiate message from the client
150 authorization = metadata.getHeader("Authorization");
151 authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
152 Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message");
153 response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
154 response.setHeader("WWW-Authenticate", NTLM_CHALLENGE, false);
155 break;
156 case 4:
157 // Web Server - Expecting a type 3 Authenticate message from the client
158 authorization = metadata.getHeader("Authorization");
159 authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
160 Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message");
161 response.setStatusLine(metadata.httpVersion, 200, "Successful");
162 break;
163 default:
164 // We should be authenticated and further requests are permitted
165 authorization = metadata.getHeader("Authorization");
166 authorization = metadata.getHeader("Proxy-Authorization");
167 Assert.isnull(authorization);
168 response.setStatusLine(metadata.httpVersion, 200, "Successful");
170 requestsMade++;
173 // Proxy responses simulating an invalid proxy password
174 // Note: that the connection should not be reused after the
175 // proxy auth fails.
177 function authHandlerInvalidProxyPassword(metadata, response) {
178 let authorization;
179 let authPrefix;
180 switch (requestsMade) {
181 case 0:
182 // Proxy - First request respond with a 407 to initiate auth sequence
183 response.setStatusLine(metadata.httpVersion, 407, "Unauthorized");
184 response.setHeader("Proxy-Authenticate", "NTLM", false);
185 break;
186 case 1:
187 // Proxy - Expecting a type 1 negotiate message from the client
188 authorization = metadata.getHeader("Proxy-Authorization");
189 authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
190 Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message");
191 response.setStatusLine(metadata.httpVersion, 407, "Unauthorized");
192 response.setHeader("Proxy-Authenticate", PROXY_CHALLENGE, false);
193 break;
194 case 2:
195 // Proxy - Expecting a type 3 Authenticate message from the client
196 // Respond with a 407 to indicate invalid credentials
198 authorization = metadata.getHeader("Proxy-Authorization");
199 authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
200 Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message");
201 response.setStatusLine(metadata.httpVersion, 407, "Unauthorized");
202 response.setHeader("Proxy-Authenticate", "NTLM", false);
203 break;
204 default:
205 // Strictly speaking the connection should not be reused at this point
206 // and reaching here should be an error, but have commented out for now
207 //dump( "ERROR: NTLM Proxy Authentication, connection should not be reused");
208 //Assert.fail();
209 response.setStatusLine(metadata.httpVersion, 407, "Unauthorized");
211 requestsMade++;
214 // Proxy and web server responses simulating a successful Proxy auth
215 // and a failed web server auth
216 // Note: the connection should not be reused once the password failure is
217 // detected
218 function authHandlerInvalidWebPassword(metadata, response) {
219 let authorization;
220 let authPrefix;
221 switch (requestsMade) {
222 case 0:
223 // Proxy - First request return a 407 to start Proxy auth
224 response.setStatusLine(metadata.httpVersion, 407, "Unauthorized");
225 response.setHeader("Proxy-Authenticate", "NTLM", false);
226 break;
227 case 1:
228 // Proxy - Expecting a type 1 negotiate message from the client
229 authorization = metadata.getHeader("Proxy-Authorization");
230 authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
231 Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message");
232 response.setStatusLine(metadata.httpVersion, 407, "Unauthorized");
233 response.setHeader("Proxy-Authenticate", NTLM_CHALLENGE, false);
234 break;
235 case 2:
236 // Proxy - Expecting a type 3 Authenticate message from the client
237 // Responds with a 401 to start web server auth
238 authorization = metadata.getHeader("Proxy-Authorization");
239 authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
240 Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message");
241 response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
242 response.setHeader("WWW-Authenticate", "NTLM", false);
243 break;
244 case 3:
245 // Web Server - Expecting a type 1 negotiate message from the client
246 authorization = metadata.getHeader("Authorization");
247 authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
248 Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message");
249 response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
250 response.setHeader("WWW-Authenticate", NTLM_CHALLENGE, false);
251 break;
252 case 4:
253 // Web Server - Expecting a type 3 Authenticate message from the client
254 // Respond with a 401 to restart the auth sequence.
255 authorization = metadata.getHeader("Authorization");
256 authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
257 Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 1 message");
258 response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
259 break;
260 default:
261 // We should not get called past step 4
262 Assert.ok(
263 false,
264 "ERROR: NTLM Auth failed connection should not be reused"
267 requestsMade++;
270 // Tests to run test_bad_proxy_pass and test_bad_web_pass are split into two stages
271 // so that once we determine how detect password dialog displays we can check
272 // that the are displayed correctly, i.e. proxy password should not be prompted
273 // for when retrying the web server password
275 var httpserver = null;
276 function setup() {
277 httpserver = new HttpServer();
278 httpserver.start(-1);
280 Services.prefs.setCharPref("network.proxy.http", "localhost");
281 Services.prefs.setIntPref(
282 "network.proxy.http_port",
283 httpserver.identity.primaryPort
285 Services.prefs.setCharPref("network.proxy.no_proxies_on", "");
286 Services.prefs.setIntPref("network.proxy.type", 1);
287 Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
289 registerCleanupFunction(async () => {
290 Services.prefs.clearUserPref("network.proxy.http");
291 Services.prefs.clearUserPref("network.proxy.http_port");
292 Services.prefs.clearUserPref("network.proxy.no_proxies_on");
293 Services.prefs.clearUserPref("network.proxy.type");
294 Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost");
296 await httpserver.stop();
299 setup();
301 var expectedRequests = 0; // Number of HTTP requests that are expected
302 var requestsMade = 0; // The number of requests that were made
303 var expectedResponse = 0; // The response code
304 // Note that any test failures in the HTTP handler
305 // will manifest as a 500 response code
307 // Common test setup
308 // Parameters:
309 // path - path component of the URL
310 // handler - http handler function for the httpserver
311 // requests - expected number oh http requests
312 // response - expected http response code
313 // clearCache - clear the authentication cache before running the test
314 function setupTest(path, handler, requests, response, clearCache) {
315 requestsMade = 0;
316 expectedRequests = requests;
317 expectedResponse = response;
319 // clear the auth cache if requested
320 if (clearCache) {
321 dump("Clearing auth cache");
322 Cc["@mozilla.org/network/http-auth-manager;1"]
323 .getService(Ci.nsIHttpAuthManager)
324 .clearAll();
327 return new Promise(resolve => {
328 var chan = makeChan(URL + path, URL);
329 httpserver.registerPathHandler(path, handler);
330 chan.notificationCallbacks = new Requestor();
331 chan.asyncOpen(new TestListener(resolve));
335 // Happy code path
336 // Succesful proxy and web server auth.
337 add_task(async function test_happy_path() {
338 dump("RUNNING TEST: test_happy_path");
339 await setupTest("/auth", authHandler, 5, 200, 1);
342 // Failed proxy authentication
343 add_task(async function test_bad_proxy_pass_stage01() {
344 dump("RUNNING TEST: test_bad_proxy_pass_stage01");
345 await setupTest("/auth", authHandlerInvalidProxyPassword, 4, 407, 1);
347 // Successful logon after failed proxy auth
348 add_task(async function test_bad_proxy_pass_stage02() {
349 dump("RUNNING TEST: test_bad_proxy_pass_stage02");
350 await setupTest("/auth", authHandler, 5, 200, 0);
353 // successful proxy logon, unsuccessful web server sign on
354 add_task(async function test_bad_web_pass_stage01() {
355 dump("RUNNING TEST: test_bad_web_pass_stage01");
356 await setupTest("/auth", authHandlerInvalidWebPassword, 5, 401, 1);
358 // successful logon after failed web server auth.
359 add_task(async function test_bad_web_pass_stage02() {
360 dump("RUNNING TEST: test_bad_web_pass_stage02");
361 await setupTest("/auth", authHandler, 5, 200, 0);