From 564cb0421247ae0e36b4fde6cb524105ce1a2334 Mon Sep 17 00:00:00 2001 From: Pierre Guillot Date: Wed, 15 May 2019 11:20:13 +0200 Subject: [PATCH] SONAR-11951 Prevent deleting all projects when using api/projects/bulk_delete without any parameter --- .../server/project/ws/BulkDeleteAction.java | 22 ++++++++++++++- .../project/ws/BulkDeleteActionTest.java | 27 ++++++++++++++++--- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java index 8d006f61207..1d55087544c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java @@ -19,6 +19,7 @@ */ package org.sonar.server.project.ws; +import com.google.common.base.Strings; import java.util.List; import javax.annotation.CheckForNull; import javax.annotation.Nullable; @@ -40,7 +41,9 @@ import org.sonar.server.project.ProjectLifeCycleListeners; import org.sonar.server.project.Visibility; import org.sonar.server.user.UserSession; +import static com.google.common.base.Preconditions.checkArgument; import static java.lang.Math.min; +import static java.lang.String.format; import static org.sonar.api.resources.Qualifiers.APP; import static org.sonar.api.resources.Qualifiers.PROJECT; import static org.sonar.api.resources.Qualifiers.VIEW; @@ -85,7 +88,9 @@ public class BulkDeleteAction implements ProjectsWsAction { "Requires 'Administer System' permission.") .setSince("5.2") .setHandler(this) - .setChangelog(new Change("6.7.2", "Only the 1'000 first items in project filters are taken into account")); + .setChangelog( + new Change("7.8", format("parameters are optionals, but at least one is required among %s, %s and %s", PARAM_ANALYZED_BEFORE, PARAM_PROJECTS, Param.TEXT_QUERY)), + new Change("6.7.2", "Only the 1'000 first items in project filters are taken into account")); support.addOrganizationParam(action); @@ -140,9 +145,12 @@ public class BulkDeleteAction implements ProjectsWsAction { public void handle(Request request, Response response) throws Exception { SearchRequest searchRequest = toSearchWsRequest(request); userSession.checkLoggedIn(); + + try (DbSession dbSession = dbClient.openSession(false)) { OrganizationDto organization = support.getOrganization(dbSession, searchRequest.getOrganization()); userSession.checkPermission(OrganizationPermission.ADMINISTER, organization); + checkAtLeastOneParameterIsPresent(searchRequest); ComponentQuery query = buildDbQuery(searchRequest); List componentDtos = dbClient.componentDao().selectByQuery(dbSession, organization.getUuid(), query, 0, Integer.MAX_VALUE); @@ -155,6 +163,18 @@ public class BulkDeleteAction implements ProjectsWsAction { response.noContent(); } + private void checkAtLeastOneParameterIsPresent(SearchRequest searchRequest) { + boolean analyzedBeforePresent = !Strings.isNullOrEmpty(searchRequest.getAnalyzedBefore()); + List projects = searchRequest.getProjects(); + boolean projectsPresent = projects != null && !projects.isEmpty(); + List projectIds = searchRequest.getProjectIds(); + boolean projectIdsPresent = projectIds != null && !projectIds.isEmpty(); + boolean queryPresent = !Strings.isNullOrEmpty(searchRequest.getQuery()); + boolean atLeastOneParameterIsPresent = analyzedBeforePresent || projectsPresent || queryPresent || projectIdsPresent; + + checkArgument(atLeastOneParameterIsPresent, format("At lease one parameter among %s, %s, %s, and %s must be provided", PARAM_ANALYZED_BEFORE, PARAM_PROJECTS, PARAM_PROJECT_IDS, Param.TEXT_QUERY)); + } + private static SearchRequest toSearchWsRequest(Request request) { return SearchRequest.builder() .setOrganization(request.param(PARAM_ORGANIZATION)) diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkDeleteActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkDeleteActionTest.java index b018e060352..8c2bcf62e25 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkDeleteActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkDeleteActionTest.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; +import java.util.stream.Stream; import org.apache.commons.lang.StringUtils; import org.junit.Before; import org.junit.Rule; @@ -128,6 +129,22 @@ public class BulkDeleteActionTest { verifyListenersOnProjectsDeleted(toDeleteInOrg1, toDeleteInOrg2); } + @Test + public void throw_IllegalArgumentException_if_request_without_any_parameters(){ + userSession.logIn().setRoot(); + db.components().insertPrivateProject(org1); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("At lease one parameter among analyzedBefore, projects, projectIds, and q must be provided"); + + try { + ws.newRequest().execute(); + } finally { + verifyNoDeletions(); + verifyZeroInteractions(projectLifeCycleListeners); + } + } + @Test public void projects_that_dont_exist_are_ignored_and_dont_break_bulk_deletion() { userSession.logIn().setRoot(); @@ -169,7 +186,7 @@ public class BulkDeleteActionTest { ComponentDto analyzedProject = db.components().insertPrivateProject(); db.components().insertSnapshot(newAnalysis(analyzedProject)); - ws.newRequest().setParam(PARAM_ON_PROVISIONED_ONLY, "true").execute(); + ws.newRequest().setParam(PARAM_PROJECTS, provisionedProject.getKey() + "," + analyzedProject.getKey()).setParam(PARAM_ON_PROVISIONED_ONLY, "true").execute(); verifyDeleted(provisionedProject); verifyListenersOnProjectsDeleted(provisionedProject); @@ -180,7 +197,8 @@ public class BulkDeleteActionTest { userSession.logIn().addPermission(ADMINISTER, db.getDefaultOrganization()); ComponentDto[] projects = IntStream.range(0, 55).mapToObj(i -> db.components().insertPrivateProject()).toArray(ComponentDto[]::new); - ws.newRequest().execute(); + List projectKeys = Stream.of(projects).map(ComponentDto::getKey).collect(Collectors.toList()); + ws.newRequest().setParam(PARAM_PROJECTS, String.join(",", projectKeys)).execute(); verifyDeleted(projects); verifyListenersOnProjectsDeleted(projects); @@ -192,7 +210,10 @@ public class BulkDeleteActionTest { ComponentDto project = db.components().insertPrivateProject(); ComponentDto view = db.components().insertView(); - ws.newRequest().setParam(PARAM_QUALIFIERS, String.join(",", Qualifiers.PROJECT, Qualifiers.VIEW)).execute(); + ws.newRequest() + .setParam(PARAM_PROJECTS, project.getKey() + "," + view.getKey()) + .setParam(PARAM_QUALIFIERS, String.join(",", Qualifiers.PROJECT, Qualifiers.VIEW)) + .execute(); verifyDeleted(project, view); verifyListenersOnProjectsDeleted(project, view); -- 2.39.5