From 3ef437426b9886f519477a288333ba77342d964c Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Thu, 11 Aug 2011 15:19:58 +0200 Subject: [PATCH] SONAR-2603 the IP address to use must be set by user --- .../org/sonar/plugins/core/CorePlugin.java | 14 +- .../org/sonar/server/platform/ServerImpl.java | 3 +- .../server/platform/ServerKeyGenerator.java | 164 ++++++++---------- .../platform/ServerKeyGeneratorTest.java | 64 +++---- 4 files changed, 100 insertions(+), 145 deletions(-) diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java index 13137d94a44..60f7d250ba0 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java @@ -49,6 +49,13 @@ import java.util.List; name = "Organization", description = "Identify your installation. Required to generate the server key and to benefit from licensed plugins. Server must be restarted for the change to take effect.", global = true), + @Property( + key = CoreProperties.SERVER_BASE_URL, + defaultValue = CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE, + name = "Server base URL", + description = "HTTP address of the Sonar server, such as http://yourhost.yourdomain/sonar. This value is used i.e. to create links in emails and to generate server key.", + project = false, + global = true), @Property( key = CoreProperties.CORE_COVERAGE_PLUGIN_PROPERTY, defaultValue = "cobertura", @@ -92,13 +99,6 @@ import java.util.List; description = "A weight is associated to each priority to calculate the Rules Compliance Index.", project = false, global = true), - @Property( - key = CoreProperties.SERVER_BASE_URL, - defaultValue = CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE, - name = "Server base URL", - description = "HTTP address of the Sonar server, such as http://yourhost.yourdomain/sonar. This value is used i.e. to create links in emails.", - project = false, - global = true), @Property( key = CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY, defaultValue = "" + CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE, diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ServerImpl.java b/sonar-server/src/main/java/org/sonar/server/platform/ServerImpl.java index acc0bdb5a69..3b2508ddc21 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/ServerImpl.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/ServerImpl.java @@ -64,8 +64,9 @@ public final class ServerImpl extends Server { private String initKey(Configuration conf) { String organization = conf.getString(CoreProperties.ORGANIZATION); + String baseUrl = conf.getString(CoreProperties.SERVER_BASE_URL, CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE); String previousKey = conf.getString(CoreProperties.SERVER_KEY); - return new ServerKeyGenerator().generate(organization, previousKey); + return new ServerKeyGenerator().generate(organization, baseUrl, previousKey); } public String getId() { diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ServerKeyGenerator.java b/sonar-server/src/main/java/org/sonar/server/platform/ServerKeyGenerator.java index e57db95edae..c3df9e063d6 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/ServerKeyGenerator.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/ServerKeyGenerator.java @@ -19,18 +19,18 @@ */ package org.sonar.server.platform; -import com.google.common.collect.Lists; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.LoggerFactory; import org.sonar.api.utils.Logs; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; +import java.io.UnsupportedEncodingException; +import java.net.*; import java.util.Enumeration; -import java.util.List; +/** + * @since 2.11 + */ public class ServerKeyGenerator { /** @@ -38,37 +38,61 @@ public class ServerKeyGenerator { */ static final String VERSION = "1"; - private static final int CHECKSUM_SIZE = 9; + static final int CHECKSUM_SIZE = 9; + + private final boolean acceptPrivateAddress; - public String generate(String organization) { - return generate(organization, null); + public ServerKeyGenerator() { + this(false); } - public String generate(String organization, String previousKey) { - List serverKeys = generateForOrganization(organization); - String external = null; - String best = null; - for (ServerKey serverKey : serverKeys) { - if (StringUtils.equals(previousKey, serverKey.getKey())) { - best = serverKey.getKey(); - } - if (serverKey.isExternal()) { - // External addresses are prefered to internal addresses. - external = serverKey.getKey(); + ServerKeyGenerator(boolean acceptPrivateAddress) { + this.acceptPrivateAddress = acceptPrivateAddress; + } + + public String generate(String organization, String baseUrl) { + return generate(organization, baseUrl, null); + } + + public String generate(String organization, String baseUrl, String previousKey) { + String key = null; + if (StringUtils.isNotBlank(organization) && StringUtils.isNotBlank(baseUrl)) { + InetAddress address = extractAddressFromUrl(baseUrl); + if (address != null && isFixed(address) && isOwner(address)) { + key = toKey(organization, address); } } - if (best == null) { - if (external!=null) { - best = external; - } else if (!serverKeys.isEmpty()) { - best = serverKeys.get(0).getKey(); + log(previousKey, key); + return key; + } + + boolean isOwner(InetAddress address) { + try { + Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + while (networkInterfaces.hasMoreElements()) { + NetworkInterface networkInterface = networkInterfaces.nextElement(); + Enumeration addresses = networkInterface.getInetAddresses(); + while (addresses.hasMoreElements()) { + InetAddress ownedAddress = addresses.nextElement(); + if (ownedAddress.equals(address)) { + return true; + } + } } + } catch (SocketException e) { + LoggerFactory.getLogger(ServerKeyGenerator.class).error("Fail to verify server key. Network interfaces can't be browsed.", e); } - log(previousKey, best); - return best; + return false; + } + + boolean isFixed(InetAddress address) { + // Loopback addresses are in the range 127/8. + // Link local addresses are in the range 169.254/16 (IPv4) or fe80::/10 (IPv6). They are "autoconfiguration" addresses. + // They can assigned pseudorandomly, so they don't guarantee to be the same between two server startups. + return acceptPrivateAddress || (!address.isLoopbackAddress() && !address.isLinkLocalAddress()); } - private void log(String previousKey, String newKey) { + void log(String previousKey, String newKey) { if (StringUtils.isNotBlank(newKey) && StringUtils.isNotBlank(previousKey) && !previousKey.equals(newKey)) { LoggerFactory.getLogger(getClass()).warn("Server key has changed. Licensed plugins may be disabled. " + "Please check the organization name (Settings page) and the server IP addresses."); @@ -82,81 +106,29 @@ public class ServerKeyGenerator { } } - List generateForOrganization(String organization) { - List keys = Lists.newArrayList(); - if (StringUtils.isNotBlank(organization)) { - try { - Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); - while (networkInterfaces.hasMoreElements()) { - NetworkInterface networkInterface = networkInterfaces.nextElement(); - Enumeration addresses = networkInterface.getInetAddresses(); - while (addresses.hasMoreElements()) { - ServerKey key = new ServerKey(organization, addresses.nextElement()); - if (key.isValid()) { - keys.add(key); - } - } - } - } catch (SocketException e) { - LoggerFactory.getLogger(getClass()).error("Fail to generate server key. Network interfaces can't be browsed.", e); - } - } - return keys; - } - - static class ServerKey { - private String organization; - private InetAddress address; - - ServerKey(String organization, InetAddress address) { - this.organization = organization; - this.address = address; + InetAddress extractAddressFromUrl(String baseUrl) { + if (StringUtils.isBlank(baseUrl)) { + return null; } + try { + URL url = new URL(baseUrl); + return InetAddress.getByName(url.getHost()); - boolean isExternal() { - return !address.isLoopbackAddress() && !address.isSiteLocalAddress() && !address.isLinkLocalAddress(); - } - - boolean isValid() { - // Loopback addresses are in the range 127/8. - // Link local addresses are in the range 169.254/16 (IPv4) or fe80::/10 (IPv6). They are "autoconfiguration" addresses. - // They can assigned pseudorandomly, so they don't guarantee to be the same between two server startups. - return !address.isLoopbackAddress() && !address.isLinkLocalAddress(); - } + } catch (MalformedURLException e) { + throw new IllegalArgumentException("Server base URL is malformed: " + baseUrl, e); - String getKey() { - String key = new StringBuilder().append(organization).append("-").append(address.getHostAddress()).toString(); - return VERSION + DigestUtils.shaHex(key.getBytes()).substring(0, CHECKSUM_SIZE); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ServerKey serverKey = (ServerKey) o; - if (!address.equals(serverKey.address)) { - return false; - } - if (!organization.equals(serverKey.organization)) { - return false; - } - return true; + } catch (UnknownHostException e) { + throw new IllegalArgumentException("Server base URL is unknown: " + baseUrl, e); } + } - @Override - public int hashCode() { - int result = organization.hashCode(); - result = 31 * result + address.hashCode(); - return result; - } + String toKey(String organization, InetAddress address) { + String key = new StringBuilder().append(organization).append("-").append(address.getHostAddress()).toString(); + try { + return VERSION + DigestUtils.shaHex(key.getBytes("UTF-8")).substring(0, CHECKSUM_SIZE); - @Override - public String toString() { - return getKey(); + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException("Organization is not UTF-8 encoded: " + organization, e); } } } diff --git a/sonar-server/src/test/java/org/sonar/server/platform/ServerKeyGeneratorTest.java b/sonar-server/src/test/java/org/sonar/server/platform/ServerKeyGeneratorTest.java index 4041825edaa..7691e7f23c0 100644 --- a/sonar-server/src/test/java/org/sonar/server/platform/ServerKeyGeneratorTest.java +++ b/sonar-server/src/test/java/org/sonar/server/platform/ServerKeyGeneratorTest.java @@ -28,7 +28,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; import static org.hamcrest.text.StringStartsWith.startsWith; -import static org.junit.Assert.*; +import static org.junit.Assert.assertThat; public class ServerKeyGeneratorTest { @@ -41,71 +41,53 @@ public class ServerKeyGeneratorTest { @Test public void keyShouldHaveTenCharacters() { - ServerKeyGenerator.ServerKey key = new ServerKeyGenerator.ServerKey("SonarSource", localhost); - assertThat(key.getKey().length(), Is.is(10)); // first character is version + 9 characters for checksum - assertThat(StringUtils.isBlank(key.getKey()), Is.is(false)); + String key = new ServerKeyGenerator().toKey("SonarSource", localhost); + assertThat(key.length(), Is.is(10)); // first character is version + 9 characters for checksum + assertThat(StringUtils.isBlank(key), Is.is(false)); } @Test public void keyShouldStartWithVersion() { - ServerKeyGenerator.ServerKey key = new ServerKeyGenerator.ServerKey("SonarSource", localhost); - assertThat(key.getKey(), startsWith(ServerKeyGenerator.VERSION)); + String key = new ServerKeyGenerator().toKey("SonarSource", localhost); + assertThat(key, startsWith(ServerKeyGenerator.VERSION)); } @Test - public void loopbackAddressesShouldNotBeValid() throws UnknownHostException { - assertThat(new ServerKeyGenerator.ServerKey("SonarSource", InetAddress.getByName("127.0.0.1")).isValid(), Is.is(false)); + public void loopbackAddressesShouldNotBeAccepted() throws UnknownHostException { + assertThat(new ServerKeyGenerator().isFixed(InetAddress.getByName("127.0.0.1")), Is.is(false)); } @Test - public void testEqualsAndHashCode() { - ServerKeyGenerator.ServerKey key1 = new ServerKeyGenerator.ServerKey("Corp One", localhost); - ServerKeyGenerator.ServerKey key2 = new ServerKeyGenerator.ServerKey("Corp Two", localhost); - assertEquals(key1, key1); - assertEquals(key1.hashCode(), key1.hashCode()); - - assertThat(key1.equals(key2), Is.is(false)); - assertThat(key2.equals(key1), Is.is(false)); - - assertThat(key1.equals("string"), Is.is(false)); + public void publicAddressesNotBeAccepted() throws UnknownHostException { + assertThat(new ServerKeyGenerator().isFixed(InetAddress.getByName("sonarsource.com")), Is.is(true)); } @Test - public void shouldGenerateKey() { - String key = new ServerKeyGenerator().generate("SonarSource"); - assertThat(StringUtils.isNotBlank(key), Is.is(true)); - } - - @Test - public void organizationShouldBeMandatory() { - assertNull(new ServerKeyGenerator().generate(null)); - assertNull(new ServerKeyGenerator().generate("")); - assertNull(new ServerKeyGenerator().generate(" ")); + public void shouldBeAddressOwner() throws UnknownHostException { + assertThat(new ServerKeyGenerator().isOwner(InetAddress.getByName("sonarsource.com")), Is.is(false)); + assertThat(new ServerKeyGenerator().isOwner(InetAddress.getByName("localhost")), Is.is(true)); + assertThat(new ServerKeyGenerator().isOwner(InetAddress.getByName("127.0.0.1")), Is.is(true)); } @Test public void keyShouldBeUniquePerOrganization() { - ServerKeyGenerator generator = new ServerKeyGenerator(); - String k1 = generator.generate("Corp One"); - String k2 = generator.generate("Corp Two"); + ServerKeyGenerator generator = new ServerKeyGenerator(true); + String k1 = generator.generate("Corp One", "http://localhost:9000"); + String k2 = generator.generate("Corp Two", "http://localhost:9000"); assertThat(StringUtils.equals(k1, k2), Is.is(false)); } @Test public void keyShouldBeReproducible() { - ServerKeyGenerator generator = new ServerKeyGenerator(); - String k1 = generator.generate("SonarSource"); - String k2 = generator.generate("SonarSource"); + ServerKeyGenerator generator = new ServerKeyGenerator(true); + String k1 = generator.generate("SonarSource", "http://localhost:9000"); + String k2 = generator.generate("SonarSource", "http://localhost:9000"); assertThat(StringUtils.equals(k1, k2), Is.is(true)); } @Test - public void shouldNotKeepPreviousKeyIfNotValid() { - ServerKeyGenerator generator = new ServerKeyGenerator(); - String key = generator.generate("SonarSource", "unvalid"); - assertNotNull(key); - assertThat(StringUtils.equals(key, "unvalid"), Is.is(false)); + public void shouldExtractAddressFromUrl() { + assertThat(new ServerKeyGenerator().extractAddressFromUrl("https://localhost:9000").getHostAddress(), Is.is("127.0.0.1")); + assertThat(new ServerKeyGenerator().extractAddressFromUrl("http://sonarsource.com/sonar").getHostName(), Is.is("sonarsource.com")); } - - } -- 2.39.5