diff --git a/lib/src/main/java/org/sonarsource/scanner/lib/EnvironmentConfig.java b/lib/src/main/java/org/sonarsource/scanner/lib/EnvironmentConfig.java index 81b5770b..cc244d41 100644 --- a/lib/src/main/java/org/sonarsource/scanner/lib/EnvironmentConfig.java +++ b/lib/src/main/java/org/sonarsource/scanner/lib/EnvironmentConfig.java @@ -40,7 +40,7 @@ public class EnvironmentConfig { private static final String GENERIC_ENV_PREFIX = "SONAR_SCANNER_"; private static final String SONAR_HOST_URL_ENV_VAR = "SONAR_HOST_URL"; private static final String SONAR_USER_HOME_ENV_VAR = "SONAR_USER_HOME"; - static final String TOKEN_ENV_VARIABLE = "SONAR_TOKEN"; + public static final String TOKEN_ENV_VARIABLE = "SONAR_TOKEN"; private EnvironmentConfig() { // only static methods diff --git a/lib/src/main/java/org/sonarsource/scanner/lib/ScannerEngineBootstrapper.java b/lib/src/main/java/org/sonarsource/scanner/lib/ScannerEngineBootstrapper.java index d73248b5..5c224361 100644 --- a/lib/src/main/java/org/sonarsource/scanner/lib/ScannerEngineBootstrapper.java +++ b/lib/src/main/java/org/sonarsource/scanner/lib/ScannerEngineBootstrapper.java @@ -54,12 +54,15 @@ import org.sonarsource.scanner.lib.internal.util.VersionUtils; import static java.util.Optional.ofNullable; +import static org.sonarsource.scanner.lib.EnvironmentConfig.TOKEN_ENV_VARIABLE; import static org.sonarsource.scanner.lib.ScannerProperties.SCANNER_ARCH; import static org.sonarsource.scanner.lib.ScannerProperties.SCANNER_OS; +import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_LOGIN; import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_KEYSTORE_PASSWORD; import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_KEYSTORE_PATH; import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_TRUSTSTORE_PASSWORD; import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_TRUSTSTORE_PATH; +import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_TOKEN; /** * Entry point to run a Sonar analysis programmatically. @@ -71,6 +74,7 @@ public class ScannerEngineBootstrapper { private static final String SONARCLOUD_HOST = "https://sonarcloud.io"; private static final String SONARCLOUD_REST_API = "https://api.sonarcloud.io"; static final String SQ_VERSION_NEW_BOOTSTRAPPING = "10.6"; + static final String SQ_VERSION_TOKEN_AUTHENTICATION = "10.0"; private static final String JAVAX_NET_SSL_TRUST_STORE = "javax.net.ssl.trustStore"; private static final String JAVAX_NET_SSL_TRUST_STORE_PASSWORD = "javax.net.ssl.trustStorePassword"; private static final String JAVAX_NET_SSL_KEY_STORE = "javax.net.ssl.keyStore"; @@ -138,6 +142,12 @@ public ScannerEngineBootstrapResult bootstrap() { scannerHttpClient.init(httpConfig); var serverVersion = !isSonarCloud ? getServerVersion(scannerHttpClient) : null; + + if (!isSonarCloud && VersionUtils.isAtLeastIgnoringQualifier(serverVersion, SQ_VERSION_TOKEN_AUTHENTICATION) && Objects.nonNull(httpConfig.getLogin())) { + LOG.warn("Use of '{}' property has been deprecated in favor of '{}' (or the env variable alternative '{}'). Please use the latter when passing a token.", SONAR_LOGIN, + SONAR_TOKEN, TOKEN_ENV_VARIABLE); + } + ScannerEngineFacade scannerFacade; if (isSonarCloud || VersionUtils.isAtLeastIgnoringQualifier(serverVersion, SQ_VERSION_NEW_BOOTSTRAPPING)) { var launcher = scannerEngineLauncherFactory.createLauncher(scannerHttpClient, fileCache, immutableProperties); @@ -162,7 +172,7 @@ private static ScannerEngineBootstrapResult handleException(MessageException e) var code = httpEx.getCode(); if (code == 401 || code == 403) { var helpMessage = "Please check the property " + ScannerProperties.SONAR_TOKEN + - " or the environment variable " + EnvironmentConfig.TOKEN_ENV_VARIABLE + "."; + " or the environment variable " + TOKEN_ENV_VARIABLE + "."; message.append(". ").append(helpMessage); } if (code == 407) { diff --git a/lib/src/main/java/org/sonarsource/scanner/lib/internal/http/HttpConfig.java b/lib/src/main/java/org/sonarsource/scanner/lib/internal/http/HttpConfig.java index c7693668..6b7c998e 100644 --- a/lib/src/main/java/org/sonarsource/scanner/lib/internal/http/HttpConfig.java +++ b/lib/src/main/java/org/sonarsource/scanner/lib/internal/http/HttpConfig.java @@ -27,6 +27,7 @@ import java.time.Duration; import java.time.format.DateTimeParseException; import java.util.Map; +import java.util.Objects; import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -39,6 +40,8 @@ import static java.lang.Integer.parseInt; import static java.lang.String.format; import static org.apache.commons.lang3.StringUtils.defaultIfBlank; +import static org.sonarsource.scanner.lib.EnvironmentConfig.TOKEN_ENV_VARIABLE; +import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_LOGIN; import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_CONNECT_TIMEOUT; import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_KEYSTORE_PASSWORD; import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_KEYSTORE_PATH; @@ -51,6 +54,7 @@ import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_SOCKET_TIMEOUT; import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_TRUSTSTORE_PASSWORD; import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_TRUSTSTORE_PATH; +import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_TOKEN; public class HttpConfig { @@ -87,6 +91,10 @@ public HttpConfig(Map bootstrapProperties, Path sonarUserHome) { this.restApiBaseUrl = StringUtils.removeEnd(bootstrapProperties.get(ScannerProperties.API_BASE_URL), "/"); this.token = bootstrapProperties.get(ScannerProperties.SONAR_TOKEN); this.login = bootstrapProperties.get(ScannerProperties.SONAR_LOGIN); + if (Objects.nonNull(this.login) && Objects.nonNull(this.token)) { + LOG.warn("Both '{}' and '{}' (or the '{}' env variable) are set, but only the latter will be used.", SONAR_LOGIN, SONAR_TOKEN, TOKEN_ENV_VARIABLE); + } + this.password = bootstrapProperties.get(ScannerProperties.SONAR_PASSWORD); this.userAgent = format("%s/%s", bootstrapProperties.get(InternalProperties.SCANNER_APP), bootstrapProperties.get(InternalProperties.SCANNER_APP_VERSION)); this.socketTimeout = loadDuration(bootstrapProperties, SONAR_SCANNER_SOCKET_TIMEOUT, READ_TIMEOUT_SEC_PROPERTY, DEFAULT_READ_TIMEOUT_SEC); diff --git a/lib/src/test/java/org/sonarsource/scanner/lib/ScannerEngineBootstrapperTest.java b/lib/src/test/java/org/sonarsource/scanner/lib/ScannerEngineBootstrapperTest.java index c0e56bc2..fba9d821 100644 --- a/lib/src/test/java/org/sonarsource/scanner/lib/ScannerEngineBootstrapperTest.java +++ b/lib/src/test/java/org/sonarsource/scanner/lib/ScannerEngineBootstrapperTest.java @@ -65,11 +65,12 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.sonarsource.scanner.lib.ScannerEngineBootstrapper.SQ_VERSION_NEW_BOOTSTRAPPING; +import static org.sonarsource.scanner.lib.ScannerEngineBootstrapper.SQ_VERSION_TOKEN_AUTHENTICATION; class ScannerEngineBootstrapperTest { @RegisterExtension - private LogTester logTester = new LogTester(); + private final LogTester logTester = new LogTester(); private final ScannerHttpClient scannerHttpClient = mock(ScannerHttpClient.class); private final ScannerEngineLauncherFactory scannerEngineLauncherFactory = mock(ScannerEngineLauncherFactory.class); @@ -122,6 +123,29 @@ void should_use_new_bootstrapping_with_sonarqube_10_6() throws Exception { assertThat(bootstrapResult.getEngineFacade().isSonarCloud()).isFalse(); verifySonarQubeServerTypeLogged(SQ_VERSION_NEW_BOOTSTRAPPING); assertThat(bootstrapResult.getEngineFacade().getServerVersion()).isEqualTo(SQ_VERSION_NEW_BOOTSTRAPPING); + assertThat(logTester.logs(Level.WARN)).isEmpty(); + } + } + + @Test + void should_issue_deprecation_warning_for_sonar_login_property_sonarqube_10_0() throws Exception { + IsolatedLauncherFactory launcherFactory = mock(IsolatedLauncherFactory.class); + when(launcherFactory.createLauncher(eq(scannerHttpClient), any(FileCache.class))) + .thenReturn(mock(IsolatedLauncherFactory.IsolatedLauncherAndClassloader.class)); + + ScannerEngineBootstrapper bootstrapper = new ScannerEngineBootstrapper("Gradle", "3.1", system, scannerHttpClient, + launcherFactory, scannerEngineLauncherFactory); + when(scannerHttpClient.callRestApi("/analysis/version")).thenThrow(new HttpException(URI.create("http://myserver").toURL(), 404, "Not Found", null)); + when(scannerHttpClient.callWebApi("/api/server/version")).thenReturn(SQ_VERSION_TOKEN_AUTHENTICATION); + + try (var bootstrapResult = bootstrapper.setBootstrapProperty(ScannerProperties.HOST_URL, "http://localhost").setBootstrapProperty(ScannerProperties.SONAR_LOGIN, + "mockTokenValue").bootstrap()) { + verify(launcherFactory).createLauncher(eq(scannerHttpClient), any(FileCache.class)); + assertThat(bootstrapResult.getEngineFacade().isSonarCloud()).isFalse(); + assertThat(logTester.logs(Level.WARN)).contains("Use of 'sonar.login' property has been deprecated in favor of 'sonar.token' (or the env variable alternative " + + "'SONAR_TOKEN'). Please use the latter when passing a token."); + verifySonarQubeServerTypeLogged(SQ_VERSION_TOKEN_AUTHENTICATION); + assertThat(bootstrapResult.getEngineFacade().getServerVersion()).isEqualTo(SQ_VERSION_TOKEN_AUTHENTICATION); } } diff --git a/lib/src/test/java/org/sonarsource/scanner/lib/internal/http/HttpConfigTest.java b/lib/src/test/java/org/sonarsource/scanner/lib/internal/http/HttpConfigTest.java index 1b3b1205..add0ee8f 100644 --- a/lib/src/test/java/org/sonarsource/scanner/lib/internal/http/HttpConfigTest.java +++ b/lib/src/test/java/org/sonarsource/scanner/lib/internal/http/HttpConfigTest.java @@ -26,7 +26,10 @@ import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.io.TempDir; +import org.slf4j.event.Level; +import testutils.LogTester; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -37,6 +40,9 @@ class HttpConfigTest { private final Map bootstrapProperties = new HashMap<>(); + @RegisterExtension + private final LogTester logTester = new LogTester(); + @TempDir private Path sonarUserHomeDir; private Path sonarUserHome; @@ -74,5 +80,14 @@ void it_should_throw_if_invalid_proxy_port() { .hasMessage("sonar.scanner.proxyPort is not a valid integer: not_a_number"); } + @Test + void should_warn_if_both_login_and_token_properties_set() { + bootstrapProperties.put("sonar.login", "mockTokenValue"); + bootstrapProperties.put("sonar.token", "mockTokenValue"); + + new HttpConfig(bootstrapProperties, sonarUserHome); + + assertThat(logTester.logs(Level.WARN)).contains("Both 'sonar.login' and 'sonar.token' (or the 'SONAR_TOKEN' env variable) are set, but only the latter will be used."); + } }