Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(421)

Side by Side Diff: tests/standalone/io/http_auth_digest_test.dart

Issue 14595006: Extend HTTP digest support with server changing nonce (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Addressed review comments Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sdk/lib/io/http_impl.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 import "package:expect/expect.dart"; 5 import "package:expect/expect.dart";
6 import 'dart:async'; 6 import 'dart:async';
7 import 'dart:crypto'; 7 import 'dart:crypto';
8 import 'dart:io'; 8 import 'dart:io';
9 import 'dart:isolate'; 9 import 'dart:isolate';
10 import 'dart:uri'; 10 import 'dart:uri';
11 import 'dart:utf'; 11 import 'dart:utf';
12 12
13 class Server { 13 class Server {
14 HttpServer server; 14 HttpServer server;
15 bool passwordChanged = false; 15 int unauthCount = 0; // Counter of the 401 responses.
16 int successCount = 0; // Counter of the successful responses.
17 int nonceCount = 0; // Counter of use of current nonce.
16 var ha1; 18 var ha1;
17 19
18 static Future<Server> start(String algorithm, String qop) { 20 static Future<Server> start(String algorithm,
19 return new Server()._start(algorithm, qop); 21 String qop,
22 {int nonceStaleAfter,
23 bool useNextNonce: false}) {
24 return new Server()._start(algorithm, qop, nonceStaleAfter, useNextNonce);
20 } 25 }
21 26
22 Future<Server> _start(String serverAlgorithm, String serverQop) { 27 Future<Server> _start(String serverAlgorithm,
28 String serverQop,
29 int nonceStaleAfter,
30 bool useNextNonce) {
31 Set ncs = new Set();
23 // Calculate ha1. 32 // Calculate ha1.
24 String realm = "test"; 33 String realm = "test";
25 String username = "dart"; 34 String username = "dart";
26 String password = "password"; 35 String password = "password";
27 var hasher = new MD5(); 36 var hasher = new MD5();
28 hasher.add("${username}:${realm}:${password}".codeUnits); 37 hasher.add("${username}:${realm}:${password}".codeUnits);
29 ha1 = CryptoUtils.bytesToHex(hasher.close()); 38 ha1 = CryptoUtils.bytesToHex(hasher.close());
30 39
40 var nonce = "12345678"; // No need for random nonce in test.
41
31 var completer = new Completer(); 42 var completer = new Completer();
32 HttpServer.bind("127.0.0.1", 0).then((s) { 43 HttpServer.bind("127.0.0.1", 0).then((s) {
33 server = s; 44 server = s;
34 server.listen((HttpRequest request) { 45 server.listen((HttpRequest request) {
35 // Just use a fixed nonce. 46 sendUnauthorizedResponse(HttpResponse response, {stale: false}) {
36 var nonce = "12345678";
37
38 sendUnauthorizedResponse(HttpResponse response) {
39 response.statusCode = HttpStatus.UNAUTHORIZED; 47 response.statusCode = HttpStatus.UNAUTHORIZED;
40 StringBuffer authHeader = new StringBuffer(); 48 StringBuffer authHeader = new StringBuffer();
41 authHeader.write('Digest'); 49 authHeader.write('Digest');
42 authHeader.write(', realm="$realm"'); 50 authHeader.write(', realm="$realm"');
43 authHeader.write(', nonce="$nonce"'); 51 authHeader.write(', nonce="$nonce"');
52 if (stale) authHeader.write(', stale="true"');
44 if (serverAlgorithm != null) { 53 if (serverAlgorithm != null) {
45 authHeader.write(', algorithm=$serverAlgorithm'); 54 authHeader.write(', algorithm=$serverAlgorithm');
46 } 55 }
47 authHeader.write(', domain="/digest/"'); 56 authHeader.write(', domain="/digest/"');
48 if (serverQop != null) authHeader.write(', qop="$serverQop"'); 57 if (serverQop != null) authHeader.write(', qop="$serverQop"');
49 response.headers.set(HttpHeaders.WWW_AUTHENTICATE, authHeader); 58 response.headers.set(HttpHeaders.WWW_AUTHENTICATE, authHeader);
59 unauthCount++;
50 } 60 }
51 61
52 var response = request.response; 62 var response = request.response;
53 if (request.headers[HttpHeaders.AUTHORIZATION] != null) { 63 if (request.headers[HttpHeaders.AUTHORIZATION] != null) {
54 Expect.equals(1, request.headers[HttpHeaders.AUTHORIZATION].length); 64 Expect.equals(1, request.headers[HttpHeaders.AUTHORIZATION].length);
55 String authorization = 65 String authorization =
56 request.headers[HttpHeaders.AUTHORIZATION][0]; 66 request.headers[HttpHeaders.AUTHORIZATION][0];
57 HeaderValue header = 67 HeaderValue header =
58 new HeaderValue.fromString( 68 new HeaderValue.fromString(
59 authorization, parameterSeparator: ","); 69 authorization, parameterSeparator: ",");
60 if (header.value == "basic") { 70 if (header.value == "basic") {
61 sendUnauthorizedResponse(response); 71 sendUnauthorizedResponse(response);
72 } else if (!useNextNonce && nonceCount == nonceStaleAfter) {
73 nonce = "87654321";
74 nonceCount = 0;
75 sendUnauthorizedResponse(response, stale: true);
62 } else { 76 } else {
63 var uri = header.parameters["uri"]; 77 var uri = header.parameters["uri"];
64 var qop = header.parameters["qop"]; 78 var qop = header.parameters["qop"];
65 var cnonce = header.parameters["cnonce"]; 79 var cnonce = header.parameters["cnonce"];
66 var nc = header.parameters["nc"]; 80 var nc = header.parameters["nc"];
67 Expect.equals("digest", header.value); 81 Expect.equals("digest", header.value);
68 Expect.equals("dart", header.parameters["username"]); 82 Expect.equals("dart", header.parameters["username"]);
69 Expect.equals(realm, header.parameters["realm"]); 83 Expect.equals(realm, header.parameters["realm"]);
70 Expect.equals("MD5", header.parameters["algorithm"]); 84 Expect.equals("MD5", header.parameters["algorithm"]);
71 Expect.equals(nonce, header.parameters["nonce"]); 85 Expect.equals(nonce, header.parameters["nonce"]);
72 Expect.equals(request.uri.path, uri); 86 Expect.equals(request.uri.path, uri);
73 if (qop != null) { 87 if (qop != null) {
74 // A server qop of auth-int is downgraded to none by the client. 88 // A server qop of auth-int is downgraded to none by the client.
75 Expect.equals("auth", serverQop); 89 Expect.equals("auth", serverQop);
76 Expect.equals("auth", header.parameters["qop"]); 90 Expect.equals("auth", header.parameters["qop"]);
77 Expect.isNotNull(cnonce); 91 Expect.isNotNull(cnonce);
78 Expect.isNotNull(nc); 92 Expect.isNotNull(nc);
93 Expect.isFalse(ncs.contains(nc));
94 ncs.add(nc);
79 } else { 95 } else {
80 Expect.isNull(cnonce); 96 Expect.isNull(cnonce);
81 Expect.isNull(nc); 97 Expect.isNull(nc);
82 } 98 }
83 Expect.isNotNull(header.parameters["response"]); 99 Expect.isNotNull(header.parameters["response"]);
84 100
85 var hasher = new MD5(); 101 var hasher = new MD5();
86 hasher.add("${request.method}:${uri}".codeUnits); 102 hasher.add("${request.method}:${uri}".codeUnits);
87 var ha2 = CryptoUtils.bytesToHex(hasher.close()); 103 var ha2 = CryptoUtils.bytesToHex(hasher.close());
88 104
89 var x; 105 var x;
90 hasher = new MD5(); 106 hasher = new MD5();
91 if (qop == null || qop == "" || qop == "none") { 107 if (qop == null || qop == "" || qop == "none") {
92 hasher.add("$ha1:${nonce}:$ha2".codeUnits); 108 hasher.add("$ha1:${nonce}:$ha2".codeUnits);
93 } else { 109 } else {
94 hasher.add("$ha1:${nonce}:${nc}:${cnonce}:${qop}:$ha2".codeUnits); 110 hasher.add("$ha1:${nonce}:${nc}:${cnonce}:${qop}:$ha2".codeUnits);
95 } 111 }
96 Expect.equals(CryptoUtils.bytesToHex(hasher.close()), 112 Expect.equals(CryptoUtils.bytesToHex(hasher.close()),
97 header.parameters["response"]); 113 header.parameters["response"]);
98 114
115 successCount++;
116 nonceCount++;
117
99 // Add a bogus Authentication-Info for testing. 118 // Add a bogus Authentication-Info for testing.
100 var info = 'rspauth="77180d1ab3d6c9de084766977790f482", ' 119 var info = 'rspauth="77180d1ab3d6c9de084766977790f482", '
101 'cnonce="8f971178", ' 120 'cnonce="8f971178", '
102 'nc=000002c74, ' 121 'nc=000002c74, '
103 'qop=auth'; 122 'qop=auth';
123 if (useNextNonce && nonceCount == nonceStaleAfter) {
124 nonce = "abcdef01";
125 info += ', nextnonce="$nonce"';
126 }
104 response.headers.set("Authentication-Info", info); 127 response.headers.set("Authentication-Info", info);
105 } 128 }
106 } else { 129 } else {
107 sendUnauthorizedResponse(response); 130 sendUnauthorizedResponse(response);
108 } 131 }
109 response.close(); 132 response.close();
110 }); 133 });
111 completer.complete(this); 134 completer.complete(this);
112 }); 135 });
113 return completer.future; 136 return completer.future;
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
183 makeRequest( 206 makeRequest(
184 Uri.parse("http://127.0.0.1:${server.port}/digest"))); 207 Uri.parse("http://127.0.0.1:${server.port}/digest")));
185 } 208 }
186 Future.wait(futures).then((_) { 209 Future.wait(futures).then((_) {
187 server.shutdown(); 210 server.shutdown();
188 client.close(); 211 client.close();
189 }); 212 });
190 }); 213 });
191 } 214 }
192 215
193 void testBasicAuthenticateCallback(String algorithm, String qop) { 216 void testAuthenticateCallback(String algorithm, String qop) {
194 Server.start(algorithm, qop).then((server) { 217 Server.start(algorithm, qop).then((server) {
195 HttpClient client = new HttpClient(); 218 HttpClient client = new HttpClient();
196 219
197 client.authenticate = (Uri url, String scheme, String realm) { 220 client.authenticate = (Uri url, String scheme, String realm) {
198 Expect.equals("Digest", scheme); 221 Expect.equals("Digest", scheme);
199 Expect.equals("test", realm); 222 Expect.equals("test", realm);
200 Completer completer = new Completer(); 223 Completer completer = new Completer();
201 new Timer(const Duration(milliseconds: 10), () { 224 new Timer(const Duration(milliseconds: 10), () {
202 client.addCredentials( 225 client.addCredentials(
203 Uri.parse("http://127.0.0.1:${server.port}/digest"), 226 Uri.parse("http://127.0.0.1:${server.port}/digest"),
(...skipping 20 matching lines...) Expand all
224 makeRequest( 247 makeRequest(
225 Uri.parse("http://127.0.0.1:${server.port}/digest"))); 248 Uri.parse("http://127.0.0.1:${server.port}/digest")));
226 } 249 }
227 Future.wait(futures).then((_) { 250 Future.wait(futures).then((_) {
228 server.shutdown(); 251 server.shutdown();
229 client.close(); 252 client.close();
230 }); 253 });
231 }); 254 });
232 } 255 }
233 256
257 void testStaleNonce() {
258 Server.start("MD5", "auth", nonceStaleAfter: 2).then((server) {
259 HttpClient client = new HttpClient();
260
261 Future makeRequest(Uri url) {
262 return client.getUrl(url)
263 .then((HttpClientRequest request) => request.close())
264 .then((HttpClientResponse response) {
265 Expect.equals(HttpStatus.OK, response.statusCode);
266 Expect.equals(1, response.headers["Authentication-Info"].length);
267 return response.fold(null, (x, y) {});
268 });
269 }
270
271 Uri uri = Uri.parse("http://127.0.0.1:${server.port}/digest");
272 var credentials = new HttpClientDigestCredentials("dart", "password");
273 client.addCredentials(uri, "test", credentials);
274
275 makeRequest(uri)
276 .then((_) => makeRequest(uri))
277 .then((_) => makeRequest(uri))
278 .then((_) => makeRequest(uri))
279 .then((_) {
280 Expect.equals(2, server.unauthCount);
281 Expect.equals(4, server.successCount);
282 server.shutdown();
283 client.close();
284 });
285 });
286 }
287
288 void testNextNonce() {
289 Server.start("MD5",
290 "auth",
291 nonceStaleAfter: 2,
292 useNextNonce: true).then((server) {
293 HttpClient client = new HttpClient();
294
295 Future makeRequest(Uri url) {
296 return client.getUrl(url)
297 .then((HttpClientRequest request) => request.close())
298 .then((HttpClientResponse response) {
299 Expect.equals(HttpStatus.OK, response.statusCode);
300 Expect.equals(1, response.headers["Authentication-Info"].length);
301 return response.fold(null, (x, y) {});
302 });
303 }
304
305 Uri uri = Uri.parse("http://127.0.0.1:${server.port}/digest");
306 var credentials = new HttpClientDigestCredentials("dart", "password");
307 client.addCredentials(uri, "test", credentials);
308
309 makeRequest(uri)
310 .then((_) => makeRequest(uri))
311 .then((_) => makeRequest(uri))
312 .then((_) => makeRequest(uri))
313 .then((_) {
314 Expect.equals(1, server.unauthCount);
315 Expect.equals(4, server.successCount);
316 server.shutdown();
317 client.close();
318 });
319 });
320 }
321
234 // An Apache virtual directory configuration like this can be used for 322 // An Apache virtual directory configuration like this can be used for
235 // running the local server tests. 323 // running the local server tests.
236 // 324 //
237 // <Directory "/usr/local/prj/website/digest/"> 325 // <Directory "/usr/local/prj/website/digest/">
238 // AllowOverride None 326 // AllowOverride None
239 // Order deny,allow 327 // Order deny,allow
240 // Deny from all 328 // Deny from all
241 // Allow from 127.0.0.0/255.0.0.0 ::1/128 329 // Allow from 127.0.0.0/255.0.0.0 ::1/128
242 // AuthType Digest 330 // AuthType Digest
243 // AuthName "test" 331 // AuthName "test"
244 // AuthDigestDomain /digest/ 332 // AuthDigestDomain /digest/
333 // AuthDigestAlgorithm MD5
334 // AuthDigestQop auth
335 // AuthDigestNonceLifetime 10
245 // AuthDigestProvider file 336 // AuthDigestProvider file
246 // AuthUserFile /usr/local/prj/apache/passwd/digest-passwd 337 // AuthUserFile /usr/local/prj/apache/passwd/digest-passwd
247 // Require valid-user 338 // Require valid-user
248 // </Directory> 339 // </Directory>
249 // 340 //
250 341
251 void testLocalServerDigest() { 342 void testLocalServerDigest() {
343 int count = 0;
252 HttpClient client = new HttpClient(); 344 HttpClient client = new HttpClient();
253 345
254 Future makeRequest() { 346 Future makeRequest() {
255 return client.getUrl(Uri.parse("http://127.0.0.1/digest/test")) 347 return client.getUrl(Uri.parse("http://127.0.0.1/digest/test"))
256 .then((HttpClientRequest request) => request.close()) 348 .then((HttpClientRequest request) => request.close())
257 .then((HttpClientResponse response) { 349 .then((HttpClientResponse response) {
350 count++;
351 if (count % 100 == 0) print(count);
258 Expect.equals(HttpStatus.OK, response.statusCode); 352 Expect.equals(HttpStatus.OK, response.statusCode);
259 return response.fold(null, (x, y) {}); 353 return response.fold(null, (x, y) {});
260 }); 354 });
261 } 355 }
262 356
263 client.addCredentials( 357 client.addCredentials(
264 Uri.parse("http://127.0.0.1/digest"), 358 Uri.parse("http://127.0.0.1/digest"),
265 "test", 359 "test",
266 new HttpClientDigestCredentials("dart", "password")); 360 new HttpClientDigestCredentials("dart", "password"));
267 361
(...skipping 12 matching lines...) Expand all
280 } 374 }
281 375
282 main() { 376 main() {
283 testNoCredentials(null, null); 377 testNoCredentials(null, null);
284 testNoCredentials("MD5", null); 378 testNoCredentials("MD5", null);
285 testNoCredentials("MD5", "auth"); 379 testNoCredentials("MD5", "auth");
286 testCredentials(null, null); 380 testCredentials(null, null);
287 testCredentials("MD5", null); 381 testCredentials("MD5", null);
288 testCredentials("MD5", "auth"); 382 testCredentials("MD5", "auth");
289 testCredentials("MD5", "auth-int"); 383 testCredentials("MD5", "auth-int");
290 testBasicAuthenticateCallback(null, null); 384 testAuthenticateCallback(null, null);
291 testBasicAuthenticateCallback("MD5", null); 385 testAuthenticateCallback("MD5", null);
292 testBasicAuthenticateCallback("MD5", "auth"); 386 testAuthenticateCallback("MD5", "auth");
293 testBasicAuthenticateCallback("MD5", "auth-int"); 387 testAuthenticateCallback("MD5", "auth-int");
388 testStaleNonce();
389 testNextNonce();
294 // These teste are not normally run. They can be used for locally 390 // These teste are not normally run. They can be used for locally
295 // testing with another web server (e.g. Apache). 391 // testing with another web server (e.g. Apache).
296 //testLocalServerDigest(); 392 //testLocalServerDigest();
297 } 393 }
OLDNEW
« no previous file with comments | « sdk/lib/io/http_impl.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698