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-aether/repo Commit: http://git-wip-us.apache.org/repos/asf/maven-aether/commit/f718cfb9 Tree: http://git-wip-us.apache.org/repos/asf/maven-aether/tree/f718cfb9 Diff: http://git-wip-us.apache.org/repos/asf/maven-aether/diff/f718cfb9 Branch: refs/heads/master Commit: f718cfb98d31b0188a165d9c5baa367dca59dc20 Parents: dae0615 Author: Benjamin Bentmann <bentm...@sonatype.com> Authored: Sun Sep 28 18:43:56 2014 +0200 Committer: Benjamin Bentmann <bentm...@sonatype.com> Committed: Sun Sep 28 18:43:56 2014 +0200 ---------------------------------------------------------------------- .../http/DemuxCredentialsProvider.java | 67 ++++++++++++++++++++ .../aether/transport/http/HttpTransporter.java | 26 +++++--- .../transport/http/HttpTransporterTest.java | 40 ++++++++++++ 3 files changed, 124 insertions(+), 9 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/maven-aether/blob/f718cfb9/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-aether/blob/f718cfb9/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 5ac9b8f..931e04e 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 @@ -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-aether/blob/f718cfb9/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 b4a38d6..4dbc730 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 @@ -1141,6 +1141,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 {