From: David Gageot Date: Wed, 24 Oct 2012 14:59:13 +0000 (+0200) Subject: SONAR-3895 User resource key and optional authent. X-Git-Tag: 3.4~446 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=0ad53bdcfa20c61945713e78030920d5b2f1e491;p=sonarqube.git SONAR-3895 User resource key and optional authent. --- diff --git a/sonar-batch/src/main/java/org/sonar/batch/local/LocalDatabase.java b/sonar-batch/src/main/java/org/sonar/batch/local/LocalDatabase.java index 51d75d53730..199541c12a0 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/local/LocalDatabase.java +++ b/sonar-batch/src/main/java/org/sonar/batch/local/LocalDatabase.java @@ -19,9 +19,11 @@ */ 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 inputSupplier; + if (Strings.isNullOrEmpty(login)) { + inputSupplier = downloader.newInputSupplier(uri); + } else { + inputSupplier = downloader.newInputSupplier(uri, login, password); + } + try { - Files.copy(new InputSupplier() { - 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); } } diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/LocalDatabaseFactory.java b/sonar-core/src/main/java/org/sonar/core/persistence/LocalDatabaseFactory.java index a2e265bd5cd..edebb896eef 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/LocalDatabaseFactory.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/LocalDatabaseFactory.java @@ -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") diff --git a/sonar-core/src/test/java/org/sonar/core/persistence/LocalDatabaseFactoryTest.java b/sonar-core/src/test/java/org/sonar/core/persistence/LocalDatabaseFactoryTest.java index 3486fb9638a..3eb0d75c300 100644 --- a/sonar-core/src/test/java/org/sonar/core/persistence/LocalDatabaseFactoryTest.java +++ b/sonar-core/src/test/java/org/sonar/core/persistence/LocalDatabaseFactoryTest.java @@ -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(); } diff --git a/sonar-core/src/test/resources/org/sonar/core/persistence/LocalDatabaseFactoryTest/should_create_database.xml b/sonar-core/src/test/resources/org/sonar/core/persistence/LocalDatabaseFactoryTest/should_create_database.xml index 281958780db..30343973116 100644 --- a/sonar-core/src/test/resources/org/sonar/core/persistence/LocalDatabaseFactoryTest/should_create_database.xml +++ b/sonar-core/src/test/resources/org/sonar/core/persistence/LocalDatabaseFactoryTest/should_create_database.xml @@ -1,6 +1,7 @@ - - - + + + + \ No newline at end of file diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java index 05e6c192ae8..1e86dd8947f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java @@ -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"; } 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 7b576266060..0f29541dc30 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 @@ -20,14 +20,15 @@ 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 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 proxies = proxySelector.select(uri); - if (proxies.size() == 1 && proxies.get(0).type().equals(Proxy.Type.DIRECT)) { - return "no proxy"; - } - - List 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 { - private final URI uri; + public static class BaseHttpDownloader { + private static final List 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 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 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 newInputSupplier(URI uri) { + return new HttpInputSupplier(uri, userAgent, null, null); + } + + public InputSupplier newInputSupplier(URI uri, String login, String password) { + return new HttpInputSupplier(uri, userAgent, login, password); + } + + private static class HttpInputSupplier implements InputSupplier { + 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; + } + } } } 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 83101c20fdb..bc626284daa 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 @@ -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. 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 diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/synchro_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/synchro_controller.rb index c9374031249..c4f7298b66b 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/synchro_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/api/synchro_controller.rb @@ -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= -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 +