|
1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 /** |
|
7 * This tests the automatic login to the proxy with password, |
|
8 * if the password is stored and the browser is restarted. |
|
9 * |
|
10 * <copied from="test_authentication.js"/> |
|
11 */ |
|
12 |
|
13 Cu.import("resource://testing-common/httpd.js"); |
|
14 |
|
15 const FLAG_RETURN_FALSE = 1 << 0; |
|
16 const FLAG_WRONG_PASSWORD = 1 << 1; |
|
17 const FLAG_PREVIOUS_FAILED = 1 << 2; |
|
18 |
|
19 function AuthPrompt2(proxyFlags, hostFlags) { |
|
20 this.proxyCred.flags = proxyFlags; |
|
21 this.hostCred.flags = hostFlags; |
|
22 } |
|
23 AuthPrompt2.prototype = { |
|
24 proxyCred : { user: "proxy", pass: "guest", |
|
25 realmExpected: "intern", flags : 0 }, |
|
26 hostCred : { user: "host", pass: "guest", |
|
27 realmExpected: "extern", flags : 0 }, |
|
28 |
|
29 QueryInterface: function authprompt2_qi(iid) { |
|
30 if (iid.equals(Ci.nsISupports) || |
|
31 iid.equals(Ci.nsIAuthPrompt2)) |
|
32 return this; |
|
33 throw Cr.NS_ERROR_NO_INTERFACE; |
|
34 }, |
|
35 |
|
36 promptAuth: |
|
37 function ap2_promptAuth(channel, encryptionLevel, authInfo) |
|
38 { |
|
39 try { |
|
40 |
|
41 // never HOST and PROXY set at the same time in prompt |
|
42 do_check_eq((authInfo.flags & Ci.nsIAuthInformation.AUTH_HOST) != 0, |
|
43 (authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) == 0); |
|
44 |
|
45 var isProxy = (authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) != 0; |
|
46 var cred = isProxy ? this.proxyCred : this.hostCred; |
|
47 |
|
48 dump("with flags: " + |
|
49 ((cred.flags & FLAG_WRONG_PASSWORD) !=0 ? "wrong password" : "")+" "+ |
|
50 ((cred.flags & FLAG_PREVIOUS_FAILED) !=0 ? "previous failed" : "")+" "+ |
|
51 ((cred.flags & FLAG_RETURN_FALSE) !=0 ? "return false" : "") + "\n"); |
|
52 |
|
53 // PROXY properly set by necko (checked using realm) |
|
54 do_check_eq(cred.realmExpected, authInfo.realm); |
|
55 |
|
56 // PREVIOUS_FAILED properly set by necko |
|
57 do_check_eq((cred.flags & FLAG_PREVIOUS_FAILED) != 0, |
|
58 (authInfo.flags & Ci.nsIAuthInformation.PREVIOUS_FAILED) != 0); |
|
59 |
|
60 if (cred.flags & FLAG_RETURN_FALSE) |
|
61 { |
|
62 cred.flags |= FLAG_PREVIOUS_FAILED; |
|
63 cred.flags &= ~FLAG_RETURN_FALSE; |
|
64 return false; |
|
65 } |
|
66 |
|
67 authInfo.username = cred.user; |
|
68 if (cred.flags & FLAG_WRONG_PASSWORD) { |
|
69 authInfo.password = cred.pass + ".wrong"; |
|
70 cred.flags |= FLAG_PREVIOUS_FAILED; |
|
71 // Now clear the flag to avoid an infinite loop |
|
72 cred.flags &= ~FLAG_WRONG_PASSWORD; |
|
73 } else { |
|
74 authInfo.password = cred.pass; |
|
75 cred.flags &= ~FLAG_PREVIOUS_FAILED; |
|
76 } |
|
77 return true; |
|
78 |
|
79 } catch (e) { do_throw(e); } |
|
80 }, |
|
81 |
|
82 asyncPromptAuth: |
|
83 function ap2_async(channel, callback, context, encryptionLevel, authInfo) |
|
84 { |
|
85 try { |
|
86 var me = this; |
|
87 var allOverAndDead = false; |
|
88 do_execute_soon(function() { |
|
89 try { |
|
90 if (allOverAndDead) |
|
91 throw "already canceled"; |
|
92 var ret = me.promptAuth(channel, encryptionLevel, authInfo); |
|
93 if (!ret) |
|
94 callback.onAuthCancelled(context, true); |
|
95 else |
|
96 callback.onAuthAvailable(context, authInfo); |
|
97 allOverAndDead = true; |
|
98 } catch (e) { do_throw(e); } |
|
99 }); |
|
100 return new Cancelable(function() { |
|
101 if (allOverAndDead) |
|
102 throw "can't cancel, already ran"; |
|
103 callback.onAuthAvailable(context, authInfo); |
|
104 allOverAndDead = true; |
|
105 }); |
|
106 } catch (e) { do_throw(e); } |
|
107 } |
|
108 }; |
|
109 |
|
110 function Cancelable(onCancelFunc) { |
|
111 this.onCancelFunc = onCancelFunc; |
|
112 } |
|
113 Cancelable.prototype = { |
|
114 QueryInterface: function cancelable_qi(iid) { |
|
115 if (iid.equals(Ci.nsISupports) || |
|
116 iid.equals(Ci.nsICancelable)) |
|
117 return this; |
|
118 throw Cr.NS_ERROR_NO_INTERFACE; |
|
119 }, |
|
120 cancel: function cancel() { |
|
121 try { |
|
122 this.onCancelFunc(); |
|
123 } catch (e) { do_throw(e); } |
|
124 } |
|
125 }; |
|
126 |
|
127 function Requestor(proxyFlags, hostFlags) { |
|
128 this.proxyFlags = proxyFlags; |
|
129 this.hostFlags = hostFlags; |
|
130 } |
|
131 Requestor.prototype = { |
|
132 QueryInterface: function requestor_qi(iid) { |
|
133 if (iid.equals(Ci.nsISupports) || |
|
134 iid.equals(Ci.nsIInterfaceRequestor)) |
|
135 return this; |
|
136 throw Cr.NS_ERROR_NO_INTERFACE; |
|
137 }, |
|
138 |
|
139 getInterface: function requestor_gi(iid) { |
|
140 if (iid.equals(Ci.nsIAuthPrompt)) { |
|
141 dump("authprompt1 not implemented\n"); |
|
142 throw Cr.NS_ERROR_NO_INTERFACE; |
|
143 } |
|
144 if (iid.equals(Ci.nsIAuthPrompt2)) { |
|
145 try { |
|
146 // Allow the prompt to store state by caching it here |
|
147 if (!this.prompt2) |
|
148 this.prompt2 = new AuthPrompt2(this.proxyFlags, this.hostFlags); |
|
149 return this.prompt2; |
|
150 } catch (e) { do_throw(e); } |
|
151 } |
|
152 throw Cr.NS_ERROR_NO_INTERFACE; |
|
153 }, |
|
154 |
|
155 prompt2: null |
|
156 }; |
|
157 |
|
158 var listener = { |
|
159 expectedCode: -1, // uninitialized |
|
160 |
|
161 onStartRequest: function test_onStartR(request, ctx) { |
|
162 try { |
|
163 // Proxy auth cancellation return failures to avoid spoofing |
|
164 if (!Components.isSuccessCode(request.status) && |
|
165 (this.expectedCode != 407)) |
|
166 do_throw("Channel should have a success code!"); |
|
167 |
|
168 if (!(request instanceof Ci.nsIHttpChannel)) |
|
169 do_throw("Expecting an HTTP channel"); |
|
170 |
|
171 do_check_eq(this.expectedCode, request.responseStatus); |
|
172 // If we expect 200, the request should have succeeded |
|
173 do_check_eq(this.expectedCode == 200, request.requestSucceeded); |
|
174 |
|
175 } catch (e) { |
|
176 do_throw("Unexpected exception: " + e); |
|
177 } |
|
178 |
|
179 throw Cr.NS_ERROR_ABORT; |
|
180 }, |
|
181 |
|
182 onDataAvailable: function test_ODA() { |
|
183 do_throw("Should not get any data!"); |
|
184 }, |
|
185 |
|
186 onStopRequest: function test_onStopR(request, ctx, status) { |
|
187 do_check_eq(status, Cr.NS_ERROR_ABORT); |
|
188 |
|
189 if (current_test < (tests.length - 1)) { |
|
190 // First, need to clear the auth cache |
|
191 Cc["@mozilla.org/network/http-auth-manager;1"] |
|
192 .getService(Ci.nsIHttpAuthManager) |
|
193 .clearAll(); |
|
194 |
|
195 current_test++; |
|
196 tests[current_test](); |
|
197 } else { |
|
198 do_test_pending(); |
|
199 httpserv.stop(do_test_finished); |
|
200 } |
|
201 |
|
202 do_test_finished(); |
|
203 } |
|
204 }; |
|
205 |
|
206 function makeChan(url) { |
|
207 if (!url) |
|
208 url = "http://somesite/"; |
|
209 var ios = Cc["@mozilla.org/network/io-service;1"] |
|
210 .getService(Ci.nsIIOService); |
|
211 var chan = ios.newChannel(url, null, null) |
|
212 .QueryInterface(Ci.nsIHttpChannel); |
|
213 |
|
214 return chan; |
|
215 } |
|
216 |
|
217 var current_test = 0; |
|
218 var httpserv = null; |
|
219 |
|
220 function run_test() { |
|
221 httpserv = new HttpServer(); |
|
222 httpserv.registerPathHandler("/", proxyAuthHandler); |
|
223 httpserv.identity.add("http", "somesite", 80); |
|
224 httpserv.start(-1); |
|
225 |
|
226 const prefs = Cc["@mozilla.org/preferences-service;1"] |
|
227 .getService(Ci.nsIPrefBranch); |
|
228 prefs.setCharPref("network.proxy.http", "localhost"); |
|
229 prefs.setIntPref("network.proxy.http_port", httpserv.identity.primaryPort); |
|
230 prefs.setCharPref("network.proxy.no_proxies_on", ""); |
|
231 prefs.setIntPref("network.proxy.type", 1); |
|
232 |
|
233 tests[current_test](); |
|
234 } |
|
235 |
|
236 function test_proxy_returnfalse() { |
|
237 dump("\ntest: proxy returnfalse\n"); |
|
238 var chan = makeChan(); |
|
239 chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 0); |
|
240 listener.expectedCode = 407; // Proxy Unauthorized |
|
241 chan.asyncOpen(listener, null); |
|
242 |
|
243 do_test_pending(); |
|
244 } |
|
245 |
|
246 function test_proxy_wrongpw() { |
|
247 dump("\ntest: proxy wrongpw\n"); |
|
248 var chan = makeChan(); |
|
249 chan.notificationCallbacks = new Requestor(FLAG_WRONG_PASSWORD, 0); |
|
250 listener.expectedCode = 200; // Eventually OK |
|
251 chan.asyncOpen(listener, null); |
|
252 do_test_pending(); |
|
253 } |
|
254 |
|
255 function test_all_ok() { |
|
256 dump("\ntest: all ok\n"); |
|
257 var chan = makeChan(); |
|
258 chan.notificationCallbacks = new Requestor(0, 0); |
|
259 listener.expectedCode = 200; // OK |
|
260 chan.asyncOpen(listener, null); |
|
261 do_test_pending(); |
|
262 } |
|
263 |
|
264 function test_host_returnfalse() { |
|
265 dump("\ntest: host returnfalse\n"); |
|
266 var chan = makeChan(); |
|
267 chan.notificationCallbacks = new Requestor(0, FLAG_RETURN_FALSE); |
|
268 listener.expectedCode = 401; // Host Unauthorized |
|
269 chan.asyncOpen(listener, null); |
|
270 |
|
271 do_test_pending(); |
|
272 } |
|
273 |
|
274 function test_host_wrongpw() { |
|
275 dump("\ntest: host wrongpw\n"); |
|
276 var chan = makeChan(); |
|
277 chan.notificationCallbacks = new Requestor(0, FLAG_WRONG_PASSWORD); |
|
278 listener.expectedCode = 200; // Eventually OK |
|
279 chan.asyncOpen(listener, null); |
|
280 do_test_pending(); |
|
281 } |
|
282 |
|
283 function test_proxy_wrongpw_host_wrongpw() { |
|
284 dump("\ntest: proxy wrongpw, host wrongpw\n"); |
|
285 var chan = makeChan(); |
|
286 chan.notificationCallbacks = |
|
287 new Requestor(FLAG_WRONG_PASSWORD, FLAG_WRONG_PASSWORD); |
|
288 listener.expectedCode = 200; // OK |
|
289 chan.asyncOpen(listener, null); |
|
290 do_test_pending(); |
|
291 } |
|
292 |
|
293 function test_proxy_wrongpw_host_returnfalse() { |
|
294 dump("\ntest: proxy wrongpw, host return false\n"); |
|
295 var chan = makeChan(); |
|
296 chan.notificationCallbacks = |
|
297 new Requestor(FLAG_WRONG_PASSWORD, FLAG_RETURN_FALSE); |
|
298 listener.expectedCode = 401; // Host Unauthorized |
|
299 chan.asyncOpen(listener, null); |
|
300 do_test_pending(); |
|
301 } |
|
302 |
|
303 var tests = [test_proxy_returnfalse, test_proxy_wrongpw, test_all_ok, |
|
304 test_host_returnfalse, test_host_wrongpw, |
|
305 test_proxy_wrongpw_host_wrongpw, test_proxy_wrongpw_host_returnfalse]; |
|
306 |
|
307 |
|
308 // PATH HANDLERS |
|
309 |
|
310 // Proxy |
|
311 function proxyAuthHandler(metadata, response) { |
|
312 try { |
|
313 var realm = "intern"; |
|
314 // btoa("proxy:guest"), but that function is not available here |
|
315 var expectedHeader = "Basic cHJveHk6Z3Vlc3Q="; |
|
316 |
|
317 var body; |
|
318 if (metadata.hasHeader("Proxy-Authorization") && |
|
319 metadata.getHeader("Proxy-Authorization") == expectedHeader) |
|
320 { |
|
321 dump("proxy password ok\n"); |
|
322 response.setHeader("Proxy-Authenticate", |
|
323 'Basic realm="' + realm + '"', false); |
|
324 |
|
325 hostAuthHandler(metadata, response); |
|
326 } |
|
327 else |
|
328 { |
|
329 dump("proxy password required\n"); |
|
330 response.setStatusLine(metadata.httpVersion, 407, |
|
331 "Unauthorized by HTTP proxy"); |
|
332 response.setHeader("Proxy-Authenticate", |
|
333 'Basic realm="' + realm + '"', false); |
|
334 body = "failed"; |
|
335 response.bodyOutputStream.write(body, body.length); |
|
336 } |
|
337 } catch (e) { do_throw(e); } |
|
338 } |
|
339 |
|
340 // Host /auth |
|
341 function hostAuthHandler(metadata, response) { |
|
342 try { |
|
343 var realm = "extern"; |
|
344 // btoa("host:guest"), but that function is not available here |
|
345 var expectedHeader = "Basic aG9zdDpndWVzdA=="; |
|
346 |
|
347 var body; |
|
348 if (metadata.hasHeader("Authorization") && |
|
349 metadata.getHeader("Authorization") == expectedHeader) |
|
350 { |
|
351 dump("host password ok\n"); |
|
352 response.setStatusLine(metadata.httpVersion, 200, |
|
353 "OK, authorized for host"); |
|
354 response.setHeader("WWW-Authenticate", |
|
355 'Basic realm="' + realm + '"', false); |
|
356 body = "success"; |
|
357 } |
|
358 else |
|
359 { |
|
360 dump("host password required\n"); |
|
361 response.setStatusLine(metadata.httpVersion, 401, |
|
362 "Unauthorized by HTTP server host"); |
|
363 response.setHeader("WWW-Authenticate", |
|
364 'Basic realm="' + realm + '"', false); |
|
365 body = "failed"; |
|
366 } |
|
367 response.bodyOutputStream.write(body, body.length); |
|
368 } catch (e) { do_throw(e); } |
|
369 } |