]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-14501 Custom security config telemetry
authorJacek <jacek.poreda@sonarsource.com>
Tue, 23 Feb 2021 10:05:24 +0000 (11:05 +0100)
committersonartech <sonartech@sonarsource.com>
Fri, 26 Feb 2021 20:07:39 +0000 (20:07 +0000)
server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryData.java
server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryDataJsonWriter.java
server/sonar-server-common/src/test/java/org/sonar/server/telemetry/TelemetryDataJsonWriterTest.java
server/sonar-webserver-core/src/main/java/org/sonar/server/telemetry/TelemetryDataLoaderImpl.java
server/sonar-webserver-core/src/test/java/org/sonar/server/telemetry/TelemetryDataLoaderImplTest.java

index 9f18a800ebaa47bb8ce177cfec58124265130440..c963b15d36eb601681497d28a9fb0794b68a1aaf 100644 (file)
  */
 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<String> 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<String> getCustomSecurityConfigs() {
+    return customSecurityConfigs;
+  }
+
   static Builder builder() {
     return new Builder();
   }
@@ -154,13 +163,14 @@ public class TelemetryData {
     private Map<String, Long> 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<String> 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<String> customSecurityConfigs) {
+      this.customSecurityConfigs = customSecurityConfigs;
+      return this;
+    }
+
     TelemetryData build() {
       requireNonNull(serverId);
       requireNonNull(version);
index 50fc376f6af06176e3ff25038c3f965159c30dd7..f29efbe3e69cbfe5db09b6e9ae0a7a2966f5965c 100644 (file)
@@ -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) {
index 9c4ce3f7a38c49bd05cb2bbea5b69c286102a6e1..dc8443f3744c790532df8168a47d0f6a4a3b0b2a 100644 (file)
@@ -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())
index 93f086e52e0988b0ec4ea8af3db436f195ef16d1..c0c518b4e33347ec755e42153e0a5f93fe6ccaeb 100644 (file)
@@ -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<String> installationDateProperty = internalProperties.read(InternalProperties.INSTALLATION_DATE);
     installationDateProperty.ifPresent(s -> data.setInstallationDate(Long.valueOf(s)));
     Optional<String> 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<String> 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<String, Long> countAlmUsage(DbSession dbSession) {
     return dbClient.almSettingDao().selectAll(dbSession).stream()
       .collect(Collectors.groupingBy(almSettingDto -> {
index a4925b0babb5b07de72e4a4d5bd054157b55029c..981839617a3add77c51ec010dcc77f2409276c19 100644 (file)
@@ -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));