From 5819bd5d7d0cbe13cd2ceda0650032f8009ef0d4 Mon Sep 17 00:00:00 2001 From: Klaudio Sinani Date: Mon, 8 Nov 2021 17:56:58 +0100 Subject: [PATCH] SONAR-15579 Improve error verbosity on permission template matching key collision + unit test --- .../template/PermissionTemplateDbTester.java | 6 ++- .../template/PermissionTemplateTesting.java | 9 +++- .../TemplateMatchingKeyException.java | 51 +++++++++++++++++++ .../TemplateMatchingKeyExceptionTest.java | 35 +++++++++++++ .../permission/PermissionTemplateService.java | 3 +- .../PermissionTemplateServiceTest.java | 28 ++++++++++ .../org/sonar/server/ws/WebServiceEngine.java | 2 +- 7 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/TemplateMatchingKeyException.java create mode 100644 server/sonar-webserver-api/src/test/java/org/sonar/server/exceptions/TemplateMatchingKeyExceptionTest.java diff --git a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/permission/template/PermissionTemplateDbTester.java b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/permission/template/PermissionTemplateDbTester.java index 96500a78418..6f1c4cc41a7 100644 --- a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/permission/template/PermissionTemplateDbTester.java +++ b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/permission/template/PermissionTemplateDbTester.java @@ -19,6 +19,7 @@ */ package org.sonar.db.permission.template; +import java.util.function.Consumer; import javax.annotation.Nullable; import org.sonar.db.DbClient; import org.sonar.db.DbSession; @@ -59,8 +60,9 @@ public class PermissionTemplateDbTester { ofNullable(portfoliosDefaultTemplate).map(PermissionTemplateDto::getUuid).orElse(null)); } - public PermissionTemplateDto insertTemplate() { - return insertTemplate(newPermissionTemplateDto()); + @SafeVarargs + public final PermissionTemplateDto insertTemplate(Consumer... populators) { + return insertTemplate(newPermissionTemplateDto(populators)); } public PermissionTemplateDto insertTemplate(PermissionTemplateDto template) { diff --git a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/permission/template/PermissionTemplateTesting.java b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/permission/template/PermissionTemplateTesting.java index 7f66840d1a6..61279e233ec 100644 --- a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/permission/template/PermissionTemplateTesting.java +++ b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/permission/template/PermissionTemplateTesting.java @@ -20,21 +20,26 @@ package org.sonar.db.permission.template; import java.util.Date; +import java.util.function.Consumer; import org.apache.commons.lang.math.RandomUtils; import org.sonar.core.util.Uuids; import org.sonar.db.permission.PermissionsTestHelper; +import static java.util.Arrays.stream; import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; import static org.apache.commons.lang.RandomStringUtils.randomAscii; public class PermissionTemplateTesting { - public static PermissionTemplateDto newPermissionTemplateDto() { - return new PermissionTemplateDto() + @SafeVarargs + public static PermissionTemplateDto newPermissionTemplateDto(Consumer... populators) { + PermissionTemplateDto dto = new PermissionTemplateDto() .setName(randomAlphanumeric(60)) .setDescription(randomAscii(500)) .setUuid(Uuids.create()) .setCreatedAt(new Date()) .setUpdatedAt(new Date()); + stream(populators).forEach(p -> p.accept(dto)); + return dto; } public static PermissionTemplateUserDto newPermissionTemplateUserDto() { diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/TemplateMatchingKeyException.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/TemplateMatchingKeyException.java new file mode 100644 index 00000000000..b296dd6f282 --- /dev/null +++ b/server/sonar-webserver-api/src/main/java/org/sonar/server/exceptions/TemplateMatchingKeyException.java @@ -0,0 +1,51 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.exceptions; + + import com.google.common.base.MoreObjects; + import java.util.List; + + import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; + import static java.util.Collections.singletonList; + +/** + * Provided request is not valid due to Permission template matching key collision. + */ +public class TemplateMatchingKeyException extends ServerException { + private final transient List errors; + + public TemplateMatchingKeyException(String errorMessage) { + super(HTTP_BAD_REQUEST, errorMessage); + this.errors = singletonList(errorMessage); + } + + public List errors() { + return this.errors; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("errors", this.errors()) + .toString(); + } + +} + diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/exceptions/TemplateMatchingKeyExceptionTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/exceptions/TemplateMatchingKeyExceptionTest.java new file mode 100644 index 00000000000..e68355a8839 --- /dev/null +++ b/server/sonar-webserver-api/src/test/java/org/sonar/server/exceptions/TemplateMatchingKeyExceptionTest.java @@ -0,0 +1,35 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.exceptions; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TemplateMatchingKeyExceptionTest { + + @Test + public void testMethods() { + TemplateMatchingKeyException ex = new TemplateMatchingKeyException("error"); + + assertThat(ex).hasToString("TemplateMatchingKeyException{errors=[error]}"); + } + +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/permission/PermissionTemplateService.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/permission/PermissionTemplateService.java index 487cf1cfa67..e3f684c7b9d 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/permission/PermissionTemplateService.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/permission/PermissionTemplateService.java @@ -46,6 +46,7 @@ import org.sonar.db.user.UserDto; import org.sonar.db.user.UserId; import org.sonar.server.es.ProjectIndexer; import org.sonar.server.es.ProjectIndexers; +import org.sonar.server.exceptions.TemplateMatchingKeyException; import org.sonar.server.permission.DefaultTemplatesResolver.ResolvedDefaultTemplates; import org.sonar.server.user.UserSession; @@ -230,7 +231,7 @@ public class PermissionTemplateService { templatesNames.append(", "); } } - throw new IllegalStateException(MessageFormat.format( + throw new TemplateMatchingKeyException(MessageFormat.format( "The \"{0}\" key matches multiple permission templates: {1}." + " A system administrator must update these templates so that only one of them matches the key.", componentKey, diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/permission/PermissionTemplateServiceTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/permission/PermissionTemplateServiceTest.java index aa55361ae54..b8e68cfd14d 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/permission/PermissionTemplateServiceTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/permission/PermissionTemplateServiceTest.java @@ -20,6 +20,8 @@ package org.sonar.server.permission; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.annotation.Nullable; import org.junit.Rule; import org.junit.Test; @@ -36,10 +38,12 @@ import org.sonar.db.user.GroupDto; import org.sonar.db.user.UserDto; import org.sonar.server.es.ProjectIndexers; import org.sonar.server.es.TestProjectIndexers; +import org.sonar.server.exceptions.TemplateMatchingKeyException; import org.sonar.server.tester.UserSessionRule; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.sonar.api.resources.Qualifiers.APP; import static org.sonar.api.resources.Qualifiers.PROJECT; import static org.sonar.api.resources.Qualifiers.VIEW; @@ -63,6 +67,7 @@ public class PermissionTemplateServiceTest { private final ProjectIndexers projectIndexers = new TestProjectIndexers(); private final PermissionTemplateService underTest = new PermissionTemplateService(dbTester.getDbClient(), projectIndexers, userSession, defaultTemplatesResolver, new SequenceUuidFactory()); + private ComponentDto privateProject; @Test public void apply_does_not_insert_permission_to_group_AnyOne_when_applying_template_on_private_project() { @@ -452,6 +457,29 @@ public class PermissionTemplateServiceTest { checkWouldUserHaveScanPermission(null, false); } + @Test + public void apply_permission_template_with_key_pattern_collision() { + final String key = "hi-test"; + final String keyPattern = ".*-test"; + + Stream templates = Stream.of( + templateDb.insertTemplate(t -> t.setKeyPattern(keyPattern)), + templateDb.insertTemplate(t -> t.setKeyPattern(keyPattern)) + ); + + String templateNames = templates + .map(PermissionTemplateDto::getName) + .sorted(String.CASE_INSENSITIVE_ORDER) + .map(x -> String.format("\"%s\"", x)) + .collect(Collectors.joining(", ")); + + ComponentDto project = dbTester.components().insertPrivateProject(p -> p.setDbKey(key)); + + assertThatThrownBy(() -> underTest.applyDefaultToNewComponent(session, project, null)) + .isInstanceOf(TemplateMatchingKeyException.class) + .hasMessageContaining("The \"%s\" key matches multiple permission templates: %s.", key, templateNames); + } + private void checkWouldUserHaveScanPermission(@Nullable String userUuid, boolean expectedResult) { assertThat(underTest.wouldUserHaveScanPermissionWithDefaultTemplate(session, userUuid, "PROJECT_KEY")) .isEqualTo(expectedResult); diff --git a/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/WebServiceEngine.java b/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/WebServiceEngine.java index 52082613569..5ba171e5ccf 100644 --- a/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/WebServiceEngine.java +++ b/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/WebServiceEngine.java @@ -37,8 +37,8 @@ import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.text.JsonWriter; -import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.BadConfigurationException; +import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.ServerException; import org.sonarqube.ws.MediaTypes; -- 2.39.5