From 10fef4f93aeefcf8d79e7eb93742d4460c75d90b Mon Sep 17 00:00:00 2001 From: Jacek Date: Tue, 23 Feb 2021 11:05:24 +0100 Subject: [PATCH] SONAR-14501 Custom security config telemetry --- .../sonar/server/telemetry/TelemetryData.java | 19 +++++++- .../telemetry/TelemetryDataJsonWriter.java | 7 +++ .../TelemetryDataJsonWriterTest.java | 13 +++++ .../telemetry/TelemetryDataLoaderImpl.java | 34 ++++++++++++-- .../TelemetryDataLoaderImplTest.java | 47 +++++++++++++++---- 5 files changed, 105 insertions(+), 15 deletions(-) diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryData.java b/server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryData.java index 9f18a800eba..c963b15d36e 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryData.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryData.java @@ -19,12 +19,15 @@ */ package org.sonar.server.telemetry; +import java.util.List; import java.util.Map; import java.util.Optional; import javax.annotation.Nullable; import org.sonar.core.platform.EditionProvider; +import org.sonar.core.platform.EditionProvider.Edition; import org.sonar.server.measure.index.ProjectMeasuresStatistics; +import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; public class TelemetryData { @@ -46,6 +49,7 @@ public class TelemetryData { private final boolean inDocker; private final Boolean hasUnanalyzedC; private final Boolean hasUnanalyzedCpp; + private final List customSecurityConfigs; private TelemetryData(Builder builder) { serverId = builder.serverId; @@ -66,6 +70,7 @@ public class TelemetryData { inDocker = builder.inDocker; hasUnanalyzedC = builder.hasUnanalyzedC; hasUnanalyzedCpp = builder.hasUnanalyzedCpp; + customSecurityConfigs = builder.customSecurityConfigs == null ? emptyList() : builder.customSecurityConfigs; } public String getServerId() { @@ -140,6 +145,10 @@ public class TelemetryData { return Optional.ofNullable(hasUnanalyzedCpp); } + public List getCustomSecurityConfigs() { + return customSecurityConfigs; + } + static Builder builder() { return new Builder(); } @@ -154,13 +163,14 @@ public class TelemetryData { private Map almIntegrationCountByAlm; private Long ncloc; private Boolean usingBranches; - private EditionProvider.Edition edition; + private Edition edition; private String licenseType; private Long installationDate; private String installationVersion; private boolean inDocker = false; private Boolean hasUnanalyzedC; private Boolean hasUnanalyzedCpp; + private List customSecurityConfigs; private Builder() { // enforce static factory method @@ -211,7 +221,7 @@ public class TelemetryData { return this; } - Builder setEdition(@Nullable EditionProvider.Edition edition) { + Builder setEdition(@Nullable Edition edition) { this.edition = edition; return this; } @@ -246,6 +256,11 @@ public class TelemetryData { return this; } + Builder setCustomSecurityConfigs(List customSecurityConfigs) { + this.customSecurityConfigs = customSecurityConfigs; + return this; + } + TelemetryData build() { requireNonNull(serverId); requireNonNull(version); diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryDataJsonWriter.java b/server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryDataJsonWriter.java index 50fc376f6af..f29efbe3e69 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryDataJsonWriter.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryDataJsonWriter.java @@ -78,6 +78,13 @@ public class TelemetryDataJsonWriter { }); json.endArray(); + if (!statistics.getCustomSecurityConfigs().isEmpty()) { + json.name("customSecurityConfig"); + json.beginArray(); + json.values(statistics.getCustomSecurityConfigs()); + json.endArray(); + } + statistics.hasUnanalyzedC().ifPresent(hasUnanalyzedC -> json.prop("hasUnanalyzedC", hasUnanalyzedC)); statistics.hasUnanalyzedCpp().ifPresent(hasUnanalyzedCpp -> json.prop("hasUnanalyzedCpp", hasUnanalyzedCpp)); if (statistics.getInstallationDate() != null) { diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/telemetry/TelemetryDataJsonWriterTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/telemetry/TelemetryDataJsonWriterTest.java index 9c4ce3f7a38..dc8443f3744 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/telemetry/TelemetryDataJsonWriterTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/telemetry/TelemetryDataJsonWriterTest.java @@ -342,6 +342,19 @@ public class TelemetryDataJsonWriterTest { "}"); } + @Test + public void writes_security_custom_config() { + TelemetryData data = SOME_TELEMETRY_DATA + .setCustomSecurityConfigs(Arrays.asList("php", "java")) + .build(); + + String json = writeTelemetryData(data); + + assertJson(json).isSimilarTo("{" + + " \"customSecurityConfig\": [\"php\", \"java\"]" + + "}"); + } + @DataProvider public static Object[][] allEditions() { return Arrays.stream(EditionProvider.Edition.values()) diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/telemetry/TelemetryDataLoaderImpl.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/telemetry/TelemetryDataLoaderImpl.java index 93f086e52e0..c0c518b4e33 100644 --- a/server/sonar-webserver-core/src/main/java/org/sonar/server/telemetry/TelemetryDataLoaderImpl.java +++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/telemetry/TelemetryDataLoaderImpl.java @@ -21,12 +21,15 @@ package org.sonar.server.telemetry; import java.sql.DatabaseMetaData; import java.sql.SQLException; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Function; import java.util.stream.Collectors; import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import org.sonar.api.config.Configuration; import org.sonar.api.platform.Server; import org.sonar.api.server.ServerSide; import org.sonar.core.platform.PlatformEditionProvider; @@ -47,9 +50,12 @@ import org.sonar.server.telemetry.TelemetryData.Database; import org.sonar.server.user.index.UserIndex; import org.sonar.server.user.index.UserQuery; +import static java.util.Arrays.asList; import static java.util.Optional.ofNullable; import static org.apache.commons.lang.StringUtils.startsWith; import static org.sonar.core.platform.EditionProvider.Edition.COMMUNITY; +import static org.sonar.core.platform.EditionProvider.Edition.DATACENTER; +import static org.sonar.core.platform.EditionProvider.Edition.ENTERPRISE; import static org.sonar.server.metric.UnanalyzedLanguageMetrics.UNANALYZED_CPP_KEY; import static org.sonar.server.metric.UnanalyzedLanguageMetrics.UNANALYZED_C_KEY; @@ -61,18 +67,19 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader { private final UserIndex userIndex; private final ProjectMeasuresIndex projectMeasuresIndex; private final PlatformEditionProvider editionProvider; + private final Configuration configuration; private final InternalProperties internalProperties; private final DockerSupport dockerSupport; @CheckForNull private final LicenseReader licenseReader; public TelemetryDataLoaderImpl(Server server, DbClient dbClient, PluginRepository pluginRepository, UserIndex userIndex, ProjectMeasuresIndex projectMeasuresIndex, - PlatformEditionProvider editionProvider, InternalProperties internalProperties, DockerSupport dockerSupport) { - this(server, dbClient, pluginRepository, userIndex, projectMeasuresIndex, editionProvider, internalProperties, dockerSupport, null); + PlatformEditionProvider editionProvider, InternalProperties internalProperties, Configuration configuration, DockerSupport dockerSupport) { + this(server, dbClient, pluginRepository, userIndex, projectMeasuresIndex, editionProvider, internalProperties, configuration, dockerSupport, null); } public TelemetryDataLoaderImpl(Server server, DbClient dbClient, PluginRepository pluginRepository, UserIndex userIndex, ProjectMeasuresIndex projectMeasuresIndex, - PlatformEditionProvider editionProvider, InternalProperties internalProperties, + PlatformEditionProvider editionProvider, InternalProperties internalProperties, Configuration configuration, DockerSupport dockerSupport, @Nullable LicenseReader licenseReader) { this.server = server; this.dbClient = dbClient; @@ -81,6 +88,7 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader { this.projectMeasuresIndex = projectMeasuresIndex; this.editionProvider = editionProvider; this.internalProperties = internalProperties; + this.configuration = configuration; this.dockerSupport = dockerSupport; this.licenseReader = licenseReader; } @@ -129,6 +137,9 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader { data.setAlmIntegrationCountByAlm(countAlmUsage(dbSession)); } + + setSecurityCustomConfigIfPresent(data); + Optional installationDateProperty = internalProperties.read(InternalProperties.INSTALLATION_DATE); installationDateProperty.ifPresent(s -> data.setInstallationDate(Long.valueOf(s))); Optional installationVersionProperty = internalProperties.read(InternalProperties.INSTALLATION_VERSION); @@ -138,6 +149,23 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader { return data.build(); } + private void setSecurityCustomConfigIfPresent(TelemetryData.Builder data) { + editionProvider.get() + .filter(edition -> asList(ENTERPRISE, DATACENTER).contains(edition)) + .ifPresent(edition -> { + List customSecurityConfigs = new LinkedList<>(); + configuration.get("sonar.security.config.javasecurity") + .ifPresent(s -> customSecurityConfigs.add("java")); + configuration.get("sonar.security.config.phpsecurity") + .ifPresent(s -> customSecurityConfigs.add("php")); + configuration.get("sonar.security.config.pythonsecurity") + .ifPresent(s -> customSecurityConfigs.add("python")); + configuration.get("sonar.security.config.roslyn.sonaranalyzer.security.cs") + .ifPresent(s -> customSecurityConfigs.add("csharp")); + data.setCustomSecurityConfigs(customSecurityConfigs); + }); + } + private Map countAlmUsage(DbSession dbSession) { return dbClient.almSettingDao().selectAll(dbSession).stream() .collect(Collectors.groupingBy(almSettingDto -> { diff --git a/server/sonar-webserver-core/src/test/java/org/sonar/server/telemetry/TelemetryDataLoaderImplTest.java b/server/sonar-webserver-core/src/test/java/org/sonar/server/telemetry/TelemetryDataLoaderImplTest.java index a4925b0babb..981839617a3 100644 --- a/server/sonar-webserver-core/src/test/java/org/sonar/server/telemetry/TelemetryDataLoaderImplTest.java +++ b/server/sonar-webserver-core/src/test/java/org/sonar/server/telemetry/TelemetryDataLoaderImplTest.java @@ -26,6 +26,7 @@ import java.util.Optional; import java.util.stream.IntStream; import org.junit.Rule; import org.junit.Test; +import org.sonar.api.config.Configuration; import org.sonar.api.impl.utils.TestSystem2; import org.sonar.core.platform.PlatformEditionProvider; import org.sonar.core.platform.PluginInfo; @@ -58,6 +59,7 @@ import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY; import static org.sonar.api.measures.CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION_KEY; import static org.sonar.core.platform.EditionProvider.Edition.COMMUNITY; import static org.sonar.core.platform.EditionProvider.Edition.DEVELOPER; +import static org.sonar.core.platform.EditionProvider.Edition.ENTERPRISE; import static org.sonar.db.component.BranchType.BRANCH; import static org.sonar.db.component.BranchType.PULL_REQUEST; import static org.sonar.server.metric.UnanalyzedLanguageMetrics.UNANALYZED_CPP_KEY; @@ -71,6 +73,7 @@ public class TelemetryDataLoaderImplTest { private final FakeServer server = new FakeServer(); private final PluginRepository pluginRepository = mock(PluginRepository.class); + private final Configuration configuration = mock(Configuration.class); private final TestSystem2 system2 = new TestSystem2().setNow(System.currentTimeMillis()); private final PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class); private final DockerSupport dockerSupport = mock(DockerSupport.class); @@ -80,9 +83,9 @@ public class TelemetryDataLoaderImplTest { private final LicenseReader licenseReader = mock(LicenseReader.class); private final TelemetryDataLoader communityUnderTest = new TelemetryDataLoaderImpl(server, db.getDbClient(), pluginRepository, new UserIndex(es.client(), system2), - new ProjectMeasuresIndex(es.client(), null, system2), editionProvider, internalProperties, dockerSupport, null); + new ProjectMeasuresIndex(es.client(), null, system2), editionProvider, internalProperties, configuration, dockerSupport, null); private final TelemetryDataLoader commercialUnderTest = new TelemetryDataLoaderImpl(server, db.getDbClient(), pluginRepository, new UserIndex(es.client(), system2), - new ProjectMeasuresIndex(es.client(), null, system2), editionProvider, internalProperties, dockerSupport, licenseReader); + new ProjectMeasuresIndex(es.client(), null, system2), editionProvider, internalProperties, configuration, dockerSupport, licenseReader); @Test public void send_telemetry_data() { @@ -144,14 +147,14 @@ public class TelemetryDataLoaderImplTest { entry("java", 500L), entry("kotlin", 2500L), entry("js", 50L)); assertThat(data.isInDocker()).isFalse(); assertThat(data.getAlmIntegrationCountByAlm()) - .containsEntry("azure_devops_server", 1L) - .containsEntry("azure_devops_cloud", 1L) - .containsEntry("bitbucket_server", 1L) - .containsEntry("bitbucket_cloud", 1L) - .containsEntry("gitlab_server", 1L) - .containsEntry("gitlab_cloud", 1L) - .containsEntry("github_cloud", 1L) - .containsEntry("github_server", 1L); + .containsEntry("azure_devops_server", 1L) + .containsEntry("azure_devops_cloud", 1L) + .containsEntry("bitbucket_server", 1L) + .containsEntry("bitbucket_cloud", 1L) + .containsEntry("gitlab_server", 1L) + .containsEntry("gitlab_cloud", 1L) + .containsEntry("github_cloud", 1L) + .containsEntry("github_server", 1L); } private void assertDatabaseMetadata(TelemetryData.Database database) { @@ -292,6 +295,30 @@ public class TelemetryDataLoaderImplTest { assertThat(data.hasUnanalyzedCpp().get()).isFalse(); } + @Test + public void populate_security_custom_config_for_languages_on_enterprise() { + when(editionProvider.get()).thenReturn(Optional.of(ENTERPRISE)); + + when(configuration.get("sonar.security.config.javasecurity")).thenReturn(Optional.of("{}")); + when(configuration.get("sonar.security.config.phpsecurity")).thenReturn(Optional.of("{}")); + when(configuration.get("sonar.security.config.pythonsecurity")).thenReturn(Optional.of("{}")); + when(configuration.get("sonar.security.config.roslyn.sonaranalyzer.security.cs")).thenReturn(Optional.of("{}")); + + TelemetryData data = commercialUnderTest.load(); + + assertThat(data.getCustomSecurityConfigs()) + .containsExactlyInAnyOrder("java", "php", "python", "csharp"); + } + + @Test + public void skip_security_custom_config_on_community() { + when(editionProvider.get()).thenReturn(Optional.of(COMMUNITY)); + + TelemetryData data = communityUnderTest.load(); + + assertThat(data.getCustomSecurityConfigs()).isEmpty(); + } + private PluginInfo newPlugin(String key, String version) { return new PluginInfo(key) .setVersion(Version.create(version)); -- 2.39.5