aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAurelien Poscia <aurelien.poscia@sonarsource.com>2022-09-16 15:20:09 +0200
committersonartech <sonartech@sonarsource.com>2022-09-19 20:03:08 +0000
commit1f9bc827e81575d6515061cace65526ca3edf18b (patch)
treef16fd9d7ffb341854f679ecbcc7860183f9425b8
parent1e1e26d5dc136036fc4cbfde759e4879e098f519 (diff)
downloadsonarqube-1f9bc827e81575d6515061cace65526ca3edf18b.tar.gz
sonarqube-1f9bc827e81575d6515061cace65526ca3edf18b.zip
SONAR-17271 Request body can be read multiple time & implement support of comments coming from GitHub webhooks
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/GithubWebhookAuthentication.java21
-rw-r--r--server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/GithubWebhookAuthenticationTest.java11
-rw-r--r--server/sonar-webserver-core/src/main/java/org/sonar/server/platform/web/RootFilter.java17
-rw-r--r--server/sonar-webserver-core/src/test/java/org/sonar/server/platform/web/RootFilterTest.java (renamed from server/sonar-webserver/src/test/java/org/sonar/server/platform/web/RootFilterTest.java)57
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/IssueUpdater.java2
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java16
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java9
-rw-r--r--server/sonar-webserver-webapi/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/issue_with_comments.json11
8 files changed, 116 insertions, 28 deletions
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/GithubWebhookAuthentication.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/GithubWebhookAuthentication.java
index 2fb47dd24d0..6197d4e4c32 100644
--- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/GithubWebhookAuthentication.java
+++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/GithubWebhookAuthentication.java
@@ -20,7 +20,6 @@
package org.sonar.server.authentication;
import com.google.common.annotations.VisibleForTesting;
-import java.io.IOException;
import java.security.MessageDigest;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
@@ -97,28 +96,18 @@ public class GithubWebhookAuthentication {
private static String getGithubSignature(HttpServletRequest request, String githubAppId) {
String githubSignature = request.getHeader(GITHUB_SIGNATURE_HEADER);
- if (isEmpty(githubSignature) ) {
+ if (isEmpty(githubSignature)) {
logAuthenticationProblemAndThrow(format(MSG_UNAUTHENTICATED_GITHUB_CALLS_DENIED, githubAppId));
}
return githubSignature;
}
private static String getBody(HttpServletRequest request) {
- Optional<String> body = getBodyInternal(request);
- if (body.isEmpty() || isEmpty(body.get())) {
- logAuthenticationProblemAndThrow(MSG_NO_BODY_FOUND);
- }
- return body.get();
- }
-
-
- private static Optional<String> getBodyInternal(HttpServletRequest request) {
try {
- String body = request.getReader().lines().collect(joining(System.lineSeparator()));
- return Optional.of(body);
- } catch (IOException e) {
- LOG.debug("Unexpected error while trying to get the body of github webhook request", e);
- return Optional.empty();
+ return request.getReader().lines().collect(joining(System.lineSeparator()));
+ } catch (Exception e) {
+ logAuthenticationProblemAndThrow(MSG_NO_BODY_FOUND);
+ return "";
}
}
diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/GithubWebhookAuthenticationTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/GithubWebhookAuthenticationTest.java
index 96b48fee579..f12309fe53d 100644
--- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/GithubWebhookAuthenticationTest.java
+++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/GithubWebhookAuthenticationTest.java
@@ -119,6 +119,17 @@ public class GithubWebhookAuthenticationTest {
assertThatExceptionOfType(AuthenticationException.class)
.isThrownBy(() -> githubWebhookAuthentication.authenticate(request))
+ .withMessage(MSG_AUTHENTICATION_FAILED);
+ assertThat(logTester.getLogs(LoggerLevel.WARN)).extracting(LogAndArguments::getFormattedMsg).contains(MSG_AUTHENTICATION_FAILED);
+ }
+
+ @Test
+ public void authenticate_withExceptionWhileReadingBody_throws() throws IOException {
+ HttpServletRequest request = mockRequest(GITHUB_PAYLOAD, GITHUB_SIGNATURE);
+ when(request.getReader()).thenThrow(new IOException());
+
+ assertThatExceptionOfType(AuthenticationException.class)
+ .isThrownBy(() -> githubWebhookAuthentication.authenticate(request))
.withMessage(MSG_NO_BODY_FOUND);
assertThat(logTester.getLogs(LoggerLevel.WARN)).extracting(LogAndArguments::getFormattedMsg).contains(MSG_NO_BODY_FOUND);
}
diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/web/RootFilter.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/web/RootFilter.java
index 17866133597..e797fb14ed2 100644
--- a/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/web/RootFilter.java
+++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/platform/web/RootFilter.java
@@ -20,7 +20,9 @@
package org.sonar.server.platform.web;
import com.google.common.annotations.VisibleForTesting;
+import java.io.BufferedReader;
import java.io.IOException;
+import java.io.StringReader;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -34,6 +36,7 @@ import javax.servlet.http.HttpSession;
import org.sonar.api.utils.log.Loggers;
import static java.lang.String.format;
+import static java.util.stream.Collectors.joining;
/**
* <p>Profile HTTP requests using platform profiling utility.</p>
@@ -91,6 +94,7 @@ public class RootFilter implements Filter {
@VisibleForTesting
static class ServletRequestWrapper extends HttpServletRequestWrapper {
+ private String body;
ServletRequestWrapper(HttpServletRequest request) {
super(request);
@@ -112,5 +116,18 @@ public class RootFilter implements Filter {
private static UnsupportedOperationException notSupported() {
return new UnsupportedOperationException("Sessions are disabled so that web server is stateless");
}
+
+ @Override
+ public BufferedReader getReader() throws IOException {
+ if (body == null) {
+ body = getBodyInternal((HttpServletRequest) getRequest());
+ }
+ return new BufferedReader(new StringReader(body));
+ }
+
+ private static String getBodyInternal(HttpServletRequest request) throws IOException {
+ return request.getReader().lines().collect(joining(System.lineSeparator()));
+ }
+
}
}
diff --git a/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/RootFilterTest.java b/server/sonar-webserver-core/src/test/java/org/sonar/server/platform/web/RootFilterTest.java
index 0d1d3a3cf78..579740128db 100644
--- a/server/sonar-webserver/src/test/java/org/sonar/server/platform/web/RootFilterTest.java
+++ b/server/sonar-webserver-core/src/test/java/org/sonar/server/platform/web/RootFilterTest.java
@@ -19,7 +19,11 @@
*/
package org.sonar.server.platform.web;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
import java.util.List;
+import java.util.stream.IntStream;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
@@ -34,8 +38,11 @@ import org.mockito.ArgumentCaptor;
import org.sonar.api.utils.log.LogTester;
import org.sonar.api.utils.log.LoggerLevel;
+import static java.util.stream.Collectors.joining;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
@@ -45,6 +52,8 @@ import static org.mockito.Mockito.when;
public class RootFilterTest {
+ private static final String PAYLOAD = "payload";
+
@Rule
public LogTester logTester = new LogTester();
@@ -117,8 +126,8 @@ public class RootFilterTest {
ArgumentCaptor<ServletRequest> requestArgumentCaptor = ArgumentCaptor.forClass(ServletRequest.class);
verify(chain).doFilter(requestArgumentCaptor.capture(), any(ServletResponse.class));
-
- assertThatThrownBy(() -> ((HttpServletRequest) requestArgumentCaptor.getValue()).getSession())
+ ServletRequest actualServletRequest = requestArgumentCaptor.getValue();
+ assertThatThrownBy(() -> ((HttpServletRequest) actualServletRequest).getSession())
.isInstanceOf(UnsupportedOperationException.class);
}
@@ -128,7 +137,8 @@ public class RootFilterTest {
ArgumentCaptor<ServletRequest> requestArgumentCaptor = ArgumentCaptor.forClass(ServletRequest.class);
verify(chain).doFilter(requestArgumentCaptor.capture(), any(ServletResponse.class));
- assertThatThrownBy(() -> ((HttpServletRequest) requestArgumentCaptor.getValue()).getSession(true))
+ ServletRequest actualServletRequest = requestArgumentCaptor.getValue();
+ assertThatThrownBy(() -> ((HttpServletRequest) actualServletRequest).getSession(true))
.isInstanceOf(UnsupportedOperationException.class);
}
@@ -145,4 +155,45 @@ public class RootFilterTest {
when(response.isCommitted()).thenReturn(committed);
return response;
}
+
+ @Test
+ public void body_can_be_read_several_times() {
+ HttpServletRequest request = mockRequestWithBody();
+ RootFilter.ServletRequestWrapper servletRequestWrapper = new RootFilter.ServletRequestWrapper(request);
+
+ IntStream.range(0,3).forEach(i -> assertThat(readBody(servletRequestWrapper)).isEqualTo(PAYLOAD));
+ }
+
+ @Test
+ public void getReader_whenIoExceptionThrown_rethrows() throws IOException {
+ HttpServletRequest request = mockRequestWithBody();
+ IOException ioException = new IOException();
+ when(request.getReader()).thenThrow(ioException);
+
+ RootFilter.ServletRequestWrapper servletRequestWrapper = new RootFilter.ServletRequestWrapper(request);
+
+ assertThatExceptionOfType(RuntimeException.class)
+ .isThrownBy(() -> readBody(servletRequestWrapper))
+ .withCause(ioException);
+ }
+
+ private static HttpServletRequest mockRequestWithBody() {
+ HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
+ try {
+ StringReader stringReader = new StringReader(PAYLOAD);
+ BufferedReader bufferedReader = new BufferedReader(stringReader);
+ when(httpServletRequest.getReader()).thenReturn(bufferedReader);
+ } catch (IOException e) {
+ fail("mockRequest threw an exception: " + e.getMessage());
+ }
+ return httpServletRequest;
+ }
+
+ private static String readBody(HttpServletRequest request) {
+ try {
+ return request.getReader().lines().collect(joining(System.lineSeparator()));
+ } catch (IOException e) {
+ throw new RuntimeException("unexpected failure", e);
+ }
+ }
}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/IssueUpdater.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/IssueUpdater.java
index 741726e68e6..7e2fa2d6efe 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/IssueUpdater.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/IssueUpdater.java
@@ -112,7 +112,7 @@ public class IssueUpdater {
|| ruleDto == null
// notification are not supported on PRs
|| !hasNotificationSupport(branchDto)
- || context.fromAlm()) {
+ || context.getWebhookSource() != null) {
return issueDto;
}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java
index cfaf240fce1..aa4ac9909c6 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java
@@ -186,12 +186,16 @@ public class SearchResponseLoader {
if (fields.contains(COMMENTS)) {
List<IssueChangeDto> comments = dbClient.issueChangeDao().selectByTypeAndIssueKeys(dbSession, collector.getIssueKeys(), IssueChangeDto.TYPE_COMMENT);
result.setComments(comments);
- for (IssueChangeDto comment : comments) {
- collector.addUserUuids(singletonList(comment.getUserUuid()));
- if (canEditOrDelete(comment)) {
- result.addUpdatableComment(comment.getKey());
- }
- }
+ comments.stream()
+ .filter(c -> c.getUserUuid() != null)
+ .forEach(comment -> loadComment(collector, result, comment));
+ }
+ }
+
+ private void loadComment(Collector collector, SearchResponseData result, IssueChangeDto comment) {
+ collector.addUserUuids(singletonList(comment.getUserUuid()));
+ if (canEditOrDelete(comment)) {
+ result.addUpdatableComment(comment.getKey());
}
}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
index 73573a7d074..293ea18a699 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
@@ -369,6 +369,15 @@ public class SearchActionTest {
.setUserUuid(fabrice.getUuid())
.setProjectUuid(project.projectUuid())
.setIssueChangeCreationDate(parseDateTime("2014-09-10T12:00:00+0000").getTime()));
+ dbClient.issueChangeDao().insert(session,
+ new IssueChangeDto()
+ .setUuid(Uuids.createFast())
+ .setIssueKey(issue.getKey())
+ .setKey("COMMENT-NO-USER")
+ .setChangeData("Another comment without user")
+ .setChangeType(IssueChangeDto.TYPE_COMMENT)
+ .setProjectUuid(project.projectUuid())
+ .setIssueChangeCreationDate(parseDateTime("2022-09-10T12:00:00+0000").getTime()));
session.commit();
indexIssues();
userSession.logIn(john);
diff --git a/server/sonar-webserver-webapi/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/issue_with_comments.json b/server/sonar-webserver-webapi/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/issue_with_comments.json
index e72950b2da9..af2541fb61c 100644
--- a/server/sonar-webserver-webapi/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/issue_with_comments.json
+++ b/server/sonar-webserver-webapi/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/issue_with_comments.json
@@ -9,7 +9,7 @@
"htmlText": "\u003cstrong\u003eMy comment\u003c/strong\u003e",
"markdown": "*My comment*",
"updatable": true,
- "createdAt": "2014-09-09T12:00:00+0000"
+ "createdAt": "2014-09-09T14:00:00+0200"
},
{
"key": "COMMENT-ABCE",
@@ -17,7 +17,14 @@
"htmlText": "Another comment",
"markdown": "Another comment",
"updatable": false,
- "createdAt": "2014-09-10T12:00:00+0000"
+ "createdAt": "2014-09-10T14:00:00+0200"
+ },
+ {
+ "key": "COMMENT-NO-USER",
+ "htmlText": "Another comment without user",
+ "markdown": "Another comment without user",
+ "updatable": false,
+ "createdAt": "2022-09-10T14:00:00+0200"
}
]
}