aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-scanner-engine/src
diff options
context:
space:
mode:
authorMatteo Mara <matteo.mara@sonarsource.com>2022-07-05 15:52:26 +0200
committersonartech <sonartech@sonarsource.com>2022-07-06 20:03:56 +0000
commit6354fe3096b07caf9eb017ab7b4e0565f6d303fc (patch)
tree593d31be7e2902f186e85d6d0a588288162a73fe /sonar-scanner-engine/src
parent6a401f73236a70f702b64646d8bdec7c5a90e15d (diff)
downloadsonarqube-6354fe3096b07caf9eb017ab7b4e0565f6d303fc.tar.gz
sonarqube-6354fe3096b07caf9eb017ab7b4e0565f6d303fc.zip
SONAR-16567 add an analysis warning when using a token expiring in less than 7 days.
Diffstat (limited to 'sonar-scanner-engine/src')
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/DefaultScannerWsClient.java45
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerWsClientProvider.java5
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/DefaultScannerWsClientTest.java59
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/PluginFilesTest.java6
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerWsClientProviderTest.java6
5 files changed, 100 insertions, 21 deletions
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/DefaultScannerWsClient.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/DefaultScannerWsClient.java
index 8dc5dc3a39f..0be294a4f99 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/DefaultScannerWsClient.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/DefaultScannerWsClient.java
@@ -23,11 +23,18 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import javax.annotation.CheckForNull;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.CoreProperties;
+import org.sonar.api.notifications.AnalysisWarnings;
import org.sonar.api.utils.MessageException;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
@@ -41,21 +48,28 @@ import org.sonarqube.ws.client.WsResponse;
import static java.lang.String.format;
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
+import static java.net.HttpURLConnection.HTTP_OK;
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
+import static org.sonar.api.utils.DateUtils.DATETIME_FORMAT;
import static org.sonar.api.utils.Preconditions.checkState;
public class DefaultScannerWsClient implements ScannerWsClient {
private static final int MAX_ERROR_MSG_LEN = 128;
+ private static final String SQ_TOKEN_EXPIRATION_HEADER = "sq-authentication-token-expiration";
private static final Logger LOG = Loggers.get(DefaultScannerWsClient.class);
+ private final Set<String> warningMessages = new HashSet<>();
+
private final WsClient target;
private final boolean hasCredentials;
private final GlobalAnalysisMode globalMode;
+ private final AnalysisWarnings analysisWarnings;
- public DefaultScannerWsClient(WsClient target, boolean hasCredentials, GlobalAnalysisMode globalMode) {
+ public DefaultScannerWsClient(WsClient target, boolean hasCredentials, GlobalAnalysisMode globalMode, AnalysisWarnings analysisWarnings) {
this.target = target;
this.hasCredentials = hasCredentials;
this.globalMode = globalMode;
+ this.analysisWarnings = analysisWarnings;
}
/**
@@ -73,6 +87,7 @@ public class DefaultScannerWsClient implements ScannerWsClient {
WsResponse response = target.wsConnector().call(request);
profiler.stopDebug(format("%s %d %s", request.getMethod(), response.code(), response.requestUrl()));
failIfUnauthorized(response);
+ checkAuthenticationWarnings(response);
return response;
}
@@ -96,7 +111,6 @@ public class DefaultScannerWsClient implements ScannerWsClient {
// not authenticated - see https://jira.sonarsource.com/browse/SONAR-4048
throw MessageException.of(format("Not authorized. Analyzing this project requires authentication. "
+ "Please provide a user token in %s or other credentials in %s and %s.", CoreProperties.LOGIN, CoreProperties.LOGIN, CoreProperties.PASSWORD));
-
}
if (code == HTTP_FORBIDDEN) {
throw MessageException.of("You're not authorized to run analysis. Please contact the project administrator.");
@@ -112,6 +126,33 @@ public class DefaultScannerWsClient implements ScannerWsClient {
response.failIfNotSuccessful();
}
+ private void checkAuthenticationWarnings(WsResponse response) {
+ if (response.code() == HTTP_OK) {
+ response.header(SQ_TOKEN_EXPIRATION_HEADER).ifPresent(expirationDate -> {
+ if (isTokenExpiringInOneWeek(expirationDate)) {
+ addAnalysisWarning(expirationDate);
+ }
+ });
+ }
+ }
+
+ private static boolean isTokenExpiringInOneWeek(String expirationDate) {
+ ZonedDateTime localDateTime = ZonedDateTime.now(ZoneOffset.UTC);
+ ZonedDateTime headerDateTime = LocalDateTime.from(DateTimeFormatter.ofPattern(DATETIME_FORMAT)
+ .parse(expirationDate)).minusDays(7).atZone(ZoneOffset.UTC);
+ return localDateTime.isAfter(headerDateTime);
+ }
+
+ private void addAnalysisWarning(String tokenExpirationDate) {
+ String warningMessage = "The token used for this analysis will expire on: " + tokenExpirationDate;
+ if (!warningMessages.contains(warningMessage)) {
+ warningMessages.add(warningMessage);
+ LOG.warn(warningMessage);
+ LOG.warn("Analysis executed with this token after the expiration date will fail.");
+ }
+ analysisWarnings.addUnique(warningMessage + "\nAnalysis executed with this token after the expiration date will fail.");
+ }
+
/**
* Tries to form a short and relevant error message from the exception, to be displayed in the console.
*/
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerWsClientProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerWsClientProvider.java
index 8e86aa8b0b4..7a45f5cc35a 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerWsClientProvider.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerWsClientProvider.java
@@ -20,6 +20,7 @@
package org.sonar.scanner.bootstrap;
import org.sonar.api.CoreProperties;
+import org.sonar.api.notifications.AnalysisWarnings;
import org.sonar.api.utils.System2;
import org.sonar.batch.bootstrapper.EnvironmentInformation;
import org.sonarqube.ws.client.HttpConnector;
@@ -37,7 +38,7 @@ public class ScannerWsClientProvider {
@Bean("DefaultScannerWsClient")
public DefaultScannerWsClient provide(ScannerProperties scannerProps, EnvironmentInformation env, GlobalAnalysisMode globalMode,
- System2 system) {
+ System2 system, AnalysisWarnings analysisWarnings) {
String url = defaultIfBlank(scannerProps.property("sonar.host.url"), "http://localhost:9000");
HttpConnector.Builder connectorBuilder = HttpConnector.newBuilder();
@@ -58,6 +59,6 @@ public class ScannerWsClientProvider {
}
return new DefaultScannerWsClient(WsClientFactories.getDefault().newClient(connectorBuilder.build()), login != null,
- globalMode);
+ globalMode, analysisWarnings);
}
}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/DefaultScannerWsClientTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/DefaultScannerWsClientTest.java
index 752b6ef0891..3da98be8901 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/DefaultScannerWsClientTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/DefaultScannerWsClientTest.java
@@ -19,12 +19,16 @@
*/
package org.sonar.scanner.bootstrap;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mockito;
+import org.sonar.api.notifications.AnalysisWarnings;
import org.sonar.api.utils.MessageException;
import org.sonar.api.utils.log.LogTester;
import org.sonar.api.utils.log.LoggerLevel;
@@ -39,6 +43,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.sonar.api.utils.DateUtils.DATETIME_FORMAT;
public class DefaultScannerWsClientTest {
@@ -47,6 +52,8 @@ public class DefaultScannerWsClientTest {
private final WsClient wsClient = mock(WsClient.class, Mockito.RETURNS_DEEP_STUBS);
+ private final AnalysisWarnings analysisWarnings = mock(AnalysisWarnings.class);
+
@Test
public void log_and_profile_request_if_debug_level() {
WsRequest request = newRequest();
@@ -54,7 +61,7 @@ public class DefaultScannerWsClientTest {
when(wsClient.wsConnector().call(request)).thenReturn(response);
logTester.setLevel(LoggerLevel.DEBUG);
- DefaultScannerWsClient underTest = new DefaultScannerWsClient(wsClient, false, new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap())));
+ DefaultScannerWsClient underTest = new DefaultScannerWsClient(wsClient, false, new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap())), analysisWarnings);
WsResponse result = underTest.call(request);
@@ -92,10 +99,10 @@ public class DefaultScannerWsClientTest {
when(wsClient.wsConnector().call(request)).thenReturn(response);
assertThatThrownBy(() -> new DefaultScannerWsClient(wsClient, false,
- new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap()))).call(request))
- .isInstanceOf(MessageException.class)
- .hasMessage("Not authorized. Analyzing this project requires authentication. Please provide a user token in sonar.login or other " +
- "credentials in sonar.login and sonar.password.");
+ new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap())), analysisWarnings).call(request))
+ .isInstanceOf(MessageException.class)
+ .hasMessage("Not authorized. Analyzing this project requires authentication. Please provide a user token in sonar.login or other " +
+ "credentials in sonar.login and sonar.password.");
}
@Test
@@ -105,9 +112,9 @@ public class DefaultScannerWsClientTest {
when(wsClient.wsConnector().call(request)).thenReturn(response);
assertThatThrownBy(() -> new DefaultScannerWsClient(wsClient, /* credentials are configured */true,
- new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap()))).call(request))
- .isInstanceOf(MessageException.class)
- .hasMessage("Not authorized. Please check the properties sonar.login and sonar.password.");
+ new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap())), analysisWarnings).call(request))
+ .isInstanceOf(MessageException.class)
+ .hasMessage("Not authorized. Please check the properties sonar.login and sonar.password.");
}
@Test
@@ -118,9 +125,33 @@ public class DefaultScannerWsClientTest {
when(wsClient.wsConnector().call(request)).thenReturn(response);
assertThatThrownBy(() -> new DefaultScannerWsClient(wsClient, true,
- new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap()))).call(request))
- .isInstanceOf(MessageException.class)
- .hasMessage("You're not authorized to run analysis. Please contact the project administrator.");
+ new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap())), analysisWarnings).call(request))
+ .isInstanceOf(MessageException.class)
+ .hasMessage("You're not authorized to run analysis. Please contact the project administrator.");
+ }
+
+ @Test
+ public void warnings_are_added_when_expiration_approaches() {
+ WsRequest request = newRequest();
+ String expirationDate = DateTimeFormatter
+ .ofPattern(DATETIME_FORMAT)
+ .format(LocalDateTime.now().atOffset(ZoneOffset.UTC).plusDays(5));
+ WsResponse response = newResponse()
+ .setCode(200)
+ .setExpirationDate(expirationDate);
+ when(wsClient.wsConnector().call(request)).thenReturn(response);
+
+ logTester.setLevel(LoggerLevel.DEBUG);
+ DefaultScannerWsClient underTest = new DefaultScannerWsClient(wsClient, false, new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap())), analysisWarnings);
+ underTest.call(request);
+ //the second call should not add the same warning twice
+ underTest.call(request);
+
+ // check logs
+ List<String> warningLogs = logTester.logs(LoggerLevel.WARN);
+ assertThat(warningLogs).hasSize(2);
+ assertThat(warningLogs.get(0)).contains("The token used for this analysis will expire on: " + expirationDate);
+ assertThat(warningLogs.get(1)).contains("Analysis executed with this token after the expiration date will fail.");
}
@Test
@@ -132,9 +163,9 @@ public class DefaultScannerWsClientTest {
when(wsClient.wsConnector().call(request)).thenReturn(response);
assertThatThrownBy(() -> new DefaultScannerWsClient(wsClient, true,
- new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap()))).call(request))
- .isInstanceOf(MessageException.class)
- .hasMessage("Boo! bad request! bad!");
+ new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap())), analysisWarnings).call(request))
+ .isInstanceOf(MessageException.class)
+ .hasMessage("Boo! bad request! bad!");
}
private MockWsResponse newResponse() {
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/PluginFilesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/PluginFilesTest.java
index d2b7cd4840d..233028242bd 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/PluginFilesTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/PluginFilesTest.java
@@ -48,6 +48,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.notifications.AnalysisWarnings;
import org.sonar.scanner.bootstrap.ScannerPluginInstaller.InstalledPlugin;
import org.sonarqube.ws.client.HttpConnector;
import org.sonarqube.ws.client.WsClientFactories;
@@ -56,6 +57,7 @@ import static org.apache.commons.io.FileUtils.moveFile;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.ThrowableAssert.ThrowingCallable;
+import static org.mockito.Mockito.mock;
public class PluginFilesTest {
@@ -64,6 +66,8 @@ public class PluginFilesTest {
@Rule
public MockWebServer server = new MockWebServer();
+ private final AnalysisWarnings analysisWarnings = mock(AnalysisWarnings.class);
+
private File userHome;
private PluginFiles underTest;
@@ -72,7 +76,7 @@ public class PluginFilesTest {
HttpConnector connector = HttpConnector.newBuilder().url(server.url("/").toString()).build();
GlobalAnalysisMode analysisMode = new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap()));
DefaultScannerWsClient wsClient = new DefaultScannerWsClient(WsClientFactories.getDefault().newClient(connector), false,
- analysisMode);
+ analysisMode, analysisWarnings);
userHome = temp.newFolder();
MapSettings settings = new MapSettings();
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerWsClientProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerWsClientProviderTest.java
index e6e6d3ba627..c4db3d9258b 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerWsClientProviderTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerWsClientProviderTest.java
@@ -40,7 +40,8 @@ public class ScannerWsClientProviderTest {
ScannerProperties settings = new ScannerProperties(new HashMap<>());
DefaultScannerWsClient client = underTest.provide(settings, env, new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap())),
- mock(System2.class));
+ mock(System2.class),warning -> {
+ });
assertThat(client).isNotNull();
assertThat(client.baseUrl()).isEqualTo("http://localhost:9000/");
@@ -61,7 +62,8 @@ public class ScannerWsClientProviderTest {
ScannerProperties settings = new ScannerProperties(props);
DefaultScannerWsClient client = underTest.provide(settings, env, new GlobalAnalysisMode(new ScannerProperties(Collections.emptyMap())),
- mock(System2.class));
+ mock(System2.class),warning -> {
+ });
assertThat(client).isNotNull();
HttpConnector httpConnector = (HttpConnector) client.wsConnector();