diff --git a/README.md b/README.md
index 1ce3ca59..5ec4d4cd 100644
--- a/README.md
+++ b/README.md
@@ -13,12 +13,11 @@
## Required parameters
- `api.tpa.host` The host of the Trusted Profile Analyzer service. Used as a Vulnerability Provider.
-- `api.tpa.token` The TPA token for default authentication to use when the `ex-tpa-token` header is not provided
- `api.snyk.token` Snyk API token for default authentication when the Snyk integration is enabled
### TPA Client Authentication
-- `quarkus.oidc-client.tpa.enabled`: Defaults to true. Set to `false` to disable `tpa` authentication
+- `quarkus.oidc-client.tpa.enabled`: Defaults to `true`. Set to `false` to disable `tpa` authentication
- `quarkus.oidc-client.tpa.auth-server-url`: Authentication server. Example: https://sso-tpa.example.com/auth/realms/myrealm
- `quarkus.oidc-client.tpa.client-id`: OIDC Client ID
- `quarkus.oidc-client.tpa.credentials.secret`: OIDC Client secret
diff --git a/pom.xml b/pom.xml
index dfcba2d6..c8e91d29 100644
--- a/pom.xml
+++ b/pom.xml
@@ -356,6 +356,10 @@
verify
+
+ **/*Test.java
+ **/*IT.java
+
${project.build.directory}/${project.build.finalName}-runner
org.jboss.logmanager.LogManager
diff --git a/src/main/java/com/redhat/exhort/integration/providers/tpa/TpaIntegration.java b/src/main/java/com/redhat/exhort/integration/providers/tpa/TpaIntegration.java
index 7b5e4238..1c30e29e 100644
--- a/src/main/java/com/redhat/exhort/integration/providers/tpa/TpaIntegration.java
+++ b/src/main/java/com/redhat/exhort/integration/providers/tpa/TpaIntegration.java
@@ -18,8 +18,6 @@
package com.redhat.exhort.integration.providers.tpa;
-import java.time.Duration;
-
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.builder.AggregationStrategies;
@@ -29,8 +27,6 @@
import com.redhat.exhort.integration.Constants;
import com.redhat.exhort.integration.providers.VulnerabilityProvider;
-import io.quarkus.oidc.client.OidcClients;
-
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.HttpMethod;
@@ -40,21 +36,13 @@
@ApplicationScoped
public class TpaIntegration extends EndpointRouteBuilder {
- private static final String TPA_CLIENT_TENANT = "tpa";
- private static final int TPA_CLIENT_TIMEOUT = 10;
-
@ConfigProperty(name = "api.tpa.timeout", defaultValue = "60s")
String timeout;
- @ConfigProperty(name = "quarkus.oidc-client.tpa.enabled", defaultValue = "true")
- boolean tpaEnabled;
-
@Inject VulnerabilityProvider vulnerabilityProvider;
@Inject TpaResponseHandler responseHandler;
@Inject TpaRequestBuilder requestBuilder;
- @Inject OidcClients oidcClients;
-
@Override
public void configure() throws Exception {
// fmt:off
@@ -75,13 +63,13 @@ public void configure() throws Exception {
.split(body(), AggregationStrategies.beanAllowNull(responseHandler, "aggregateSplit"))
.parallelProcessing()
.transform().method(requestBuilder, "buildRequest")
- .process(this::processRequest)
- .process(requestBuilder::addAuthentication)
- .circuitBreaker()
+ .circuitBreaker()
.faultToleranceConfiguration()
.timeoutEnabled(true)
.timeoutDuration(timeout)
.end()
+ .process(this::processRequest)
+ .process(requestBuilder::addAuthentication)
.to(http("{{api.tpa.host}}"))
.transform(method(responseHandler, "responseToIssues"))
.onFallback()
@@ -140,20 +128,6 @@ private void processRequest(Exchange exchange) {
message.setHeader(Exchange.CONTENT_TYPE, MediaType.APPLICATION_JSON);
message.setHeader(Exchange.HTTP_PATH, Constants.TPA_ANALYZE_PATH);
message.setHeader(Exchange.HTTP_METHOD, HttpMethod.POST);
-
- String token = message.getHeader(Constants.TPA_TOKEN_HEADER, String.class);
- if (token == null && !tpaEnabled) {
- token = "placeholder";
- }
- if (token == null) {
- token =
- oidcClients
- .getClient(TPA_CLIENT_TENANT)
- .getTokens()
- .await()
- .atMost(Duration.ofSeconds(TPA_CLIENT_TIMEOUT))
- .getAccessToken();
- }
}
private void processHealthRequest(Exchange exchange) {
diff --git a/src/main/java/com/redhat/exhort/integration/providers/tpa/TpaRequestBuilder.java b/src/main/java/com/redhat/exhort/integration/providers/tpa/TpaRequestBuilder.java
index ffe8e716..fe283afc 100644
--- a/src/main/java/com/redhat/exhort/integration/providers/tpa/TpaRequestBuilder.java
+++ b/src/main/java/com/redhat/exhort/integration/providers/tpa/TpaRequestBuilder.java
@@ -18,9 +18,9 @@
package com.redhat.exhort.integration.providers.tpa;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import org.apache.camel.Exchange;
import org.eclipse.microprofile.config.inject.ConfigProperty;
@@ -31,18 +31,25 @@
import com.redhat.exhort.integration.Constants;
import com.redhat.exhort.model.DependencyTree;
+import io.quarkus.oidc.client.OidcClients;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
@ApplicationScoped
@RegisterForReflection
public class TpaRequestBuilder {
+ @ConfigProperty(name = "quarkus.oidc-client.tpa.enabled", defaultValue = "true")
+ boolean authEnabled;
+
+ @Inject OidcClients oidcClients;
+
private static final int BULK_SIZE = 128;
- @ConfigProperty(name = "api.tpa.token")
- Optional defaultToken;
+ public static final String TPA_CLIENT_TENANT = "tpa";
+ private static final int TPA_CLIENT_TIMEOUT = 10;
private final ObjectMapper mapper = ObjectMapperProducer.newInstance();
@@ -73,11 +80,20 @@ public List> split(DependencyTree tree) {
public void addAuthentication(Exchange exchange) {
var message = exchange.getMessage();
var userToken = message.getHeader(Constants.TPA_TOKEN_HEADER, String.class);
- String token = null;
+ String token;
+ if (!authEnabled) {
+ return;
+ }
if (userToken != null) {
token = userToken;
- } else if (defaultToken.isPresent()) {
- token = defaultToken.get();
+ } else {
+ token =
+ oidcClients
+ .getClient(TPA_CLIENT_TENANT)
+ .getTokens()
+ .await()
+ .atMost(Duration.ofSeconds(TPA_CLIENT_TIMEOUT))
+ .getAccessToken();
}
if (token != null) {
message.setHeader("Authorization", "Bearer " + token);
diff --git a/src/test/java/com/redhat/exhort/extensions/OidcWiremockExtension.java b/src/test/java/com/redhat/exhort/extensions/OidcWiremockExtension.java
new file mode 100644
index 00000000..a39745d7
--- /dev/null
+++ b/src/test/java/com/redhat/exhort/extensions/OidcWiremockExtension.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2023 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.redhat.exhort.extensions;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.containing;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class OidcWiremockExtension extends WiremockExtension {
+
+ private static final String CLIENT_ID = "test-tpa-client";
+ private static final String CLIENT_SECRET = "test-tpa-secret";
+
+ @Override
+ public Map start() {
+ var base = super.start();
+
+ stubTpaClientToken();
+ var oidcConfig = new HashMap<>(base);
+
+ oidcConfig.put("keycloak.url", server.baseUrl());
+ return oidcConfig;
+ }
+
+ protected void stubTpaClientToken() {
+ server.stubFor(
+ com.github.tomakehurst.wiremock.client.WireMock.post("/auth/realms/tpa/token")
+ .withBasicAuth(CLIENT_ID, CLIENT_SECRET)
+ .withHeader(
+ "Content-Type",
+ com.github.tomakehurst.wiremock.client.WireMock.equalTo(
+ "application/x-www-form-urlencoded"))
+ .withRequestBody(containing("grant_type=client_credentials"))
+ .willReturn(
+ aResponse()
+ .withStatus(200)
+ .withHeader("Content-Type", "application/json")
+ .withBody(
+ String.format(
+ "{\"access_token\":\"%s\",\"token_type\":\"Bearer\",\"expires_in\":300}",
+ TPA_TOKEN))));
+
+ String openIdConfigJson =
+ String.format(
+ """
+ {
+ "jwks_uri": "%1$s/auth/realms/tpa/protocol/openid-connect/certs",
+ "token_introspection_endpoint": "%1$s/auth/realms/tpa/protocol/openid-connect/token/introspect",
+ "authorization_endpoint": "%1$s/auth/realms/tpa",
+ "userinfo_endpoint": "%1$s/auth/realms/tpa/protocol/openid-connect/userinfo",
+ "token_endpoint": "%1$s/auth/realms/tpa/token",
+ "issuer" : "https://server.example.com",
+ "introspection_endpoint": "%1$s/auth/realms/tpa/protocol/openid-connect/token/introspect",
+ "end_session_endpoint": "%1$s/auth/realms/tpa/protocol/openid-connect/end-session"
+ }
+ """,
+ server.baseUrl());
+
+ server.stubFor(
+ com.github.tomakehurst.wiremock.client.WireMock.get(
+ "/realms/tpa/.well-known/openid-configuration")
+ .willReturn(
+ aResponse()
+ .withStatus(200)
+ .withHeader("Content-Type", "application/json")
+ .withBody(openIdConfigJson)));
+ }
+}
diff --git a/src/test/java/com/redhat/exhort/extensions/WiremockExtension.java b/src/test/java/com/redhat/exhort/extensions/WiremockExtension.java
index 5a3bdad7..cad7f8bd 100644
--- a/src/test/java/com/redhat/exhort/extensions/WiremockExtension.java
+++ b/src/test/java/com/redhat/exhort/extensions/WiremockExtension.java
@@ -31,7 +31,7 @@ public class WiremockExtension implements QuarkusTestResourceLifecycleManager {
public static final String SNYK_TOKEN = "snyk-token-xyz";
public static final String TPA_TOKEN = "tpa-token-abc";
- private final WireMockServer server = new WireMockServer(options().dynamicPort());
+ final WireMockServer server = new WireMockServer(options().dynamicPort());
@Override
public Map start() {
@@ -43,8 +43,7 @@ public Map start() {
"api.trustedcontent.host", server.baseUrl(),
"api.ossindex.host", server.baseUrl(),
"api.onguard.host", server.baseUrl(),
- "api.tpa.host", server.baseUrl(),
- "api.tpa.token", TPA_TOKEN);
+ "api.tpa.host", server.baseUrl());
}
@Override
diff --git a/src/test/java/com/redhat/exhort/integration/AbstractAnalysisTest.java b/src/test/java/com/redhat/exhort/integration/AbstractAnalysisTest.java
index 028841e6..578c4397 100644
--- a/src/test/java/com/redhat/exhort/integration/AbstractAnalysisTest.java
+++ b/src/test/java/com/redhat/exhort/integration/AbstractAnalysisTest.java
@@ -91,7 +91,9 @@ public abstract class AbstractAnalysisTest {
@AfterEach
void resetMock() {
- server.resetAll();
+ if (server != null) {
+ server.resetAll();
+ }
}
protected void assertJson(String expectedFile, String currentBody) {
@@ -443,7 +445,8 @@ protected void stubTpaTokenRequests() {
get(urlPathEqualTo(Constants.TPA_TOKEN_PATH))
.withQueryParam("limit", equalTo("0"))
.willReturn(aResponse().withStatus(401)));
- // Default request
+
+ // Accepted tokens
server.stubFor(
get(urlPathEqualTo(Constants.TPA_TOKEN_PATH))
.withHeader(
@@ -455,12 +458,14 @@ protected void stubTpaTokenRequests() {
.withStatus(200)
.withHeader(Exchange.CONTENT_TYPE, MediaType.APPLICATION_JSON)
.withBodyFile("tpa/empty_report.json")));
+
// Internal Error
server.stubFor(
get(urlPathEqualTo(Constants.TPA_TOKEN_PATH))
.withHeader(Constants.AUTHORIZATION_HEADER, equalTo("Bearer " + ERROR_TOKEN))
.withQueryParam("limit", equalTo("0"))
.willReturn(aResponse().withStatus(500).withBody("This is an example error")));
+
// Invalid token
server.stubFor(
get(urlPathEqualTo(Constants.TPA_TOKEN_PATH))
diff --git a/src/test/java/com/redhat/exhort/integration/AnalysisTest.java b/src/test/java/com/redhat/exhort/integration/AnalysisTest.java
index 2a91dc37..3f6fb4df 100644
--- a/src/test/java/com/redhat/exhort/integration/AnalysisTest.java
+++ b/src/test/java/com/redhat/exhort/integration/AnalysisTest.java
@@ -57,7 +57,9 @@
import com.redhat.exhort.api.v4.DependencyReport;
import com.redhat.exhort.api.v4.Scanned;
import com.redhat.exhort.api.v4.SourceSummary;
+import com.redhat.exhort.extensions.OidcWiremockExtension;
+import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.ws.rs.core.MediaType;
@@ -65,6 +67,7 @@
import jakarta.ws.rs.core.Response.Status;
@QuarkusTest
+@QuarkusTestResource(OidcWiremockExtension.class)
public class AnalysisTest extends AbstractAnalysisTest {
private static final String CYCLONEDX = "cyclonedx";
diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties
index c28e4f23..a69110e6 100644
--- a/src/test/resources/application.properties
+++ b/src/test/resources/application.properties
@@ -4,7 +4,6 @@ telemetry.disabled=true
api.ossindex.disabled=false
api.snyk.disabled=false
api.tpa.disabled=false
-quarkus.oidc-client.tpa.enabled=false
quarkus.hibernate-orm.persistence-xml.ignore=true
quarkus.keycloak.devservices.enabled=false
quarkus.datasource.devservices.enabled=false
@@ -13,3 +12,9 @@ quarkus.datasource.jdbc.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.flyway.enabled=false
quarkus.hibernate-orm.sql-load-script=db/h2/V2__insert_data.sql
+
+# TPA OIDC Client Configuration for testing
+quarkus.oidc-client.tpa.auth-server-url=${keycloak.url}/realms/tpa
+quarkus.oidc-client.tpa.client-id=test-tpa-client
+quarkus.oidc-client.tpa.credentials.secret=test-tpa-secret
+quarkus.oidc-client.tpa.grant.type=client