]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7985 Create /api/server_id/show WS 1203/head
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 2 Sep 2016 13:01:00 +0000 (15:01 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 5 Sep 2016 12:26:32 +0000 (14:26 +0200)
13 files changed:
server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java
server/sonar-server/src/main/java/org/sonar/server/platform/ServerIdGenerator.java
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
server/sonar-server/src/main/java/org/sonar/server/serverid/ws/ServerIdWs.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/serverid/ws/ServerIdWsAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/serverid/ws/ServerIdWsModule.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/serverid/ws/ShowAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/serverid/ws/package-info.java [new file with mode: 0644]
server/sonar-server/src/main/resources/org/sonar/server/serverid/ws/show-example.json [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java
server/sonar-server/src/test/java/org/sonar/server/serverid/ws/ServerIdWsModuleTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/serverid/ws/ShowActionTest.java [new file with mode: 0644]
sonar-ws/src/main/protobuf/ws-serverid.proto [new file with mode: 0644]

index f29a0807c74df1717269950f56c9b58301a9dc9f..0be0bfcc9eba5b220d8bd66de202748210087b1f 100644 (file)
@@ -59,7 +59,7 @@ public class UserSessionInitializer {
     "/setup/*",
     "/sessions/*",
     "/api/system/db_migration_status", "/api/system/status", "/api/system/migrate_db",
-    "/api/server/*",
+    "/api/server/index", "/api/server/setup", "/api/server/version",
     AUTH_LOGIN_URL, AUTH_VALIDATE_URL);
 
   private static final UrlPattern URL_PATTERN = UrlPattern.builder()
index 7c9b1299a6606c6bddf7acf3e8a56563c3d64076..86d03a72efed7836e7b444f69d94e883428f162f 100644 (file)
@@ -29,6 +29,7 @@ import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.List;
 import java.util.regex.Pattern;
+import javax.annotation.CheckForNull;
 import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.commons.lang.StringUtils;
 import org.sonar.api.utils.log.Loggers;
@@ -55,6 +56,7 @@ public class ServerIdGenerator {
     this.acceptPrivateAddress = acceptPrivateAddress;
   }
 
+  @CheckForNull
   public String generate(String organisationName, String ipAddress) {
     String id = null;
     String organisation = organisationName.trim();
index 4ae90268689da5c965fdddc8a1f4c99e4f3bd255..6070b48943658bd7196d7fc93551b3c2f7240b27 100644 (file)
@@ -236,6 +236,7 @@ import org.sonar.server.rule.ws.RuleMapper;
 import org.sonar.server.rule.ws.RuleQueryFactory;
 import org.sonar.server.rule.ws.RulesWs;
 import org.sonar.server.rule.ws.TagsAction;
+import org.sonar.server.serverid.ws.ServerIdWsModule;
 import org.sonar.server.setting.ProjectSettingsFactory;
 import org.sonar.server.setting.ws.SettingsWsModule;
 import org.sonar.server.source.HtmlSourceDecorator;
@@ -615,6 +616,9 @@ public class PlatformLevel4 extends PlatformLevel {
       ChangeLogLevelAction.class,
       DbMigrationStatusAction.class,
 
+      // Server id
+      ServerIdWsModule.class,
+
       // Plugins WS
       PluginWSCommons.class,
       PluginUpdateAggregator.class,
diff --git a/server/sonar-server/src/main/java/org/sonar/server/serverid/ws/ServerIdWs.java b/server/sonar-server/src/main/java/org/sonar/server/serverid/ws/ServerIdWs.java
new file mode 100644 (file)
index 0000000..0825f9f
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.serverid.ws;
+
+import org.sonar.api.server.ws.WebService;
+
+public class ServerIdWs implements WebService {
+
+  private final ServerIdWsAction[] actions;
+
+  public ServerIdWs(ServerIdWsAction... actions) {
+    this.actions = actions;
+  }
+
+  @Override
+  public void define(Context context) {
+    NewController controller = context.createController("api/server_id")
+      .setDescription("Get server id information and generate server id.");
+    for (ServerIdWsAction action : actions) {
+      action.define(controller);
+    }
+    controller.done();
+  }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/serverid/ws/ServerIdWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/serverid/ws/ServerIdWsAction.java
new file mode 100644 (file)
index 0000000..8076f43
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.serverid.ws;
+
+import org.sonar.server.ws.WsAction;
+
+public interface ServerIdWsAction extends WsAction {
+  // Marker interface
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/serverid/ws/ServerIdWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/serverid/ws/ServerIdWsModule.java
new file mode 100644 (file)
index 0000000..2f30d5c
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.serverid.ws;
+
+import org.sonar.core.platform.Module;
+
+public class ServerIdWsModule extends Module {
+  @Override
+  protected void configureModule() {
+    add(
+      ServerIdWs.class,
+      ShowAction.class);
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/serverid/ws/ShowAction.java b/server/sonar-server/src/main/java/org/sonar/server/serverid/ws/ShowAction.java
new file mode 100644 (file)
index 0000000..183f00e
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.serverid.ws;
+
+import com.google.common.collect.ImmutableSet;
+import java.net.InetAddress;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import javax.annotation.Nullable;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.core.util.stream.Collectors;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.property.PropertyDto;
+import org.sonar.server.platform.ServerIdGenerator;
+import org.sonar.server.user.UserSession;
+
+import static java.util.stream.Collectors.toList;
+import static org.sonar.api.CoreProperties.ORGANISATION;
+import static org.sonar.api.CoreProperties.PERMANENT_SERVER_ID;
+import static org.sonar.api.CoreProperties.SERVER_ID_IP_ADDRESS;
+import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
+import static org.sonarqube.ws.ServerId.ShowWsResponse;
+
+public class ShowAction implements ServerIdWsAction {
+
+  private static final Set<String> SETTINGS_KEYS = ImmutableSet.of(PERMANENT_SERVER_ID, ORGANISATION, SERVER_ID_IP_ADDRESS);
+
+  private final UserSession userSession;
+  private final ServerIdGenerator serverIdGenerator;
+  private final DbClient dbClient;
+
+  public ShowAction(UserSession userSession, ServerIdGenerator serverIdGenerator, DbClient dbClient) {
+    this.userSession = userSession;
+    this.serverIdGenerator = serverIdGenerator;
+    this.dbClient = dbClient;
+  }
+
+  @Override
+  public void define(WebService.NewController controller) {
+    controller.createAction("show")
+      .setDescription("Get server id configuration.<br/>" +
+        "Requires 'System Administer' permissions")
+      .setSince("6.1")
+      .setInternal(true)
+      .setResponseExample(getClass().getResource("show-example.json"))
+      .setHandler(this);
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    userSession.checkPermission(SYSTEM_ADMIN);
+    DbSession dbSession = dbClient.openSession(true);
+    try {
+      Map<String, PropertyDto> properties = dbClient.propertiesDao().selectGlobalPropertiesByKeys(dbSession, SETTINGS_KEYS).stream()
+        .collect(Collectors.uniqueIndex(PropertyDto::getKey, Function.identity()));
+      writeProtobuf(doHandle(properties), request, response);
+    } finally {
+      dbClient.closeSession(dbSession);
+    }
+  }
+
+  private ShowWsResponse doHandle(Map<String, PropertyDto> properties) {
+    ShowWsResponse.Builder responseBuilder = ShowWsResponse.newBuilder();
+    List<String> validIpAdresses = serverIdGenerator.getAvailableAddresses().stream().map(InetAddress::getHostAddress).collect(toList());
+    if (!validIpAdresses.isEmpty()) {
+      responseBuilder.addAllValidIpAdresses(validIpAdresses);
+    }
+
+    Optional<String> serverId = getSettingValue(properties.get(PERMANENT_SERVER_ID));
+    if (serverId.isPresent()) {
+      responseBuilder.setServerId(serverId.get());
+      Optional<String> organisation = getSettingValue(properties.get(ORGANISATION));
+      if (organisation.isPresent()) {
+        responseBuilder.setOrganization(organisation.get());
+      }
+      Optional<String> ip = getSettingValue(properties.get(SERVER_ID_IP_ADDRESS));
+      if (ip.isPresent()) {
+        responseBuilder.setIp(ip.get());
+      }
+      boolean isValidServId = isValidServerId(serverId.get(), organisation, ip);
+      if (!isValidServId) {
+        responseBuilder.setInvalidServerId(true);
+      }
+    }
+    return responseBuilder.build();
+  }
+
+  private static Optional<String> getSettingValue(@Nullable PropertyDto propertyDto) {
+    return propertyDto != null ? Optional.of(propertyDto.getValue()) : Optional.empty();
+  }
+
+  private boolean isValidServerId(String serverId, Optional<String> organisation, Optional<String> ip) {
+    if (organisation.isPresent() && ip.isPresent()) {
+      String generatedServerId = serverIdGenerator.generate(organisation.get(), ip.get());
+      return Objects.equals(generatedServerId, serverId);
+    }
+    return false;
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/serverid/ws/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/serverid/ws/package-info.java
new file mode 100644 (file)
index 0000000..56b2376
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.serverid.ws;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/serverid/ws/show-example.json b/server/sonar-server/src/main/resources/org/sonar/server/serverid/ws/show-example.json
new file mode 100644 (file)
index 0000000..1f322a4
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "serverId": "server_id",
+  "organization": "home",
+  "ip": "127.0.1",
+  "validIpAdresses": [
+    "192.168.1.1",
+    "127.0.1"
+  ]
+}
index dba52e8d4dce2ca49e8ce2551a7a8a284ab2edba..5b0a13780fcab0c4fbb0d9ed99737075ee4c927e 100644 (file)
 
 package org.sonar.server.authentication;
 
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-import static org.sonar.db.user.UserTesting.newUserDto;
-
 import java.util.Optional;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -49,6 +37,18 @@ import org.sonar.server.user.ServerUserSession;
 import org.sonar.server.user.ThreadLocalUserSession;
 import org.sonar.server.user.UserSession;
 
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+import static org.sonar.db.user.UserTesting.newUserDto;
+
 public class UserSessionInitializerTest {
 
   @Rule
@@ -84,6 +84,7 @@ public class UserSessionInitializerTest {
   public void check_urls() throws Exception {
     assertPathIsNotIgnored("/");
     assertPathIsNotIgnored("/foo");
+    assertPathIsNotIgnored("/api/server_id/show");
 
     assertPathIsIgnored("/api/authentication/login");
     assertPathIsIgnored("/api/authentication/validate");
@@ -97,6 +98,8 @@ public class UserSessionInitializerTest {
     assertPathIsIgnored("/api/system/status");
     assertPathIsIgnored("/api/system/migrate_db");
     assertPathIsIgnored("/api/server/index");
+    assertPathIsIgnored("/api/server/setup");
+    assertPathIsIgnored("/api/server/version");
 
     // exclude static resources
     assertPathIsIgnored("/css/style.css");
diff --git a/server/sonar-server/src/test/java/org/sonar/server/serverid/ws/ServerIdWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/serverid/ws/ServerIdWsModuleTest.java
new file mode 100644 (file)
index 0000000..7c0bf9c
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.serverid.ws;
+
+import org.junit.Test;
+import org.sonar.core.platform.ComponentContainer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ServerIdWsModuleTest {
+  @Test
+  public void verify_count_of_added_components() {
+    ComponentContainer container = new ComponentContainer();
+    new ServerIdWsModule().configure(container);
+    assertThat(container.size()).isEqualTo(2 + 2);
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/serverid/ws/ShowActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/serverid/ws/ShowActionTest.java
new file mode 100644 (file)
index 0000000..2a7dd64
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.serverid.ws;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.property.PropertyDbTester;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.platform.ServerIdGenerator;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.WsActionTester;
+import org.sonar.test.JsonAssert;
+import org.sonarqube.ws.MediaTypes;
+import org.sonarqube.ws.ServerId.ShowWsResponse;
+
+import static org.assertj.core.api.Java6Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.core.permission.GlobalPermissions.DASHBOARD_SHARING;
+import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
+import static org.sonar.db.property.PropertyTesting.newGlobalPropertyDto;
+import static org.sonarqube.ws.MediaTypes.JSON;
+
+public class ShowActionTest {
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Rule
+  public UserSessionRule userSession = UserSessionRule.standalone();
+
+  @Rule
+  public DbTester db = DbTester.create(System2.INSTANCE);
+
+  DbClient dbClient = db.getDbClient();
+  PropertyDbTester propertyDb = new PropertyDbTester(db);
+  ServerIdGenerator generator = mock(ServerIdGenerator.class);
+
+  WsActionTester ws = new WsActionTester(new ShowAction(userSession, generator, dbClient));
+
+  @Test
+  public void return_server_id_info() throws Exception {
+    setUserAsSystemAdmin();
+    when(generator.generate("home", "127.0.1")).thenReturn("server_id");
+    setAvailableIpAdresses("192.168.1.1", "127.0.1");
+    insertConfiguration("server_id", "home", "127.0.1");
+
+    ShowWsResponse response = executeRequest();
+
+    assertThat(response.getServerId()).isEqualTo("server_id");
+    assertThat(response.getOrganization()).isEqualTo("home");
+    assertThat(response.getIp()).isEqualTo("127.0.1");
+    assertThat(response.getValidIpAdressesList()).containsOnly("192.168.1.1", "127.0.1");
+    assertThat(response.hasInvalidServerId()).isFalse();
+  }
+
+  @Test
+  public void return_invalid_server_id() throws Exception {
+    setUserAsSystemAdmin();
+    when(generator.generate("home", "127.0.1")).thenReturn("server_id");
+    insertConfiguration("invalid", null, null);
+
+    ShowWsResponse response = executeRequest();
+
+    assertThat(response.hasInvalidServerId()).isTrue();
+    assertThat(response.getServerId()).isEqualTo("invalid");
+    assertThat(response.hasOrganization()).isFalse();
+    assertThat(response.hasIp()).isFalse();
+    assertThat(response.getValidIpAdressesList()).isEmpty();
+  }
+
+  @Test
+  public void return_no_server_id_info_when_no_settings_and_no_available_ips() throws Exception {
+    setUserAsSystemAdmin();
+
+    ShowWsResponse response = executeRequest();
+
+    assertThat(response.hasServerId()).isFalse();
+    assertThat(response.hasOrganization()).isFalse();
+    assertThat(response.hasIp()).isFalse();
+    assertThat(response.getValidIpAdressesList()).isEmpty();
+    assertThat(response.hasInvalidServerId()).isFalse();
+  }
+
+  @Test
+  public void return_no_server_id_info_when_no_server_id_but_other_settings() throws Exception {
+    setUserAsSystemAdmin();
+    insertConfiguration(null, "home", "127.0.1");
+
+    ShowWsResponse response = executeRequest();
+
+    assertThat(response.hasServerId()).isFalse();
+    assertThat(response.hasOrganization()).isFalse();
+    assertThat(response.hasIp()).isFalse();
+    assertThat(response.getValidIpAdressesList()).isEmpty();
+    assertThat(response.hasInvalidServerId()).isFalse();
+  }
+
+  @Test
+  public void return_available_ips_even_if_no_settings() throws Exception {
+    setUserAsSystemAdmin();
+    setAvailableIpAdresses("192.168.1.1", "127.0.1");
+
+    ShowWsResponse response = executeRequest();
+
+    assertThat(response.hasServerId()).isFalse();
+    assertThat(response.hasOrganization()).isFalse();
+    assertThat(response.hasIp()).isFalse();
+    assertThat(response.getValidIpAdressesList()).containsOnly("192.168.1.1", "127.0.1");
+    assertThat(response.hasInvalidServerId()).isFalse();
+  }
+
+  @Test
+  public void fail_when_not_system_admin() throws Exception {
+    userSession.login("not-admin").setGlobalPermissions(DASHBOARD_SHARING);
+
+    expectedException.expect(ForbiddenException.class);
+
+    executeRequest();
+  }
+
+  @Test
+  public void test_ws_definition() {
+    WebService.Action action = ws.getDef();
+    assertThat(action).isNotNull();
+    assertThat(action.isInternal()).isTrue();
+    assertThat(action.isPost()).isFalse();
+    assertThat(action.responseExampleAsString()).isNotEmpty();
+    assertThat(action.params()).isEmpty();
+  }
+
+  @Test
+  public void test_example_json_response() {
+    setUserAsSystemAdmin();
+    when(generator.generate("home", "127.0.1")).thenReturn("server_id");
+    setAvailableIpAdresses("192.168.1.1", "127.0.1");
+    insertConfiguration("server_id", "home", "127.0.1");
+
+    String result = ws.newRequest()
+      .setMediaType(JSON)
+      .execute()
+      .getInput();
+
+    JsonAssert.assertJson(ws.getDef().responseExampleAsString()).isSimilarTo(result);
+  }
+
+  private void insertConfiguration(@Nullable String serverId, @Nullable String organisation, @Nullable String ip) {
+    if (serverId != null) {
+      propertyDb.insertProperties(newGlobalPropertyDto().setKey("sonar.server_id").setValue(serverId));
+    }
+    if (organisation != null) {
+      propertyDb.insertProperties(newGlobalPropertyDto().setKey("sonar.organisation").setValue(organisation));
+    }
+    if (ip != null) {
+      propertyDb.insertProperties(newGlobalPropertyDto().setKey("sonar.server_id.ip_address").setValue(ip));
+    }
+  }
+
+  private void setAvailableIpAdresses(String... ips) {
+    List<InetAddress> availableAddresses = new ArrayList<>();
+    for (String ip : ips) {
+      InetAddress inetAddress = mock(InetAddress.class);
+      when(inetAddress.getHostAddress()).thenReturn(ip);
+      availableAddresses.add(inetAddress);
+    }
+    when(generator.getAvailableAddresses()).thenReturn(availableAddresses);
+  }
+
+  private ShowWsResponse executeRequest() {
+    TestRequest request = ws.newRequest()
+      .setMediaType(MediaTypes.PROTOBUF);
+    try {
+      return ShowWsResponse.parseFrom(request.execute().getInputStream());
+    } catch (IOException e) {
+      throw new IllegalStateException(e);
+    }
+  }
+
+  private void setUserAsSystemAdmin() {
+    userSession.login("admin").setGlobalPermissions(SYSTEM_ADMIN);
+  }
+}
diff --git a/sonar-ws/src/main/protobuf/ws-serverid.proto b/sonar-ws/src/main/protobuf/ws-serverid.proto
new file mode 100644 (file)
index 0000000..18a2a81
--- /dev/null
@@ -0,0 +1,38 @@
+// SonarQube, open source software quality management tool.
+// Copyright (C) 2008-2015 SonarSource
+// mailto:contact AT sonarsource DOT com
+//
+// SonarQube is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 3 of the License, or (at your option) any later version.
+//
+// SonarQube is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+syntax = "proto2";
+
+package sonarqube.ws.serverid;
+
+option java_package = "org.sonarqube.ws";
+option java_outer_classname = "ServerId";
+option optimize_for = SPEED;
+
+// Response of GET api/server_id/show
+message ShowWsResponse {
+  optional string serverId = 1;
+  optional string organization = 2;
+  optional string ip = 3;
+  repeated string validIpAdresses = 4;
+  optional bool invalidServerId = 5;
+}
+
+
+
+