Index: sdk/lib/io/http_impl.dart |
diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart |
index ee855ec8661e76acc5bd920272ab9938bce258f8..8ee2458208dc720ce0e79171e764abeab90cdcec 100644 |
--- a/sdk/lib/io/http_impl.dart |
+++ b/sdk/lib/io/http_impl.dart |
@@ -274,8 +274,7 @@ class _HttpClientResponse |
Future<HttpClientResponse> _authenticate() { |
Future<HttpClientResponse> retryWithCredentials(_Credentials cr) { |
if (cr != null) { |
- // TODO(sgjesse): Support digest. |
- if (cr.scheme == _AuthenticationScheme.BASIC) { |
+ if (cr.scheme != _AuthenticationScheme.UNKNOWN) { |
Anders Johnsen
2013/05/02 19:10:19
Merge ifs.
Søren Gjesse
2013/05/03 08:10:36
Done.
|
// Drain body and retry. |
return fold(null, (x, y) {}).then((_) { |
return _httpClient._openUrlFromRequest(_httpRequest.method, |
@@ -299,12 +298,28 @@ class _HttpClientResponse |
new _AuthenticationScheme.fromString(header.value); |
String realm = header.parameters["realm"]; |
- // See if any credentials are available. |
+ // See if any matching credentials are available. |
_Credentials cr = _httpClient._findCredentials(_httpRequest.uri, scheme); |
+ if (cr != null) { |
+ if (cr.scheme == _AuthenticationScheme.BASIC && !cr.used) { |
+ // If credentials found prepare for retrying the request. |
+ return retryWithCredentials(cr); |
+ } |
- if (cr != null && !cr.used) { |
- // If credentials found prepare for retrying the request. |
- return retryWithCredentials(cr); |
+ // Digest authentication only supports the MD5 algorithm. |
+ if (cr.scheme == _AuthenticationScheme.DIGEST && |
Anders Johnsen
2013/05/02 19:10:19
No used check? If missing, maybe move it to line 3
Søren Gjesse
2013/05/03 08:10:36
The checking for whether the nonce is set works li
|
+ (header.parameters["algorithm"] == null || |
Anders Johnsen
2013/05/02 19:10:19
!= String, or do we always know it's either null o
Søren Gjesse
2013/05/03 08:10:36
Done.
Søren Gjesse
2013/05/03 08:10:36
The HeaderValue only adds Strings to the map.
|
+ header.parameters["algorithm"].toLowerCase() == "md5")) { |
+ if (cr.nonce == null) { |
+ // Set up authentication state. |
+ cr.nonce = header.parameters["nonce"]; |
+ cr.algorithm = "MD5"; |
+ cr.qop = header.parameters["qop"]; |
+ cr.nonceCount = 0; |
+ } |
+ // If credentials found prepare for retrying the request. |
+ return retryWithCredentials(cr); |
+ } |
} |
// Ask for more credentials if none found or the one found has |
@@ -1948,7 +1963,16 @@ class _AuthenticationScheme { |
class _Credentials { |
- _Credentials(this.uri, this.realm, this.credentials); |
+ _Credentials(this.uri, this.realm, this.credentials) { |
+ // Calculate the H(A1) value once. |
+ var hasher = new MD5(); |
+ hasher.add(credentials.username.codeUnits); |
Anders Johnsen
2013/05/02 19:10:19
Should we do any String encoding here, and below?
Søren Gjesse
2013/05/03 08:10:36
Added UTF-8 encoding and moved the coment from lin
|
+ hasher.add([_CharCode.COLON]); |
+ hasher.add(realm.codeUnits); |
+ hasher.add([_CharCode.COLON]); |
+ hasher.add(credentials.password.codeUnits); |
+ ha1 = CryptoUtils.bytesToHex(hasher.close()); |
+ } |
_AuthenticationScheme get scheme => credentials.scheme; |
@@ -1963,6 +1987,12 @@ class _Credentials { |
} |
void authorize(HttpClientRequest request) { |
+ // Digest credentials cannot be used without a nonce from the |
+ // server. |
+ if (credentials.scheme == _AuthenticationScheme.DIGEST && |
+ nonce == null) { |
+ return; |
+ } |
credentials.authorize(this, request); |
used = true; |
} |
@@ -1973,9 +2003,11 @@ class _Credentials { |
_HttpClientCredentials credentials; |
// Digest specific fields. |
+ String ha1; |
String nonce; |
String algorithm; |
String qop; |
+ int nonceCount; |
} |
@@ -2047,9 +2079,65 @@ class _HttpClientDigestCredentials |
_AuthenticationScheme get scheme => _AuthenticationScheme.DIGEST; |
+ String authorization(_Credentials credentials, HttpClientRequest request) { |
+ // There is no mentioning of username/password encoding in RFC |
+ // 2617. However there is an open draft for adding an additional |
+ // accept-charset parameter to the WWW-Authenticate and |
+ // Proxy-Authenticate headers, see |
+ // http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For |
+ // now always use UTF-8 encoding. |
+ MD5 hasher = new MD5(); |
+ hasher.add(request.method.codeUnits); |
+ hasher.add([_CharCode.COLON]); |
+ hasher.add(request.uri.path.codeUnits); |
+ var ha2 = CryptoUtils.bytesToHex(hasher.close()); |
+ |
+ String qop; |
+ String cnonce; |
+ String nc; |
+ var x; |
+ hasher = new MD5(); |
+ hasher.add(credentials.ha1.codeUnits); |
+ hasher.add([_CharCode.COLON]); |
+ if (credentials.qop == "auth") { |
+ qop = credentials.qop; |
+ cnonce = CryptoUtils.bytesToHex(_IOCrypto.getRandomBytes(4)); |
+ nc = (++credentials.nonceCount).toRadixString(16); |
+ nc = "00000000".substring(0, 8 - nc.length + 1) + nc; |
+ hasher.add(credentials.nonce.codeUnits); |
+ hasher.add([_CharCode.COLON]); |
+ hasher.add(nc.codeUnits); |
+ hasher.add([_CharCode.COLON]); |
+ hasher.add(cnonce.codeUnits); |
+ hasher.add([_CharCode.COLON]); |
+ hasher.add(credentials.qop.codeUnits); |
+ hasher.add([_CharCode.COLON]); |
+ hasher.add(ha2.codeUnits); |
+ } else { |
+ hasher.add(credentials.nonce.codeUnits); |
+ hasher.add([_CharCode.COLON]); |
+ hasher.add(ha2.codeUnits); |
+ } |
+ var response = CryptoUtils.bytesToHex(hasher.close()); |
+ |
+ StringBuffer buffer = new StringBuffer(); |
+ buffer.write('Digest '); |
+ buffer.write('username="$username"'); |
+ buffer.write(', realm="${credentials.realm}"'); |
+ buffer.write(', nonce="${credentials.nonce}"'); |
+ buffer.write(', uri="${request.uri.path}"'); |
+ buffer.write(', algorithm="${credentials.algorithm}"'); |
+ if (qop == "auth") { |
+ buffer.write(', qop="$qop"'); |
+ buffer.write(', cnonce="$cnonce"'); |
+ buffer.write(', nc="$nc"'); |
+ } |
+ buffer.write(', response="$response"'); |
+ return buffer.toString(); |
+ } |
+ |
void authorize(_Credentials credentials, HttpClientRequest request) { |
- // TODO(sgjesse): Implement!!! |
- throw new UnsupportedError("Digest authentication not yet supported"); |
+ request.headers.set(HttpHeaders.AUTHORIZATION, authorization(credentials, request)); |
} |
void authorizeProxy(_ProxyCredentials credentials, |