"/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()
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;
this.acceptPrivateAddress = acceptPrivateAddress;
}
+ @CheckForNull
public String generate(String organisationName, String ipAddress) {
String id = null;
String organisation = organisationName.trim();
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;
ChangeLogLevelAction.class,
DbMigrationStatusAction.class,
+ // Server id
+ ServerIdWsModule.class,
+
// Plugins WS
PluginWSCommons.class,
PluginUpdateAggregator.class,
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
--- /dev/null
+{
+ "serverId": "server_id",
+ "organization": "home",
+ "ip": "127.0.1",
+ "validIpAdresses": [
+ "192.168.1.1",
+ "127.0.1"
+ ]
+}
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;
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
public void check_urls() throws Exception {
assertPathIsNotIgnored("/");
assertPathIsNotIgnored("/foo");
+ assertPathIsNotIgnored("/api/server_id/show");
assertPathIsIgnored("/api/authentication/login");
assertPathIsIgnored("/api/authentication/validate");
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");
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+// 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;
+}
+
+
+
+