]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11594 scanner authentication token
authorMichal Duda <michal.duda@sonarsource.com>
Tue, 18 Dec 2018 14:41:25 +0000 (15:41 +0100)
committerSonarTech <sonartech@sonarsource.com>
Wed, 9 Jan 2019 19:21:08 +0000 (20:21 +0100)
Support passing the scanner authentication token with $SONAR_TOKEN

server/sonar-docs/src/pages/sonarcloud/integrations/bitbucketcloud.md
sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerWsClientProvider.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/report/AnalysisContextReportPublisher.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerWsClientProviderTest.java

index 5485432ecf0e66d7b87a3eaaa3edd5b05623064d..5ed96887e7412b90e803e3e4d577df23d863d279 100644 (file)
@@ -13,25 +13,34 @@ In Bitbucket Cloud, go to your team's "Settings > Find integrations" page, searc
 
 ## Analyzing with Pipelines
 
-SonarCloud integrates with Bitbucket Pipelines to make it easier to trigger analyses. Follow the steps:
+SonarCloud integrates with Bitbucket Pipelines to make it easier to trigger analyses. Follow these steps:
 
-1.  On SonarCloud, once your project is created, follow the tutorial on the dashboard of the project. You can copy-paste the command line displayed at the end.
+1.  On SonarCloud, once your project is created, follow the tutorial on the dashboard of the project. Copy-paste the command line displayed at the end but without the `sonar.login` setting.
 
-2.  On Bitbucket Cloud, go to the "Settings > Pipelines > Environment variables" page of your team, and add a new SONAR_TOKEN variable that contains the value of the SonarCloud token (something like `9ad01c85336b265406fa6554a9a681a4b281135f`) which you created during the tutorial (and which is available inside the command line that you copy-pasted). **Make sure that you click on the "Lock" icon to encrypt and hide this token.**
+2.  On Bitbucket Cloud, go to the "Settings > Pipelines > Account variables" page of your team, and add a new SONAR_TOKEN variable that contains the value of the SonarCloud token which you created during the tutorial (something like `9ad01c85336b265406fa6554a9a681a4b281135f`). **Make sure that you click on the "Lock" icon to encrypt and hide this token.**
 
-3.  Inside the `bitbucket-pipelines.yml` file of your repository, copy the command line provided by the tutorial and replace the actual token by its variable name. For example, for a Java Maven-based project, you should have something like:
+3.  Inside the `bitbucket-pipelines.yml` file of your repository paste the command you copied in step 1. For example, for a Java Maven-based project, you should have something like:
 
 ```
 script:
-  -mvn sonar:sonar -Dsonar.host.url=https://sonarcloud.io -Dsonar.projectKey=my-project -Dsonar.organization=my-team-org -Dsonar.login=$SONAR_TOKEN
+  -mvn sonar:sonar -Dsonar.host.url=https://sonarcloud.io -Dsonar.projectKey=my-project -Dsonar.organization=my-team-org
 ```
 
-When this change on `bitbucket-pipelines.yml` is committed and pushed, Pipelines should automatically run a new build and therefore trigger the analysis of the repository. Shortly after, your project will appear on SonarCloud in your organization.
+When this change on `bitbucket-pipelines.yml` is committed and pushed, Pipelines should automatically run a new build and therefore trigger the analysis of the repository. Shortly after, your project will appear on SonarCloud in your organization. 
 
-From now on, everytime Pipelines triggers a build, SonarCloud will:
+## Analyzing pull requests with Pipelines
 
-* Analyze every new branch that contains the change on the `bitbucket-pipelines.yml` file.
-* Analyze and decorate every pull request based on such a branch.
+In order to trigger SonarCloud analysis on each pull request update, you have to supply the copied command in `pull-requests` section of `bitbucket-pipelines.yml` (check [Configure bitbucket-pipelines.yml](https://confluence.atlassian.com/bitbucket/configure-bitbucket-pipelines-yml-792298910.html#Configurebitbucket-pipelines.yml-ci_pull-requests) for more details about that section). Here is a sample configuration:
+```
+pipelines:
+  ...
+  pull-requests:
+    feature/*:
+      - step:
+          script:
+            - mvn -B verify sonar:sonar -Dsonar.host.url=https://sonarcloud.io -Dsonar.projectKey=... -Dsonar.organization=...
+  ...
+```
 
 ## Quality widget
 
index 7101bfd96bfd4bbeafb866a8acb61d493905e1b0..67fe816a83f369592ccc721be2dc1c45028be6fd 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.scanner.bootstrap;
 import org.picocontainer.injectors.ProviderAdapter;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.ScannerSide;
+import org.sonar.api.utils.System2;
 import org.sonar.batch.bootstrapper.EnvironmentInformation;
 import org.sonarqube.ws.client.HttpConnector;
 import org.sonarqube.ws.client.WsClientFactories;
@@ -39,13 +40,15 @@ public class ScannerWsClientProvider extends ProviderAdapter {
 
   private ScannerWsClient wsClient;
 
-  public synchronized ScannerWsClient provide(final GlobalProperties settings, final EnvironmentInformation env, GlobalAnalysisMode globalMode) {
+  public synchronized ScannerWsClient provide(final GlobalProperties settings, final EnvironmentInformation env,
+                                              GlobalAnalysisMode globalMode, System2 system) {
     if (wsClient == null) {
       String url = defaultIfBlank(settings.property("sonar.host.url"), CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE);
       HttpConnector.Builder connectorBuilder = HttpConnector.newBuilder();
 
       String timeoutSec = defaultIfBlank(settings.property(READ_TIMEOUT_SEC_PROPERTY), valueOf(DEFAULT_READ_TIMEOUT_SEC));
-      String login = defaultIfBlank(settings.property(CoreProperties.LOGIN), null);
+      String token = defaultIfBlank(system.envVariable("SONAR_TOKEN"), null);
+      String login = defaultIfBlank(settings.property(CoreProperties.LOGIN), token);
       connectorBuilder
         .readTimeoutMilliseconds(parseInt(timeoutSec) * 1_000)
         .connectTimeoutMilliseconds(CONNECT_TIMEOUT_MS)
index 237ad54f65d3b5b1c7b58045130c3411a0cea043..8badd6715df20b89a8c83a78d2a0ed9d2344dd43 100644 (file)
@@ -112,9 +112,15 @@ public class AnalysisContextReportPublisher {
   private void writeEnvVariables(BufferedWriter fileWriter) throws IOException {
     fileWriter.append("Environment variables:\n");
     Map<String, String> envVariables = system.envVariables();
-    for (String env : new TreeSet<>(envVariables.keySet())) {
-      fileWriter.append(String.format(KEY_VALUE_FORMAT, env, envVariables.get(env))).append('\n');
-    }
+    new TreeSet<>(envVariables.keySet())
+      .forEach(envKey -> {
+        try {
+          String envValue = isSensitiveEnvVariable(envKey) ? "******" : envVariables.get(envKey);
+          fileWriter.append(String.format(KEY_VALUE_FORMAT, envKey, envValue)).append('\n');
+        } catch (IOException e) {
+          throw new IllegalStateException(e);
+        }
+      });
   }
 
   private void writeGlobalSettings(BufferedWriter fileWriter) throws IOException {
@@ -146,7 +152,7 @@ public class AnalysisContextReportPublisher {
   }
 
   private static void dumpPropIfNotSensitive(BufferedWriter fileWriter, String prop, String value) throws IOException {
-    fileWriter.append(String.format(KEY_VALUE_FORMAT, prop, sensitive(prop) ? "******" : StringUtils.abbreviate(value, MAX_WIDTH))).append('\n');
+    fileWriter.append(String.format(KEY_VALUE_FORMAT, prop, isSensitiveProperty(prop) ? "******" : StringUtils.abbreviate(value, MAX_WIDTH))).append('\n');
   }
 
   /**
@@ -183,7 +189,11 @@ public class AnalysisContextReportPublisher {
     return propKey.startsWith(ENV_PROP_PREFIX) && system.envVariables().containsKey(propKey.substring(ENV_PROP_PREFIX.length()));
   }
 
-  private static boolean sensitive(String key) {
-    return key.equals(CoreProperties.LOGIN) || key.contains(".password") || key.contains(".secured");
+  private static boolean isSensitiveEnvVariable(String key) {
+    return key.contains("_TOKEN") || key.contains("_PASSWORD") || key.contains("_SECURED");
+  }
+
+  private static boolean isSensitiveProperty(String key) {
+    return key.equals(CoreProperties.LOGIN) || key.contains(".password") || key.contains(".secured") || key.contains(".token");
   }
 }
index c72c88f9031a3ab3d64cfd14a76fc6cf5a4f8061..804107b7886e4aa124cf6ce6e90ec817fedb3bef 100644 (file)
@@ -23,10 +23,12 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import org.junit.Test;
+import org.sonar.api.utils.System2;
 import org.sonar.batch.bootstrapper.EnvironmentInformation;
 import org.sonarqube.ws.client.HttpConnector;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
 
 public class ScannerWsClientProviderTest {
 
@@ -37,7 +39,7 @@ public class ScannerWsClientProviderTest {
   public void provide_client_with_default_settings() {
     GlobalProperties settings = new GlobalProperties(new HashMap<>());
 
-    ScannerWsClient client = underTest.provide(settings, env, new GlobalAnalysisMode(new GlobalProperties(Collections.emptyMap())));
+    ScannerWsClient client = underTest.provide(settings, env, new GlobalAnalysisMode(new GlobalProperties(Collections.emptyMap())), mock(System2.class));
 
     assertThat(client).isNotNull();
     assertThat(client.baseUrl()).isEqualTo("http://localhost:9000/");
@@ -57,7 +59,7 @@ public class ScannerWsClientProviderTest {
     props.put("sonar.ws.timeout", "42");
     GlobalProperties settings = new GlobalProperties(props);
 
-    ScannerWsClient client = underTest.provide(settings, env, new GlobalAnalysisMode(new GlobalProperties(Collections.emptyMap())));
+    ScannerWsClient client = underTest.provide(settings, env, new GlobalAnalysisMode(new GlobalProperties(Collections.emptyMap())), mock(System2.class));
 
     assertThat(client).isNotNull();
     HttpConnector httpConnector = (HttpConnector) client.wsConnector();
@@ -67,9 +69,11 @@ public class ScannerWsClientProviderTest {
 
   @Test
   public void build_singleton() {
+    System2 system = mock(System2.class);
+
     GlobalProperties settings = new GlobalProperties(new HashMap<>());
-    ScannerWsClient first = underTest.provide(settings, env, new GlobalAnalysisMode(new GlobalProperties(Collections.emptyMap())));
-    ScannerWsClient second = underTest.provide(settings, env, new GlobalAnalysisMode(new GlobalProperties(Collections.emptyMap())));
+    ScannerWsClient first = underTest.provide(settings, env, new GlobalAnalysisMode(new GlobalProperties(Collections.emptyMap())), system);
+    ScannerWsClient second = underTest.provide(settings, env, new GlobalAnalysisMode(new GlobalProperties(Collections.emptyMap())), system);
     assertThat(first).isSameAs(second);
   }
 }