Browse Source

SONAR-3895 User resource key and optional authent.

tags/3.4
David Gageot 11 years ago
parent
commit
0ad53bdcfa

+ 22
- 9
sonar-batch/src/main/java/org/sonar/batch/local/LocalDatabase.java View File

@@ -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<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);
}
}


+ 6
- 4
sonar-core/src/main/java/org/sonar/core/persistence/LocalDatabaseFactory.java View 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")

+ 2
- 2
sonar-core/src/test/java/org/sonar/core/persistence/LocalDatabaseFactoryTest.java View 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();
}


+ 4
- 3
sonar-core/src/test/resources/org/sonar/core/persistence/LocalDatabaseFactoryTest/should_create_database.xml View 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>

+ 10
- 0
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java View 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";
}

+ 128
- 100
sonar-plugin-api/src/main/java/org/sonar/api/utils/HttpDownloader.java View File

@@ -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<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;
}
}
}
}

+ 2
- 2
sonar-plugin-api/src/test/java/org/sonar/api/utils/HttpDownloaderTest.java View 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

+ 15
- 3
sonar-server/src/main/webapp/WEB-INF/app/controllers/api/synchro_controller.rb View 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


Loading…
Cancel
Save