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

Side by Side Diff: sdk/lib/io/http_impl.dart

Issue 14740015: Implementation of HTTP digest authentication (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fix analyzer staticerrors 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.dart ('k') | tests/standalone/io/http_auth_digest_test.dart » ('j') | 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 part of dart.io; 5 part of dart.io;
6 6
7 class _HttpIncoming extends Stream<List<int>> { 7 class _HttpIncoming extends Stream<List<int>> {
8 final int _transferLength; 8 final int _transferLength;
9 final Completer _dataCompleter = new Completer(); 9 final Completer _dataCompleter = new Completer();
10 Stream<List<int>> _stream; 10 Stream<List<int>> _stream;
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after
266 266
267 bool get _shouldAuthenticate { 267 bool get _shouldAuthenticate {
268 // Only try to authenticate if there is a challenge in the response. 268 // Only try to authenticate if there is a challenge in the response.
269 List<String> challenge = headers[HttpHeaders.WWW_AUTHENTICATE]; 269 List<String> challenge = headers[HttpHeaders.WWW_AUTHENTICATE];
270 return statusCode == HttpStatus.UNAUTHORIZED && 270 return statusCode == HttpStatus.UNAUTHORIZED &&
271 challenge != null && challenge.length == 1; 271 challenge != null && challenge.length == 1;
272 } 272 }
273 273
274 Future<HttpClientResponse> _authenticate() { 274 Future<HttpClientResponse> _authenticate() {
275 Future<HttpClientResponse> retryWithCredentials(_Credentials cr) { 275 Future<HttpClientResponse> retryWithCredentials(_Credentials cr) {
276 if (cr != null) { 276 if (cr != null && cr.scheme != _AuthenticationScheme.UNKNOWN) {
277 // TODO(sgjesse): Support digest. 277 // Drain body and retry.
278 if (cr.scheme == _AuthenticationScheme.BASIC) { 278 return fold(null, (x, y) {}).then((_) {
279 // Drain body and retry. 279 return _httpClient._openUrlFromRequest(_httpRequest.method,
280 return fold(null, (x, y) {}).then((_) { 280 _httpRequest.uri,
281 return _httpClient._openUrlFromRequest(_httpRequest.method, 281 _httpRequest)
282 _httpRequest.uri, 282 .then((request) => request.close());
283 _httpRequest) 283 });
284 .then((request) => request.close());
285 });
286 }
287 } 284 }
288 285
289 // Fall through to here to perform normal response handling if 286 // Fall through to here to perform normal response handling if
290 // there is no sensible authorization handling. 287 // there is no sensible authorization handling.
291 return new Future.value(this); 288 return new Future.value(this);
292 } 289 }
293 290
294 List<String> challenge = headers[HttpHeaders.WWW_AUTHENTICATE]; 291 List<String> challenge = headers[HttpHeaders.WWW_AUTHENTICATE];
295 assert(challenge != null || challenge.length == 1); 292 assert(challenge != null || challenge.length == 1);
296 _HeaderValue header = 293 _HeaderValue header =
297 new _HeaderValue.fromString(challenge[0], parameterSeparator: ","); 294 new _HeaderValue.fromString(challenge[0], parameterSeparator: ",");
298 _AuthenticationScheme scheme = 295 _AuthenticationScheme scheme =
299 new _AuthenticationScheme.fromString(header.value); 296 new _AuthenticationScheme.fromString(header.value);
300 String realm = header.parameters["realm"]; 297 String realm = header.parameters["realm"];
301 298
302 // See if any credentials are available. 299 // See if any matching credentials are available.
303 _Credentials cr = _httpClient._findCredentials(_httpRequest.uri, scheme); 300 _Credentials cr = _httpClient._findCredentials(_httpRequest.uri, scheme);
301 if (cr != null) {
302 // For basic authentication don't retry already used credentials
303 // as they must have already been added to the request causing
304 // this authenticate response.
305 if (cr.scheme == _AuthenticationScheme.BASIC && !cr.used) {
306 // Credentials where found, prepare for retrying the request.
307 return retryWithCredentials(cr);
308 }
304 309
305 if (cr != null && !cr.used) { 310 // Digest authentication only supports the MD5 algorithm.
306 // If credentials found prepare for retrying the request. 311 if (cr.scheme == _AuthenticationScheme.DIGEST &&
307 return retryWithCredentials(cr); 312 (header.parameters["algorithm"] == null ||
313 header.parameters["algorithm"].toLowerCase() == "md5")) {
314 // If the nonce is not set then this is the first authenticate
315 // response for these credentials.
316 // TODO(sgjesse): Check for changed nonce.
317 if (cr.nonce == null) {
318 // Set up authentication state.
319 cr.nonce = header.parameters["nonce"];
320 cr.algorithm = "MD5";
321 cr.qop = header.parameters["qop"];
322 cr.nonceCount = 0;
323 }
324 // Credentials where found, prepare for retrying the request.
325 return retryWithCredentials(cr);
326 }
308 } 327 }
309 328
310 // Ask for more credentials if none found or the one found has 329 // Ask for more credentials if none found or the one found has
311 // already been used. If it has already been used it must now be 330 // already been used. If it has already been used it must now be
312 // invalid and is removed. 331 // invalid and is removed.
313 if (cr != null) { 332 if (cr != null) {
314 _httpClient._removeCredentials(cr); 333 _httpClient._removeCredentials(cr);
315 cr = null; 334 cr = null;
316 } 335 }
317 if (_httpClient._authenticate != null) { 336 if (_httpClient._authenticate != null) {
(...skipping 1623 matching lines...) Expand 10 before | Expand all | Expand 10 after
1941 if (this == BASIC) return "Basic"; 1960 if (this == BASIC) return "Basic";
1942 if (this == DIGEST) return "Digest"; 1961 if (this == DIGEST) return "Digest";
1943 return "Unknown"; 1962 return "Unknown";
1944 } 1963 }
1945 1964
1946 final int _scheme; 1965 final int _scheme;
1947 } 1966 }
1948 1967
1949 1968
1950 class _Credentials { 1969 class _Credentials {
1951 _Credentials(this.uri, this.realm, this.credentials); 1970 _Credentials(this.uri, this.realm, this.credentials) {
1971 if (credentials.scheme == _AuthenticationScheme.DIGEST) {
1972 // Calculate the H(A1) value once. There is no mentioning of
1973 // username/password encoding in RFC 2617. However there is an
1974 // open draft for adding an additional accept-charset parameter to
1975 // the WWW-Authenticate and Proxy-Authenticate headers, see
1976 // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For
1977 // now always use UTF-8 encoding.
1978 _HttpClientDigestCredentials creds = credentials;
1979 var hasher = new MD5();
1980 hasher.add(_encodeString(creds.username));
1981 hasher.add([_CharCode.COLON]);
1982 hasher.add(realm.codeUnits);
1983 hasher.add([_CharCode.COLON]);
1984 hasher.add(_encodeString(creds.password));
1985 ha1 = CryptoUtils.bytesToHex(hasher.close());
1986 }
1987 }
1952 1988
1953 _AuthenticationScheme get scheme => credentials.scheme; 1989 _AuthenticationScheme get scheme => credentials.scheme;
1954 1990
1955 bool applies(Uri uri, _AuthenticationScheme scheme) { 1991 bool applies(Uri uri, _AuthenticationScheme scheme) {
1956 if (scheme != null && credentials.scheme != scheme) return false; 1992 if (scheme != null && credentials.scheme != scheme) return false;
1957 if (uri.domain != this.uri.domain) return false; 1993 if (uri.domain != this.uri.domain) return false;
1958 int thisPort = 1994 int thisPort =
1959 this.uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : this.uri.port; 1995 this.uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : this.uri.port;
1960 int otherPort = uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : uri.port; 1996 int otherPort = uri.port == 0 ? HttpClient.DEFAULT_HTTP_PORT : uri.port;
1961 if (otherPort != thisPort) return false; 1997 if (otherPort != thisPort) return false;
1962 return uri.path.startsWith(this.uri.path); 1998 return uri.path.startsWith(this.uri.path);
1963 } 1999 }
1964 2000
1965 void authorize(HttpClientRequest request) { 2001 void authorize(HttpClientRequest request) {
2002 // Digest credentials cannot be used without a nonce from the
2003 // server.
2004 if (credentials.scheme == _AuthenticationScheme.DIGEST &&
2005 nonce == null) {
2006 return;
2007 }
1966 credentials.authorize(this, request); 2008 credentials.authorize(this, request);
1967 used = true; 2009 used = true;
1968 } 2010 }
1969 2011
1970 bool used = false; 2012 bool used = false;
1971 Uri uri; 2013 Uri uri;
1972 String realm; 2014 String realm;
1973 _HttpClientCredentials credentials; 2015 _HttpClientCredentials credentials;
1974 2016
1975 // Digest specific fields. 2017 // Digest specific fields.
2018 String ha1;
1976 String nonce; 2019 String nonce;
1977 String algorithm; 2020 String algorithm;
1978 String qop; 2021 String qop;
2022 int nonceCount;
1979 } 2023 }
1980 2024
1981 2025
1982 class _ProxyCredentials { 2026 class _ProxyCredentials {
1983 _ProxyCredentials(this.host, this.port, this.realm, this.credentials); 2027 _ProxyCredentials(this.host, this.port, this.realm, this.credentials);
1984 2028
1985 _AuthenticationScheme get scheme => credentials.scheme; 2029 _AuthenticationScheme get scheme => credentials.scheme;
1986 2030
1987 bool applies(_Proxy proxy, _AuthenticationScheme scheme) { 2031 bool applies(_Proxy proxy, _AuthenticationScheme scheme) {
1988 return proxy.host == host && proxy.port == port; 2032 return proxy.host == host && proxy.port == port;
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
2040 2084
2041 2085
2042 class _HttpClientDigestCredentials 2086 class _HttpClientDigestCredentials
2043 extends _HttpClientCredentials 2087 extends _HttpClientCredentials
2044 implements HttpClientDigestCredentials { 2088 implements HttpClientDigestCredentials {
2045 _HttpClientDigestCredentials(this.username, 2089 _HttpClientDigestCredentials(this.username,
2046 this.password); 2090 this.password);
2047 2091
2048 _AuthenticationScheme get scheme => _AuthenticationScheme.DIGEST; 2092 _AuthenticationScheme get scheme => _AuthenticationScheme.DIGEST;
2049 2093
2094 String authorization(_Credentials credentials, HttpClientRequest request) {
2095 MD5 hasher = new MD5();
2096 hasher.add(request.method.codeUnits);
2097 hasher.add([_CharCode.COLON]);
2098 hasher.add(request.uri.path.codeUnits);
2099 var ha2 = CryptoUtils.bytesToHex(hasher.close());
2100
2101 String qop;
2102 String cnonce;
2103 String nc;
2104 var x;
2105 hasher = new MD5();
2106 hasher.add(credentials.ha1.codeUnits);
2107 hasher.add([_CharCode.COLON]);
2108 if (credentials.qop == "auth") {
2109 qop = credentials.qop;
2110 cnonce = CryptoUtils.bytesToHex(_IOCrypto.getRandomBytes(4));
2111 ++credentials.nonceCount;
2112 nc = credentials.nonceCount.toRadixString(16);
2113 nc = "00000000".substring(0, 8 - nc.length + 1) + nc;
2114 hasher.add(credentials.nonce.codeUnits);
2115 hasher.add([_CharCode.COLON]);
2116 hasher.add(nc.codeUnits);
2117 hasher.add([_CharCode.COLON]);
2118 hasher.add(cnonce.codeUnits);
2119 hasher.add([_CharCode.COLON]);
2120 hasher.add(credentials.qop.codeUnits);
2121 hasher.add([_CharCode.COLON]);
2122 hasher.add(ha2.codeUnits);
2123 } else {
2124 hasher.add(credentials.nonce.codeUnits);
2125 hasher.add([_CharCode.COLON]);
2126 hasher.add(ha2.codeUnits);
2127 }
2128 var response = CryptoUtils.bytesToHex(hasher.close());
2129
2130 StringBuffer buffer = new StringBuffer();
2131 buffer.write('Digest ');
2132 buffer.write('username="$username"');
2133 buffer.write(', realm="${credentials.realm}"');
2134 buffer.write(', nonce="${credentials.nonce}"');
2135 buffer.write(', uri="${request.uri.path}"');
2136 buffer.write(', algorithm="${credentials.algorithm}"');
2137 if (qop == "auth") {
2138 buffer.write(', qop="$qop"');
2139 buffer.write(', cnonce="$cnonce"');
2140 buffer.write(', nc="$nc"');
2141 }
2142 buffer.write(', response="$response"');
2143 return buffer.toString();
2144 }
2145
2050 void authorize(_Credentials credentials, HttpClientRequest request) { 2146 void authorize(_Credentials credentials, HttpClientRequest request) {
2051 // TODO(sgjesse): Implement!!! 2147 request.headers.set(HttpHeaders.AUTHORIZATION, authorization(credentials, re quest));
2052 throw new UnsupportedError("Digest authentication not yet supported");
2053 } 2148 }
2054 2149
2055 void authorizeProxy(_ProxyCredentials credentials, 2150 void authorizeProxy(_ProxyCredentials credentials,
2056 HttpClientRequest request) { 2151 HttpClientRequest request) {
2057 // TODO(sgjesse): Implement!!! 2152 // TODO(sgjesse): Implement!!!
2058 throw new UnsupportedError("Digest authentication not yet supported"); 2153 throw new UnsupportedError("Digest authentication not yet supported");
2059 } 2154 }
2060 2155
2061 String username; 2156 String username;
2062 String password; 2157 String password;
2063 } 2158 }
2064 2159
2065 2160
2066 class _RedirectInfo implements RedirectInfo { 2161 class _RedirectInfo implements RedirectInfo {
2067 const _RedirectInfo(int this.statusCode, 2162 const _RedirectInfo(int this.statusCode,
2068 String this.method, 2163 String this.method,
2069 Uri this.location); 2164 Uri this.location);
2070 final int statusCode; 2165 final int statusCode;
2071 final String method; 2166 final String method;
2072 final Uri location; 2167 final Uri location;
2073 } 2168 }
OLDNEW
« no previous file with comments | « sdk/lib/io/http.dart ('k') | tests/standalone/io/http_auth_digest_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698