Skip to content

Commit 7c292d2

Browse files
committed
issue-1182: Token propagation throws java.lang.UnsupportedOperationException when adding the token to the request header parameters
1 parent f303697 commit 7c292d2

File tree

7 files changed

+73
-39
lines changed

7 files changed

+73
-39
lines changed

client/integration-tests/security/src/main/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationResource.java

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import jakarta.ws.rs.POST;
44
import jakarta.ws.rs.Path;
5+
import jakarta.ws.rs.core.Response;
56

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

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

2627
@POST
2728
@Path("service1")
28-
public String service1() {
29-
defaultApi1.executeQuery1();
30-
return "hello";
29+
public Response service1() {
30+
return defaultApi1.executeQuery1();
3131
}
3232

3333
@POST
3434
@Path("service2")
35-
public String service2() {
36-
defaultApi2.executeQuery2();
37-
return "hello";
35+
public Response service2() {
36+
return defaultApi2.executeQuery2();
3837
}
3938

4039
@POST
4140
@Path("service3")
42-
public String service3() {
43-
defaultApi3.executeQuery3();
44-
return "hello";
41+
public Response service3() {
42+
return defaultApi3.executeQuery3();
4543
}
4644

4745
@POST
4846
@Path("service4")
49-
public String service4() {
50-
defaultApi4.executeQuery4();
51-
return "hello";
47+
public Response service4() {
48+
return defaultApi4.executeQuery4();
5249
}
5350

5451
@POST
5552
@Path("service5")
56-
public String service5() {
57-
defaultApi5.executeQuery5();
58-
return "hello";
53+
public Response service5() {
54+
return defaultApi5.executeQuery5();
5955
}
6056
}

client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/TokenPropagationExternalServicesMock.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323

2424
public class TokenPropagationExternalServicesMock implements QuarkusTestResourceLifecycleManager {
2525

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

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

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

5758
// stub the token-propagation-external-service3 invocation with the expected token
5859
stubForExternalService("/token-propagation-external-service3/executeQuery3", SERVICE3_AUTHORIZATION_TOKEN);
Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
11
package io.quarkiverse.openapi.generator.it.security;
22

3-
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.AUTHORIZATION_TOKEN;
3+
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE1_AUTHORIZATION_TOKEN;
4+
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE2_AUTHORIZATION_TOKEN;
45
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE3_AUTHORIZATION_TOKEN;
56
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE3_HEADER_TO_PROPAGATE;
67
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE4_AUTHORIZATION_TOKEN;
78
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE4_HEADER_TO_PROPAGATE;
89
import static io.restassured.RestAssured.given;
910

11+
import java.util.Collections;
1012
import java.util.HashMap;
13+
import java.util.List;
1114
import java.util.Map;
15+
import java.util.stream.Stream;
1216

1317
import jakarta.ws.rs.core.HttpHeaders;
1418

1519
import org.junit.jupiter.api.Tag;
1620
import org.junit.jupiter.params.ParameterizedTest;
17-
import org.junit.jupiter.params.provider.ValueSource;
21+
import org.junit.jupiter.params.provider.Arguments;
22+
import org.junit.jupiter.params.provider.MethodSource;
1823

1924
import io.quarkus.test.common.QuarkusTestResource;
2025
import io.quarkus.test.junit.QuarkusTest;
@@ -26,21 +31,40 @@
2631
@Tag("resteasy-classic")
2732
class TokenPropagationTest {
2833

29-
@ParameterizedTest
30-
@ValueSource(strings = { "service1", "service2", "service3", "service4", "service5" })
31-
void service1(String service) {
32-
Map<String, String> headers = new HashMap<>();
33-
// service token-propagation-external-service1 and token-propagation-external-service2 will receive the AUTHORIZATION_TOKEN
34-
headers.put(HttpHeaders.AUTHORIZATION, AUTHORIZATION_TOKEN);
35-
// service token-propagation-external-service3 will receive the SERVICE3_AUTHORIZATION_TOKEN
36-
headers.put(SERVICE3_HEADER_TO_PROPAGATE, SERVICE3_AUTHORIZATION_TOKEN);
37-
// service token-propagation-external-service4 will receive the SERVICE4_AUTHORIZATION_TOKEN
38-
headers.put(SERVICE4_HEADER_TO_PROPAGATE, SERVICE4_AUTHORIZATION_TOKEN);
34+
private static class HeaderArgument {
35+
String headerName;
36+
String headerValue;
37+
38+
HeaderArgument(String headerName, String headerValue) {
39+
this.headerName = headerName;
40+
this.headerValue = headerValue;
41+
}
42+
43+
}
3944

45+
@ParameterizedTest
46+
@MethodSource("serviceInvocationParams")
47+
void invokeService(String service, HeaderArgument headerArgument) {
48+
Map<String, List<String>> headers = new HashMap<>();
49+
headers.put(headerArgument.headerName, Collections.singletonList(headerArgument.headerValue));
4050
given()
4151
.headers(headers)
4252
.post("/token_propagation/" + service)
4353
.then()
4454
.statusCode(200);
4555
}
56+
57+
private static Stream<Arguments> serviceInvocationParams() {
58+
return Stream.of(
59+
// service token-propagation-external-service1 will receive the SERVICE1_AUTHORIZATION_TOKEN
60+
Arguments.of("service1", new HeaderArgument(HttpHeaders.AUTHORIZATION, SERVICE1_AUTHORIZATION_TOKEN)),
61+
// service token-propagation-external-service2 will receive the SERVICE2_AUTHORIZATION_TOKEN
62+
Arguments.of("service2", new HeaderArgument(HttpHeaders.AUTHORIZATION, SERVICE2_AUTHORIZATION_TOKEN)),
63+
// service token-propagation-external-service3 will receive the SERVICE3_AUTHORIZATION_TOKEN
64+
Arguments.of("service3", new HeaderArgument(SERVICE3_HEADER_TO_PROPAGATE, SERVICE3_AUTHORIZATION_TOKEN)),
65+
// service token-propagation-external-service4 will receive the SERVICE4_AUTHORIZATION_TOKEN
66+
Arguments.of("service4", new HeaderArgument(SERVICE4_HEADER_TO_PROPAGATE, SERVICE4_AUTHORIZATION_TOKEN))
67+
68+
);
69+
}
4670
}

client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/providers/OAuth2AuthenticationProvider.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static io.quarkiverse.openapi.generator.AuthConfig.TOKEN_PROPAGATION;
44

55
import java.io.IOException;
6+
import java.util.Collections;
67
import java.util.List;
78

89
import jakarta.ws.rs.client.ClientRequestContext;
@@ -32,9 +33,12 @@ public OAuth2AuthenticationProvider(String name,
3233
@Override
3334
public void filter(ClientRequestContext requestContext) throws IOException {
3435
if (isTokenPropagation()) {
35-
String bearerToken = getTokenForPropagation(requestContext.getHeaders());
36-
bearerToken = sanitizeBearerToken(bearerToken);
37-
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, OidcConstants.BEARER_SCHEME + " " + bearerToken);
36+
String bearerToken = sanitizeBearerToken(getTokenForPropagation(requestContext.getHeaders()));
37+
if (!isEmptyOrBlank(bearerToken)) {
38+
requestContext.getHeaders().remove(HttpHeaders.AUTHORIZATION);
39+
requestContext.getHeaders().put(HttpHeaders.AUTHORIZATION,
40+
Collections.singletonList(OidcConstants.BEARER_SCHEME + " " + bearerToken));
41+
}
3842
} else {
3943
delegate.filter(requestContext);
4044
}

client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AbstractAuthProvider.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public boolean isTokenPropagation() {
6868
public String getTokenForPropagation(MultivaluedMap<String, Object> httpHeaders) {
6969
String headerName = getHeaderName() != null ? getHeaderName() : HttpHeaders.AUTHORIZATION;
7070
String propagatedHeaderName = propagationHeaderName(getOpenApiSpecId(), getName(), headerName);
71-
return Objects.toString(httpHeaders.getFirst(propagatedHeaderName));
71+
return Objects.toString(httpHeaders.getFirst(propagatedHeaderName), null);
7272
}
7373

7474
public String getHeaderName() {
@@ -88,4 +88,8 @@ public final String getCanonicalAuthConfigPropertyName(String authPropertyName)
8888
public static String getCanonicalAuthConfigPropertyName(String authPropertyName, String openApiSpecId, String authName) {
8989
return String.format(CANONICAL_AUTH_CONFIG_PROPERTY_NAME, openApiSpecId, authName, authPropertyName);
9090
}
91+
92+
protected static boolean isEmptyOrBlank(String value) {
93+
return value == null || value.isBlank();
94+
}
9195
}

client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BasicAuthenticationProvider.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.quarkiverse.openapi.generator.providers;
22

33
import java.io.IOException;
4+
import java.util.Collections;
45
import java.util.List;
56

67
import jakarta.ws.rs.client.ClientRequestContext;
@@ -44,9 +45,10 @@ public void filter(ClientRequestContext requestContext) throws IOException {
4445
basicToken = sanitizeBasicToken(getTokenForPropagation(requestContext.getHeaders()));
4546
}
4647

47-
if (!basicToken.isBlank()) {
48-
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION,
49-
AuthUtils.basicAuthAccessToken(basicToken));
48+
if (!isEmptyOrBlank(basicToken)) {
49+
requestContext.getHeaders().remove(HttpHeaders.AUTHORIZATION);
50+
requestContext.getHeaders().put(HttpHeaders.AUTHORIZATION,
51+
Collections.singletonList(AuthUtils.basicAuthAccessToken(basicToken)));
5052
}
5153

5254
}

client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BearerAuthenticationProvider.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.quarkiverse.openapi.generator.providers;
22

33
import java.io.IOException;
4+
import java.util.Collections;
45
import java.util.List;
56

67
import jakarta.ws.rs.client.ClientRequestContext;
@@ -34,8 +35,10 @@ public void filter(ClientRequestContext requestContext) throws IOException {
3435
bearerToken = sanitizeBearerToken(getTokenForPropagation(requestContext.getHeaders()));
3536
}
3637

37-
if (!bearerToken.isBlank()) {
38-
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, AuthUtils.authTokenOrBearer(this.scheme, bearerToken));
38+
if (!isEmptyOrBlank(bearerToken)) {
39+
requestContext.getHeaders().remove(HttpHeaders.AUTHORIZATION);
40+
requestContext.getHeaders().put(HttpHeaders.AUTHORIZATION,
41+
Collections.singletonList(AuthUtils.authTokenOrBearer(this.scheme, bearerToken)));
3942
}
4043
}
4144

0 commit comments

Comments
 (0)