From: Sébastien Lesaint Date: Wed, 22 Apr 2015 09:49:40 +0000 (+0200) Subject: SONAR-6380 add Java WS to uninstall a plugin X-Git-Tag: 5.2-RC1~2136 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=9e2100fc96ce528da9615723e6be77376b2dd43b;p=sonarqube.git SONAR-6380 add Java WS to uninstall a plugin --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 3d1f3456f2f..95a8268d529 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -19,7 +19,13 @@ */ package org.sonar.server.platform; -import com.google.common.collect.Lists; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Properties; + +import javax.annotation.Nullable; + import org.sonar.api.config.EmailSettings; import org.sonar.api.issue.action.Actions; import org.sonar.api.platform.ComponentContainer; @@ -222,6 +228,7 @@ import org.sonar.server.plugins.ws.InstalledPluginsWsAction; import org.sonar.server.plugins.ws.PendingPluginsWsAction; import org.sonar.server.plugins.ws.PluginWSCommons; import org.sonar.server.plugins.ws.PluginsWs; +import org.sonar.server.plugins.ws.UninstallPluginsWsAction; import org.sonar.server.plugins.ws.UpdatePluginsWsAction; import org.sonar.server.plugins.ws.UpdatesPluginsWsAction; import org.sonar.server.properties.ProjectSettingsFactory; @@ -383,11 +390,7 @@ import org.sonar.server.view.index.ViewIndexer; import org.sonar.server.ws.ListingWs; import org.sonar.server.ws.WebServiceEngine; -import javax.annotation.Nullable; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Properties; +import com.google.common.collect.Lists; class ServerComponents { @@ -895,6 +898,7 @@ class ServerComponents { pico.addSingleton(PendingPluginsWsAction.class); pico.addSingleton(InstallPluginsWsAction.class); pico.addSingleton(UpdatePluginsWsAction.class); + pico.addSingleton(UninstallPluginsWsAction.class); pico.addSingleton(PluginsWs.class); // Compute engine diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UninstallPluginsWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UninstallPluginsWsAction.java new file mode 100644 index 00000000000..aa74b48bf0b --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UninstallPluginsWsAction.java @@ -0,0 +1,93 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 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. + */ +package org.sonar.server.plugins.ws; + +import com.google.common.base.Predicate; +import org.sonar.api.platform.PluginMetadata; +import org.sonar.api.platform.PluginRepository; +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.permission.GlobalPermissions; +import org.sonar.server.plugins.ServerPluginJarsInstaller; +import org.sonar.server.user.UserSession; + +import javax.annotation.Nullable; + +import static com.google.common.collect.Iterables.find; +import static java.lang.String.format; + +/** + * Implementation of the {@code uninstall} action for the Plugins WebService. + */ +public class UninstallPluginsWsAction implements PluginsWsAction { + private static final String PARAM_KEY = "key"; + + private final PluginRepository pluginRepository; + private final ServerPluginJarsInstaller pluginJarsInstaller; + + public UninstallPluginsWsAction(PluginRepository pluginRepository, ServerPluginJarsInstaller pluginJarsInstaller) { + this.pluginRepository = pluginRepository; + this.pluginJarsInstaller = pluginJarsInstaller; + } + + @Override + public void define(WebService.NewController controller) { + WebService.NewAction action = controller.createAction("uninstall") + .setPost(true) + .setDescription("Uninstalls the plugin specified by its key." + + "
" + + "Requires user to be authenticated with Administer System permissions") + .setHandler(this); + + action.createParam(PARAM_KEY) + .setDescription("The key identifying the plugin to uninstall") + .setRequired(true); + } + + @Override + public void handle(Request request, Response response) throws Exception { + UserSession.get().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN); + String key = request.mandatoryParam(PARAM_KEY); + ensurePluginIsInstalled(key); + pluginJarsInstaller.uninstall(key); + response.noContent(); + } + + private void ensurePluginIsInstalled(String key) { + if (find(pluginRepository.getMetadata(), new PluginKeyPredicate(key), null) == null) { + throw new IllegalArgumentException( + format("No plugin with key '%s' or plugin '%s' is not installed", key, key)); + } + } + + private static class PluginKeyPredicate implements Predicate { + private final String key; + + public PluginKeyPredicate(String key) { + this.key = key; + } + + @Override + public boolean apply(@Nullable PluginMetadata input) { + return input != null && key.equals(input.getKey()); + } + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UninstallPluginsWsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UninstallPluginsWsActionTest.java new file mode 100644 index 00000000000..ecc71e4d719 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UninstallPluginsWsActionTest.java @@ -0,0 +1,129 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 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. + */ +package org.sonar.server.plugins.ws; + +import com.google.common.collect.ImmutableList; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.platform.PluginMetadata; +import org.sonar.api.platform.PluginRepository; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.WebService; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.plugins.DefaultPluginMetadata; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.plugins.ServerPluginJarsInstaller; +import org.sonar.server.user.MockUserSession; +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.verify; +import static org.mockito.Mockito.when; + +public class UninstallPluginsWsActionTest { + private static final String DUMMY_CONTROLLER_KEY = "dummy"; + private static final String CONTROLLER_KEY = "api/plugins"; + private static final String ACTION_KEY = "uninstall"; + private static final String KEY_PARAM = "key"; + private static final String PLUGIN_KEY = "pluginKey"; + + private PluginRepository pluginRepository = mock(PluginRepository.class); + private ServerPluginJarsInstaller pluginJarsInstaller = mock(ServerPluginJarsInstaller.class); + private UninstallPluginsWsAction underTest = new UninstallPluginsWsAction(pluginRepository, pluginJarsInstaller); + + private WsTester wsTester = new WsTester(new PluginsWs(underTest)); + private Request invalidRequest = wsTester.newGetRequest(CONTROLLER_KEY, ACTION_KEY); + private Request validRequest = wsTester.newGetRequest(CONTROLLER_KEY, ACTION_KEY).setParam(KEY_PARAM, PLUGIN_KEY); + private WsTester.TestResponse response = new WsTester.TestResponse(); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Before + public void setUp() throws Exception { + MockUserSession.set().setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN); + } + + @Test + public void user_must_have_system_admin_permission() throws Exception { + expectedException.expect(ForbiddenException.class); + expectedException.expectMessage("Insufficient privileges"); + + // no permission on user + MockUserSession.set().setGlobalPermissions(); + + underTest.handle(validRequest, response); + } + + @Test + public void action_uninstall_is_defined() throws Exception { + WsTester wsTester = new WsTester(); + WebService.NewController newController = wsTester.context().createController(DUMMY_CONTROLLER_KEY); + + underTest.define(newController); + newController.done(); + + WebService.Controller controller = wsTester.controller(DUMMY_CONTROLLER_KEY); + assertThat(controller.actions()).extracting("key").containsExactly(ACTION_KEY); + + WebService.Action action = controller.actions().iterator().next(); + assertThat(action.isPost()).isTrue(); + assertThat(action.description()).isNotEmpty(); + assertThat(action.responseExample()).isNull(); + + assertThat(action.params()).hasSize(1); + WebService.Param keyParam = action.param(KEY_PARAM); + assertThat(keyParam).isNotNull(); + assertThat(keyParam.isRequired()).isTrue(); + assertThat(keyParam.description()).isNotNull(); + } + + @Test + public void IAE_is_raised_when_key_param_is_not_provided() throws Exception { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Parameter 'key' is missing"); + + underTest.handle(invalidRequest, response); + } + + @Test + public void IAE_is_raised_when_plugin_is_not_installed() throws Exception { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("No plugin with key 'pluginKey'"); + + underTest.handle(validRequest, response); + } + + @Test + public void if_plugin_is_installed_uninstallation_is_triggered() throws Exception { + when(pluginRepository.getMetadata()).thenReturn(ImmutableList.of( + DefaultPluginMetadata.create(PLUGIN_KEY) + )); + + underTest.handle(validRequest, response); + + verify(pluginJarsInstaller).uninstall(PLUGIN_KEY); + assertThat(response.outputAsString()).isEmpty(); + } + +}