]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-3895 User resource key and optional authent.
authorDavid Gageot <david@gageot.net>
Wed, 24 Oct 2012 14:59:13 +0000 (16:59 +0200)
committerDavid Gageot <david@gageot.net>
Wed, 24 Oct 2012 17:20:27 +0000 (19:20 +0200)
sonar-batch/src/main/java/org/sonar/batch/local/LocalDatabase.java
sonar-core/src/main/java/org/sonar/core/persistence/LocalDatabaseFactory.java
sonar-core/src/test/java/org/sonar/core/persistence/LocalDatabaseFactoryTest.java
sonar-core/src/test/resources/org/sonar/core/persistence/LocalDatabaseFactoryTest/should_create_database.xml
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
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/webapp/WEB-INF/app/controllers/api/synchro_controller.rb

index 51d75d5373016e14b0c2da1b6568781828767291..199541c12a04a7044e8b80c869078fecfef62562 100644 (file)
  */
 package org.sonar.batch.local;
 
+import com.google.common.base.Strings;
 import com.google.common.io.Files;
 import com.google.common.io.InputSupplier;
 import org.sonar.api.BatchComponent;
+import org.sonar.api.CoreProperties;
 import org.sonar.api.config.Settings;
 import org.sonar.api.database.DatabaseProperties;
 import org.sonar.api.platform.Server;
@@ -49,14 +51,12 @@ public class LocalDatabase implements BatchComponent {
   private final LocalMode localMode;
   private final Settings settings;
   private final Server server;
-  private final HttpDownloader httpDownloader;
   private final TempDirectories tempDirectories;
 
-  public LocalDatabase(LocalMode localMode, Settings settings, Server server, HttpDownloader httpDownloader, TempDirectories tempDirectories) {
+  public LocalDatabase(LocalMode localMode, Settings settings, Server server, TempDirectories tempDirectories) {
     this.localMode = localMode;
     this.settings = settings;
     this.server = server;
-    this.httpDownloader = httpDownloader;
     this.tempDirectories = tempDirectories;
   }
 
@@ -73,14 +73,27 @@ public class LocalDatabase implements BatchComponent {
   }
 
   private void downloadDatabase(File toFile) {
+    String login = settings.getString(CoreProperties.LOGIN);
+    String password = settings.getString(CoreProperties.PASSWORD);
+    String resourceKey = settings.getString("sonar.resource");
+    if (null == resourceKey) {
+      throw new SonarException("No resource key was provided using sonar.resource property");
+    }
+
+    URI uri = URI.create(server.getURL() + API_SYNCHRO + "?resource=" + resourceKey);
+
+    HttpDownloader.BaseHttpDownloader downloader = new HttpDownloader.BaseHttpDownloader(settings, null);
+    InputSupplier<InputStream> inputSupplier;
+    if (Strings.isNullOrEmpty(login)) {
+      inputSupplier = downloader.newInputSupplier(uri);
+    } else {
+      inputSupplier = downloader.newInputSupplier(uri, login, password);
+    }
+
     try {
-      Files.copy(new InputSupplier<InputStream>() {
-        public InputStream getInput() {
-          return httpDownloader.openStream(URI.create(server.getURL() + API_SYNCHRO));
-        }
-      }, toFile);
+      Files.copy(inputSupplier, toFile);
     } catch (IOException e) {
-      throw new SonarException("Unable to download database", e);
+      throw new SonarException("Unable to save local database to file: " + toFile, e);
     }
   }
 
index a2e265bd5cd04f0c94b8276734dca7a4b12c3c8d..edebb896eef141fa05b249112bc8251c78ef0392 100644 (file)
@@ -46,12 +46,12 @@ public class LocalDatabaseFactory implements ServerComponent {
     this.serverFileSystem = serverFileSystem;
   }
 
-  public byte[] createDatabaseForLocalMode() {
+  public byte[] createDatabaseForLocalMode(int resourceId) {
     String name = serverFileSystem.getTempDir().getAbsolutePath() + "db-" + System.nanoTime();
 
     try {
       BasicDataSource destination = create(DIALECT, DRIVER, USER, PASSWORD, URL + name);
-      copy(database.getDataSource(), destination);
+      copy(database.getDataSource(), destination, resourceId);
       close(destination);
 
       return dbFileContent(name);
@@ -60,8 +60,10 @@ public class LocalDatabaseFactory implements ServerComponent {
     }
   }
 
-  private void copy(DataSource source, DataSource dest) {
-    new DbTemplate().copyTable(source, dest, "PROPERTIES", "SELECT * FROM PROPERTIES WHERE (USER_ID IS NULL) AND (RESOURCE_ID IS NULL) AND NOT (PROP_KEY LIKE '%.secured')")
+  private void copy(DataSource source, DataSource dest, int resourceId) {
+    new DbTemplate()
+        .copyTable(source, dest, "PROPERTIES",
+            "SELECT * FROM PROPERTIES WHERE (((USER_ID IS NULL) AND (RESOURCE_ID IS NULL)) OR (RESOURCE_ID='" + resourceId + "')) AND NOT (PROP_KEY LIKE '%.secured')")
         .copyTable(source, dest, "RULES_PROFILES", "SELECT * FROM RULES_PROFILES")
         .copyTable(source, dest, "RULES", "SELECT * FROM RULES")
         .copyTable(source, dest, "RULES_PARAMETERS", "SELECT * FROM RULES_PARAMETERS")
index 3486fb9638a13248e594d9b2a1bbdde87a86b911..3eb0d75c30000ba615beb83b38a2338a1a0a564e 100644 (file)
@@ -63,10 +63,10 @@ public class LocalDatabaseFactoryTest extends AbstractDaoTestCase {
 
     when(serverFileSystem.getTempDir()).thenReturn(temporaryFolder.getRoot());
 
-    byte[] database = localDatabaseFactory.createDatabaseForLocalMode();
+    byte[] database = localDatabaseFactory.createDatabaseForLocalMode(1);
     dataSource = createDatabase(database);
 
-    assertThat(rowCount("PROPERTIES")).isEqualTo(1);
+    assertThat(rowCount("PROPERTIES")).isEqualTo(2);
     assertThat(rowCount("PROJECTS")).isZero();
   }
 
index 281958780db52e20136303129e3aead0c4a0562b..3034397311615b5dad753e348180591b7941ae68 100644 (file)
@@ -1,6 +1,7 @@
 <dataset>
   <properties id="1" prop_key="resourceProperty" text_value="value1" resource_id="1" user_id="[null]"/>
-  <properties id="2" prop_key="globalProperty" text_value="value2" resource_id="[null]" user_id="[null]"/>
-  <properties id="3" prop_key="userProperty" text_value="value3" resource_id="[null]" user_id="1"/>
-  <properties id="4" prop_key="property.secured" text_value="value4" resource_id="[null]" user_id="[null]"/>
+  <properties id="2" prop_key="resourceProperty" text_value="value2" resource_id="2" user_id="[null]"/>
+  <properties id="3" prop_key="globalProperty" text_value="value3" resource_id="[null]" user_id="[null]"/>
+  <properties id="4" prop_key="userProperty" text_value="value4" resource_id="[null]" user_id="1"/>
+  <properties id="5" prop_key="property.secured" text_value="value5" resource_id="[null]" user_id="[null]"/>
 </dataset>
\ No newline at end of file
index 05e6c192ae857d2753bf30d51ed5601f28519486..1e86dd8947f4437fbf036b884b50fa3495c1deea 100644 (file)
@@ -334,4 +334,14 @@ public interface CoreProperties {
    * @since 3.3
    */
   String LINKS_SOURCES_DEV = "sonar.links.scm_dev";
+
+  /**
+   * @since 3.4
+   */
+  String LOGIN = "sonar.login";
+
+  /**
+   * @since 3.4
+   */
+  String PASSWORD = "sonar.password";
 }
index 7b5762660600bfa1e220f88717a50210962fa5ef..0f29541dc300ed86541e36c1795f3147cc6da317 100644 (file)
 package org.sonar.api.utils;
 
 import com.google.common.annotations.VisibleForTesting;
-
 import com.google.common.base.Joiner;
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.io.ByteStreams;
 import com.google.common.io.CharStreams;
 import com.google.common.io.Files;
 import com.google.common.io.InputSupplier;
+import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.io.FileUtils;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.BatchComponent;
@@ -53,75 +54,16 @@ import java.util.List;
  * @since 2.2
  */
 public class HttpDownloader extends UriReader.SchemeProcessor implements BatchComponent, ServerComponent {
-
   public static final int TIMEOUT_MILLISECONDS = 20 * 1000;
-  private static final List<String> PROXY_SETTINGS = ImmutableList.of(
-      "http.proxyHost", "http.proxyPort", "http.nonProxyHosts",
-      "http.auth.ntlm.domain", "socksProxyHost", "socksProxyPort");
 
-  private String userAgent;
+  private final BaseHttpDownloader downloader;
 
   public HttpDownloader(Server server, Settings settings) {
-    this(settings, server.getVersion());
+    downloader = new BaseHttpDownloader(settings, server.getVersion());
   }
 
   public HttpDownloader(Settings settings) {
-    this(settings, null);
-  }
-
-  private HttpDownloader(Settings settings, String userAgent) {
-    initProxy(settings);
-    initUserAgent(userAgent);
-  }
-
-  private void initProxy(Settings settings) {
-    propagateProxySystemProperties(settings);
-    if (requiresProxyAuthentication(settings)) {
-      registerProxyCredentials(settings);
-    }
-  }
-
-  private void initUserAgent(String sonarVersion) {
-    userAgent = (sonarVersion == null ? "Sonar" : String.format("Sonar %s", sonarVersion));
-    System.setProperty("http.agent", userAgent);
-  }
-
-  public String getProxySynthesis(URI uri) {
-    return getProxySynthesis(uri, ProxySelector.getDefault());
-  }
-
-  @VisibleForTesting
-  static String getProxySynthesis(URI uri, ProxySelector proxySelector) {
-    List<Proxy> proxies = proxySelector.select(uri);
-    if (proxies.size() == 1 && proxies.get(0).type().equals(Proxy.Type.DIRECT)) {
-      return "no proxy";
-    }
-
-    List<String> descriptions = Lists.newArrayList();
-    for (Proxy proxy : proxies) {
-      if (proxy.type() != Proxy.Type.DIRECT) {
-        descriptions.add("proxy: " + proxy.address());
-      }
-    }
-
-    return Joiner.on(", ").join(descriptions);
-  }
-
-  private void registerProxyCredentials(Settings settings) {
-    Authenticator.setDefault(new ProxyAuthenticator(settings.getString("http.proxyUser"), settings
-        .getString("http.proxyPassword")));
-  }
-
-  private boolean requiresProxyAuthentication(Settings settings) {
-    return settings.getString("http.proxyUser") != null;
-  }
-
-  private void propagateProxySystemProperties(Settings settings) {
-    for (String key : PROXY_SETTINGS) {
-      if (settings.getString(key) != null) {
-        System.setProperty(key, settings.getString(key));
-      }
-    }
+    downloader = new BaseHttpDownloader(settings, null);
   }
 
   @Override
@@ -136,41 +78,45 @@ public class HttpDownloader extends UriReader.SchemeProcessor implements BatchCo
 
   @Override
   byte[] readBytes(URI uri) {
-    try {
-      return ByteStreams.toByteArray(new HttpInputSupplier(uri));
-    } catch (IOException e) {
-      throw failToDownload(uri, e);
-    }
+    return download(uri);
   }
 
   @Override
   String readString(URI uri, Charset charset) {
     try {
-      return CharStreams.toString(CharStreams.newReaderSupplier(new HttpInputSupplier(uri), charset));
+      return CharStreams.toString(CharStreams.newReaderSupplier(downloader.newInputSupplier(uri), charset));
     } catch (IOException e) {
       throw failToDownload(uri, e);
     }
   }
 
+  public String downloadPlainText(URI uri, String encoding) {
+    return readString(uri, Charset.forName(encoding));
+  }
+
   public byte[] download(URI uri) {
-    return readBytes(uri);
+    try {
+      return ByteStreams.toByteArray(downloader.newInputSupplier(uri));
+    } catch (IOException e) {
+      throw failToDownload(uri, e);
+    }
   }
 
-  public String downloadPlainText(URI uri, String encoding) {
-    return readString(uri, Charset.forName(encoding));
+  public String getProxySynthesis(URI uri) {
+    return downloader.getProxySynthesis(uri);
   }
 
   public InputStream openStream(URI uri) {
     try {
-      return new HttpInputSupplier(uri).getInput();
-    } catch (Exception e) {
-      throw new SonarException("Fail to download the file: " + uri + " (" + getProxySynthesis(uri) + ")", e);
+      return downloader.newInputSupplier(uri).getInput();
+    } catch (IOException e) {
+      throw failToDownload(uri, e);
     }
   }
 
   public void download(URI uri, File toFile) {
     try {
-      Files.copy(new HttpInputSupplier(uri), toFile);
+      Files.copy(downloader.newInputSupplier(uri), toFile);
     } catch (IOException e) {
       FileUtils.deleteQuietly(toFile);
       throw failToDownload(uri, e);
@@ -178,39 +124,121 @@ public class HttpDownloader extends UriReader.SchemeProcessor implements BatchCo
   }
 
   private SonarException failToDownload(URI uri, IOException e) {
-    return new SonarException(String.format("Fail to download the file: %s (%s)", uri, getProxySynthesis(uri)), e);
+    throw new SonarException(String.format("Fail to download: %s (%s)", uri, getProxySynthesis(uri)), e);
   }
 
-  class HttpInputSupplier implements InputSupplier<InputStream> {
-    private final URI uri;
+  public static class BaseHttpDownloader {
+    private static final List<String> PROXY_SETTINGS = ImmutableList.of(
+        "http.proxyHost", "http.proxyPort", "http.nonProxyHosts",
+        "http.auth.ntlm.domain", "socksProxyHost", "socksProxyPort");
 
-    HttpInputSupplier(URI uri) {
-      this.uri = uri;
+    private String userAgent;
+
+    public BaseHttpDownloader(Settings settings, String userAgent) {
+      initProxy(settings);
+      initUserAgent(userAgent);
     }
 
-    public InputStream getInput() throws IOException {
-      LoggerFactory.getLogger(getClass()).debug("Download: " + uri + " (" + getProxySynthesis(uri) + ")");
+    private void initProxy(Settings settings) {
+      propagateProxySystemProperties(settings);
+      if (requiresProxyAuthentication(settings)) {
+        registerProxyCredentials(settings);
+      }
+    }
 
-      HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection();
-      connection.setConnectTimeout(TIMEOUT_MILLISECONDS);
-      connection.setReadTimeout(TIMEOUT_MILLISECONDS);
-      connection.setUseCaches(true);
-      connection.setInstanceFollowRedirects(true);
-      connection.setRequestProperty("User-Agent", userAgent);
-      return connection.getInputStream();
+    private void initUserAgent(String sonarVersion) {
+      userAgent = (sonarVersion == null ? "Sonar" : String.format("Sonar %s", sonarVersion));
+      System.setProperty("http.agent", userAgent);
     }
-  }
-}
 
-class ProxyAuthenticator extends Authenticator {
-  private PasswordAuthentication auth;
+    private String getProxySynthesis(URI uri) {
+      return getProxySynthesis(uri, ProxySelector.getDefault());
+    }
 
-  ProxyAuthenticator(String user, String password) {
-    auth = new PasswordAuthentication(user, password == null ? new char[0] : password.toCharArray());
-  }
+    @VisibleForTesting
+    static String getProxySynthesis(URI uri, ProxySelector proxySelector) {
+      List<Proxy> proxies = proxySelector.select(uri);
+      if (proxies.size() == 1 && proxies.get(0).type().equals(Proxy.Type.DIRECT)) {
+        return "no proxy";
+      }
 
-  @Override
-  protected PasswordAuthentication getPasswordAuthentication() {
-    return auth;
+      List<String> descriptions = Lists.newArrayList();
+      for (Proxy proxy : proxies) {
+        if (proxy.type() != Proxy.Type.DIRECT) {
+          descriptions.add("proxy: " + proxy.address());
+        }
+      }
+
+      return Joiner.on(", ").join(descriptions);
+    }
+
+    private void registerProxyCredentials(Settings settings) {
+      Authenticator.setDefault(new ProxyAuthenticator(
+          settings.getString("http.proxyUser"),
+          settings.getString("http.proxyPassword")));
+    }
+
+    private boolean requiresProxyAuthentication(Settings settings) {
+      return settings.getString("http.proxyUser") != null;
+    }
+
+    private void propagateProxySystemProperties(Settings settings) {
+      for (String key : PROXY_SETTINGS) {
+        if (settings.getString(key) != null) {
+          System.setProperty(key, settings.getString(key));
+        }
+      }
+    }
+
+    public InputSupplier<InputStream> newInputSupplier(URI uri) {
+      return new HttpInputSupplier(uri, userAgent, null, null);
+    }
+
+    public InputSupplier<InputStream> newInputSupplier(URI uri, String login, String password) {
+      return new HttpInputSupplier(uri, userAgent, login, password);
+    }
+
+    private static class HttpInputSupplier implements InputSupplier<InputStream> {
+      private final String login;
+      private final String password;
+      private final URI uri;
+      private final String userAgent;
+
+      HttpInputSupplier(URI uri, String userAgent, String login, String password) {
+        this.uri = uri;
+        this.userAgent = userAgent;
+        this.login = login;
+        this.password = password;
+      }
+
+      public InputStream getInput() throws IOException {
+        LoggerFactory.getLogger(getClass()).debug("Download: " + uri + " (" + getProxySynthesis(uri, ProxySelector.getDefault()) + ")");
+
+        HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection();
+        if (!Strings.isNullOrEmpty(login)) {
+          String encoded = new String(Base64.encodeBase64((login + ":" + password).getBytes()));
+          connection.setRequestProperty("Authorization", "Basic " + encoded);
+        }
+        connection.setConnectTimeout(TIMEOUT_MILLISECONDS);
+        connection.setReadTimeout(TIMEOUT_MILLISECONDS);
+        connection.setUseCaches(true);
+        connection.setInstanceFollowRedirects(true);
+        connection.setRequestProperty("User-Agent", userAgent);
+        return connection.getInputStream();
+      }
+    }
+
+    private static class ProxyAuthenticator extends Authenticator {
+      private final PasswordAuthentication auth;
+
+      ProxyAuthenticator(String user, String password) {
+        auth = new PasswordAuthentication(user, password == null ? new char[0] : password.toCharArray());
+      }
+
+      @Override
+      protected PasswordAuthentication getPasswordAuthentication() {
+        return auth;
+      }
+    }
   }
 }
index 83101c20fdb3e921e244fa39c55946909bb0a259..bc626284daac7356a94778365e13a9fae4cb5d6a 100644 (file)
@@ -153,14 +153,14 @@ public class HttpDownloaderTest {
   public void shouldGetDirectProxySynthesis() throws URISyntaxException {
     ProxySelector proxySelector = mock(ProxySelector.class);
     when(proxySelector.select(any(URI.class))).thenReturn(Arrays.asList(Proxy.NO_PROXY));
-    assertThat(HttpDownloader.getProxySynthesis(new URI("http://an_url"), proxySelector)).isEqualTo("no proxy");
+    assertThat(HttpDownloader.BaseHttpDownloader.getProxySynthesis(new URI("http://an_url"), proxySelector)).isEqualTo("no proxy");
   }
 
   @Test
   public void shouldGetProxySynthesis() throws URISyntaxException {
     ProxySelector proxySelector = mock(ProxySelector.class);
     when(proxySelector.select(any(URI.class))).thenReturn(Arrays.<Proxy> asList(new FakeProxy()));
-    assertThat(HttpDownloader.getProxySynthesis(new URI("http://an_url"), proxySelector)).isEqualTo("proxy: http://proxy_url:4040");
+    assertThat(HttpDownloader.BaseHttpDownloader.getProxySynthesis(new URI("http://an_url"), proxySelector)).isEqualTo("proxy: http://proxy_url:4040");
   }
 
   @Test
index c9374031249d8da40f270ac56d95dc565dde9e23..c4f7298b66b72a281485f4560946c02fdf7bbfdf 100644 (file)
@@ -22,12 +22,24 @@ require "json"
 
 class Api::SynchroController < Api::ApiController
 
-  # curl http://localhost:9000/api/synchro -v
+  # curl http://localhost:9000/api/synchro?resource=<resource> -v [-u user:password]
   def index
-    database_factory = java_facade.getCoreComponentByClassname('org.sonar.core.persistence.LocalDatabaseFactory')
+    require_parameters :resource
+    load_resource()
 
-    dbFileContent = database_factory.createDatabaseForLocalMode()
+    database_factory = java_facade.getCoreComponentByClassname('org.sonar.core.persistence.LocalDatabaseFactory')
+    dbFileContent = database_factory.createDatabaseForLocalMode(@resource.id)
 
     send_data String.from_java_bytes(dbFileContent)
   end
+
+  private
+
+  def load_resource
+    resource_id = params[:resource]
+    @resource = Project.by_key(resource_id)
+    return not_found("Resource [#{resource_id}] not found") if @resource.nil?
+    return access_denied unless is_user?(@resource)
+  end
 end
+