Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Response;

import org.eclipse.microprofile.rest.client.inject.RestClient;

Expand All @@ -25,36 +26,31 @@ public class TokenPropagationResource {

@POST
@Path("service1")
public String service1() {
defaultApi1.executeQuery1();
return "hello";
public Response service1() {
return defaultApi1.executeQuery1();
}

@POST
@Path("service2")
public String service2() {
defaultApi2.executeQuery2();
return "hello";
public Response service2() {
return defaultApi2.executeQuery2();
}

@POST
@Path("service3")
public String service3() {
defaultApi3.executeQuery3();
return "hello";
public Response service3() {
return defaultApi3.executeQuery3();
}

@POST
@Path("service4")
public String service4() {
defaultApi4.executeQuery4();
return "hello";
public Response service4() {
return defaultApi4.executeQuery4();
}

@POST
@Path("service5")
public String service5() {
defaultApi5.executeQuery5();
return "hello";
public Response service5() {
return defaultApi5.executeQuery5();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@

public class TokenPropagationExternalServicesMock implements QuarkusTestResourceLifecycleManager {

public static final String AUTHORIZATION_TOKEN = "AUTHORIZATION_TOKEN";
public static final String SERVICE1_AUTHORIZATION_TOKEN = "SERVICE1_AUTHORIZATION_TOKEN";
public static final String SERVICE2_AUTHORIZATION_TOKEN = "SERVICE2_AUTHORIZATION_TOKEN";
public static final String SERVICE3_HEADER_TO_PROPAGATE = "SERVICE3_HEADER_TO_PROPAGATE";
public static final String SERVICE3_AUTHORIZATION_TOKEN = "SERVICE3_AUTHORIZATION_TOKEN";
public static final String SERVICE4_HEADER_TO_PROPAGATE = "SERVICE4_HEADER_TO_PROPAGATE";
Expand All @@ -49,10 +50,10 @@ public Map<String, String> start() {
LOGGER.info("Mocked Server started at {}", wireMockServer.baseUrl());

// stub the token-propagation-external-service1 invocation with the expected token
stubForExternalService("/token-propagation-external-service1/executeQuery1", AUTHORIZATION_TOKEN);
stubForExternalService("/token-propagation-external-service1/executeQuery1", SERVICE1_AUTHORIZATION_TOKEN);

// stub the token-propagation-external-service2 invocation with the expected token
stubForExternalService("/token-propagation-external-service2/executeQuery2", AUTHORIZATION_TOKEN);
stubForExternalService("/token-propagation-external-service2/executeQuery2", SERVICE2_AUTHORIZATION_TOKEN);

// stub the token-propagation-external-service3 invocation with the expected token
stubForExternalService("/token-propagation-external-service3/executeQuery3", SERVICE3_AUTHORIZATION_TOKEN);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
package io.quarkiverse.openapi.generator.it.security;

import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.AUTHORIZATION_TOKEN;
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE1_AUTHORIZATION_TOKEN;
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE2_AUTHORIZATION_TOKEN;
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE3_AUTHORIZATION_TOKEN;
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE3_HEADER_TO_PROPAGATE;
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE4_AUTHORIZATION_TOKEN;
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE4_HEADER_TO_PROPAGATE;
import static io.restassured.RestAssured.given;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

import jakarta.ws.rs.core.HttpHeaders;

import org.junit.jupiter.api.Tag;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;
Expand All @@ -26,21 +31,40 @@
@Tag("resteasy-classic")
class TokenPropagationTest {

@ParameterizedTest
@ValueSource(strings = { "service1", "service2", "service3", "service4", "service5" })
void service1(String service) {
Map<String, String> headers = new HashMap<>();
// service token-propagation-external-service1 and token-propagation-external-service2 will receive the AUTHORIZATION_TOKEN
headers.put(HttpHeaders.AUTHORIZATION, AUTHORIZATION_TOKEN);
// service token-propagation-external-service3 will receive the SERVICE3_AUTHORIZATION_TOKEN
headers.put(SERVICE3_HEADER_TO_PROPAGATE, SERVICE3_AUTHORIZATION_TOKEN);
// service token-propagation-external-service4 will receive the SERVICE4_AUTHORIZATION_TOKEN
headers.put(SERVICE4_HEADER_TO_PROPAGATE, SERVICE4_AUTHORIZATION_TOKEN);
private static class HeaderArgument {
String headerName;
String headerValue;

HeaderArgument(String headerName, String headerValue) {
this.headerName = headerName;
this.headerValue = headerValue;
}

}

@ParameterizedTest
@MethodSource("serviceInvocationParams")
void invokeService(String service, HeaderArgument headerArgument) {
Map<String, List<String>> headers = new HashMap<>();
headers.put(headerArgument.headerName, Collections.singletonList(headerArgument.headerValue));
given()
.headers(headers)
.post("/token_propagation/" + service)
.then()
.statusCode(200);
}

private static Stream<Arguments> serviceInvocationParams() {
return Stream.of(
// service token-propagation-external-service1 will receive the SERVICE1_AUTHORIZATION_TOKEN
Arguments.of("service1", new HeaderArgument(HttpHeaders.AUTHORIZATION, SERVICE1_AUTHORIZATION_TOKEN)),
// service token-propagation-external-service2 will receive the SERVICE2_AUTHORIZATION_TOKEN
Arguments.of("service2", new HeaderArgument(HttpHeaders.AUTHORIZATION, SERVICE2_AUTHORIZATION_TOKEN)),
// service token-propagation-external-service3 will receive the SERVICE3_AUTHORIZATION_TOKEN
Arguments.of("service3", new HeaderArgument(SERVICE3_HEADER_TO_PROPAGATE, SERVICE3_AUTHORIZATION_TOKEN)),
// service token-propagation-external-service4 will receive the SERVICE4_AUTHORIZATION_TOKEN
Arguments.of("service4", new HeaderArgument(SERVICE4_HEADER_TO_PROPAGATE, SERVICE4_AUTHORIZATION_TOKEN))

);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import java.util.List;

import jakarta.ws.rs.client.ClientRequestContext;
import jakarta.ws.rs.core.HttpHeaders;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -46,10 +45,14 @@ public void filter(ClientRequestContext requestContext) throws IOException {
.build());
}

if (bearerToken != null && !bearerToken.isBlank()) {
requestContext.getHeaders().remove(HttpHeaders.AUTHORIZATION);
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION,
if (!isEmptyOrBlank(bearerToken)) {
addAuthorizationHeader(requestContext.getHeaders(),
AuthUtils.authTokenOrBearer("Bearer", AbstractAuthProvider.sanitizeBearerToken(bearerToken)));
} else {
LOGGER.debug("No bearer token was found for the oauth2 security scheme: {}." +
" You must verify that a Quarkus OIDC Client with the name: {} is properly configured," +
" or the request header: {} is set when the token propagation is enabled.",
getName(), getName(), getHeaderForPropagation(getOpenApiSpecId(), getName()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static io.quarkiverse.openapi.generator.providers.AbstractAuthenticationPropagationHeadersFactory.propagationHeaderName;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

Expand Down Expand Up @@ -65,16 +66,20 @@ public boolean isTokenPropagation() {

public static String getTokenForPropagation(MultivaluedMap<String, Object> httpHeaders, String openApiSpecId,
String authName) {
String headerName = getHeaderName(openApiSpecId, authName) != null ? getHeaderName(openApiSpecId, authName)
: HttpHeaders.AUTHORIZATION;
String headerName = getHeaderForPropagation(openApiSpecId, authName);
String propagatedHeaderName = propagationHeaderName(openApiSpecId, authName, headerName);
return Objects.toString(httpHeaders.getFirst(propagatedHeaderName));
return Objects.toString(httpHeaders.getFirst(propagatedHeaderName), null);
}

public String getTokenForPropagation(MultivaluedMap<String, Object> httpHeaders) {
return getTokenForPropagation(httpHeaders, getOpenApiSpecId(), getName());
}

public static String getHeaderForPropagation(String openApiSpecId, String authName) {
return getHeaderName(openApiSpecId, authName) != null ? getHeaderName(openApiSpecId, authName)
: HttpHeaders.AUTHORIZATION;
}

public String getHeaderName() {
return ConfigProvider.getConfig()
.getOptionalValue(getCanonicalAuthConfigPropertyName(AuthConfig.HEADER_NAME), String.class).orElse(null);
Expand Down Expand Up @@ -110,4 +115,12 @@ public static boolean isTokenPropagation(String openApiSpecId, String authName)
public CredentialsProvider getCredentialsProvider() {
return credentialsProvider;
}

protected void addAuthorizationHeader(MultivaluedMap<String, Object> headers, String value) {
headers.put(HttpHeaders.AUTHORIZATION, Collections.singletonList(value));
}

protected static boolean isEmptyOrBlank(String value) {
return value == null || value.isBlank();
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package io.quarkiverse.openapi.generator.providers;

import static io.quarkiverse.openapi.generator.providers.ConfigCredentialsProvider.PASSWORD;
import static io.quarkiverse.openapi.generator.providers.ConfigCredentialsProvider.USER_NAME;

import java.io.IOException;
import java.util.List;

import jakarta.ws.rs.client.ClientRequestContext;
import jakarta.ws.rs.core.HttpHeaders;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -48,10 +50,14 @@ public void filter(ClientRequestContext requestContext) throws IOException {
basicToken = sanitizeBasicToken(getTokenForPropagation(requestContext.getHeaders()));
}

if (!basicToken.isBlank()) {
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION,
AuthUtils.basicAuthAccessToken(basicToken));
if (!isEmptyOrBlank(basicToken)) {
addAuthorizationHeader(requestContext.getHeaders(), AuthUtils.basicAuthAccessToken(basicToken));
} else {
LOGGER.debug("No basic authentication token was found for the security scheme: {}." +
" You must verify that the properties: {} and {} are properly configured, or the request header: {} is set when the token propagation is enabled.",
getName(), getCanonicalAuthConfigPropertyName(USER_NAME, getOpenApiSpecId(), getName()),
getCanonicalAuthConfigPropertyName(PASSWORD, getOpenApiSpecId(), getName()),
getHeaderForPropagation(getOpenApiSpecId(), getName()));
}

}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package io.quarkiverse.openapi.generator.providers;

import static io.quarkiverse.openapi.generator.providers.ConfigCredentialsProvider.BEARER_TOKEN;

import java.io.IOException;
import java.util.List;

import jakarta.ws.rs.client.ClientRequestContext;
import jakarta.ws.rs.core.HttpHeaders;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -34,8 +35,13 @@ public void filter(ClientRequestContext requestContext) throws IOException {
bearerToken = sanitizeBearerToken(getTokenForPropagation(requestContext.getHeaders()));
}

if (!bearerToken.isBlank()) {
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, AuthUtils.authTokenOrBearer(this.scheme, bearerToken));
if (!isEmptyOrBlank(bearerToken)) {
addAuthorizationHeader(requestContext.getHeaders(), AuthUtils.authTokenOrBearer(this.scheme, bearerToken));
} else {
LOGGER.debug("No bearer token was found for the security scheme: {}." +
" You must verify that the property: {} is properly configured, or the request header: {} is set when the token propagation is enabled.",
getName(), getCanonicalAuthConfigPropertyName(BEARER_TOKEN, getOpenApiSpecId(), getName()),
getHeaderForPropagation(getOpenApiSpecId(), getName()));
}
LOGGER.debug("Header keys set in filtered requestContext: {}", requestContext.getHeaders().keySet());
}
Expand Down