]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-1943 Add proxy authentication support for update center
authorsimonbrandhof <simon.brandhof@gmail.com>
Tue, 9 Nov 2010 14:36:57 +0000 (14:36 +0000)
committersimonbrandhof <simon.brandhof@gmail.com>
Tue, 9 Nov 2010 14:36:57 +0000 (14:36 +0000)
sonar-application/src/main/assembly/conf/sonar.properties
sonar-plugin-api/src/main/java/org/sonar/api/utils/HttpDownloader.java
sonar-plugin-api/src/test/java/org/sonar/api/utils/HttpDownloaderTest.java
sonar-server/src/main/java/org/sonar/server/plugins/UpdateCenterClient.java
sonar-server/src/test/java/org/sonar/server/plugins/UpdateCenterClientTest.java

index 8702cdb73a756eb84ae1de0fe29585a1b3130519..6aeedbba69505efd1e64680435c3fbfeb0a351b1 100644 (file)
@@ -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
index 670707c3f0c3eeae4ae84ec4b2030773075d8117..7b0e96a11ca693c7882d238fc5e930288ad936d7 100644 (file)
  */
 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<String> descriptions = Lists.newArrayList();
+    List<Proxy> 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;
   }
 }
index 9b89c49ce7231ead4bcfb7323d8753360e1b4246..a61c858516cf62dbac6221cfef6e5350268ed094 100644 (file)
@@ -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));
   }
 }
index 84e2c8707ca8c0da5ae9e2c608d09c42f190e912..a85570f1a015b2741eb87201b21fd4df4bd7093a 100644 (file)
  */
 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<String> descriptions = Lists.newArrayList();
-      List<Proxy> 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");
-    }
-  }
 }
index 1186e26e25204a7333fa5c574f11c06366451267..1031c88fd2359658e883295b47864ee287cf494c 100644 (file)
@@ -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