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

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

Issue 14740015: Implementation of HTTP digest authentication (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: 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
« sdk/lib/io/http_impl.dart ('K') | « 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 import "package:expect/expect.dart";
6 import 'dart:async';
7 import 'dart:crypto';
8 import 'dart:io';
9 import 'dart:isolate';
10 import 'dart:uri';
11 import 'dart:utf';
12
13 class Server {
14 HttpServer server;
15 bool passwordChanged = false;
16 var ha1;
17
18 Future<Server> start(String serverAlgorithm, String serverQop) {
Anders Johnsen 2013/05/02 19:10:19 Nit. This method could be made static.
Søren Gjesse 2013/05/03 08:10:36 Done.
19 // Calculate ha1.
20 String realm = "test";
21 String username = "dart";
22 String password = "password";
23 var hasher = new MD5();
24 hasher.add("${username}:${realm}:${password}".codeUnits);
25 ha1 = CryptoUtils.bytesToHex(hasher.close());
26
27 var completer = new Completer();
28 HttpServer.bind("127.0.0.1", 0).then((s) {
29 server = s;
30 server.listen((HttpRequest request) {
31 // Just use a fixed nonce.
32 var nonce = "12345678";
33
34 sendUnauthorizedResponse(HttpResponse response) {
35 response.statusCode = HttpStatus.UNAUTHORIZED;
36 StringBuffer authHeader = new StringBuffer();
37 authHeader.write('Digest');
38 authHeader.write(', realm="$realm"');
39 authHeader.write(', nonce="$nonce"');
40 if (serverAlgorithm != null) {
41 authHeader.write(', algorithm=$serverAlgorithm');
42 }
43 authHeader.write(', domain="/digest/"');
44 if (serverQop != null) authHeader.write(', qop="$serverQop"');
45 response.headers.set(HttpHeaders.WWW_AUTHENTICATE, authHeader);
46 }
47
48 var response = request.response;
49 if (request.headers[HttpHeaders.AUTHORIZATION] != null) {
50 Expect.equals(1, request.headers[HttpHeaders.AUTHORIZATION].length);
51 String authorization =
52 request.headers[HttpHeaders.AUTHORIZATION][0];
53 HeaderValue header = new HeaderValue.fromString(authorization, paramet erSeparator: ",");
54 if (header.value == "basic") {
55 sendUnauthorizedResponse(response);
56 } else {
57 var uri = header.parameters["uri"];
58 var qop = header.parameters["qop"];
59 var cnonce = header.parameters["cnonce"];
60 var nc = header.parameters["nc"];
61 Expect.equals("digest", header.value);
62 Expect.equals("dart", header.parameters["username"]);
63 Expect.equals(realm, header.parameters["realm"]);
64 Expect.equals("MD5", header.parameters["algorithm"]);
65 Expect.equals(nonce, header.parameters["nonce"]);
66 Expect.equals(request.uri.path, uri);
67 if (qop != null) {
68 // A server qop of auth-int is downgraded to none by the client.
69 Expect.equals("auth", serverQop);
70 Expect.equals("auth", header.parameters["qop"]);
71 Expect.isNotNull(cnonce);
72 Expect.isNotNull(nc);
73 } else {
74 Expect.isNull(cnonce);
75 Expect.isNull(nc);
76 }
77 Expect.isNotNull(header.parameters["response"]);
78
79 var hasher = new MD5();
80 hasher.add("${request.method}:${uri}".codeUnits);
81 var ha2 = CryptoUtils.bytesToHex(hasher.close());
82
83 var x;
84 hasher = new MD5();
85 if (qop == null || qop == "" || qop == "none") {
86 hasher.add("$ha1:${nonce}:$ha2".codeUnits);
87 } else {
88 hasher.add("$ha1:${nonce}:${nc}:${cnonce}:${qop}:$ha2".codeUnits);
89 }
90 Expect.equals(CryptoUtils.bytesToHex(hasher.close()),
91 header.parameters["response"]);
92
93 // Add a bogus Authentication-Info for testing.
94 var info = 'rspauth="77180d1ab3d6c9de084766977790f482", '
95 'cnonce="8f971178", '
96 'nc=000002c74, '
97 'qop=auth';
98 response.headers.set("Authentication-Info", info);
99 }
100 } else {
101 sendUnauthorizedResponse(response);
102 }
103 response.close();
104 });
105 completer.complete(this);
106 });
107 return completer.future;
108 }
109
110 void shutdown() {
111 server.close();
112 }
113
114 int get port => server.port;
115 }
116
117 Future<Server> setupServer(String algorithm, String qop) {
118 return new Server().start(algorithm, qop);
119 }
120
121 void testNoCredentials(String algorithm, String qop) {
122 setupServer(algorithm, qop).then((server) {
123 HttpClient client = new HttpClient();
124
125 // Add digest credentials which does not match the path requested.
126 client.addCredentials(
127 Uri.parse("http://127.0.0.1:${server.port}/xxx"),
128 "test",
129 new HttpClientDigestCredentials("dart", "password"));
130
131 // Add basic credentials for the path requested.
132 client.addCredentials(
133 Uri.parse("http://127.0.0.1:${server.port}/digest"),
134 "test",
135 new HttpClientBasicCredentials("dart", "password"));
136
137 Future makeRequest(Uri url) {
138 return client.getUrl(url)
139 .then((HttpClientRequest request) => request.close())
140 .then((HttpClientResponse response) {
141 Expect.equals(HttpStatus.UNAUTHORIZED, response.statusCode);
142 return response.fold(null, (x, y) {});
143 });
144 }
145
146 var futures = [];
147 for (int i = 0; i < 5; i++) {
148 futures.add(
149 makeRequest(
150 Uri.parse("http://127.0.0.1:${server.port}/digest")));
151 }
152 Future.wait(futures).then((_) {
153 server.shutdown();
154 client.close();
155 });
156 });
157 }
158
159 void testCredentials(String algorithm, String qop) {
160 setupServer(algorithm, qop).then((server) {
161 HttpClient client = new HttpClient();
162
163 Future makeRequest(Uri url) {
164 return client.getUrl(url)
165 .then((HttpClientRequest request) => request.close())
166 .then((HttpClientResponse response) {
167 Expect.equals(HttpStatus.OK, response.statusCode);
168 Expect.equals(1, response.headers["Authentication-Info"].length);
169 return response.fold(null, (x, y) {});
170 });
171 }
172
173 client.addCredentials(
174 Uri.parse("http://127.0.0.1:${server.port}/digest"),
175 "test",
176 new HttpClientDigestCredentials("dart", "password"));
177
178 var futures = [];
179 for (int i = 0; i < 5; i++) {
180 futures.add(
181 makeRequest(
182 Uri.parse("http://127.0.0.1:${server.port}/digest")));
183 }
184 Future.wait(futures).then((_) {
185 server.shutdown();
186 client.close();
187 });
188 });
189 }
190
191 void testBasicAuthenticateCallback(String algorithm, String qop) {
192 setupServer(algorithm, qop).then((server) {
193 HttpClient client = new HttpClient();
194
195 client.authenticate = (Uri url, String scheme, String realm) {
196 Expect.equals("Digest", scheme);
197 Expect.equals("test", realm);
198 Completer completer = new Completer();
199 new Timer(const Duration(milliseconds: 10), () {
200 client.addCredentials(
201 Uri.parse("http://127.0.0.1:${server.port}/digest"),
202 "test",
203 new HttpClientDigestCredentials("dart", "password"));
204 completer.complete(true);
205 });
206 return completer.future;
207 };
208
209 Future makeRequest(Uri url) {
210 return client.getUrl(url)
211 .then((HttpClientRequest request) => request.close())
212 .then((HttpClientResponse response) {
213 Expect.equals(HttpStatus.OK, response.statusCode);
214 Expect.equals(1, response.headers["Authentication-Info"].length);
215 return response.fold(null, (x, y) {});
216 });
217 }
218
219 var futures = [];
220 for (int i = 0; i < 5; i++) {
221 futures.add(
222 makeRequest(
223 Uri.parse("http://127.0.0.1:${server.port}/digest")));
224 }
225 Future.wait(futures).then((_) {
226 server.shutdown();
227 client.close();
228 });
229 });
230 }
231
232 // An Apache virtual directory configuration like this can be used for
233 // running the local server tests.
234 //
235 // <Directory "/usr/local/prj/website/digest/">
236 // AllowOverride None
237 // Order deny,allow
238 // Deny from all
239 // Allow from 127.0.0.0/255.0.0.0 ::1/128
240 // AuthType Digest
241 // AuthName "test"
242 // AuthDigestDomain /digest/
243 // AuthDigestProvider file
244 // AuthUserFile /usr/local/prj/apache/passwd/digest-passwd
245 // Require valid-user
246 // </Directory>
247 //
248
249 void testLocalServerDigest() {
250 HttpClient client = new HttpClient();
251
252 Future makeRequest() {
253 return client.getUrl(Uri.parse("http://127.0.0.1/digest/test"))
254 .then((HttpClientRequest request) => request.close())
255 .then((HttpClientResponse response) {
256 Expect.equals(HttpStatus.OK, response.statusCode);
257 return response.fold(null, (x, y) {});
258 });
259 }
260
261 client.addCredentials(
262 Uri.parse("http://127.0.0.1/digest"),
263 "test",
264 new HttpClientDigestCredentials("dart", "password"));
265
266 client.authenticate = (Uri url, String scheme, String realm) {
267 client.addCredentials(
268 Uri.parse("http://127.0.0.1/digest"),
269 "test",
270 new HttpClientDigestCredentials("dart", "password"));
271 return new Future.value(true);
272 };
273
274 next() {
275 makeRequest().then((_) => next());
276 }
277 next();
278 }
279
280 main() {
281 testNoCredentials(null, null);
282 testNoCredentials("MD5", null);
283 testNoCredentials("MD5", "auth");
284 testCredentials(null, null);
285 testCredentials("MD5", null);
286 testCredentials("MD5", "auth");
287 testCredentials("MD5", "auth-int");
288 testBasicAuthenticateCallback(null, null);
289 testBasicAuthenticateCallback("MD5", null);
290 testBasicAuthenticateCallback("MD5", "auth");
291 testBasicAuthenticateCallback("MD5", "auth-int");
292 // These teste are not normally run. They can be used for locally
293 // testing with another web server (e.g. Apache).
294 //testLocalServerDigest();
295 }
OLDNEW
« sdk/lib/io/http_impl.dart ('K') | « sdk/lib/io/http_impl.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698