diff --git a/client/integration-tests/security/src/main/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationResource.java b/client/integration-tests/security/src/main/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationResource.java index cabb361ee..4253357f3 100644 --- a/client/integration-tests/security/src/main/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationResource.java +++ b/client/integration-tests/security/src/main/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationResource.java @@ -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; @@ -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(); } } diff --git a/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationExternalServicesMock.java b/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationExternalServicesMock.java index 43b6289e3..53ecbf2ac 100644 --- a/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationExternalServicesMock.java +++ b/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationExternalServicesMock.java @@ -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"; @@ -49,10 +50,10 @@ public Map 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); diff --git a/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationTest.java b/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationTest.java index 544e360d3..a131c53e5 100644 --- a/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationTest.java +++ b/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationTest.java @@ -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; @@ -26,21 +31,40 @@ @Tag("resteasy-classic") class TokenPropagationTest { - @ParameterizedTest - @ValueSource(strings = { "service1", "service2", "service3", "service4", "service5" }) - void service1(String service) { - Map 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> headers = new HashMap<>(); + headers.put(headerArgument.headerName, Collections.singletonList(headerArgument.headerValue)); given() .headers(headers) .post("/token_propagation/" + service) .then() .statusCode(200); } + + private static Stream 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)) + + ); + } } diff --git a/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/providers/OAuth2AuthenticationProvider.java b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/providers/OAuth2AuthenticationProvider.java index f4f7009ed..7b8e3b0c4 100644 --- a/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/providers/OAuth2AuthenticationProvider.java +++ b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/providers/OAuth2AuthenticationProvider.java @@ -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; @@ -32,9 +31,13 @@ public OAuth2AuthenticationProvider(String name, @Override public void filter(ClientRequestContext requestContext) throws IOException { if (isTokenPropagation()) { - String bearerToken = getTokenForPropagation(requestContext.getHeaders()); - bearerToken = sanitizeBearerToken(bearerToken); - requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, OidcConstants.BEARER_SCHEME + " " + bearerToken); + String bearerToken = sanitizeBearerToken(getTokenForPropagation(requestContext.getHeaders())); + if (!isEmptyOrBlank(bearerToken)) { + addAuthorizationHeader(requestContext.getHeaders(), OidcConstants.BEARER_SCHEME + " " + bearerToken); + } else { + LOGGER.debug("No oauth2 bearer token was found to propagate for the security scheme: {}." + + " You must verify that the request header: {} is set.", getName(), getHeaderForPropagation()); + } } else { delegate.filter(requestContext); } diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AbstractAuthProvider.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AbstractAuthProvider.java index d5a776fc1..ff738a16e 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AbstractAuthProvider.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AbstractAuthProvider.java @@ -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; @@ -66,9 +67,12 @@ public boolean isTokenPropagation() { } public String getTokenForPropagation(MultivaluedMap httpHeaders) { - String headerName = getHeaderName() != null ? getHeaderName() : HttpHeaders.AUTHORIZATION; - String propagatedHeaderName = propagationHeaderName(getOpenApiSpecId(), getName(), headerName); - return Objects.toString(httpHeaders.getFirst(propagatedHeaderName)); + String propagatedHeaderName = propagationHeaderName(getOpenApiSpecId(), getName(), getHeaderForPropagation()); + return Objects.toString(httpHeaders.getFirst(propagatedHeaderName), null); + } + + public String getHeaderForPropagation() { + return getHeaderName() != null ? getHeaderName() : HttpHeaders.AUTHORIZATION; } public String getHeaderName() { @@ -88,4 +92,12 @@ public final String getCanonicalAuthConfigPropertyName(String authPropertyName) public static String getCanonicalAuthConfigPropertyName(String authPropertyName, String openApiSpecId, String authName) { return String.format(CANONICAL_AUTH_CONFIG_PROPERTY_NAME, openApiSpecId, authName, authPropertyName); } + + protected void addAuthorizationHeader(MultivaluedMap headers, String value) { + headers.put(HttpHeaders.AUTHORIZATION, Collections.singletonList(value)); + } + + protected static boolean isEmptyOrBlank(String value) { + return value == null || value.isBlank(); + } } diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BasicAuthenticationProvider.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BasicAuthenticationProvider.java index 10011a9b3..343b2e18d 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BasicAuthenticationProvider.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BasicAuthenticationProvider.java @@ -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; @@ -44,10 +46,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()); } - } } diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BearerAuthenticationProvider.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BearerAuthenticationProvider.java index 5454b1808..d5dae2a74 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BearerAuthenticationProvider.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BearerAuthenticationProvider.java @@ -1,10 +1,14 @@ 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; /** * Provides bearer token authentication or any other valid scheme. @@ -13,6 +17,8 @@ */ public class BearerAuthenticationProvider extends AbstractAuthProvider { + private static final Logger LOGGER = LoggerFactory.getLogger(BearerAuthenticationProvider.class); + private final String scheme; public BearerAuthenticationProvider(final String openApiSpecId, final String name, final String scheme, @@ -34,8 +40,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()); } }