]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-2603 the IP address to use must be set by user
authorSimon Brandhof <simon.brandhof@gmail.com>
Thu, 11 Aug 2011 13:19:58 +0000 (15:19 +0200)
committerSimon Brandhof <simon.brandhof@gmail.com>
Thu, 11 Aug 2011 13:19:58 +0000 (15:19 +0200)
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
sonar-server/src/main/java/org/sonar/server/platform/ServerImpl.java
sonar-server/src/main/java/org/sonar/server/platform/ServerKeyGenerator.java
sonar-server/src/test/java/org/sonar/server/platform/ServerKeyGeneratorTest.java

index 13137d94a44162cf08df13253391d1323078a094..60f7d250ba0c3608daaf0acdaf545aef4666b618 100644 (file)
@@ -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 <i>http://yourhost.yourdomain/sonar</i>. 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 <i>http://yourhost.yourdomain/sonar</i>. 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,
index acc0bdb5a69dcbd293c5f27cc1a857d8d0e448ee..3b2508ddc21a223d78f5c86cb52dd0261032fef6 100644 (file)
@@ -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() {
index e57db95edaeb0a6a84208eaa1b17e9afb13ca0ac..c3df9e063d63b175665c2989f9d01106645411cd 100644 (file)
  */
 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<ServerKey> 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<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+      while (networkInterfaces.hasMoreElements()) {
+        NetworkInterface networkInterface = networkInterfaces.nextElement();
+        Enumeration<InetAddress> 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<ServerKey> generateForOrganization(String organization) {
-    List<ServerKey> keys = Lists.newArrayList();
-    if (StringUtils.isNotBlank(organization)) {
-      try {
-        Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
-        while (networkInterfaces.hasMoreElements()) {
-          NetworkInterface networkInterface = networkInterfaces.nextElement();
-          Enumeration<InetAddress> 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);
     }
   }
 }
index 4041825edaa231928826e0c258563933a74898e6..7691e7f23c05f05df4d6b4c0448857d752cd1618 100644 (file)
@@ -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"));
   }
-
-
 }