From 5d4e9ac850d45d6c4eaacce502993f74aff895d4 Mon Sep 17 00:00:00 2001 From: Jesse Wilson Date: Thu, 20 Sep 2012 20:31:13 -0400 Subject: [PATCH] Fix Authenticator callbacks to include full information. These were passing the wrong parameters for proxies, and not including enough information for origin servers. Related to: http://code.google.com/p/android/issues/detail?id=18856 --- .../net/http/HttpURLConnectionImpl.java | 15 ++- .../libcore/net/http/URLConnectionTest.java | 110 ++++++++++++------ 2 files changed, 89 insertions(+), 36 deletions(-) diff --git a/src/main/java/libcore/net/http/HttpURLConnectionImpl.java b/src/main/java/libcore/net/http/HttpURLConnectionImpl.java index bd5dbfb75634..e0c80a24b858 100644 --- a/src/main/java/libcore/net/http/HttpURLConnectionImpl.java +++ b/src/main/java/libcore/net/http/HttpURLConnectionImpl.java @@ -445,9 +445,18 @@ private String getAuthorizationCredentials(RawHeaders responseHeaders, String ch for (Challenge challenge : challenges) { // use the global authenticator to get the password - PasswordAuthentication auth = Authenticator.requestPasswordAuthentication( - getConnectToInetAddress(), getConnectToPort(), url.getProtocol(), - challenge.realm, challenge.scheme); + PasswordAuthentication auth; + if (responseHeaders.getResponseCode() == HTTP_PROXY_AUTH) { + InetSocketAddress proxyAddress = (InetSocketAddress) proxy.address(); + auth = Authenticator.requestPasswordAuthentication( + proxyAddress.getHostName(), getConnectToInetAddress(), + proxyAddress.getPort(), url.getProtocol(), challenge.realm, + challenge.scheme, url, Authenticator.RequestorType.PROXY); + } else { + auth = Authenticator.requestPasswordAuthentication( + url.getHost(), getConnectToInetAddress(), url.getPort(), url.getProtocol(), + challenge.realm, challenge.scheme, url, Authenticator.RequestorType.SERVER); + } if (auth == null) { continue; } diff --git a/src/test/java/libcore/net/http/URLConnectionTest.java b/src/test/java/libcore/net/http/URLConnectionTest.java index 723e32788a30..c5fe45566986 100644 --- a/src/test/java/libcore/net/http/URLConnectionTest.java +++ b/src/test/java/libcore/net/http/URLConnectionTest.java @@ -78,13 +78,6 @@ * Android's URLConnectionTest. */ public final class URLConnectionTest extends TestCase { - - private static final Authenticator SIMPLE_AUTHENTICATOR = new Authenticator() { - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication("username", "password".toCharArray()); - } - }; - /** base64("username:password") */ private static final String BASE_64_CREDENTIALS = "dXNlcm5hbWU6cGFzc3dvcmQ="; @@ -717,7 +710,7 @@ public void testProxyConnectIncludesProxyHeadersOnly() } public void testProxyAuthenticateOnConnect() throws Exception { - Authenticator.setDefault(SIMPLE_AUTHENTICATOR); + Authenticator.setDefault(new RecordingAuthenticator()); server.useHttps(sslContext.getSocketFactory(), true); server.enqueue(new MockResponse() .setResponseCode(407) @@ -1069,7 +1062,7 @@ private void testAuthenticateWithStreamingPost(StreamingMode streamingMode) thro server.enqueue(pleaseAuthenticate); server.play(); - Authenticator.setDefault(SIMPLE_AUTHENTICATOR); + Authenticator.setDefault(new RecordingAuthenticator()); OkHttpConnection connection = openConnection(server.getUrl("/")); connection.setDoOutput(true); byte[] requestBody = { 'A', 'B', 'C', 'D' }; @@ -1094,36 +1087,78 @@ private void testAuthenticateWithStreamingPost(StreamingMode streamingMode) thro } public void testNonStandardAuthenticationScheme() throws Exception { - RecordingAuthenticator authenticator = new RecordingAuthenticator(); - Authenticator.setDefault(authenticator); - MockResponse pleaseAuthenticate = new MockResponse() - .setResponseCode(401) - .addHeader("WWW-Authenticate: Foo") - .setBody("Please authenticate."); - server.enqueue(pleaseAuthenticate); - server.play(); - - OkHttpConnection connection = openConnection(server.getUrl("/")); - assertEquals(401, connection.getResponseCode()); - assertEquals(Collections.emptyList(), authenticator.calls); + List calls = authCallsForHeader("WWW-Authenticate: Foo"); + assertEquals(Collections.emptyList(), calls); } public void testNonStandardAuthenticationSchemeWithRealm() throws Exception { - RecordingAuthenticator authenticator = new RecordingAuthenticator(); + List calls = authCallsForHeader("WWW-Authenticate: Foo realm=\"Bar\""); + assertEquals(1, calls.size()); + String call = calls.get(0); + assertTrue(call, call.contains("scheme=Foo")); + assertTrue(call, call.contains("prompt=Bar")); + } + + // Digest auth is currently unsupported. Test that digest requests should fail reasonably. + // http://code.google.com/p/android/issues/detail?id=11140 + public void testDigestAuthentication() throws Exception { + List calls = authCallsForHeader("WWW-Authenticate: Digest " + + "realm=\"testrealm@host.com\", qop=\"auth,auth-int\", " + + "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", " + + "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\""); + assertEquals(1, calls.size()); + String call = calls.get(0); + assertTrue(call, call.contains("scheme=Digest")); + assertTrue(call, call.contains("prompt=testrealm@host.com")); + } + + public void testAllAttributesSetInServerAuthenticationCallbacks() throws Exception { + List calls = authCallsForHeader("WWW-Authenticate: Basic realm=\"Bar\""); + assertEquals(1, calls.size()); + URL url = server.getUrl("/"); + String call = calls.get(0); + assertTrue(call, call.contains("host=" + url.getHost())); + assertTrue(call, call.contains("port=" + url.getPort())); + assertTrue(call, call.contains("site=" + InetAddress.getAllByName(url.getHost())[0])); + assertTrue(call, call.contains("url=" + url)); + assertTrue(call, call.contains("type=" + Authenticator.RequestorType.SERVER)); + assertTrue(call, call.contains("prompt=Bar")); + assertTrue(call, call.contains("protocol=http")); + assertTrue(call, call.toLowerCase().contains("scheme=basic")); // lowercase for the RI. + } + + public void testAllAttributesSetInProxyAuthenticationCallbacks() throws Exception { + List calls = authCallsForHeader("Proxy-Authenticate: Basic realm=\"Bar\""); + assertEquals(1, calls.size()); + URL url = server.getUrl("/"); + String call = calls.get(0); + assertTrue(call, call.contains("host=" + url.getHost())); + assertTrue(call, call.contains("port=" + url.getPort())); + assertTrue(call, call.contains("site=" + InetAddress.getAllByName(url.getHost())[0])); + assertTrue(call, call.contains("url=http://android.com")); + assertTrue(call, call.contains("type=" + Authenticator.RequestorType.PROXY)); + assertTrue(call, call.contains("prompt=Bar")); + assertTrue(call, call.contains("protocol=http")); + assertTrue(call, call.toLowerCase().contains("scheme=basic")); // lowercase for the RI. + } + + private List authCallsForHeader(String authHeader) throws IOException { + boolean proxy = authHeader.startsWith("Proxy-"); + int responseCode = proxy ? 407 : 401; + RecordingAuthenticator authenticator = new RecordingAuthenticator(null); Authenticator.setDefault(authenticator); MockResponse pleaseAuthenticate = new MockResponse() - .setResponseCode(401) - .addHeader("WWW-Authenticate: Foo realm=\"Bar\"") + .setResponseCode(responseCode) + .addHeader(authHeader) .setBody("Please authenticate."); server.enqueue(pleaseAuthenticate); server.play(); - OkHttpConnection connection = openConnection(server.getUrl("/")); - assertEquals(401, connection.getResponseCode()); - assertEquals(1, authenticator.calls.size()); - String call = authenticator.calls.get(0); - assertTrue(call, call.contains("scheme=Foo")); - assertTrue(call, call.contains("prompt=Bar")); + OkHttpConnection connection = proxy + ? openConnection(new URL("http://android.com"), server.toProxyAddress()) + : openConnection(server.getUrl("/")); + assertEquals(responseCode, connection.getResponseCode()); + return authenticator.calls; } public void testSetValidRequestMethod() throws Exception { @@ -1283,7 +1318,7 @@ public void testAuthenticateWithPost() throws Exception { server.enqueue(new MockResponse().setBody("Successful auth!")); server.play(); - Authenticator.setDefault(SIMPLE_AUTHENTICATOR); + Authenticator.setDefault(new RecordingAuthenticator()); OkHttpConnection connection = openConnection(server.getUrl("/")); connection.setDoOutput(true); byte[] requestBody = { 'A', 'B', 'C', 'D' }; @@ -1318,7 +1353,7 @@ public void testAuthenticateWithGet() throws Exception { server.enqueue(new MockResponse().setBody("Successful auth!")); server.play(); - Authenticator.setDefault(SIMPLE_AUTHENTICATOR); + Authenticator.setDefault(new RecordingAuthenticator()); OkHttpConnection connection = openConnection(server.getUrl("/")); assertEquals("Successful auth!", readAscii(connection.getInputStream(), Integer.MAX_VALUE)); @@ -2226,6 +2261,15 @@ public boolean verify(String hostname, SSLSession session) { private static class RecordingAuthenticator extends Authenticator { private final List calls = new ArrayList(); + private final PasswordAuthentication authentication; + + public RecordingAuthenticator(PasswordAuthentication authentication) { + this.authentication = authentication; + } + + public RecordingAuthenticator() { + this(new PasswordAuthentication("username", "password".toCharArray())); + } @Override protected PasswordAuthentication getPasswordAuthentication() { this.calls.add("host=" + getRequestingHost() @@ -2236,7 +2280,7 @@ private static class RecordingAuthenticator extends Authenticator { + " prompt=" + getRequestingPrompt() + " protocol=" + getRequestingProtocol() + " scheme=" + getRequestingScheme()); - return null; + return authentication; } } }