Bug 445283 - HTTP transporter can mistake server authentication for proxy authentication
Stored server and proxy credentials in separate providers and used new DemuxCredentialsProvider to delegate to one of those providers depending on host in auth request Project: http://git-wip-us.apache.org/repos/asf/maven-resolver/repo Commit: http://git-wip-us.apache.org/repos/asf/maven-resolver/commit/535df408 Tree: http://git-wip-us.apache.org/repos/asf/maven-resolver/tree/535df408 Diff: http://git-wip-us.apache.org/repos/asf/maven-resolver/diff/535df408 Branch: refs/heads/master Commit: 535df4084a29e9aa91a7d0bbeac5a5e6e7948454 Parents: e114670 Author: Benjamin Bentmann <bentm...@sonatype.com> Authored: Tue Nov 11 15:00:35 2014 +0100 Committer: Benjamin Bentmann <bentm...@sonatype.com> Committed: Tue Nov 11 15:00:35 2014 +0100 ---------------------------------------------------------------------- .../http/DemuxCredentialsProvider.java | 67 ++++++++++++++++++++ .../aether/transport/http/HttpTransporter.java | 28 +++++--- .../transport/http/HttpTransporterTest.java | 42 +++++++++++- 3 files changed, 126 insertions(+), 11 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/535df408/aether-transport-http/src/main/java/org/eclipse/aether/transport/http/DemuxCredentialsProvider.java ---------------------------------------------------------------------- diff --git a/aether-transport-http/src/main/java/org/eclipse/aether/transport/http/DemuxCredentialsProvider.java b/aether-transport-http/src/main/java/org/eclipse/aether/transport/http/DemuxCredentialsProvider.java new file mode 100644 index 0000000..4b89639 --- /dev/null +++ b/aether-transport-http/src/main/java/org/eclipse/aether/transport/http/DemuxCredentialsProvider.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2014 Sonatype, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.aether.transport.http; + +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.Credentials; +import org.apache.http.client.CredentialsProvider; + +/** + * Credentials provider that helps to isolate server from proxy credentials. Apache HttpClient uses a single provider + * for both server and proxy auth, using the auth scope (host, port, etc.) to select the proper credentials. With regard + * to redirects, we use an auth scope for server credentials that's not specific enough to not be mistaken for proxy + * auth. This provider helps to maintain the proper isolation. + */ +final class DemuxCredentialsProvider + implements CredentialsProvider +{ + + private final CredentialsProvider serverCredentialsProvider; + + private final CredentialsProvider proxyCredentialsProvider; + + private final HttpHost proxy; + + public DemuxCredentialsProvider( CredentialsProvider serverCredentialsProvider, + CredentialsProvider proxyCredentialsProvider, HttpHost proxy ) + { + this.serverCredentialsProvider = serverCredentialsProvider; + this.proxyCredentialsProvider = proxyCredentialsProvider; + this.proxy = proxy; + } + + private CredentialsProvider getDelegate( AuthScope authScope ) + { + if ( proxy.getPort() == authScope.getPort() && proxy.getHostName().equalsIgnoreCase( authScope.getHost() ) ) + { + return proxyCredentialsProvider; + } + return serverCredentialsProvider; + } + + public Credentials getCredentials( AuthScope authScope ) + { + return getDelegate( authScope ).getCredentials( authScope ); + } + + public void setCredentials( AuthScope authScope, Credentials credentials ) + { + getDelegate( authScope ).setCredentials( authScope, credentials ); + } + + public void clear() + { + serverCredentialsProvider.clear(); + proxyCredentialsProvider.clear(); + } + +} http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/535df408/aether-transport-http/src/main/java/org/eclipse/aether/transport/http/HttpTransporter.java ---------------------------------------------------------------------- diff --git a/aether-transport-http/src/main/java/org/eclipse/aether/transport/http/HttpTransporter.java b/aether-transport-http/src/main/java/org/eclipse/aether/transport/http/HttpTransporter.java index 1eda25e..001d760 100644 --- a/aether-transport-http/src/main/java/org/eclipse/aether/transport/http/HttpTransporter.java +++ b/aether-transport-http/src/main/java/org/eclipse/aether/transport/http/HttpTransporter.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013 Sonatype, Inc. + * Copyright (c) 2013, 2014 Sonatype, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -32,6 +32,7 @@ import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.auth.AuthScope; import org.apache.http.auth.params.AuthParams; +import org.apache.http.client.CredentialsProvider; import org.apache.http.client.HttpClient; import org.apache.http.client.HttpResponseException; import org.apache.http.client.methods.HttpGet; @@ -134,13 +135,7 @@ final class HttpTransporter configureClient( client.getParams(), session, repository, proxy ); - DeferredCredentialsProvider credsProvider = new DeferredCredentialsProvider(); - addCredentials( credsProvider, server.getHostName(), AuthScope.ANY_PORT, repoAuthContext ); - if ( proxy != null ) - { - addCredentials( credsProvider, proxy.getHostName(), proxy.getPort(), proxyAuthContext ); - } - client.setCredentialsProvider( credsProvider ); + client.setCredentialsProvider( toCredentialsProvider( server, repoAuthContext, proxy, proxyAuthContext ) ); this.client = new DecompressingHttpClient( client ); } @@ -182,9 +177,21 @@ final class HttpTransporter ConfigurationProperties.USER_AGENT ) ); } - private static void addCredentials( DeferredCredentialsProvider provider, String host, int port, - AuthenticationContext ctx ) + private static CredentialsProvider toCredentialsProvider( HttpHost server, AuthenticationContext serverAuthCtx, + HttpHost proxy, AuthenticationContext proxyAuthCtx ) + { + CredentialsProvider provider = toCredentialsProvider( server.getHostName(), AuthScope.ANY_PORT, serverAuthCtx ); + if ( proxy != null ) + { + CredentialsProvider p = toCredentialsProvider( proxy.getHostName(), proxy.getPort(), proxyAuthCtx ); + provider = new DemuxCredentialsProvider( provider, p, proxy ); + } + return provider; + } + + private static CredentialsProvider toCredentialsProvider( String host, int port, AuthenticationContext ctx ) { + DeferredCredentialsProvider provider = new DeferredCredentialsProvider(); if ( ctx != null ) { AuthScope basicScope = new AuthScope( host, port ); @@ -193,6 +200,7 @@ final class HttpTransporter AuthScope ntlmScope = new AuthScope( host, port, AuthScope.ANY_REALM, "ntlm" ); provider.setCredentials( ntlmScope, new DeferredCredentialsProvider.NtlmFactory( ctx ) ); } + return provider; } LocalState getState() http://git-wip-us.apache.org/repos/asf/maven-resolver/blob/535df408/aether-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpTransporterTest.java ---------------------------------------------------------------------- diff --git a/aether-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpTransporterTest.java b/aether-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpTransporterTest.java index 16fe0c4..1c85c38 100644 --- a/aether-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpTransporterTest.java +++ b/aether-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpTransporterTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013 Sonatype, Inc. + * Copyright (c) 2013, 2014 Sonatype, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -1042,6 +1042,46 @@ public class HttpTransporterTest } @Test + public void testServerAuthScope_NotUsedForProxy() + throws Exception + { + String username = "testuser", password = "testpass"; + httpServer.setProxyAuthentication( username, password ); + auth = new AuthenticationBuilder().addUsername( username ).addPassword( password ).build(); + proxy = new Proxy( Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort() ); + newTransporter( "http://" + httpServer.getHost() + ":12/" ); + try + { + transporter.get( new GetTask( URI.create( "repo/file.txt" ) ) ); + fail( "Server auth must not be used as proxy auth" ); + } + catch ( HttpResponseException e ) + { + assertEquals( 407, e.getStatusCode() ); + } + } + + @Test + public void testProxyAuthScope_NotUsedForServer() + throws Exception + { + String username = "testuser", password = "testpass"; + httpServer.setAuthentication( username, password ); + Authentication auth = new AuthenticationBuilder().addUsername( username ).addPassword( password ).build(); + proxy = new Proxy( Proxy.TYPE_HTTP, httpServer.getHost(), httpServer.getHttpPort(), auth ); + newTransporter( "http://" + httpServer.getHost() + ":12/" ); + try + { + transporter.get( new GetTask( URI.create( "repo/file.txt" ) ) ); + fail( "Proxy auth must not be used as server auth" ); + } + catch ( HttpResponseException e ) + { + assertEquals( 401, e.getStatusCode() ); + } + } + + @Test public void testAuthSchemeReuse() throws Exception {