Skip to content

Commit

Permalink
Bug fixes for sonar 10.2.1
Browse files Browse the repository at this point in the history
  • Loading branch information
C4tWithShell committed Nov 11, 2023
1 parent cd047c8 commit 62f90e9
Show file tree
Hide file tree
Showing 15 changed files with 323 additions and 276 deletions.
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ SONARQUBE_VERSION=latest
DOCKERFILE=Dockerfile

# The version of the plugin to include in the image
PLUGIN_VERSION=1.15.0-SNAPSHOT
PLUGIN_VERSION=1.16.1-SNAPSHOT
8 changes: 4 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,17 @@ tasks.withType(JavaCompile) {
dependencies {
compileOnly(fileTree(dir: sonarLibraries, include: '**/*.jar', exclude: 'extensions/*.jar'))
testImplementation(fileTree(dir: sonarLibraries, include: '**/*.jar', exclude: 'extensions/*.jar'))
testImplementation('org.mockito:mockito-core:5.2.0')
testImplementation('org.mockito:mockito-core:5.4.0')
testImplementation('org.assertj:assertj-core:3.24.2')
testImplementation('com.github.tomakehurst:wiremock:2.27.2')
zip("sonarqube:sonarqube:${sonarqubeVersion}@zip")
implementation('org.bouncycastle:bcpkix-jdk15on:1.70')
implementation(files('lib/nodes-0.5.0.jar'))
runtimeOnly('com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.2')
runtimeOnly('com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2')
compileOnly('com.google.code.findbugs:jsr305:3.0.2')
implementation('org.javassist:javassist:3.29.2-GA')
implementation('com.squareup.okhttp3:logging-interceptor:4.10.0')
testImplementation(platform('org.junit:junit-bom:5.9.2'))
implementation('com.squareup.okhttp3:logging-interceptor:4.11.0')
testImplementation(platform('org.junit:junit-bom:5.10.0'))
testImplementation('org.junit.jupiter:junit-jupiter')
testImplementation('junit:junit:4.13.2')
testRuntimeOnly('org.junit.vintage:junit-vintage-engine')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2022 Michael Clarke
* Copyright (C) 2020-2023 Michael Clarke
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -28,9 +28,14 @@
import com.github.mc1arke.sonarqube.plugin.almclient.github.v3.model.AppToken;
import com.github.mc1arke.sonarqube.plugin.almclient.github.v3.model.InstallationRepositories;
import com.github.mc1arke.sonarqube.plugin.almclient.github.v3.model.Repository;
import com.google.common.annotations.VisibleForTesting;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.DefaultJwtBuilder;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.server.ServerSide;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
Expand All @@ -41,16 +46,8 @@
import java.time.Clock;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.server.ServerSide;

@ServerSide
@ComputeEngineSide
Expand All @@ -75,68 +72,70 @@ public RestApplicationAuthenticationProvider(Clock clock, LinkHeaderReader linkH
this.objectMapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
}

@VisibleForTesting
protected List<AppInstallation> getAppInstallations(ObjectMapper objectMapper, String apiUrl, String jwtToken) throws IOException {
@Override
public RepositoryAuthenticationToken getInstallationToken(String apiUrl, String appId, String apiPrivateKey,
String projectPath) throws IOException {

Instant issued = clock.instant().minus(10, ChronoUnit.SECONDS);
Instant expiry = issued.plus(2, ChronoUnit.MINUTES);
String jwtToken = new DefaultJwtBuilder().setIssuedAt(Date.from(issued)).setExpiration(Date.from(expiry))
.claim("iss", appId).signWith(createPrivateKey(apiPrivateKey), SignatureAlgorithm.RS256).compact();

List<AppInstallation> appInstallations = new ArrayList<>();
Optional<RepositoryAuthenticationToken> repositoryAuthenticationToken = findTokenFromAppInstallationList(getV3Url(apiUrl) + "/app/installations", jwtToken, projectPath);

return repositoryAuthenticationToken.orElseThrow(() -> new InvalidConfigurationException(InvalidConfigurationException.Scope.PROJECT,
"No token could be found with access to the requested repository using the given application ID and key"));
}

private Optional<RepositoryAuthenticationToken> findTokenFromAppInstallationList(String apiUrl, String jwtToken, String projectPath) throws IOException {
URLConnection appConnection = urlProvider.createUrlConnection(apiUrl);
appConnection.setRequestProperty(ACCEPT_HEADER, APP_PREVIEW_ACCEPT_HEADER);
appConnection.setRequestProperty(AUTHORIZATION_HEADER, BEARER_AUTHORIZATION_HEADER_PREFIX + jwtToken);

try (Reader reader = new InputStreamReader(appConnection.getInputStream())) {
appInstallations.addAll(Arrays.asList(objectMapper.readerFor(AppInstallation[].class).readValue(reader)));
AppInstallation[] appInstallations = objectMapper.readerFor(AppInstallation[].class).readValue(reader);
for (AppInstallation appInstallation : appInstallations) {
Optional<RepositoryAuthenticationToken> repositoryAuthenticationToken = findAppTokenFromAppInstallation(appInstallation, jwtToken, projectPath);

if (repositoryAuthenticationToken.isPresent()) {
return repositoryAuthenticationToken;
}
}
}

Optional<String> nextLink = linkHeaderReader.findNextLink(appConnection.getHeaderField("Link"));
if (nextLink.isPresent()) {
appInstallations.addAll(getAppInstallations(objectMapper, nextLink.get(), jwtToken));
if (nextLink.isEmpty()) {
return Optional.empty();
}

return appInstallations;
return findTokenFromAppInstallationList(nextLink.get(), jwtToken, projectPath);
}

@Override
public RepositoryAuthenticationToken getInstallationToken(String apiUrl, String appId, String apiPrivateKey,
String projectPath) throws IOException {

Instant issued = clock.instant().minus(10, ChronoUnit.SECONDS);
Instant expiry = issued.plus(2, ChronoUnit.MINUTES);
String jwtToken = new DefaultJwtBuilder().setIssuedAt(Date.from(issued)).setExpiration(Date.from(expiry))
.claim("iss", appId).signWith(createPrivateKey(apiPrivateKey), SignatureAlgorithm.RS256).compact();

ObjectMapper objectMapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

List<AppInstallation> appInstallations = getAppInstallations(objectMapper, getV3Url(apiUrl) + "/app/installations", jwtToken);
private Optional<RepositoryAuthenticationToken> findAppTokenFromAppInstallation(AppInstallation installation, String jwtToken, String projectPath) throws IOException {
URLConnection accessTokenConnection = urlProvider.createUrlConnection(installation.getAccessTokensUrl());
((HttpURLConnection) accessTokenConnection).setRequestMethod("POST");
accessTokenConnection.setRequestProperty(ACCEPT_HEADER, APP_PREVIEW_ACCEPT_HEADER);
accessTokenConnection
.setRequestProperty(AUTHORIZATION_HEADER, BEARER_AUTHORIZATION_HEADER_PREFIX + jwtToken);

for (AppInstallation installation : appInstallations) {
URLConnection accessTokenConnection = urlProvider.createUrlConnection(installation.getAccessTokensUrl());
((HttpURLConnection) accessTokenConnection).setRequestMethod("POST");
accessTokenConnection.setRequestProperty(ACCEPT_HEADER, APP_PREVIEW_ACCEPT_HEADER);
accessTokenConnection
.setRequestProperty(AUTHORIZATION_HEADER, BEARER_AUTHORIZATION_HEADER_PREFIX + jwtToken);
try (Reader reader = new InputStreamReader(accessTokenConnection.getInputStream())) {
AppToken appToken = objectMapper.readerFor(AppToken.class).readValue(reader);

String targetUrl = installation.getRepositoriesUrl();

try (Reader reader = new InputStreamReader(accessTokenConnection.getInputStream())) {
AppToken appToken = objectMapper.readerFor(AppToken.class).readValue(reader);

String targetUrl = installation.getRepositoriesUrl();

Optional<RepositoryAuthenticationToken> potentialRepositoryAuthenticationToken = findRepositoryAuthenticationToken(appToken, targetUrl, projectPath, objectMapper);

if (potentialRepositoryAuthenticationToken.isPresent()) {
return potentialRepositoryAuthenticationToken.get();
}
Optional<RepositoryAuthenticationToken> potentialRepositoryAuthenticationToken = findRepositoryAuthenticationToken(appToken, targetUrl, projectPath);

if (potentialRepositoryAuthenticationToken.isPresent()) {
return potentialRepositoryAuthenticationToken;
}

}

throw new InvalidConfigurationException(InvalidConfigurationException.Scope.PROJECT,
"No token could be found with access to the requested repository using the given application ID and key");
return Optional.empty();
}

private Optional<RepositoryAuthenticationToken> findRepositoryAuthenticationToken(AppToken appToken, String targetUrl,
String projectPath, ObjectMapper objectMapper) throws IOException {
String projectPath) throws IOException {
URLConnection installationRepositoriesConnection = urlProvider.createUrlConnection(targetUrl);
((HttpURLConnection) installationRepositoriesConnection).setRequestMethod("GET");
installationRepositoriesConnection.setRequestProperty(ACCEPT_HEADER, APP_PREVIEW_ACCEPT_HEADER);
Expand All @@ -161,7 +160,7 @@ private Optional<RepositoryAuthenticationToken> findRepositoryAuthenticationToke
return Optional.empty();
}

return findRepositoryAuthenticationToken(appToken, nextLink.get(), projectPath, objectMapper);
return findRepositoryAuthenticationToken(appToken, nextLink.get(), projectPath);
}

private static String getV3Url(String apiUrl) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,24 @@
*/
package com.github.mc1arke.sonarqube.plugin.ce.pullrequest.markup;

import java.util.Set;

public final class Link extends Node {

private static final Set<Class<? extends Node>> SUPPORTED_CHILDREN = Set.of(Text.class, Image.class);
private final String url;

public Link(String url, Node... children) {
super(children);
this.url=url;
}
this.url = url;
}

public String getUrl() {
return url;
}

@Override
boolean isValidChild(Node child) {
return child instanceof Text;
return child != null && SUPPORTED_CHILDREN.contains(child.getClass());
}
}
Loading

0 comments on commit 62f90e9

Please sign in to comment.