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

Unified 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, 8 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..31f2a489eb8368291fe54e74e50cfb261bfeac33 100644
--- a/sdk/lib/io/http_impl.dart
+++ b/sdk/lib/io/http_impl.dart
@@ -273,17 +273,14 @@ class _HttpClientResponse
Future<HttpClientResponse> _authenticate() {
Future<HttpClientResponse> retryWithCredentials(_Credentials cr) {
- if (cr != null) {
- // TODO(sgjesse): Support digest.
- if (cr.scheme == _AuthenticationScheme.BASIC) {
- // Drain body and retry.
- return fold(null, (x, y) {}).then((_) {
- return _httpClient._openUrlFromRequest(_httpRequest.method,
- _httpRequest.uri,
- _httpRequest)
- .then((request) => request.close());
- });
- }
+ if (cr != null && cr.scheme != _AuthenticationScheme.UNKNOWN) {
+ // Drain body and retry.
+ return fold(null, (x, y) {}).then((_) {
+ return _httpClient._openUrlFromRequest(_httpRequest.method,
+ _httpRequest.uri,
+ _httpRequest)
+ .then((request) => request.close());
+ });
}
// Fall through to here to perform normal response handling if
@@ -299,12 +296,34 @@ 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) {
+ // For basic authentication don't retry already used credentials
+ // as they must have already been added to the request causing
+ // this authenticate response.
+ if (cr.scheme == _AuthenticationScheme.BASIC && !cr.used) {
+ // Credentials where 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 &&
+ (header.parameters["algorithm"] == null ||
+ header.parameters["algorithm"].toLowerCase() == "md5")) {
+ // If the nonce is not set then this is the first authenticate
+ // response for these credentials.
+ // TODO(sgjesse): Check for changed nonce.
+ if (cr.nonce == null) {
+ // Set up authentication state.
+ cr.nonce = header.parameters["nonce"];
+ cr.algorithm = "MD5";
+ cr.qop = header.parameters["qop"];
+ cr.nonceCount = 0;
+ }
+ // Credentials where found, prepare for retrying the request.
+ return retryWithCredentials(cr);
+ }
}
// Ask for more credentials if none found or the one found has
@@ -1948,7 +1967,24 @@ class _AuthenticationScheme {
class _Credentials {
- _Credentials(this.uri, this.realm, this.credentials);
+ _Credentials(this.uri, this.realm, this.credentials) {
+ if (credentials.scheme == _AuthenticationScheme.DIGEST) {
+ // Calculate the H(A1) value once. 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.
+ _HttpClientDigestCredentials creds = credentials;
+ var hasher = new MD5();
+ hasher.add(_encodeString(creds.username));
+ hasher.add([_CharCode.COLON]);
+ hasher.add(realm.codeUnits);
+ hasher.add([_CharCode.COLON]);
+ hasher.add(_encodeString(creds.password));
+ ha1 = CryptoUtils.bytesToHex(hasher.close());
+ }
+ }
_AuthenticationScheme get scheme => credentials.scheme;
@@ -1963,6 +1999,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 +2015,11 @@ class _Credentials {
_HttpClientCredentials credentials;
// Digest specific fields.
+ String ha1;
String nonce;
String algorithm;
String qop;
+ int nonceCount;
}
@@ -2047,9 +2091,60 @@ class _HttpClientDigestCredentials
_AuthenticationScheme get scheme => _AuthenticationScheme.DIGEST;
+ String authorization(_Credentials credentials, HttpClientRequest request) {
+ 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));
+ ++credentials.nonceCount;
+ 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,
« 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