diff --git a/client/deployment/pom.xml b/client/deployment/pom.xml index a5099bd34..cbd5321d5 100644 --- a/client/deployment/pom.xml +++ b/client/deployment/pom.xml @@ -36,6 +36,11 @@ quarkus-openapi-generator ${project.version} + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-oidc + ${project.version} + diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/GeneratorProcessor.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/GeneratorProcessor.java index 92bff1491..d719673e3 100644 --- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/GeneratorProcessor.java +++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/GeneratorProcessor.java @@ -14,23 +14,26 @@ import org.jboss.jandex.ClassType; import org.jboss.jandex.DotName; import org.jboss.jandex.ParameterizedType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import io.quarkiverse.openapi.generator.AuthName; import io.quarkiverse.openapi.generator.AuthenticationRecorder; -import io.quarkiverse.openapi.generator.ClassicOidcClientRequestFilterDelegate; import io.quarkiverse.openapi.generator.OidcClient; import io.quarkiverse.openapi.generator.OpenApiGeneratorConfig; import io.quarkiverse.openapi.generator.OpenApiSpec; -import io.quarkiverse.openapi.generator.ReactiveOidcClientRequestFilterDelegate; import io.quarkiverse.openapi.generator.markers.ApiKeyAuthenticationMarker; import io.quarkiverse.openapi.generator.markers.BasicAuthenticationMarker; import io.quarkiverse.openapi.generator.markers.BearerAuthenticationMarker; import io.quarkiverse.openapi.generator.markers.OauthAuthenticationMarker; import io.quarkiverse.openapi.generator.markers.OperationMarker; +import io.quarkiverse.openapi.generator.oidc.ClassicOidcClientRequestFilterDelegate; +import io.quarkiverse.openapi.generator.oidc.OidcAuthenticationRecorder; +import io.quarkiverse.openapi.generator.oidc.ReactiveOidcClientRequestFilterDelegate; +import io.quarkiverse.openapi.generator.oidc.providers.OAuth2AuthenticationProvider; import io.quarkiverse.openapi.generator.providers.ApiKeyIn; import io.quarkiverse.openapi.generator.providers.AuthProvider; import io.quarkiverse.openapi.generator.providers.CompositeAuthenticationProvider; -import io.quarkiverse.openapi.generator.providers.OAuth2AuthenticationProvider.OidcClientRequestFilterDelegate; import io.quarkiverse.openapi.generator.providers.OperationAuthInfo; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; @@ -54,71 +57,79 @@ public class GeneratorProcessor { private static final String ABSTRACT_TOKEN_PRODUCER = "io.quarkus.oidc.client.runtime.AbstractTokensProducer"; + private static final Logger LOGGER = LoggerFactory.getLogger(GeneratorProcessor.class); + + private static String sanitizeAuthName(String schemeName) { + return OpenApiGeneratorConfig.getSanitizedSecuritySchemeName(schemeName); + } + + private static Map> getOperationsBySpec(CombinedIndexBuildItem beanArchiveBuildItem) { + return beanArchiveBuildItem.getIndex().getAnnotationsWithRepeatable(OPERATION_MARKER, beanArchiveBuildItem.getIndex()) + .stream().collect(Collectors.groupingBy( + marker -> marker.value("openApiSpecId").asString() + "_" + marker.value("name").asString())); + } + + private static List getOperations(Map> operationsBySpec, + String openApiSpecId, String name) { + return operationsBySpec.getOrDefault(openApiSpecId + "_" + name, List.of()).stream() + .map(op -> OperationAuthInfo.builder().withPath(op.value("path").asString()) + .withId(op.value("operationId").asString()).withMethod(op.value("method").asString()).build()) + .collect(Collectors.toList()); + } + @BuildStep FeatureBuildItem feature() { return new FeatureBuildItem(FEATURE); } @BuildStep - void additionalBean( - Capabilities capabilities, - BuildProducer producer) { + void additionalBean(Capabilities capabilities, BuildProducer producer) { if (!isClassPresentAtRuntime(ABSTRACT_TOKEN_PRODUCER)) { + LOGGER.debug("{} class not found in runtime, skipping OidcClientRequestFilterDelegate bean generation", + ABSTRACT_TOKEN_PRODUCER); return; } + LOGGER.debug("{} class found in runtime, producing OidcClientRequestFilterDelegate bean generation", + ABSTRACT_TOKEN_PRODUCER); if (capabilities.isPresent(Capability.REST_CLIENT_REACTIVE)) { - producer.produce( - AdditionalBeanBuildItem.builder().addBeanClass(ReactiveOidcClientRequestFilterDelegate.class) - .setDefaultScope(DotName.createSimple(Dependent.class)) - .setUnremovable() - .build()); + producer.produce(AdditionalBeanBuildItem.builder().addBeanClass(ReactiveOidcClientRequestFilterDelegate.class) + .setDefaultScope(DotName.createSimple(Dependent.class)).setUnremovable().build()); } else { - producer.produce( - AdditionalBeanBuildItem.builder().addBeanClass(ClassicOidcClientRequestFilterDelegate.class) - .setDefaultScope(DotName.createSimple(Dependent.class)) - .setUnremovable() - .build()); + producer.produce(AdditionalBeanBuildItem.builder().addBeanClass(ClassicOidcClientRequestFilterDelegate.class) + .setDefaultScope(DotName.createSimple(Dependent.class)).setUnremovable().build()); } + } @BuildStep @Record(ExecutionTime.STATIC_INIT) - void produceCompositeProviders(AuthenticationRecorder recorder, - List authProviders, + void produceCompositeProviders(AuthenticationRecorder recorder, List authProviders, BuildProducer beanProducer) { Map> providersBySpec = authProviders.stream() .collect(Collectors.groupingBy(AuthProviderBuildItem::getOpenApiSpecId)); providersBySpec.forEach((openApiSpecId, providers) -> { - beanProducer.produce(SyntheticBeanBuildItem.configure(CompositeAuthenticationProvider.class) - .scope(Dependent.class) - .addQualifier() - .annotation(OpenApiSpec.class) - .addValue("openApiSpecId", openApiSpecId) - .done() - .addInjectionPoint( - ParameterizedType.create(Instance.class, ClassType.create(AuthProvider.class)), - AnnotationInstance.builder(OpenApiSpec.class) - .add("openApiSpecId", openApiSpecId) - .build()) - .createWith(recorder.recordCompositeProvider(openApiSpecId)) - .done()); + beanProducer.produce(SyntheticBeanBuildItem.configure(CompositeAuthenticationProvider.class).scope(Dependent.class) + .addQualifier().annotation(OpenApiSpec.class).addValue("openApiSpecId", openApiSpecId).done() + .addInjectionPoint(ParameterizedType.create(Instance.class, ClassType.create(AuthProvider.class)), + AnnotationInstance.builder(OpenApiSpec.class).add("openApiSpecId", openApiSpecId).build()) + .createWith(recorder.recordCompositeProvider(openApiSpecId)).done()); }); } @BuildStep @Record(ExecutionTime.STATIC_INIT) - void produceOauthAuthentication( - CombinedIndexBuildItem beanArchiveBuildItem, - BuildProducer authenticationProviders, - BuildProducer beanProducer, - AuthenticationRecorder recorder) { + void produceOauthAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, + BuildProducer authenticationProviders, BuildProducer beanProducer, + OidcAuthenticationRecorder oidcRecorder) { if (!isClassPresentAtRuntime(ABSTRACT_TOKEN_PRODUCER)) { + LOGGER.debug("{} class not found in runtime, skipping OAuth bean generation", ABSTRACT_TOKEN_PRODUCER); return; } + LOGGER.debug("{} class found in runtime, producing OAuth bean generation", ABSTRACT_TOKEN_PRODUCER); Collection authenticationMarkers = beanArchiveBuildItem.getIndex() .getAnnotationsWithRepeatable(OAUTH_AUTHENTICATION_MARKER, beanArchiveBuildItem.getIndex()); @@ -129,34 +140,20 @@ void produceOauthAuthentication( String openApiSpecId = authenticationMarker.value("openApiSpecId").asString(); List operations = getOperations(operationsBySpec, openApiSpecId, name); authenticationProviders.produce(new AuthProviderBuildItem(openApiSpecId, name)); - beanProducer.produce(SyntheticBeanBuildItem.configure(AuthProvider.class) - .scope(Dependent.class) - .addQualifier() - .annotation(AuthName.class) - .addValue("name", name) - .done() - .addQualifier() - .annotation(OpenApiSpec.class) - .addValue("openApiSpecId", openApiSpecId) - .done() - .addInjectionPoint(ClassType.create(OidcClientRequestFilterDelegate.class), - AnnotationInstance.builder(OidcClient.class) - .add("name", sanitizeAuthName(name)) - .build()) - .createWith(recorder.recordOauthAuthProvider( - sanitizeAuthName(name), - openApiSpecId, - operations)) - .unremovable() - .done()); + beanProducer.produce(SyntheticBeanBuildItem.configure(AuthProvider.class).scope(Dependent.class).addQualifier() + .annotation(AuthName.class).addValue("name", name).done().addQualifier().annotation(OpenApiSpec.class) + .addValue("openApiSpecId", openApiSpecId).done() + .addInjectionPoint(ClassType.create(OAuth2AuthenticationProvider.OidcClientRequestFilterDelegate.class), + AnnotationInstance.builder(OidcClient.class).add("name", sanitizeAuthName(name)).build()) + .createWith(oidcRecorder.recordOauthAuthProvider(sanitizeAuthName(name), openApiSpecId, operations)) + .unremovable().done()); } } @BuildStep @Record(ExecutionTime.STATIC_INIT) void produceBasicAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, - BuildProducer authenticationProviders, - BuildProducer beanProducer, + BuildProducer authenticationProviders, BuildProducer beanProducer, AuthenticationRecorder recorder) { Collection authenticationMarkers = beanArchiveBuildItem.getIndex() @@ -171,30 +168,18 @@ void produceBasicAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, authenticationProviders.produce(new AuthProviderBuildItem(openApiSpecId, name)); - beanProducer.produce(SyntheticBeanBuildItem.configure(AuthProvider.class) - .scope(Dependent.class) - .addQualifier() - .annotation(AuthName.class) - .addValue("name", name) - .done() - .addQualifier() - .annotation(OpenApiSpec.class) - .addValue("openApiSpecId", openApiSpecId) - .done() - .createWith(recorder.recordBasicAuthProvider( - sanitizeAuthName(name), - openApiSpecId, - operations)) - .unremovable() - .done()); + beanProducer.produce(SyntheticBeanBuildItem.configure(AuthProvider.class).scope(Dependent.class).addQualifier() + .annotation(AuthName.class).addValue("name", name).done().addQualifier().annotation(OpenApiSpec.class) + .addValue("openApiSpecId", openApiSpecId).done() + .createWith(recorder.recordBasicAuthProvider(sanitizeAuthName(name), openApiSpecId, operations)) + .unremovable().done()); } } @BuildStep @Record(ExecutionTime.STATIC_INIT) void produceBearerAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, - BuildProducer authenticationProviders, - BuildProducer beanProducer, + BuildProducer authenticationProviders, BuildProducer beanProducer, AuthenticationRecorder recorder) { Collection authenticationMarkers = beanArchiveBuildItem.getIndex() @@ -208,23 +193,11 @@ void produceBearerAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, List operations = getOperations(operationsBySpec, openApiSpecId, name); authenticationProviders.produce(new AuthProviderBuildItem(openApiSpecId, name)); - beanProducer.produce(SyntheticBeanBuildItem.configure(AuthProvider.class) - .scope(Dependent.class) - .addQualifier() - .annotation(AuthName.class) - .addValue("name", name) - .done() - .addQualifier() - .annotation(OpenApiSpec.class) - .addValue("openApiSpecId", openApiSpecId) - .done() - .createWith(recorder.recordBearerAuthProvider( - sanitizeAuthName(name), - scheme, - openApiSpecId, - operations)) - .unremovable() - .done()); + beanProducer.produce(SyntheticBeanBuildItem.configure(AuthProvider.class).scope(Dependent.class).addQualifier() + .annotation(AuthName.class).addValue("name", name).done().addQualifier().annotation(OpenApiSpec.class) + .addValue("openApiSpecId", openApiSpecId).done() + .createWith(recorder.recordBearerAuthProvider(sanitizeAuthName(name), scheme, openApiSpecId, operations)) + .unremovable().done()); } } @@ -232,8 +205,7 @@ void produceBearerAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, @BuildStep @Record(ExecutionTime.STATIC_INIT) void produceApiKeyAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, - BuildProducer authenticationProviders, - BuildProducer beanProducer, + BuildProducer authenticationProviders, BuildProducer beanProducer, AuthenticationRecorder recorder) { Collection authenticationMarkers = beanArchiveBuildItem.getIndex() @@ -249,48 +221,12 @@ void produceApiKeyAuthentication(CombinedIndexBuildItem beanArchiveBuildItem, authenticationProviders.produce(new AuthProviderBuildItem(openApiSpecId, name)); - beanProducer.produce(SyntheticBeanBuildItem.configure(AuthProvider.class) - .scope(Dependent.class) - .addQualifier() - .annotation(AuthName.class) - .addValue("name", name) - .done() - .addQualifier() - .annotation(OpenApiSpec.class) - .addValue("openApiSpecId", openApiSpecId) - .done() - .createWith(recorder.recordApiKeyAuthProvider( - sanitizeAuthName(name), - openApiSpecId, - apiKeyIn, - apiKeyName, - operations)) - .unremovable() - .done()); + beanProducer.produce(SyntheticBeanBuildItem.configure(AuthProvider.class).scope(Dependent.class).addQualifier() + .annotation(AuthName.class).addValue("name", name).done().addQualifier().annotation(OpenApiSpec.class) + .addValue("openApiSpecId", openApiSpecId).done().createWith(recorder + .recordApiKeyAuthProvider(sanitizeAuthName(name), openApiSpecId, apiKeyIn, apiKeyName, operations)) + .unremovable().done()); } } - - private static String sanitizeAuthName(String schemeName) { - return OpenApiGeneratorConfig.getSanitizedSecuritySchemeName(schemeName); - } - - private static Map> getOperationsBySpec(CombinedIndexBuildItem beanArchiveBuildItem) { - Map> operationsBySpec = beanArchiveBuildItem.getIndex() - .getAnnotationsWithRepeatable(OPERATION_MARKER, beanArchiveBuildItem.getIndex()).stream() - .collect(Collectors.groupingBy( - marker -> marker.value("openApiSpecId").asString() + "_" + marker.value("name").asString())); - return operationsBySpec; - } - - private static List getOperations(Map> operationsBySpec, - String openApiSpecId, String name) { - return operationsBySpec.getOrDefault(openApiSpecId + "_" + name, List.of()).stream() - .map(op -> OperationAuthInfo.builder() - .withPath(op.value("path").asString()) - .withId(op.value("operationId").asString()) - .withMethod(op.value("method").asString()) - .build()) - .collect(Collectors.toList()); - } } diff --git a/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/authentication/OpenApiSpecProviderTest.java b/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/authentication/OpenApiSpecProviderTest.java index 2c13db0dd..5a0f652c7 100644 --- a/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/authentication/OpenApiSpecProviderTest.java +++ b/client/deployment/src/test/java/io/quarkiverse/openapi/generator/deployment/authentication/OpenApiSpecProviderTest.java @@ -17,11 +17,11 @@ import io.quarkiverse.openapi.generator.markers.ApiKeyAuthenticationMarker; import io.quarkiverse.openapi.generator.markers.BasicAuthenticationMarker; import io.quarkiverse.openapi.generator.markers.OauthAuthenticationMarker; +import io.quarkiverse.openapi.generator.oidc.providers.OAuth2AuthenticationProvider; import io.quarkiverse.openapi.generator.providers.ApiKeyAuthenticationProvider; import io.quarkiverse.openapi.generator.providers.AuthProvider; import io.quarkiverse.openapi.generator.providers.BasicAuthenticationProvider; import io.quarkiverse.openapi.generator.providers.CompositeAuthenticationProvider; -import io.quarkiverse.openapi.generator.providers.OAuth2AuthenticationProvider; import io.quarkus.test.QuarkusUnitTest; public class OpenApiSpecProviderTest { diff --git a/client/integration-tests/enum-unexpected/pom.xml b/client/integration-tests/enum-unexpected/pom.xml index 34d9bd4ec..9d3aea7c4 100644 --- a/client/integration-tests/enum-unexpected/pom.xml +++ b/client/integration-tests/enum-unexpected/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-enum-unexpected - Quarkus - Openapi Generator - Integration Tests - Enum Unexpected + Quarkus - Openapi Generator - Integration Tests - Client - Enum Unexpected Example project for OpenAPI with enum unexpected value diff --git a/client/integration-tests/polymorphism/pom.xml b/client/integration-tests/polymorphism/pom.xml index 50987d9cd..780db7791 100644 --- a/client/integration-tests/polymorphism/pom.xml +++ b/client/integration-tests/polymorphism/pom.xml @@ -8,7 +8,7 @@ 4.0.0 quarkus-openapi-generator-it-polymorphism - Quarkus - Openapi Generator - Integration Tests - Polymorphism + Quarkus - Openapi Generator - Integration Tests - Client - Polymorphism diff --git a/client/integration-tests/pom.xml b/client/integration-tests/pom.xml index f74befd08..5af185407 100644 --- a/client/integration-tests/pom.xml +++ b/client/integration-tests/pom.xml @@ -56,6 +56,11 @@ quarkus-openapi-generator-it-generation-input ${project.version} + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-oidc + ${project.version} + @@ -69,14 +74,6 @@ io.quarkus quarkus-resteasy-client-jackson - - io.quarkus - quarkus-resteasy-client-oidc-filter - - - io.quarkus - quarkus-resteasy-multipart - @@ -104,10 +101,6 @@ io.quarkus quarkus-rest-client-jackson - - io.quarkus - quarkus-rest-client-oidc-filter - jakarta.validation jakarta.validation-api diff --git a/client/integration-tests/security/pom.xml b/client/integration-tests/security/pom.xml index 11035298f..26ce6f3fd 100644 --- a/client/integration-tests/security/pom.xml +++ b/client/integration-tests/security/pom.xml @@ -16,6 +16,10 @@ io.quarkiverse.openapi.generator quarkus-openapi-generator + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-oidc + io.quarkus quarkus-junit5 @@ -91,5 +95,30 @@ native + + resteasy-reactive + + + io.quarkus + quarkus-rest-client-oidc-filter + + + + + resteasy-classic + + true + + + + io.quarkus + quarkus-resteasy-client-oidc-filter + + + io.quarkus + quarkus-resteasy-multipart + + + \ No newline at end of file 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 a29ab6b40..43b6289e3 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 @@ -14,6 +14,9 @@ import jakarta.ws.rs.core.HttpHeaders; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.github.tomakehurst.wiremock.WireMockServer; import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; @@ -25,18 +28,25 @@ public class TokenPropagationExternalServicesMock implements QuarkusTestResource public static final String SERVICE3_AUTHORIZATION_TOKEN = "SERVICE3_AUTHORIZATION_TOKEN"; public static final String SERVICE4_HEADER_TO_PROPAGATE = "SERVICE4_HEADER_TO_PROPAGATE"; public static final String SERVICE4_AUTHORIZATION_TOKEN = "SERVICE4_AUTHORIZATION_TOKEN"; - - private static final String BEARER = "Bearer "; - public static final String TOKEN_PROPAGATION_EXTERNAL_SERVICE_MOCK_URL = "propagation-external-service-mock.url"; - + private static final String BEARER = "Bearer "; + private static final Logger LOGGER = LoggerFactory.getLogger(TokenPropagationExternalServicesMock.class); private WireMockServer wireMockServer; + private static void stubForExternalService(String tokenPropagationExternalServiceUrl, String authorizationToken) { + stubFor(post(tokenPropagationExternalServiceUrl) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo(BEARER + authorizationToken)) + .willReturn(aResponse() + .withHeader(CONTENT_TYPE, APPLICATION_JSON) + .withBody("{}"))); + } + @Override public Map start() { wireMockServer = new WireMockServer(options().dynamicPort()); wireMockServer.start(); configureFor(wireMockServer.port()); + 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); @@ -58,14 +68,6 @@ public Map start() { return Map.of(TOKEN_PROPAGATION_EXTERNAL_SERVICE_MOCK_URL, wireMockServer.baseUrl()); } - private static void stubForExternalService(String tokenPropagationExternalServiceUrl, String authorizationToken) { - stubFor(post(tokenPropagationExternalServiceUrl) - .withHeader(HttpHeaders.AUTHORIZATION, equalTo(BEARER + authorizationToken)) - .willReturn(aResponse() - .withHeader(CONTENT_TYPE, APPLICATION_JSON) - .withBody("{}"))); - } - @Override public void stop() { if (wireMockServer != null) { diff --git a/client/integration-tests/without-oidc/pom.xml b/client/integration-tests/without-oidc/pom.xml index 18c904388..92911e96e 100644 --- a/client/integration-tests/without-oidc/pom.xml +++ b/client/integration-tests/without-oidc/pom.xml @@ -1,10 +1,9 @@ - quarkus-openapi-generator-parent + quarkus-openapi-generator-integration-tests io.quarkiverse.openapi.generator 3.0.0-SNAPSHOT - ../../../pom.xml 4.0.0 @@ -19,7 +18,7 @@ io.quarkus - quarkus-rest-client-jackson + quarkus-hibernate-validator org.assertj diff --git a/client/integration-tests/without-oidc/src/main/resources/application.properties b/client/integration-tests/without-oidc/src/main/resources/application.properties index 07439c101..11a6c1a9f 100644 --- a/client/integration-tests/without-oidc/src/main/resources/application.properties +++ b/client/integration-tests/without-oidc/src/main/resources/application.properties @@ -1,3 +1 @@ quarkus.rest-client.quarkus_simple_openapi_yaml.url=http://localhost:8080 - -quarkus.keycloak.devservices.enabled=false \ No newline at end of file diff --git a/client/oidc/pom.xml b/client/oidc/pom.xml new file mode 100644 index 000000000..9b4601ab7 --- /dev/null +++ b/client/oidc/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-client-parent + 3.0.0-SNAPSHOT + + + quarkus-openapi-generator-oidc + Quarkus - Openapi Generator - Client - OIDC + OIDC Capabilities for Runtime + + + + io.quarkiverse.openapi.generator + quarkus-openapi-generator + ${project.version} + + + io.quarkus + quarkus-rest-client-oidc-filter + provided + + + io.quarkus + quarkus-resteasy-client-oidc-filter + provided + + + + \ No newline at end of file diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/ClassicOidcClientRequestFilterDelegate.java b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/ClassicOidcClientRequestFilterDelegate.java similarity index 82% rename from client/runtime/src/main/java/io/quarkiverse/openapi/generator/ClassicOidcClientRequestFilterDelegate.java rename to client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/ClassicOidcClientRequestFilterDelegate.java index 9f25d1057..f37895c8a 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/ClassicOidcClientRequestFilterDelegate.java +++ b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/ClassicOidcClientRequestFilterDelegate.java @@ -1,4 +1,4 @@ -package io.quarkiverse.openapi.generator; +package io.quarkiverse.openapi.generator.oidc; import java.io.IOException; @@ -10,7 +10,9 @@ import org.jboss.logging.Logger; -import io.quarkiverse.openapi.generator.providers.OAuth2AuthenticationProvider; +import io.quarkiverse.openapi.generator.OidcClient; +import io.quarkiverse.openapi.generator.OpenApiGeneratorConfig; +import io.quarkiverse.openapi.generator.oidc.providers.OAuth2AuthenticationProvider; import io.quarkus.oidc.client.runtime.AbstractTokensProducer; import io.quarkus.oidc.client.runtime.DisabledOidcClientException; @@ -19,15 +21,13 @@ public class ClassicOidcClientRequestFilterDelegate extends AbstractTokensProducer implements ClientRequestFilter, OAuth2AuthenticationProvider.OidcClientRequestFilterDelegate { - private static final Logger LOG = Logger - .getLogger(ClassicOidcClientRequestFilterDelegate.class); + private static final Logger LOG = Logger.getLogger(ClassicOidcClientRequestFilterDelegate.class); final String clientId; ClassicOidcClientRequestFilterDelegate(InjectionPoint injectionPoint) { OidcClient annotation = (OidcClient) injectionPoint.getQualifiers().stream() - .filter(x -> x.annotationType().equals(OidcClient.class)) - .findFirst().orElseThrow(); + .filter(x -> x.annotationType().equals(OidcClient.class)).findFirst().orElseThrow(); this.clientId = OpenApiGeneratorConfig.getSanitizedSecuritySchemeName(annotation.name()); } diff --git a/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/OidcAuthenticationRecorder.java b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/OidcAuthenticationRecorder.java new file mode 100644 index 000000000..1dbfe705e --- /dev/null +++ b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/OidcAuthenticationRecorder.java @@ -0,0 +1,33 @@ +package io.quarkiverse.openapi.generator.oidc; + +import java.util.List; +import java.util.function.Function; + +import io.quarkiverse.openapi.generator.AuthenticationRecorder; +import io.quarkiverse.openapi.generator.OidcClient; +import io.quarkiverse.openapi.generator.OpenApiGeneratorConfig; +import io.quarkiverse.openapi.generator.oidc.providers.OAuth2AuthenticationProvider; +import io.quarkiverse.openapi.generator.providers.AuthProvider; +import io.quarkiverse.openapi.generator.providers.OperationAuthInfo; +import io.quarkus.arc.SyntheticCreationalContext; +import io.quarkus.runtime.annotations.Recorder; + +@Recorder +public class OidcAuthenticationRecorder { + private final OpenApiGeneratorConfig generatorConfig; + + public OidcAuthenticationRecorder(OpenApiGeneratorConfig generatorConfig) { + this.generatorConfig = generatorConfig; + } + + public Function, AuthProvider> recordOauthAuthProvider( + String name, + String openApiSpecId, + List operations) { + return context -> new OAuth2AuthenticationProvider( + AuthenticationRecorder.getAuthConfig(generatorConfig, openApiSpecId, name), name, openApiSpecId, + context.getInjectedReference(OAuth2AuthenticationProvider.OidcClientRequestFilterDelegate.class, + new OidcClient.Literal(name)), + operations); + } +} diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/ReactiveOidcClientRequestFilterDelegate.java b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/ReactiveOidcClientRequestFilterDelegate.java similarity index 62% rename from client/runtime/src/main/java/io/quarkiverse/openapi/generator/ReactiveOidcClientRequestFilterDelegate.java rename to client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/ReactiveOidcClientRequestFilterDelegate.java index d4fb3f4df..cdbd57961 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/ReactiveOidcClientRequestFilterDelegate.java +++ b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/ReactiveOidcClientRequestFilterDelegate.java @@ -1,7 +1,6 @@ -package io.quarkiverse.openapi.generator; +package io.quarkiverse.openapi.generator.oidc; import java.io.IOException; -import java.util.function.Consumer; import jakarta.annotation.Priority; import jakarta.enterprise.inject.spi.InjectionPoint; @@ -13,8 +12,9 @@ import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestContext; import org.jboss.resteasy.reactive.client.spi.ResteasyReactiveClientRequestFilter; -import io.quarkiverse.openapi.generator.providers.OAuth2AuthenticationProvider; -import io.quarkus.oidc.client.Tokens; +import io.quarkiverse.openapi.generator.OidcClient; +import io.quarkiverse.openapi.generator.OpenApiGeneratorConfig; +import io.quarkiverse.openapi.generator.oidc.providers.OAuth2AuthenticationProvider; import io.quarkus.oidc.client.runtime.AbstractTokensProducer; import io.quarkus.oidc.client.runtime.DisabledOidcClientException; import io.quarkus.oidc.common.runtime.OidcConstants; @@ -24,16 +24,14 @@ public class ReactiveOidcClientRequestFilterDelegate extends AbstractTokensProducer implements ResteasyReactiveClientRequestFilter, OAuth2AuthenticationProvider.OidcClientRequestFilterDelegate { - private static final Logger LOG = Logger - .getLogger(ReactiveOidcClientRequestFilterDelegate.class); + private static final Logger LOG = Logger.getLogger(ReactiveOidcClientRequestFilterDelegate.class); private static final String BEARER_SCHEME_WITH_SPACE = OidcConstants.BEARER_SCHEME + " "; final String clientId; ReactiveOidcClientRequestFilterDelegate(InjectionPoint injectionPoint) { OidcClient annotation = (OidcClient) injectionPoint.getQualifiers().stream() - .filter(x -> x.annotationType().equals(OidcClient.class)) - .findFirst().orElseThrow(); + .filter(x -> x.annotationType().equals(OidcClient.class)).findFirst().orElseThrow(); this.clientId = OpenApiGeneratorConfig.getSanitizedSecuritySchemeName(annotation.name()); } @@ -58,23 +56,17 @@ public void filter(ClientRequestContext requestContext) throws IOException { public void filter(ResteasyReactiveClientRequestContext requestContext) { requestContext.suspend(); - super.getTokens().subscribe().with(new Consumer<>() { - @Override - public void accept(Tokens tokens) { - requestContext.getHeaders().putSingle(HttpHeaders.AUTHORIZATION, - BEARER_SCHEME_WITH_SPACE + tokens.getAccessToken()); + super.getTokens().subscribe().with(tokens -> { + requestContext.getHeaders().putSingle(HttpHeaders.AUTHORIZATION, + BEARER_SCHEME_WITH_SPACE + tokens.getAccessToken()); + requestContext.resume(); + }, t -> { + if (t instanceof DisabledOidcClientException) { + LOG.debug("Client is disabled, acquiring and propagating the token is not necessary"); requestContext.resume(); - } - }, new Consumer<>() { - @Override - public void accept(Throwable t) { - if (t instanceof DisabledOidcClientException) { - LOG.debug("Client is disabled, acquiring and propagating the token is not necessary"); - requestContext.resume(); - } else { - LOG.debugf("Access token is not available, cause: %s, aborting the request", t.getMessage()); - requestContext.resume((t instanceof RuntimeException) ? t : new RuntimeException(t)); - } + } else { + LOG.debugf("Access token is not available, cause: %s, aborting the request", t.getMessage()); + requestContext.resume((t instanceof RuntimeException) ? t : new RuntimeException(t)); } }); } diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/OAuth2AuthenticationProvider.java b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/providers/OAuth2AuthenticationProvider.java similarity index 91% rename from client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/OAuth2AuthenticationProvider.java rename to client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/providers/OAuth2AuthenticationProvider.java index ff5368ced..df87186c1 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/OAuth2AuthenticationProvider.java +++ b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/providers/OAuth2AuthenticationProvider.java @@ -1,4 +1,4 @@ -package io.quarkiverse.openapi.generator.providers; +package io.quarkiverse.openapi.generator.oidc.providers; import static io.quarkiverse.openapi.generator.AuthConfig.TOKEN_PROPAGATION; @@ -12,6 +12,8 @@ import org.slf4j.LoggerFactory; import io.quarkiverse.openapi.generator.AuthConfig; +import io.quarkiverse.openapi.generator.providers.AbstractAuthProvider; +import io.quarkiverse.openapi.generator.providers.OperationAuthInfo; import io.quarkus.oidc.common.runtime.OidcConstants; public class OAuth2AuthenticationProvider extends AbstractAuthProvider { diff --git a/client/pom.xml b/client/pom.xml index c82086b00..dded3c51d 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -15,5 +15,6 @@ deployment runtime test-utils + oidc \ No newline at end of file diff --git a/client/runtime/pom.xml b/client/runtime/pom.xml index b0b2ab4aa..d50c39f2c 100644 --- a/client/runtime/pom.xml +++ b/client/runtime/pom.xml @@ -37,16 +37,6 @@ provided - - io.quarkus - quarkus-rest-client-oidc-filter - true - - - io.quarkus - quarkus-resteasy-client-oidc-filter - true - org.assertj assertj-core @@ -67,6 +57,11 @@ quarkus-junit5 test + + org.jboss.resteasy + resteasy-core + test + diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthConfig.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthConfig.java index 5a1e57987..e768cdd59 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthConfig.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthConfig.java @@ -17,7 +17,6 @@ public class AuthConfig { public static final String TOKEN_PROPAGATION = "token-propagation"; - public static final String HEADER_NAME = "header-name"; /** * Enables the authentication token propagation for this particular securityScheme. diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthenticationRecorder.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthenticationRecorder.java index c94127edc..be8dabbc0 100644 --- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthenticationRecorder.java +++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/AuthenticationRecorder.java @@ -14,8 +14,6 @@ import io.quarkiverse.openapi.generator.providers.BasicAuthenticationProvider; import io.quarkiverse.openapi.generator.providers.BearerAuthenticationProvider; import io.quarkiverse.openapi.generator.providers.CompositeAuthenticationProvider; -import io.quarkiverse.openapi.generator.providers.OAuth2AuthenticationProvider; -import io.quarkiverse.openapi.generator.providers.OAuth2AuthenticationProvider.OidcClientRequestFilterDelegate; import io.quarkiverse.openapi.generator.providers.OperationAuthInfo; import io.quarkus.arc.SyntheticCreationalContext; import io.quarkus.runtime.annotations.Recorder; @@ -45,7 +43,7 @@ public Function, AuthProvider> recordAp String apiKeyName, List operations) { return context -> new ApiKeyAuthenticationProvider(openApiSpecId, name, apiKeyIn, apiKeyName, - getAuthConfig(openApiSpecId, name), + getAuthConfig(generatorConfig, openApiSpecId, name), operations); } @@ -54,7 +52,8 @@ public Function, AuthProvider> recordBe String scheme, String openApiSpecId, List operations) { - return context -> new BearerAuthenticationProvider(openApiSpecId, name, scheme, getAuthConfig(openApiSpecId, name), + return context -> new BearerAuthenticationProvider(openApiSpecId, name, scheme, + getAuthConfig(generatorConfig, openApiSpecId, name), operations); } @@ -62,18 +61,11 @@ public Function, AuthProvider> recordBa String name, String openApiSpecId, List operations) { - return context -> new BasicAuthenticationProvider(openApiSpecId, name, getAuthConfig(openApiSpecId, name), operations); + return context -> new BasicAuthenticationProvider(openApiSpecId, name, + getAuthConfig(generatorConfig, openApiSpecId, name), operations); } - public Function, AuthProvider> recordOauthAuthProvider( - String name, - String openApiSpecId, - List operations) { - return context -> new OAuth2AuthenticationProvider(getAuthConfig(openApiSpecId, name), name, openApiSpecId, - context.getInjectedReference(OidcClientRequestFilterDelegate.class, new OidcClient.Literal(name)), operations); - } - - AuthConfig getAuthConfig(String openApiSpecId, String name) { + public static AuthConfig getAuthConfig(OpenApiGeneratorConfig generatorConfig, String openApiSpecId, String name) { return Objects.requireNonNull(generatorConfig, "generatorConfig can't be null.") .getItemConfig(openApiSpecId) .flatMap(SpecItemConfig::getAuth) diff --git a/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/AbstractOpenApiSpecProviderTest.java b/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/AbstractOpenApiSpecProviderTest.java index 2387cff63..2e4523efc 100644 --- a/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/AbstractOpenApiSpecProviderTest.java +++ b/client/runtime/src/test/java/io/quarkiverse/openapi/generator/providers/AbstractOpenApiSpecProviderTest.java @@ -1,8 +1,5 @@ package io.quarkiverse.openapi.generator.providers; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.lenient; - import java.util.HashMap; import java.util.Optional; @@ -10,9 +7,11 @@ import jakarta.ws.rs.core.MultivaluedHashMap; import jakarta.ws.rs.core.MultivaluedMap; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import io.quarkiverse.openapi.generator.AuthConfig; @@ -59,11 +58,11 @@ protected void createConfiguration() { specItemConfig.auth.authConfigs.put(AUTH_SCHEME_NAME, authConfig); generatorConfig.itemConfigs.put(OPEN_API_FILE_SPEC_ID, specItemConfig); headers = new MultivaluedHashMap<>(); - lenient().doReturn(headers).when(requestContext).getHeaders(); + Mockito.lenient().doReturn(headers).when(requestContext).getHeaders(); } protected void assertHeader(MultivaluedMap headers, String headerName, String value) { - assertThat(headers.getFirst(headerName)) + Assertions.assertThat(headers.getFirst(headerName)) .isNotNull() .isEqualTo(value); } diff --git a/docs/modules/ROOT/pages/includes/authentication-support.adoc b/docs/modules/ROOT/pages/includes/authentication-support.adoc index a51e9bc1d..4fa038e08 100644 --- a/docs/modules/ROOT/pages/includes/authentication-support.adoc +++ b/docs/modules/ROOT/pages/includes/authentication-support.adoc @@ -134,12 +134,18 @@ quarkus.oidc-client.petstore_auth.client-id=petstore-app The configuration suffix `quarkus.oidc-client.petstore_auth` is exclusive for the schema defined in the specification file and the `schemaName` is sanitized by applying the rules described above. -For this to work you **must** add https://quarkus.io/guides/security-openid-connect-client#oidc-client-filter[Quarkus OIDC Client Filter Extension] to your project: +For this to work you **must** add https://quarkus.io/guides/security-openid-connect-client#oidc-client-filter[Quarkus OIDC Client Filter Extension] to your project. + +IMPORTANT: From version 2.7.0 and onwards you must also add the `quarkus-openapi-generator-oidc` additional dependency. Please see the details below. RESTEasy Classic: [source ,xml] ---- + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-oidc + io.quarkus quarkus-oidc-client-filter @@ -150,6 +156,10 @@ RESTEasy Reactive: [source ,xml] ---- + + io.quarkiverse.openapi.generator + quarkus-openapi-generator-oidc + io.quarkus quarkus-oidc-client-reactive-filter