]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8535 SONAR-7304 sanitize WS api/server/*
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Wed, 14 Dec 2016 09:47:50 +0000 (10:47 +0100)
committerGitHub <noreply@github.com>
Wed, 14 Dec 2016 09:47:50 +0000 (10:47 +0100)
- SONAR-8535 drop api/server/index
- SONAR-8535 drop api/server/setup
- SONAR-7304 refactor api/server/version in Java

Signed-off-by: Simon Brandhof <simon.brandhof@sonarsource.com>
14 files changed:
it/it-tests/src/test/java/it/serverSystem/ServerSystemRestartingOrchestrator.java
it/it-tests/src/test/java/it/serverSystem/ServerSystemTest.java
server/sonar-server/src/main/java/org/sonar/server/authentication/JwtCsrfVerifier.java
server/sonar-server/src/main/java/org/sonar/server/authentication/UserSessionInitializer.java
server/sonar-server/src/main/java/org/sonar/server/platform/ws/ServerWs.java
server/sonar-server/src/main/resources/org/sonar/server/platform/ws/example-index.json [deleted file]
server/sonar-server/src/main/resources/org/sonar/server/platform/ws/example-server-version.txt [new file with mode: 0644]
server/sonar-server/src/main/resources/org/sonar/server/platform/ws/example-setup.json [deleted file]
server/sonar-server/src/test/java/org/sonar/server/authentication/JwtCsrfVerifierTest.java
server/sonar-server/src/test/java/org/sonar/server/authentication/UserSessionInitializerTest.java
server/sonar-server/src/test/java/org/sonar/server/platform/ws/ServerWsTest.java
server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/server_controller.rb [deleted file]
server/sonar-web/src/main/webapp/WEB-INF/app/models/database_migration_manager.rb [deleted file]
server/sonar-web/src/main/webapp/WEB-INF/config/routes.rb

index 8161521a34b57312c4a39f9668c34459268fd557..090d38fadd8e0aa7dd0fb4147eb807546252cd34 100644 (file)
@@ -22,17 +22,19 @@ package it.serverSystem;
 import com.sonar.orchestrator.Orchestrator;
 import com.sonar.orchestrator.locator.FileLocation;
 import java.io.File;
+import java.util.Map;
 import org.apache.commons.io.FileUtils;
 import org.junit.After;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
-import org.sonar.wsclient.services.Server;
-import org.sonar.wsclient.services.ServerQuery;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.WsResponse;
 import util.ItUtils;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.fail;
+import static util.ItUtils.newWsClient;
 
 /**
  * This class start a new orchestrator on each test case
@@ -77,8 +79,9 @@ public class ServerSystemRestartingOrchestrator {
       .build();
     orchestrator.start();
 
-    Server.Status status = orchestrator.getServer().getAdminWsClient().find(new ServerQuery()).getStatus();
-    assertThat(status).isEqualTo(Server.Status.UP);
+    WsResponse statusResponse = newWsClient(orchestrator).wsConnector().call(new GetRequest("api/system/status"));
+    Map<String, Object> json = ItUtils.jsonToMap(statusResponse.content());
+    assertThat(json.get("status")).isEqualTo("UP");
   }
 
   // SONAR-4748
index 1cd6ca181a48a8826367149142d9a6a5c8de2adf..e4f08d62afc95e1c8d494951e67a390243c9c1d1 100644 (file)
@@ -28,13 +28,10 @@ import java.util.Map;
 import okhttp3.Response;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.StringUtils;
 import org.json.simple.JSONValue;
 import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Test;
-import org.sonar.wsclient.services.Server;
-import org.sonar.wsclient.services.ServerQuery;
 import org.sonarqube.ws.MediaTypes;
 import org.sonarqube.ws.ServerId.ShowWsResponse;
 import org.sonarqube.ws.client.GetRequest;
@@ -44,10 +41,12 @@ import pageobjects.Navigation;
 import pageobjects.ServerIdPage;
 import util.ItUtils;
 
+import static org.apache.commons.lang.StringUtils.startsWithAny;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.fail;
 import static util.ItUtils.call;
 import static util.ItUtils.newAdminWsClient;
+import static util.ItUtils.newWsClient;
 import static util.selenium.Selenese.runSelenese;
 
 public class ServerSystemTest {
@@ -62,15 +61,18 @@ public class ServerSystemTest {
 
   @Test
   public void get_sonarqube_version() {
-    String version = orchestrator.getServer().getWsClient().find(new ServerQuery()).getVersion();
-    if (!StringUtils.startsWithAny(version, new String[] {"5.", "6."})) {
+    Map<String, Object> json = callStatus();
+
+    String version = (String)json.get("version");
+    if (!startsWithAny(version, new String[] {"6."})) {
       fail("Bad version: " + version);
     }
   }
 
   @Test
   public void get_server_status() {
-    assertThat(orchestrator.getServer().getWsClient().find(new ServerQuery()).getStatus()).isEqualTo(Server.Status.UP);
+    Map<String, Object> json = callStatus();
+    assertThat(json.get("status")).isEqualTo("UP");
   }
 
   @Test
@@ -99,6 +101,11 @@ public class ServerSystemTest {
     assertThat(serverId).isNotEmpty();
   }
 
+  private Map<String, Object> callStatus() {
+    WsResponse statusResponse = newWsClient(orchestrator).wsConnector().call(new GetRequest("api/system/status"));
+    return ItUtils.jsonToMap(statusResponse.content());
+  }
+
   @Test
   public void display_system_info() {
     runSelenese(orchestrator, "/serverSystem/ServerSystemTest/system_info.html");
index 18e460a1a329a51d1a30862d376e6f4b04b95a8c..89bb917aef7c8021098da435e5f06f010319fbcb 100644 (file)
@@ -51,7 +51,6 @@ public class JwtCsrfVerifier {
     "/api/issues/bulk_change",
     "/api/projects/create",
     "/api/properties/create",
-    "/api/server",
     "/api/user_properties");
 
   public String generateState(HttpServletRequest request, HttpServletResponse response, int timeoutInSeconds) {
index b0fcf8a7a72a6927838445b32f0bbe620b7b393f..c419f03465308ef6b8bec98b49de6ad5207d4846 100644 (file)
@@ -64,7 +64,7 @@ public class UserSessionInitializer {
     "/setup/*",
     "/sessions/*",
     "/api/system/db_migration_status", "/api/system/status", "/api/system/migrate_db",
-    "/api/server/index", "/api/server/setup", "/api/server/version",
+    "/api/server/version",
     "/api/users/identity_providers", "/api/l10n/index",
     LOGIN_URL, LOGOUT_URL, VALIDATE_URL);
 
index 4683e3a28385fd0f2358f0908a93804767469e0f..9ccf5690922cb476fd9ea5e63b7668853f204ed1 100644 (file)
 package org.sonar.server.platform.ws;
 
 import com.google.common.io.Resources;
-import org.sonar.api.server.ws.RailsHandler;
+import org.apache.commons.io.IOUtils;
+import org.sonar.api.platform.Server;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
+import org.sonarqube.ws.MediaTypes;
 
-public class ServerWs implements WebService {
+public class ServerWs implements WebService, RequestHandler {
 
-  @Override
-  public void define(Context context) {
-    NewController controller = context.createController("api/server")
-      .setDescription("Get system properties and upgrade db")
-      .setSince("2.10");
-
-    defineIndexAction(controller);
-    defineSetupAction(controller);
+  private final Server server;
 
-    controller.done();
+  public ServerWs(Server server) {
+    this.server = server;
   }
 
-  private void defineIndexAction(NewController controller) {
-    NewAction action = controller.createAction("index")
-      .setDescription("Get the server status:" +
-        "<ul>" +
-        "<li>UP</li>" +
-        "<li>DOWN (generally for database connection failures)</li>" +
-        "<li>SETUP (if the server must be upgraded)</li>" +
-        "<li>MIGRATION_RUNNING (the upgrade process is currently running)</li>" +
-        "</ul>")
-      .setSince("2.10")
-      .setHandler(RailsHandler.INSTANCE)
-      .setInternal(true)
-      .setResponseExample(Resources.getResource(this.getClass(), "example-index.json"));
-
-    RailsHandler.addFormatParam(action);
-  }
+  @Override
+  public void define(Context context) {
+    NewController controller = context.createController("api/server");
 
-  private void defineSetupAction(NewController controller) {
-    NewAction action = controller.createAction("setup")
-      .setDescription("Upgrade the SonarQube database")
+    controller.createAction("version")
+      .setDescription("Version of SonarQube in plain text")
       .setSince("2.10")
-      .setPost(true)
-      .setInternal(true)
-      .setHandler(RailsHandler.INSTANCE)
-      .setResponseExample(Resources.getResource(this.getClass(), "example-setup.json"));
+      .setResponseExample(Resources.getResource(this.getClass(), "example-server-version.txt"))
+      .setHandler(this);
 
-    action.createParam("format")
-      .setDescription("Response format")
-      .setPossibleValues("json", "csv", "text");
+    controller.done();
   }
 
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    response.stream().setMediaType(MediaTypes.TXT);
+    IOUtils.write(server.getVersion(), response.stream().output());
+  }
 }
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/platform/ws/example-index.json b/server/sonar-server/src/main/resources/org/sonar/server/platform/ws/example-index.json
deleted file mode 100644 (file)
index 2465118..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "id": "20140507170538",
-  "version": "4.3",
-  "status": "UP"
-}
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/platform/ws/example-server-version.txt b/server/sonar-server/src/main/resources/org/sonar/server/platform/ws/example-server-version.txt
new file mode 100644 (file)
index 0000000..67eadac
--- /dev/null
@@ -0,0 +1 @@
+6.3.0.1234
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/platform/ws/example-setup.json b/server/sonar-server/src/main/resources/org/sonar/server/platform/ws/example-setup.json
deleted file mode 100644 (file)
index e941957..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "status": "ok",
-  "migration_status": "NO_MIGRATION",
-  "message": "Database is up-to-date, no migration needed."
-}
index d3044aa63752c0eab93b1b61c09c90b59eff67cd..61babbeab1cdcf3d6c7c1a1e491cb1778cdd738a 100644 (file)
@@ -156,7 +156,6 @@ public class JwtCsrfVerifierTest {
     executeVerifyStateDoesNotFailOnRequest("/api/issues/bulk_change?key=ABCD", "POST");
     executeVerifyStateDoesNotFailOnRequest("/api/projects/create?key=ABCD", "POST");
     executeVerifyStateDoesNotFailOnRequest("/api/properties/create?key=ABCD", "POST");
-    executeVerifyStateDoesNotFailOnRequest("/api/server", "POST");
     executeVerifyStateDoesNotFailOnRequest("/api/user_properties", "POST");
   }
 
index 30857db415447232b026e04e0d2b5c8ff26e9ead..75097985af95dcd757c91b38c828572894541050 100644 (file)
@@ -107,8 +107,6 @@ public class UserSessionInitializerTest {
     assertPathIsIgnored("/api/system/db_migration_status");
     assertPathIsIgnored("/api/system/status");
     assertPathIsIgnored("/api/system/migrate_db");
-    assertPathIsIgnored("/api/server/index");
-    assertPathIsIgnored("/api/server/setup");
     assertPathIsIgnored("/api/server/version");
     assertPathIsIgnored("/api/users/identity_providers");
     assertPathIsIgnored("/api/l10n/index");
index 77c2e9ed6fc8a45bfdf13b3ffb31e8b7c2db3c7c..29ca6ee1b7f55dc8b99beb9073068fabbc138661 100644 (file)
 package org.sonar.server.platform.ws;
 
 import org.junit.Test;
-import org.sonar.api.server.ws.RailsHandler;
+import org.sonar.api.platform.Server;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.server.ws.WsTester;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 public class ServerWsTest {
 
-  WsTester tester = new WsTester(new ServerWs());
+  private Server server = mock(Server.class);
+  private WsTester tester = new WsTester(new ServerWs(server));
 
   @Test
-  public void define_controller() {
+  public void define_version_action() {
     WebService.Controller controller = tester.controller("api/server");
-    assertThat(controller).isNotNull();
-    assertThat(controller.since()).isEqualTo("2.10");
-    assertThat(controller.description()).isNotEmpty();
-    assertThat(controller.actions()).hasSize(2);
+    assertThat(controller.actions()).hasSize(1);
+
+    WebService.Action versionAction = controller.action("version");
+    assertThat(versionAction.since()).isEqualTo("2.10");
+    assertThat(versionAction.description()).isNotEmpty();
+    assertThat(versionAction.isPost()).isFalse();
   }
 
   @Test
-  public void define_index_action() {
-    WebService.Controller controller = tester.controller("api/server");
-
-    WebService.Action action = controller.action("index");
-    assertThat(action).isNotNull();
-    assertThat(action.handler()).isInstanceOf(RailsHandler.class);
-    assertThat(action.responseExampleAsString()).isNotEmpty();
-    assertThat(action.params()).hasSize(1);
+  public void returns_version_as_plain_text() throws Exception {
+    when(server.getVersion()).thenReturn("6.4-SNAPSHOT");
+    WsTester.Result result = tester.newGetRequest("api/server", "version").execute();
+    assertThat(result.outputAsString()).isEqualTo("6.4-SNAPSHOT");
   }
 
   @Test
-  public void define_setup_action() {
-    WebService.Controller controller = tester.controller("api/server");
-
-    WebService.Action action = controller.action("setup");
-    assertThat(action).isNotNull();
-    assertThat(action.handler()).isInstanceOf(RailsHandler.class);
-    assertThat(action.responseExampleAsString()).isNotEmpty();
-    assertThat(action.params()).hasSize(1);
+  public void test_example_of_version() throws Exception {
+    WebService.Action versionAction = tester.action("api/server", "version");
+    assertThat(versionAction.responseExampleAsString()).isEqualTo("6.3.0.1234");
   }
-
 }
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/server_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/server_controller.rb
deleted file mode 100644 (file)
index d1b8982..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2016 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.
-#
-class Api::ServerController < Api::ApiController
-
-  skip_before_filter :check_authentication
-
-  # prevent HTTP proxies from caching server status
-  before_filter :set_cache_buster, :only => 'index'
-
-  # execute database setup
-  skip_before_filter :check_database_version, :setup
-
-  def version
-    render :text => Java::OrgSonarServerPlatform::Platform.getServer().getVersion()
-  end
-
-  def index
-    hash={:id => Java::OrgSonarServerPlatform::Platform.getServer().getId(), :version => Java::OrgSonarServerPlatform::Platform.getServer().getVersion()}
-    complete_with_status(hash)
-    respond_to do |format|
-      format.json{ render :json => jsonp(hash) }
-      format.xml { render :xml => hash.to_xml(:skip_types => true, :root => 'server') }
-      format.text { render :text => text_not_supported}
-    end
-  end
-
-  def setup
-    verify_post_request
-    manager=DatabaseMigrationManager.instance
-    begin
-      # Ask the DB migration manager to start the migration
-      # => No need to check for authorizations (actually everybody can run the upgrade)
-      # nor concurrent calls (this is handled directly by DatabaseMigrationManager)
-      manager.start_migration
-
-      operational=manager.is_sonar_access_allowed?
-      current_status = operational ? "ok" : "ko"
-      hash={
-        # deprecated fields
-        :status => current_status,
-        :migration_status => manager.status,
-
-        # correct fields
-        :operational => operational,
-        :state => manager.status
-      }
-      hash[:message]=manager.message if manager.message
-      hash[:startedAt]=manager.migration_start_time if manager.migration_start_time
-
-      respond_to do |format|
-        format.json{ render :json => jsonp(hash) }
-        format.xml { render :xml => hash.to_xml(:skip_types => true, :root => 'setup') }
-        format.text { render :text => hash[:status] }
-      end
-    rescue => e
-      hash={
-        # deprecated fields
-        :status => 'ko',
-        :msg => e.message,
-
-        # correct fields
-        :message => e.message,
-        :state => manager.status
-      }
-      respond_to do |format|
-        format.json{ render :json => jsonp(hash) }
-        format.xml { render :xml => hash.to_xml(:skip_types => true, :root => 'setup') }
-        format.text { render :text => hash[:status] }
-      end
-    end
-  end
-
-  private
-
-  def server_properties_to_json(properties)
-    hash={}
-    properties.each do |prop|
-      hash[prop[0].to_s]=prop[1].to_s
-    end
-    hash
-  end
-
-  def complete_with_status(hash)
-    if DatabaseMigrationManager.instance.is_sonar_access_allowed?
-      hash[:status]='UP'
-    elsif DatabaseMigrationManager.instance.migration_running?
-      hash[:status]='MIGRATION_RUNNING'
-    elsif DatabaseMigrationManager.instance.requires_migration?
-      hash[:status]='SETUP'
-    else
-      # migration failed or not connected to the database 
-      hash[:status]='DOWN'
-      hash[:status_msg]=DatabaseMigrationManager.instance.message
-    end
-  end
-
-  def set_cache_buster
-    response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
-    response.headers["Pragma"] = "no-cache"
-    response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
-  end
-end
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/models/database_migration_manager.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/models/database_migration_manager.rb
deleted file mode 100644 (file)
index 139da4a..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2016 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.
-#
-
-#
-# Class that centralizes the management the DB migration
-#
-
-require 'singleton'
-require 'thread'
-
-class DatabaseMigrationManager
-  
-  # mixin the singleton module to ensure we have only one instance of the class
-  # it will be accessible with "DatabaseMigrationManager.instance"
-  include Singleton
-  
-  # the status of the migration
-  @status
-  MIGRATION_NEEDED = "MIGRATION_NEEDED"
-  MIGRATION_RUNNING = "MIGRATION_RUNNING"
-  MIGRATION_FAILED = "MIGRATION_FAILED"
-  MIGRATION_SUCCEEDED = "MIGRATION_SUCCEEDED"
-  NO_MIGRATION = "NO_MIGRATION"
-  
-  # the corresponding message that can be given to the user
-  @message
-  
-  # the time when the migration was started
-  @start_time
-  
-  def initialize
-    if !ActiveRecord::Base.connected?
-      @status = MIGRATION_FAILED
-      @message = "Not connected to database."
-    elsif DatabaseVersion.uptodate?
-      @status = NO_MIGRATION
-      @message = "Database is up-to-date, no migration needed."
-    else
-      if DatabaseVersion.production?
-        @status = MIGRATION_NEEDED
-        @message = "Migration required."
-      else
-        @status = MIGRATION_FAILED
-        @message = "Upgrade is not supported. Please use a <a href=\"http://redirect.sonarsource.com/doc/requirements.html\">production-ready database</a>."
-      end
-    end
-  end
-  
-  def message
-    @message
-  end
-  
-  def status
-    @status
-  end
-  
-  def requires_migration?
-    @status==MIGRATION_NEEDED
-  end
-  
-  def migration_running?
-    @status==MIGRATION_RUNNING
-  end
-  
-  def migration_failed?
-    @status==MIGRATION_FAILED
-  end
-  
-  def is_sonar_access_allowed?
-    @status==NO_MIGRATION || @status==MIGRATION_SUCCEEDED
-  end
-  
-  def migration_start_time
-    @start_time
-  end
-  
-  def start_migration
-    # Use an exclusive block of code to ensure that only 1 thread will be able to proceed with the migration
-    requires_migration = false
-    Thread.exclusive do
-      requires_migration = requires_migration?
-    end
-    
-    if requires_migration
-      Thread.new do
-        begin
-          @status = MIGRATION_RUNNING
-          @message = "Database migration is running"
-          Thread.current[:name] = "Database Upgrade"
-          @start_time = Time.now
-
-          DatabaseVersion.upgrade_and_start
-
-          @status = MIGRATION_SUCCEEDED
-          @message = "Migration succeeded."
-        rescue Exception => e
-          @status = MIGRATION_FAILED
-          @message = "Migration failed: " + Api::Utils.exception_message(e) + ".<br/> Please check logs."
-          Api::Utils.java_facade.logError("Fail to upgrade database\n#{Api::Utils.exception_message(e, :backtrace => true)}")
-        end
-      end
-    end
-  end
-  
-end
index 2931db1259083f079b1ae3a4f612f56d4f9e3419..e51f89c25d8fca4b8c9443f62349bde281f85db0 100644 (file)
@@ -8,7 +8,6 @@ ActionController::Routing::Routes.draw do |map|
 
   map.connect 'api', :controller => 'api/java_ws', :action => 'redirect_to_ws_listing'
 
-  map.connect 'api/server/:action', :controller => 'api/server'
   map.connect 'api/resoures', :controller => 'api/resources', :action => 'index'
 
   map.resources 'properties', :path_prefix => 'api', :controller => 'api/properties', :requirements => { :id => /.*/ }