diff options
author | Guillaume Jambet <guillaume.jambet@sonarsource.com> | 2018-02-08 15:50:43 +0100 |
---|---|---|
committer | Guillaume Jambet <guillaume.jambet@gmail.com> | 2018-03-01 15:21:05 +0100 |
commit | a5a65a2c81c6f44b1eb37d5630bd37ffb46c085a (patch) | |
tree | 95a1881d785bd678cdee65a0faeefd9c8562a3db /server/sonar-server | |
parent | 07a20e83c4037ab7853c57aa92e614d47be74cf4 (diff) | |
download | sonarqube-a5a65a2c81c6f44b1eb37d5630bd37ffb46c085a.tar.gz sonarqube-a5a65a2c81c6f44b1eb37d5630bd37ffb46c085a.zip |
SONAR-10345 Add Webhooks delete ws
Diffstat (limited to 'server/sonar-server')
5 files changed, 298 insertions, 2 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/DeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/DeleteAction.java new file mode 100644 index 00000000000..3888bb5747f --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/DeleteAction.java @@ -0,0 +1,109 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info 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.webhook.ws; + +import java.util.Optional; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.organization.OrganizationDto; +import org.sonar.db.webhook.WebhookDto; +import org.sonar.server.user.UserSession; + +import static java.util.Optional.ofNullable; +import static org.sonar.server.webhook.ws.WebhooksWsParameters.DELETE_ACTION; +import static org.sonar.server.webhook.ws.WebhooksWsParameters.KEY_PARAM; +import static org.sonar.server.webhook.ws.WebhooksWsParameters.KEY_PARAM_MAXIMUN_LENGTH; +import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; +import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; +import static org.sonar.server.ws.WsUtils.checkStateWithOptional; + +public class DeleteAction implements WebhooksWsAction { + + private final DbClient dbClient; + private final UserSession userSession; + private final WebhookSupport webhookSupport; + + public DeleteAction(DbClient dbClient, UserSession userSession, WebhookSupport webhookSupport) { + this.dbClient = dbClient; + this.userSession = userSession; + this.webhookSupport = webhookSupport; + } + + @Override + public void define(WebService.NewController controller) { + + WebService.NewAction action = controller.createAction(DELETE_ACTION) + .setPost(true) + .setDescription("Delete a Webhook.<br>" + + "Requires the global, organization or project permission.") + .setSince("7.1") + .setHandler(this); + + action.createParam(KEY_PARAM) + .setRequired(true) + .setMaximumLength(KEY_PARAM_MAXIMUN_LENGTH) + .setDescription("The key of the webhook to be deleted") + .setExampleValue(KEY_PROJECT_EXAMPLE_001); + + } + + @Override + public void handle(Request request, Response response) throws Exception { + + userSession.checkLoggedIn(); + + String webhookKey = request.param(KEY_PARAM); + + try (DbSession dbSession = dbClient.openSession(false)) { + + Optional<WebhookDto> dtoOptional = dbClient.webhookDao().selectByUuid(dbSession, webhookKey); + WebhookDto webhookDto = checkFoundWithOptional(dtoOptional, "No webhook with key '%s'", webhookKey); + + String organizationUuid = webhookDto.getOrganizationUuid(); + if (organizationUuid != null) { + Optional<OrganizationDto> optionalDto = dbClient.organizationDao().selectByUuid(dbSession, organizationUuid); + OrganizationDto organizationDto = checkStateWithOptional(optionalDto, "the requested organization '%s' was not found", organizationUuid); + webhookSupport.checkUserPermissionOn(organizationDto); + deleteWebhook(dbSession, webhookDto); + } + + String projectUuid = webhookDto.getProjectUuid(); + if (projectUuid != null) { + Optional<ComponentDto> optionalDto = ofNullable(dbClient.componentDao().selectByUuid(dbSession, projectUuid).orNull()); + ComponentDto componentDto = checkStateWithOptional(optionalDto, "the requested project '%s' was not found", projectUuid); + webhookSupport.checkUserPermissionOn(componentDto); + deleteWebhook(dbSession, webhookDto); + } + + dbSession.commit(); + } + + response.noContent(); + } + + private void deleteWebhook(DbSession dbSession, WebhookDto webhookDto) { + dbClient.webhookDao().delete(dbSession, webhookDto.getUuid()); + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsModule.java index 3927c5da41b..12625ea3634 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsModule.java @@ -30,6 +30,7 @@ public class WebhooksWsModule extends Module { SearchAction.class, CreateAction.class, UpdateAction.class, + DeleteAction.class, WebhookDeliveryAction.class, WebhookDeliveriesAction.class); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsParameters.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsParameters.java index 10b2c7b0498..bf7c97c2ca6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsParameters.java +++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsParameters.java @@ -24,9 +24,10 @@ class WebhooksWsParameters { static final String WEBHOOKS_CONTROLLER = "api/webhooks"; + static final String SEARCH_ACTION = "search"; static final String ACTION_CREATE = "create"; static final String UPDATE_ACTION = "update"; - static final String SEARCH_ACTION = "search"; + static final String DELETE_ACTION = "delete"; static final String ORGANIZATION_KEY_PARAM = "organization"; diff --git a/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/DeleteActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/DeleteActionTest.java new file mode 100644 index 00000000000..f54d55a6bd1 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/DeleteActionTest.java @@ -0,0 +1,185 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info 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.webhook.ws; + +import java.util.Optional; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.server.ws.WebService; +import org.sonar.db.DbClient; +import org.sonar.db.DbTester; +import org.sonar.db.component.ComponentDbTester; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.organization.OrganizationDbTester; +import org.sonar.db.organization.OrganizationDto; +import org.sonar.db.webhook.WebhookDbTester; +import org.sonar.db.webhook.WebhookDto; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.exceptions.UnauthorizedException; +import org.sonar.server.organization.DefaultOrganizationProvider; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.ws.TestResponse; +import org.sonar.server.ws.WsActionTester; + +import static java.net.HttpURLConnection.HTTP_NO_CONTENT; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.tuple; +import static org.junit.rules.ExpectedException.none; +import static org.sonar.api.web.UserRole.ADMIN; +import static org.sonar.db.DbTester.create; +import static org.sonar.db.permission.OrganizationPermission.ADMINISTER; +import static org.sonar.server.organization.TestDefaultOrganizationProvider.from; +import static org.sonar.server.tester.UserSessionRule.standalone; +import static org.sonar.server.webhook.ws.WebhooksWsParameters.KEY_PARAM; + +public class DeleteActionTest { + + @Rule + public ExpectedException expectedException = none(); + + @Rule + public UserSessionRule userSession = standalone(); + + @Rule + public DbTester db = create(); + private DbClient dbClient = db.getDbClient(); + private WebhookDbTester webhookDbTester = db.webhooks(); + private OrganizationDbTester organizationDbTester = db.organizations(); + private ComponentDbTester componentDbTester = db.components(); + + private DefaultOrganizationProvider defaultOrganizationProvider = from(db); + + private WebhookSupport webhookSupport = new WebhookSupport(userSession); + private DeleteAction underTest = new DeleteAction(dbClient, userSession, webhookSupport); + private WsActionTester wsActionTester = new WsActionTester(underTest); + + @Test + public void test_ws_definition() { + + WebService.Action action = wsActionTester.getDef(); + assertThat(action).isNotNull(); + assertThat(action.isInternal()).isFalse(); + assertThat(action.isPost()).isTrue(); + + assertThat(action.params()) + .extracting(WebService.Param::key, WebService.Param::isRequired) + .containsExactlyInAnyOrder(tuple("key", true)); + + } + + @Test + public void delete_a_project_webhook() { + + ComponentDto project = componentDbTester.insertPrivateProject(); + WebhookDto dto = webhookDbTester.insertWebhookForProjectUuid(project.uuid()); + userSession.logIn().addProjectPermission(ADMIN, project); + + TestResponse response = wsActionTester.newRequest() + .setParam(KEY_PARAM, dto.getUuid()) + .execute(); + + assertThat(response.getStatus()).isEqualTo(HTTP_NO_CONTENT); + Optional<WebhookDto> reloaded = webhookDbTester.selectWebhook(dto.getUuid()); + assertThat(reloaded).isEmpty(); + + } + + @Test + public void delete_an_organization_webhook() { + + OrganizationDto organization = organizationDbTester.insert(); + WebhookDto dto = webhookDbTester.insertForOrganizationUuid(organization.getUuid()); + userSession.logIn().addPermission(ADMINISTER, organization.getUuid()); + + TestResponse response = wsActionTester.newRequest() + .setParam(KEY_PARAM, dto.getUuid()) + .execute(); + + assertThat(response.getStatus()).isEqualTo(HTTP_NO_CONTENT); + Optional<WebhookDto> reloaded = webhookDbTester.selectWebhook(dto.getUuid()); + assertThat(reloaded).isEmpty(); + + } + + @Test + public void fail_if_webhook_does_not_exist() { + + userSession.logIn().addPermission(ADMINISTER, defaultOrganizationProvider.get().getUuid()); + + expectedException.expect(NotFoundException.class); + expectedException.expectMessage("No webhook with key 'inexistent-webhook-uuid'"); + + wsActionTester.newRequest() + .setParam(KEY_PARAM, "inexistent-webhook-uuid") + .execute(); + } + + @Test + public void fail_if_not_logged_in() throws Exception { + + OrganizationDto organization = organizationDbTester.insert(); + WebhookDto dto = webhookDbTester.insertForOrganizationUuid(organization.getUuid()); + userSession.anonymous(); + + expectedException.expect(UnauthorizedException.class); + + wsActionTester.newRequest() + .setParam(KEY_PARAM, dto.getUuid()) + .execute(); + + } + + @Test + public void fail_if_no_permission_on_webhook_scope_project() { + + ComponentDto project = componentDbTester.insertPrivateProject(); + WebhookDto dto = webhookDbTester.insertWebhookForProjectUuid(project.uuid()); + + userSession.logIn(); + + expectedException.expect(ForbiddenException.class); + expectedException.expectMessage("Insufficient privileges"); + + wsActionTester.newRequest() + .setParam(KEY_PARAM, dto.getUuid()) + .execute(); + + } + + @Test + public void fail_if_no_permission_on_webhook_scope_organization() { + + OrganizationDto organization = organizationDbTester.insert(); + WebhookDto dto = webhookDbTester.insertForOrganizationUuid(organization.getUuid()); + + userSession.logIn(); + + expectedException.expect(ForbiddenException.class); + expectedException.expectMessage("Insufficient privileges"); + + wsActionTester.newRequest() + .setParam(KEY_PARAM, dto.getUuid()) + .execute(); + + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhooksWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhooksWsModuleTest.java index 7afb02511bd..3ef537af8ab 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhooksWsModuleTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhooksWsModuleTest.java @@ -32,7 +32,7 @@ public class WebhooksWsModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); underTest.configure(container); - assertThat(container.size()).isEqualTo(3 + 6); + assertThat(container.size()).isEqualTo(3 + 7); } } |