Skip to content

Commit b9d2981

Browse files
committed
issue-1235: Token propagation is not working when the openapi endpoint contains a base path
1 parent 7f17541 commit b9d2981

File tree

7 files changed

+135
-4
lines changed

7 files changed

+135
-4
lines changed

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ public class TokenPropagationResource {
2424
@RestClient
2525
org.acme.externalservice5.api.DefaultApi defaultApi5;
2626

27+
@RestClient
28+
org.acme.externalservice6.api.DefaultApi defaultApi6;
29+
30+
@RestClient
31+
org.acme.externalservice7.api.DefaultApi defaultApi7;
32+
2733
@POST
2834
@Path("service1")
2935
public Response service1() {
@@ -53,4 +59,16 @@ public Response service4() {
5359
public Response service5() {
5460
return defaultApi5.executeQuery5();
5561
}
62+
63+
@POST
64+
@Path("service6")
65+
public Response service6() {
66+
return defaultApi6.executeQuery6();
67+
}
68+
69+
@POST
70+
@Path("service7")
71+
public Response service7() {
72+
return defaultApi7.executeQuery7();
73+
}
5674
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
openapi: 3.0.3
3+
info:
4+
title: token-propagation-external-service6-with-base-url API
5+
version: 3.0.0-lts-SNAPSHOT
6+
paths:
7+
/token-propagation-external-service6-with-base-url/executeQuery6:
8+
post:
9+
operationId: executeQuery6
10+
responses:
11+
"200":
12+
description: OK
13+
security:
14+
- service6-http-bearer: []
15+
components:
16+
securitySchemes:
17+
service6-http-bearer:
18+
type: http
19+
scheme: bearer
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
openapi: 3.0.3
3+
info:
4+
title: token-propagation-external-service7-with-base-url API
5+
version: 3.0.0-lts-SNAPSHOT
6+
paths:
7+
/token-propagation-external-service7-with-base-url/executeQuery7:
8+
post:
9+
operationId: executeQuery7
10+
responses:
11+
"200":
12+
description: OK
13+
security:
14+
- service7-http-bearer: []
15+
components:
16+
securitySchemes:
17+
service7-http-bearer:
18+
type: http
19+
scheme: bearer

client/integration-tests/security/src/main/resources/application.properties

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,17 @@ quarkus.openapi-generator.codegen.spec.token_propagation_external_service2_yaml.
3232
quarkus.openapi-generator.codegen.spec.token_propagation_external_service3_yaml.base-package=org.acme.externalservice3
3333
quarkus.openapi-generator.codegen.spec.token_propagation_external_service4_yaml.base-package=org.acme.externalservice4
3434
quarkus.openapi-generator.codegen.spec.token_propagation_external_service5_yaml.base-package=org.acme.externalservice5
35+
quarkus.openapi-generator.codegen.spec.token_propagation_external_service6_with_base_url_yaml.base-package=org.acme.externalservice6
36+
quarkus.openapi-generator.codegen.spec.token_propagation_external_service7_with_base_url_yaml.base-package=org.acme.externalservice7
3537

3638
quarkus.rest-client.token_propagation_external_service1_yaml.url=${propagation-external-service-mock.url}
3739
quarkus.rest-client.token_propagation_external_service2_yaml.url=${propagation-external-service-mock.url}
3840
quarkus.rest-client.token_propagation_external_service3_yaml.url=${propagation-external-service-mock.url}
3941
quarkus.rest-client.token_propagation_external_service4_yaml.url=${propagation-external-service-mock.url}
4042
quarkus.rest-client.token_propagation_external_service5_yaml.url=${propagation-external-service-mock.url}
43+
# service6 and service7 endpoints has the base url /external/api/v1
44+
quarkus.rest-client.token_propagation_external_service6_with_base_url_yaml.url=${propagation-external-service-mock.url}/external/api/v1
45+
quarkus.rest-client.token_propagation_external_service7_with_base_url_yaml.url=${propagation-external-service-mock.url}/external/api/v1
4146

4247
# default propagation for token_propagation_external_service1 invocation
4348
quarkus.openapi-generator.token_propagation_external_service1_yaml.auth.service1_http_bearer.token-propagation=true
@@ -50,6 +55,13 @@ quarkus.openapi-generator.token_propagation_external_service3_yaml.auth.service3
5055
quarkus.openapi-generator.token_propagation_external_service4_yaml.auth.service4_oauth2.token-propagation=true
5156
quarkus.openapi-generator.token_propagation_external_service4_yaml.auth.service4_oauth2.header-name=SERVICE4_HEADER_TO_PROPAGATE
5257

58+
# default propagation for token_propagation_external_service6_with_base_url invocation
59+
quarkus.openapi-generator.token_propagation_external_service6_with_base_url_yaml.auth.service6_http_bearer.token-propagation=true
60+
61+
# propagate the token coming in the header SERVICE7_HEADER_TO_PROPAGATE for token_propagation_external_service7_with_base_url invocation
62+
quarkus.openapi-generator.token_propagation_external_service7_with_base_url_yaml.auth.service7_http_bearer.token-propagation=true
63+
quarkus.openapi-generator.token_propagation_external_service7_with_base_url_yaml.auth.service7_http_bearer.header-name=SERVICE7_HEADER_TO_PROPAGATE
64+
5365
# Oidc clients for the services that has oauth2 security.
5466
# Oidc client used by the token_propagation_external_service2
5567
quarkus.oidc-client.service2_oauth2.auth-server-url=${keycloak.mock.service.url}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ public class TokenPropagationExternalServicesMock implements QuarkusTestResource
2929
public static final String SERVICE3_AUTHORIZATION_TOKEN = "SERVICE3_AUTHORIZATION_TOKEN";
3030
public static final String SERVICE4_HEADER_TO_PROPAGATE = "SERVICE4_HEADER_TO_PROPAGATE";
3131
public static final String SERVICE4_AUTHORIZATION_TOKEN = "SERVICE4_AUTHORIZATION_TOKEN";
32+
public static final String SERVICE6_AUTHORIZATION_TOKEN = "SERVICE6_AUTHORIZATION_TOKEN";
33+
public static final String SERVICE7_HEADER_TO_PROPAGATE = "SERVICE7_HEADER_TO_PROPAGATE";
34+
public static final String SERVICE7_AUTHORIZATION_TOKEN = "SERVICE7_AUTHORIZATION_TOKEN";
3235
public static final String TOKEN_PROPAGATION_EXTERNAL_SERVICE_MOCK_URL = "propagation-external-service-mock.url";
3336
private static final String BEARER = "Bearer ";
3437
private static final Logger LOGGER = LoggerFactory.getLogger(TokenPropagationExternalServicesMock.class);
@@ -66,6 +69,14 @@ public Map<String, String> start() {
6669
// configured.
6770
stubForExternalService("/token-propagation-external-service5/executeQuery5", KEYCLOAK_ACCESS_TOKEN);
6871

72+
// stub the token-propagation-external-service6-with-base-url invocation with the expected token, emulate also the endpoint base url /external/api/v1
73+
stubForExternalService("/external/api/v1/token-propagation-external-service6-with-base-url/executeQuery6",
74+
SERVICE6_AUTHORIZATION_TOKEN);
75+
76+
// stub the token-propagation-external-service7-with-base-url invocation with the expected token, emulate also the endpoint base url /external/api/v1
77+
stubForExternalService("/external/api/v1/token-propagation-external-service7-with-base-url/executeQuery7",
78+
SERVICE7_AUTHORIZATION_TOKEN);
79+
6980
return Map.of(TOKEN_PROPAGATION_EXTERNAL_SERVICE_MOCK_URL, wireMockServer.baseUrl());
7081
}
7182

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE3_HEADER_TO_PROPAGATE;
77
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE4_AUTHORIZATION_TOKEN;
88
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE4_HEADER_TO_PROPAGATE;
9+
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE6_AUTHORIZATION_TOKEN;
10+
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE7_AUTHORIZATION_TOKEN;
11+
import static io.quarkiverse.openapi.generator.it.security.TokenPropagationExternalServicesMock.SERVICE7_HEADER_TO_PROPAGATE;
912
import static io.restassured.RestAssured.given;
1013

1114
import java.util.Collections;
@@ -63,8 +66,10 @@ private static Stream<Arguments> serviceInvocationParams() {
6366
// service token-propagation-external-service3 will receive the SERVICE3_AUTHORIZATION_TOKEN
6467
Arguments.of("service3", new HeaderArgument(SERVICE3_HEADER_TO_PROPAGATE, SERVICE3_AUTHORIZATION_TOKEN)),
6568
// 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+
Arguments.of("service4", new HeaderArgument(SERVICE4_HEADER_TO_PROPAGATE, SERVICE4_AUTHORIZATION_TOKEN)),
70+
// service token-propagation-external-service6-with-base-url will receive the SERVICE6_AUTHORIZATION_TOKEN
71+
Arguments.of("service6", new HeaderArgument(HttpHeaders.AUTHORIZATION, SERVICE6_AUTHORIZATION_TOKEN)),
72+
// service token-propagation-external-service7-with-base-url will receive the SERVICE7_AUTHORIZATION_TOKEN
73+
Arguments.of("service7", new HeaderArgument(SERVICE7_HEADER_TO_PROPAGATE, SERVICE7_AUTHORIZATION_TOKEN)));
6974
}
7075
}

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

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@
33
import static io.quarkiverse.openapi.generator.providers.AbstractAuthenticationPropagationHeadersFactory.propagationHeaderNamePrefix;
44

55
import java.io.IOException;
6+
import java.net.URI;
67
import java.util.HashSet;
78
import java.util.List;
89
import java.util.Set;
910

1011
import jakarta.ws.rs.client.ClientRequestContext;
1112
import jakarta.ws.rs.client.ClientRequestFilter;
1213

14+
import org.eclipse.microprofile.config.ConfigProvider;
15+
1316
import io.quarkiverse.openapi.generator.OpenApiGeneratorConfig;
1417

1518
/**
@@ -47,9 +50,53 @@ public void filter(ClientRequestContext requestContext) throws IOException {
4750
* It can perform the authentication filter only if this operation requires it (has a security reference)
4851
*/
4952
private boolean canFilter(final AuthProvider authProvider, final ClientRequestContext requestContext) {
53+
String sanitizedPath = sanitizePath(authProvider, requestContext.getUri());
5054
return authProvider.operationsToFilter().stream()
5155
.anyMatch(o -> o.getHttpMethod().equals(requestContext.getMethod()) &&
52-
o.matchPath(requestContext.getUri().getPath()));
56+
o.matchPath(sanitizedPath));
57+
}
58+
59+
/**
60+
* Calculates the path to realize the matching with the OpenAPI operations, considering that the Quarkus client is
61+
* configured with a URI that refers to the OpenAPI service endpoint. e.g.:
62+
*
63+
* In the OpenAPI document below the service endpoint is: http://https://development.gigantic-server.com/v1,
64+
*
65+
* openapi: 3.0.3
66+
* servers:
67+
* - url: https://development.gigantic-server.com/v1
68+
* description: Development server
69+
* ...
70+
* paths:
71+
* /some-operation:
72+
* post:
73+
* operationId: SomeOperation
74+
* ...
75+
*
76+
* And thus, the Quarkus client must be configured like this:
77+
* quarkus.rest-client.example-auth_yaml.url=https://development.gigantic-server.com/v1
78+
*
79+
* @param authProvider authentication provider to realize the matching with.
80+
* @param requestUri the request url to invoke, e.g. https://development.gigantic-server.com/v1/some-operation
81+
* @return The sanitized path to realize the matching with the OpenApi operations path, following the example above
82+
* must be: /some-operation
83+
*/
84+
protected String sanitizePath(AuthProvider authProvider, URI requestUri) {
85+
String basePath = getRestClientURLConfig(authProvider).getPath();
86+
basePath = basePath.endsWith("/") ? basePath.substring(0, basePath.length() - 1) : basePath;
87+
String requestPath = requestUri.getPath();
88+
if (!basePath.isEmpty() && requestPath.startsWith(basePath)) {
89+
return requestPath.substring(basePath.length());
90+
}
91+
return requestPath;
92+
}
93+
94+
protected URI getRestClientURLConfig(AuthProvider authProvider) {
95+
String openApiSpecId = ((AbstractAuthProvider) authProvider).getOpenApiSpecId();
96+
String restClientProperty = String.format("quarkus.rest-client.%s.url", openApiSpecId);
97+
return ConfigProvider.getConfig().getOptionalValue(restClientProperty, URI.class)
98+
.orElseThrow(() -> new IllegalArgumentException(
99+
"Required rest client property is not configured: " + restClientProperty));
53100
}
54101

55102
protected static String sanitizeAuthName(String schemeName) {

0 commit comments

Comments
 (0)