From f24c8ebe314d5278287558577aafb790fea98bd4 Mon Sep 17 00:00:00 2001 From: simonbrandhof Date: Tue, 9 Nov 2010 14:36:57 +0000 Subject: [PATCH] SONAR-1943 Add proxy authentication support for update center --- .../src/main/assembly/conf/sonar.properties | 21 +++- .../org/sonar/api/utils/HttpDownloader.java | 110 +++++++++++++++--- .../sonar/api/utils/HttpDownloaderTest.java | 27 ++++- .../server/plugins/UpdateCenterClient.java | 44 ++----- .../plugins/UpdateCenterClientTest.java | 33 +----- 5 files changed, 146 insertions(+), 89 deletions(-) diff --git a/sonar-application/src/main/assembly/conf/sonar.properties b/sonar-application/src/main/assembly/conf/sonar.properties index 8702cdb73a7..6aeedbba695 100644 --- a/sonar-application/src/main/assembly/conf/sonar.properties +++ b/sonar-application/src/main/assembly/conf/sonar.properties @@ -112,9 +112,20 @@ sonar.jdbc.timeBetweenEvictionRunsMillis: 30000 #--------------------------------------------------------- # The Update Center requires an internet connection to request http://update.sonarsource.org -# If needed, HTTP proxy can be configured with standard Java environment variables : -Dhttp.proxyHost -# and -Dhttp.proxyPort. System configuration can also be used with -Djava.net.useSystemProxies=true. -# These properties can not be set in this file. - -# Update Center is activated by default. +# It is activated by default: #sonar.updatecenter.activate=true + +# HTTP proxy (default none) +#http.proxyHost= +#http.proxyPort= + +# NT domain name if NTLM proxy is used +#http.auth.ntlm.domain= + +# SOCKS proxy (default none) +#socksProxyHost= +#socksProxyPort= + +# proxy authentication. The 2 following properties are used for HTTP and SOCKS proxies. +#http.proxyUser= +#http.proxyPassword= \ No newline at end of file diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/HttpDownloader.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/HttpDownloader.java index 670707c3f0c..7b0e96a11ca 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/HttpDownloader.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/HttpDownloader.java @@ -19,6 +19,10 @@ */ package org.sonar.api.utils; +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.slf4j.LoggerFactory; @@ -30,25 +34,93 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URI; +import java.net.*; +import java.util.List; /** - * Simple class to download a file from a HTTP repository. - * + * This component downloads HTTP files. It currently does not reuse maven proxy configuration on the batch side. + * * @since 2.2 */ public class HttpDownloader implements BatchComponent, ServerComponent { public static final int TIMEOUT_MILLISECONDS = 20 * 1000; + + private String userAgent; + + public HttpDownloader(Server server, Configuration configuration) { + this(configuration, server.getVersion()); + } + + public HttpDownloader(Configuration configuration) { + this(configuration, null); + } + + /** + * for unit tests + */ + HttpDownloader() { + this(new PropertiesConfiguration(), null); + } - private Server server = null; + private HttpDownloader(Configuration configuration, String userAgent) { + initProxy(configuration); + initUserAgent(userAgent); + } + + private void initProxy(Configuration configuration) { + propagateProxySystemProperties(configuration); + if (requiresProxyAuthentication(configuration)) { + registerProxyCredentials(configuration); + } + } - public HttpDownloader(Server server) { - this.server = server; + private void initUserAgent(String sonarVersion) { + String userAgent = (sonarVersion == null ? "Sonar" : String.format("Sonar %s", sonarVersion)); + System.setProperty("http.agent", userAgent); + this.userAgent = userAgent; } - public HttpDownloader() { + public String getProxySynthesis(URI uri) { + return getProxySynthesis(uri, ProxySelector.getDefault()); + } + + static String getProxySynthesis(URI uri, ProxySelector proxySelector) { + List descriptions = Lists.newArrayList(); + List proxies = proxySelector.select(uri); + if (proxies.size() == 1 && proxies.get(0).type().equals(Proxy.Type.DIRECT)) { + descriptions.add("no proxy"); + } else { + for (Proxy proxy : proxies) { + if (!proxy.type().equals(Proxy.Type.DIRECT)) { + descriptions.add("proxy: " + proxy.address().toString()); + } + } + } + return Joiner.on(", ").join(descriptions); + } + + private void registerProxyCredentials(Configuration configuration) { + Authenticator.setDefault(new ProxyAuthenticator(configuration.getString("http.proxyUser"), configuration.getString("http.proxyPassword"))); + } + + private boolean requiresProxyAuthentication(Configuration configuration) { + return configuration.getString("http.proxyUser") != null; + } + + private void propagateProxySystemProperties(Configuration configuration) { + propagateSystemProperty(configuration, "http.proxyHost"); + propagateSystemProperty(configuration, "http.proxyPort"); + propagateSystemProperty(configuration, "http.nonProxyHosts"); + propagateSystemProperty(configuration, "http.auth.ntlm.domain"); + propagateSystemProperty(configuration, "socksProxyHost"); + propagateSystemProperty(configuration, "socksProxyPort"); + } + + private void propagateSystemProperty(Configuration configuration, String key) { + if (configuration.getString(key) != null) { + System.setProperty(key, configuration.getString(key)); + } } public void download(URI uri, File toFile) { @@ -63,7 +135,7 @@ public class HttpDownloader implements BatchComponent, ServerComponent { } catch (Exception e) { IOUtils.closeQuietly(output); FileUtils.deleteQuietly(toFile); - throw new SonarException("Fail to download the file: " + uri, e); + throw new SonarException("Fail to download the file: " + uri + getProxySynthesis(uri), e); } finally { IOUtils.closeQuietly(input); @@ -79,7 +151,7 @@ public class HttpDownloader implements BatchComponent, ServerComponent { return IOUtils.toByteArray(input); } catch (Exception e) { - throw new SonarException("Fail to download the file: " + uri, e); + throw new SonarException("Fail to download the file: " + uri + getProxySynthesis(uri), e); } finally { IOUtils.closeQuietly(input); @@ -92,22 +164,30 @@ public class HttpDownloader implements BatchComponent, ServerComponent { return connection.getInputStream(); } catch (Exception e) { - throw new SonarException("Fail to download the file: " + uri, e); + throw new SonarException("Fail to download the file: " + uri + getProxySynthesis(uri), e); } } private HttpURLConnection newHttpConnection(URI uri) throws IOException { - LoggerFactory.getLogger(getClass()).info("Download: " + uri); + LoggerFactory.getLogger(getClass()).info("Download: " + uri + getProxySynthesis(uri)); HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection(); connection.setConnectTimeout(TIMEOUT_MILLISECONDS); connection.setReadTimeout(TIMEOUT_MILLISECONDS); connection.setUseCaches(true); - connection.setRequestProperty("User-Agent", getUserAgent()); connection.setInstanceFollowRedirects(true); + connection.setRequestProperty("User-Agent", userAgent); return connection; } +} + +class ProxyAuthenticator extends Authenticator { + private PasswordAuthentication auth; + + ProxyAuthenticator(String user, String password) { + auth = new PasswordAuthentication(user, password == null ? new char[0] : password.toCharArray()); + } - private String getUserAgent() { - return (server != null ? "Sonar " + server.getVersion() : "Sonar"); + protected PasswordAuthentication getPasswordAuthentication() { + return auth; } } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/HttpDownloaderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/HttpDownloaderTest.java index 9b89c49ce72..a61c858516c 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/utils/HttpDownloaderTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/utils/HttpDownloaderTest.java @@ -19,6 +19,7 @@ */ package org.sonar.api.utils; +import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.junit.After; @@ -29,14 +30,15 @@ import org.sonar.api.platform.Server; import java.io.File; import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; +import java.net.*; +import java.util.Arrays; import java.util.Properties; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static org.junit.internal.matchers.StringContains.containsString; +import static org.mockito.Matchers.anyObject; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -104,7 +106,7 @@ public class HttpDownloaderTest { Server server = mock(Server.class); when(server.getVersion()).thenReturn("2.2"); - byte[] bytes = new HttpDownloader(server).download(new URI(baseUrl)); + byte[] bytes = new HttpDownloader(server, new PropertiesConfiguration()).download(new URI(baseUrl)); Properties props = new Properties(); props.load(IOUtils.toInputStream(new String(bytes))); assertThat(props.getProperty("agent"), is("Sonar 2.2")); @@ -114,6 +116,25 @@ public class HttpDownloaderTest { public void followRedirect() throws URISyntaxException { byte[] bytes = new HttpDownloader().download(new URI(baseUrl + "/redirect/")); assertThat(new String(bytes), containsString("count")); + } + + @Test + public void shouldGetDirectProxySynthesis() throws URISyntaxException { + ProxySelector proxySelector = mock(ProxySelector.class); + when(proxySelector.select((URI)anyObject())).thenReturn(Arrays.asList(Proxy.NO_PROXY)); + assertThat(HttpDownloader.getProxySynthesis(new URI("http://an_url"), proxySelector), is("no proxy")); + } + + @Test + public void shouldGetProxySynthesis() throws URISyntaxException { + ProxySelector proxySelector = mock(ProxySelector.class); + when(proxySelector.select((URI)anyObject())).thenReturn(Arrays.asList((Proxy)new FakeProxy())); + assertThat(HttpDownloader.getProxySynthesis(new URI("http://an_url"), proxySelector), is("proxy: http://proxy_url:4040")); + } +} +class FakeProxy extends Proxy { + public FakeProxy() { + super(Type.HTTP, new InetSocketAddress("http://proxy_url", 4040)); } } diff --git a/sonar-server/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java b/sonar-server/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java index 84e2c8707ca..a85570f1a01 100644 --- a/sonar-server/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java +++ b/sonar-server/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java @@ -19,25 +19,19 @@ */ package org.sonar.server.plugins; -import com.google.common.base.Joiner; -import com.google.common.collect.Lists; import org.apache.commons.configuration.Configuration; import org.apache.commons.io.IOUtils; import org.slf4j.LoggerFactory; import org.sonar.api.ServerComponent; import org.sonar.api.utils.HttpDownloader; import org.sonar.api.utils.Logs; -import org.sonar.api.utils.SonarException; import org.sonar.updatecenter.common.UpdateCenter; import org.sonar.updatecenter.common.UpdateCenterDeserializer; import java.io.InputStream; -import java.net.Proxy; -import java.net.ProxySelector; import java.net.URI; import java.net.URISyntaxException; import java.util.Date; -import java.util.List; import java.util.Properties; /** @@ -51,7 +45,7 @@ public class UpdateCenterClient implements ServerComponent { public static final String DEFAULT_URL = "http://update.sonarsource.org/update-center.properties"; public static final int PERIOD_IN_MILLISECONDS = 60 * 60 * 1000; - private String url; + private URI uri; private UpdateCenter center = null; private long lastRefreshDate = 0; private HttpDownloader downloader; @@ -59,14 +53,14 @@ public class UpdateCenterClient implements ServerComponent { /** * for unit tests */ - UpdateCenterClient(HttpDownloader downloader, String url) { + UpdateCenterClient(HttpDownloader downloader, URI uri) { this.downloader = downloader; - this.url = url; - Logs.INFO.info("Update center: " + url + " (" + sumUpProxyConfiguration(url) + ")"); + this.uri = uri; + Logs.INFO.info("Update center: " + uri + " (" + downloader.getProxySynthesis(uri) + ")"); } - public UpdateCenterClient(HttpDownloader downloader, Configuration configuration) { - this(downloader, configuration.getString(URL_PROPERTY, DEFAULT_URL)); + public UpdateCenterClient(HttpDownloader downloader, Configuration configuration) throws URISyntaxException { + this(downloader, new URI(configuration.getString(URL_PROPERTY, DEFAULT_URL))); } public UpdateCenter getCenter() { @@ -92,7 +86,7 @@ public class UpdateCenterClient implements ServerComponent { private UpdateCenter download() { InputStream input = null; try { - input = downloader.openStream(new URI(url)); + input = downloader.openStream(uri); if (input != null) { Properties properties = new Properties(); properties.load(input); @@ -107,28 +101,4 @@ public class UpdateCenterClient implements ServerComponent { } return null; } - - private static String sumUpProxyConfiguration(String url) { - return sumUpProxyConfiguration(url, ProxySelector.getDefault()); - } - - static String sumUpProxyConfiguration(String url, ProxySelector proxySelector) { - try { - List descriptions = Lists.newArrayList(); - List proxies = proxySelector.select(new URI(url)); - if (proxies.size() == 1 && proxies.get(0).type().equals(Proxy.Type.DIRECT)) { - descriptions.add("no HTTP proxy"); - } else { - for (Proxy proxy : proxies) { - if (!proxy.type().equals(Proxy.Type.DIRECT)) { - descriptions.add("proxy: " + proxy.address().toString()); - } - } - } - return Joiner.on(", ").join(descriptions); - - } catch (URISyntaxException e) { - throw new SonarException("Can not load configuration of HTTP proxies"); - } - } } diff --git a/sonar-server/src/test/java/org/sonar/server/plugins/UpdateCenterClientTest.java b/sonar-server/src/test/java/org/sonar/server/plugins/UpdateCenterClientTest.java index 1186e26e252..1031c88fd23 100644 --- a/sonar-server/src/test/java/org/sonar/server/plugins/UpdateCenterClientTest.java +++ b/sonar-server/src/test/java/org/sonar/server/plugins/UpdateCenterClientTest.java @@ -24,15 +24,13 @@ import org.junit.Before; import org.junit.Test; import org.sonar.api.utils.HttpDownloader; import org.sonar.api.utils.SonarException; -import org.sonar.server.plugins.UpdateCenterClient; import org.sonar.updatecenter.common.UpdateCenter; import org.sonar.updatecenter.common.Version; -import java.net.*; -import java.util.Arrays; +import java.net.URI; +import java.net.URISyntaxException; import static junit.framework.Assert.assertNull; -import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static org.junit.internal.matchers.IsCollectionContaining.hasItems; import static org.mockito.Matchers.anyObject; @@ -47,7 +45,7 @@ public class UpdateCenterClientTest { @Before public void startServer() throws Exception { downloader = mock(HttpDownloader.class); - client = new UpdateCenterClient(downloader, BASE_URL); + client = new UpdateCenterClient(downloader, new URI(BASE_URL)); } @Test @@ -84,27 +82,4 @@ public class UpdateCenterClientTest { verify(downloader, times(2)).openStream(new URI(BASE_URL)); } - - @Test - public void shouldSumUpDirectProxyConfiguration() { - ProxySelector proxySelector = mock(ProxySelector.class); - when(proxySelector.select((URI)anyObject())).thenReturn(Arrays.asList(Proxy.NO_PROXY)); - assertThat(UpdateCenterClient.sumUpProxyConfiguration("http://updatecenter", proxySelector), is("no HTTP proxy")); - } - - @Test - public void shouldSumUpProxyConfiguration() { - ProxySelector proxySelector = mock(ProxySelector.class); - when(proxySelector.select((URI)anyObject())).thenReturn(Arrays.asList((Proxy)new FakeProxy())); - assertThat(UpdateCenterClient.sumUpProxyConfiguration("http://updatecenter", proxySelector), is("proxy: http://updatecenter:80")); - } - - -} - -class FakeProxy extends Proxy { - - public FakeProxy() { - super(Type.HTTP, new InetSocketAddress("http://updatecenter", 80)); - } -} +} \ No newline at end of file -- 2.39.5