From 2fa3148c3ffa99ca8ad4e02f0eba43275f8c2bec Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Lievremont Date: Tue, 1 Oct 2013 09:25:34 +0200 Subject: [PATCH] SONAR-4713 Fail on unprovisioned project Add new property to make provisioning mandatory Turn ProjectReactorValidator into an IoC-managed component Add check at project scan bootstrap to fail if project does not exist --- .../org/sonar/plugins/core/CorePlugin.java | 10 ++++++- .../sonar/batch/scan/ProjectReactorReady.java | 14 +++++----- .../batch/scan/ProjectReactorValidator.java | 12 ++++++++- .../java/org/sonar/batch/scan/ScanTask.java | 1 + .../batch/scan/ProjectReactorReadyTest.java | 7 +++-- .../scan/ProjectReactorValidatorTest.java | 27 ++++++++++++++++++- .../org/sonar/batch/scan/ScanTaskTest.java | 5 ++-- .../java/org/sonar/api/CoreProperties.java | 5 ++++ 8 files changed, 64 insertions(+), 17 deletions(-) diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java index 5c12d99859a..ac4dbd8b359 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java @@ -377,8 +377,16 @@ public final class CorePlugin extends SonarPlugin { .description("Forcing user authentication stops un-logged users to access SonarQube.") .type(PropertyType.BOOLEAN) .category(CoreProperties.CATEGORY_SECURITY) + .build(), + + PropertyDefinition.builder(CoreProperties.CORE_PREVENT_AUTOMATIC_PROJECT_CREATION) + .defaultValue(Boolean.toString(false)) + .name("Prevent automatic project creation") + .description("Set to true to prevent automatic project creation at first analysis and force project provisioning.") + .type(PropertyType.BOOLEAN) + .category(CoreProperties.CATEGORY_SECURITY) .build() - ); + ); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorReady.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorReady.java index 7ac565a9661..d6569adedef 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorReady.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorReady.java @@ -20,9 +20,8 @@ package org.sonar.batch.scan; import org.sonar.api.batch.bootstrap.ProjectBuilder; -import org.sonar.api.batch.bootstrap.internal.ProjectBuilderContext; import org.sonar.api.batch.bootstrap.ProjectReactor; -import org.sonar.api.config.Settings; +import org.sonar.api.batch.bootstrap.internal.ProjectBuilderContext; import javax.annotation.Nullable; @@ -40,19 +39,19 @@ import javax.annotation.Nullable; public class ProjectReactorReady { private final ProjectReactor reactor; - private final Settings settings; private ProjectBuilder[] projectBuilders; private ProjectExclusions exclusions; + private ProjectReactorValidator validator; - public ProjectReactorReady(ProjectExclusions exclusions, ProjectReactor reactor, Settings settings, @Nullable ProjectBuilder[] projectBuilders) { + public ProjectReactorReady(ProjectExclusions exclusions, ProjectReactor reactor, @Nullable ProjectBuilder[] projectBuilders, ProjectReactorValidator validator) { this.exclusions = exclusions; this.reactor = reactor; - this.settings = settings; this.projectBuilders = projectBuilders; + this.validator = validator; } - public ProjectReactorReady(ProjectExclusions exclusions, ProjectReactor reactor, Settings settings) { - this(exclusions, reactor, settings, new ProjectBuilder[0]); + public ProjectReactorReady(ProjectExclusions exclusions, ProjectReactor reactor, ProjectReactorValidator validator) { + this(exclusions, reactor, new ProjectBuilder[0], validator); } public void start() { @@ -67,7 +66,6 @@ public class ProjectReactorReady { exclusions.apply(); // 3 Validate final reactor - ProjectReactorValidator validator = new ProjectReactorValidator(settings); validator.validate(reactor); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java index 79668e2c3f0..539da5974e7 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectReactorValidator.java @@ -19,6 +19,7 @@ */ package org.sonar.batch.scan; +import org.sonar.core.resource.ResourceDao; import com.google.common.base.Joiner; import org.apache.commons.lang.math.NumberUtils; import org.codehaus.plexus.util.StringUtils; @@ -39,12 +40,21 @@ public class ProjectReactorValidator { private static final String VALID_MODULE_KEY_REGEXP = "[0-9a-zA-Z\\-_\\.:]+"; private final Settings settings; + private final ResourceDao resourceDao; - public ProjectReactorValidator(Settings settings) { + public ProjectReactorValidator(Settings settings, ResourceDao resourceDao) { this.settings = settings; + this.resourceDao = resourceDao; } public void validate(ProjectReactor reactor) { + if (settings.getBoolean(CoreProperties.CORE_PREVENT_AUTOMATIC_PROJECT_CREATION)) { + String projectKey = reactor.getRoot().getKey(); + if (resourceDao.findByKey(projectKey) == null) { + throw new SonarException("Unable to scan non-existing project " + projectKey); + } + } + List validationMessages = new ArrayList(); for (ProjectDefinition moduleDef : reactor.getProjects()) { validateModule(moduleDef, validationMessages); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ScanTask.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ScanTask.java index b3aa45d7c08..4438112c1d8 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ScanTask.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ScanTask.java @@ -51,6 +51,7 @@ public class ScanTask implements Task { new Phases().enable(Phases.Phase.values()), DefaultProjectTree.class, ProjectExclusions.class, + ProjectReactorValidator.class, ProjectReactorReady.class, DefaultProfileLoader.class, DefaultSensorMatcher.class); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorReadyTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorReadyTest.java index 7d15154e0b4..cc5a7aa9133 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorReadyTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorReadyTest.java @@ -22,7 +22,6 @@ package org.sonar.batch.scan; import org.junit.Test; import org.sonar.api.batch.bootstrap.ProjectBuilder; import org.sonar.api.batch.bootstrap.ProjectReactor; -import org.sonar.api.config.Settings; import static org.mockito.Mockito.mock; @@ -30,14 +29,14 @@ public class ProjectReactorReadyTest { @Test public void should_do_nothing() { // it's only a barrier - ProjectReactorReady barrier = new ProjectReactorReady(mock(ProjectExclusions.class), mock(ProjectReactor.class), new Settings(), - new ProjectBuilder[] {mock(ProjectBuilder.class)}); + ProjectReactorReady barrier = new ProjectReactorReady(mock(ProjectExclusions.class), mock(ProjectReactor.class), + new ProjectBuilder[] {mock(ProjectBuilder.class)}, mock(ProjectReactorValidator.class)); barrier.start(); } @Test public void project_builders_should_be_optional() { - ProjectReactorReady barrier = new ProjectReactorReady(mock(ProjectExclusions.class), mock(ProjectReactor.class), new Settings()); + ProjectReactorReady barrier = new ProjectReactorReady(mock(ProjectExclusions.class), mock(ProjectReactor.class), mock(ProjectReactorValidator.class)); barrier.start(); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java index efd2b6a7540..34887e22188 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectReactorValidatorTest.java @@ -26,8 +26,13 @@ import org.junit.rules.ExpectedException; import org.sonar.api.CoreProperties; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.component.Component; import org.sonar.api.config.Settings; import org.sonar.api.utils.SonarException; +import org.sonar.core.resource.ResourceDao; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class ProjectReactorValidatorTest { @@ -36,11 +41,31 @@ public class ProjectReactorValidatorTest { private ProjectReactorValidator validator; private Settings settings; + private ResourceDao resourceDao; @Before public void prepare() { settings = new Settings(); - validator = new ProjectReactorValidator(settings); + resourceDao = mock(ResourceDao.class); + validator = new ProjectReactorValidator(settings, resourceDao); + } + + @Test + public void not_fail_if_prosivioning_enforced_and_project_exists() throws Exception { + String key = "project-key"; + settings.setProperty(CoreProperties.CORE_PREVENT_AUTOMATIC_PROJECT_CREATION, true); + when(resourceDao.findByKey(key)).thenReturn(mock(Component.class)); + ProjectReactor reactor = createProjectReactor(key); + validator.validate(reactor); + } + + @Test(expected = SonarException.class) + public void fail_if_prosivioning_enforced_and_project_not_provisioned() throws Exception { + String key = "project-key"; + settings.setProperty(CoreProperties.CORE_PREVENT_AUTOMATIC_PROJECT_CREATION, true); + when(resourceDao.findByKey(key)).thenReturn(null); + ProjectReactor reactor = createProjectReactor(key); + validator.validate(reactor); } @Test diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/ScanTaskTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/ScanTaskTest.java index 8be63457c64..cfae60c9bb5 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/ScanTaskTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/ScanTaskTest.java @@ -19,6 +19,8 @@ */ package org.sonar.batch.scan; +import org.sonar.core.resource.ResourceDao; + import org.junit.Test; import org.sonar.api.CoreProperties; import org.sonar.api.batch.bootstrap.ProjectReactor; @@ -27,7 +29,6 @@ import org.sonar.api.platform.ComponentContainer; import org.sonar.batch.ProjectConfigurator; import org.sonar.batch.bootstrap.TaskContainer; import org.sonar.batch.phases.Phases; - import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -42,7 +43,7 @@ public class ScanTaskTest { public void should_enable_all_phases() { ScanTask task = new ScanTask(mock(TaskContainer.class)); ComponentContainer projectScanContainer = new ComponentContainer(); - projectScanContainer.add(mock(ProjectConfigurator.class), mock(ProjectReactor.class), mock(Settings.class), mock(ProjectSettingsReady.class)); + projectScanContainer.add(mock(ProjectConfigurator.class), mock(ProjectReactor.class), mock(Settings.class), mock(ProjectSettingsReady.class), mock(ResourceDao.class)); task.scan(projectScanContainer); Phases phases = projectScanContainer.getComponentByType(Phases.class); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java index b410c6bc295..9aa3b74b85b 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java @@ -465,4 +465,9 @@ public interface CoreProperties { * @since 3.7 */ String DRY_RUN_READ_TIMEOUT_SEC = "sonar.dryRun.readTimeout"; + + /** + * @since 4.0 + */ + String CORE_PREVENT_AUTOMATIC_PROJECT_CREATION = "sonar.preventAutoProjectCreation"; } -- 2.39.5