From 80112614e041527f74552a15c63ca151f3c6bef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Phillip=20Kr=C3=BCger?= Date: Mon, 22 Apr 2024 10:32:10 +1000 Subject: [PATCH 01/60] Update codeblock to 1.0.13 (cherry picked from commit a58c5a0d7e52a7849c8a3d053624b6fcc99880de) --- bom/dev-ui/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/dev-ui/pom.xml b/bom/dev-ui/pom.xml index c461772d70c91..a8c23cc5f4a11 100644 --- a/bom/dev-ui/pom.xml +++ b/bom/dev-ui/pom.xml @@ -28,7 +28,7 @@ 1.7.5 1.7.0 5.5.0 - 1.0.12 + 1.0.13 1.8.3 2.4.0 From 5ae8a2643a7ac3526119b5b54b95034e5fc2912b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 21:40:24 +0000 Subject: [PATCH 02/60] Bump hibernate-orm.version from 6.4.4.Final to 6.4.5.Final Bumps `hibernate-orm.version` from 6.4.4.Final to 6.4.5.Final. Updates `org.hibernate.orm:hibernate-core` from 6.4.4.Final to 6.4.5.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.5/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.4...6.4.5) Updates `org.hibernate.orm:hibernate-graalvm` from 6.4.4.Final to 6.4.5.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.5/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.4...6.4.5) Updates `org.hibernate.orm:hibernate-envers` from 6.4.4.Final to 6.4.5.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.5/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.4...6.4.5) Updates `org.hibernate.orm:hibernate-jpamodelgen` from 6.4.4.Final to 6.4.5.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.5/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.4...6.4.5) Updates `org.hibernate:hibernate-jpamodelgen` from 6.4.4.Final to 6.4.5.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.5/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.4...6.4.5) Updates `org.hibernate.orm:hibernate-community-dialects` from 6.4.4.Final to 6.4.5.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.5/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.4...6.4.5) --- updated-dependencies: - dependency-name: org.hibernate.orm:hibernate-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate.orm:hibernate-graalvm dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate.orm:hibernate-envers dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate.orm:hibernate-jpamodelgen dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate:hibernate-jpamodelgen dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate.orm:hibernate-community-dialects dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] (cherry picked from commit 44114389ec782f3a85417efe66e4ab78237b7da6) --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index a38220001f0d0..317534ac1993f 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -101,7 +101,7 @@ bytebuddy.version (just below), hibernate-orm.version-for-documentation (in docs/pom.xml) and both hibernate-orm.version and antlr.version in build-parent/pom.xml WARNING again for diffs that don't provide enough context: when updating, see above --> - 6.4.4.Final + 6.4.5.Final 1.14.11 6.0.6.Final 2.2.2.Final From 0df299d54cf92e1f05b6599beee7fea89140ff00 Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Tue, 23 Apr 2024 11:06:25 -0300 Subject: [PATCH 03/60] Bump Quarkus HTTP to 5.2.2.Final (cherry picked from commit 288a54d889b341b8aaf3d230263eaa95add3420e) --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 317534ac1993f..98c88375807a1 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -34,7 +34,7 @@ 1.32.0 1.32.0-alpha 1.21.0-alpha - 5.2.1.Final + 5.2.2.Final 1.12.4 2.1.12 0.22.0 From e3f7022cde6bee2fdc81a933f5206b8b2e11cb98 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Wed, 24 Apr 2024 16:31:32 +0300 Subject: [PATCH 04/60] Move reinitialization of vertx classes out of NettyProcessor Vert.x is not a dependency of netty, meaning that the classes may not always be on the classpath. Fixes https://github.com/quarkusio/quarkus/issues/40243 Supersedes https://github.com/quarkusio/quarkus/pull/40248 (cherry picked from commit c610a8b9a217443bed1cfaa641615d95d3b18d2a) --- .../netty/deployment/NettyProcessor.java | 8 +------- .../vertx/deployment/VertxProcessor.java | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/extensions/netty/deployment/src/main/java/io/quarkus/netty/deployment/NettyProcessor.java b/extensions/netty/deployment/src/main/java/io/quarkus/netty/deployment/NettyProcessor.java index 4c2741d2092ed..361033561c391 100644 --- a/extensions/netty/deployment/src/main/java/io/quarkus/netty/deployment/NettyProcessor.java +++ b/extensions/netty/deployment/src/main/java/io/quarkus/netty/deployment/NettyProcessor.java @@ -171,14 +171,8 @@ NativeImageConfigBuildItem build( if (QuarkusClassLoader.isClassPresentAtRuntime("io.netty.buffer.UnpooledByteBufAllocator")) { builder.addRuntimeReinitializedClass("io.netty.buffer.UnpooledByteBufAllocator") .addRuntimeReinitializedClass("io.netty.buffer.Unpooled") - .addRuntimeReinitializedClass("io.vertx.core.http.impl.Http1xServerResponse") .addRuntimeReinitializedClass("io.netty.handler.codec.http.HttpObjectAggregator") - .addRuntimeReinitializedClass("io.netty.handler.codec.ReplayingDecoderByteBuf") - .addRuntimeReinitializedClass("io.vertx.core.parsetools.impl.RecordParserImpl"); - - if (QuarkusClassLoader.isClassPresentAtRuntime("io.vertx.ext.web.client.impl.MultipartFormUpload")) { - builder.addRuntimeReinitializedClass("io.vertx.ext.web.client.impl.MultipartFormUpload"); - } + .addRuntimeReinitializedClass("io.netty.handler.codec.ReplayingDecoderByteBuf"); if (QuarkusClassLoader .isClassPresentAtRuntime("org.jboss.resteasy.reactive.client.impl.multipart.QuarkusMultipartFormUpload")) { diff --git a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/VertxProcessor.java b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/VertxProcessor.java index 2d69d4a6d7be9..9bd62eba808f2 100644 --- a/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/VertxProcessor.java +++ b/extensions/vertx/deployment/src/main/java/io/quarkus/vertx/deployment/VertxProcessor.java @@ -28,6 +28,7 @@ import io.quarkus.arc.processor.BeanInfo; import io.quarkus.arc.processor.BuildExtension; import io.quarkus.arc.processor.BuiltinScope; +import io.quarkus.bootstrap.classloading.QuarkusClassLoader; import io.quarkus.deployment.Capabilities; import io.quarkus.deployment.Capability; import io.quarkus.deployment.Feature; @@ -44,6 +45,7 @@ import io.quarkus.deployment.builditem.LaunchModeBuildItem; import io.quarkus.deployment.builditem.ServiceStartBuildItem; import io.quarkus.deployment.builditem.ShutdownContextBuildItem; +import io.quarkus.deployment.builditem.nativeimage.NativeImageConfigBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.gizmo.ClassOutput; @@ -196,6 +198,23 @@ void faultToleranceIntegration(Capabilities capabilities, BuildProducer tryLoad(String name, ClassLoader tccl) { try { return tccl.loadClass(name); From 741cfc1c7f61d2ecc07b4de183d6475b7344821a Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Mon, 22 Apr 2024 23:24:29 +0200 Subject: [PATCH 05/60] Reset relocations for runtime dependencies (cherry picked from commit 2a995a7adabfff4fd15c09ee0529b51624f7f123) --- .../resolver/maven/ApplicationDependencyTreeResolver.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/ApplicationDependencyTreeResolver.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/ApplicationDependencyTreeResolver.java index 7a3fd7574fb5d..0b8eb190884e2 100644 --- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/ApplicationDependencyTreeResolver.java +++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/ApplicationDependencyTreeResolver.java @@ -427,6 +427,10 @@ private void visitRuntimeDependency(DependencyNode node) { final ExtensionDependency extDep = getExtensionDependencyOrNull(node, artifact); if (dep == null) { + // in case it was relocated it might not survive conflict resolution in the deployment graph + if (!node.getRelocations().isEmpty()) { + ((DefaultDependencyNode) node).setRelocations(List.of()); + } WorkspaceModule module = null; if (resolver.getProjectModuleResolver() != null) { module = resolver.getProjectModuleResolver().getProjectModule(artifact.getGroupId(), From b3593a91a84a050caab40704606e8e5024d97b42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Apr 2024 21:44:36 +0000 Subject: [PATCH 06/60] Bump hibernate-search.version from 7.1.0.Final to 7.1.1.Final Bumps `hibernate-search.version` from 7.1.0.Final to 7.1.1.Final. Updates `org.hibernate.search:hibernate-search-bom` from 7.1.0.Final to 7.1.1.Final - [Release notes](https://github.com/hibernate/hibernate-search/releases) - [Changelog](https://github.com/hibernate/hibernate-search/blob/main/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-search/compare/7.1.0.Final...7.1.1.Final) Updates `org.hibernate.search:hibernate-search-mapper-orm` from 7.1.0.Final to 7.1.1.Final - [Release notes](https://github.com/hibernate/hibernate-search/releases) - [Changelog](https://github.com/hibernate/hibernate-search/blob/main/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-search/compare/7.1.0.Final...7.1.1.Final) Updates `org.hibernate.search:hibernate-search-engine` from 7.1.0.Final to 7.1.1.Final - [Release notes](https://github.com/hibernate/hibernate-search/releases) - [Changelog](https://github.com/hibernate/hibernate-search/blob/main/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-search/compare/7.1.0.Final...7.1.1.Final) Updates `org.hibernate.search:hibernate-search-mapper-pojo-base` from 7.1.0.Final to 7.1.1.Final - [Release notes](https://github.com/hibernate/hibernate-search/releases) - [Changelog](https://github.com/hibernate/hibernate-search/blob/main/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-search/compare/7.1.0.Final...7.1.1.Final) Updates `org.hibernate.search:hibernate-search-util-common` from 7.1.0.Final to 7.1.1.Final - [Release notes](https://github.com/hibernate/hibernate-search/releases) - [Changelog](https://github.com/hibernate/hibernate-search/blob/main/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-search/compare/7.1.0.Final...7.1.1.Final) --- updated-dependencies: - dependency-name: org.hibernate.search:hibernate-search-bom dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate.search:hibernate-search-mapper-orm dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate.search:hibernate-search-engine dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate.search:hibernate-search-mapper-pojo-base dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate.search:hibernate-search-util-common dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] (cherry picked from commit 014d60f03c1451d463143a1939482adc745f5ea5) --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 98c88375807a1..8439a5d1660e3 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -107,7 +107,7 @@ 2.2.2.Final 8.0.1.Final - 7.1.0.Final + 7.1.1.Final 7.0.1.Final 2.3 8.0.0.Final From 1a15697bdd06c5a0a898db5a45c1ab015e1d4a82 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Thu, 25 Apr 2024 08:51:40 +0200 Subject: [PATCH 07/60] ArC: prevent NPE when EagerInstanceHandle.UNAVAILABLE is closed (cherry picked from commit 6ecefbe247b848c98eee256e54074688084c231f) --- .../main/java/io/quarkus/arc/impl/AbstractInstanceHandle.java | 4 ++-- .../main/java/io/quarkus/arc/impl/EagerInstanceHandle.java | 4 +++- .../src/main/java/io/quarkus/arc/impl/LazyInstanceHandle.java | 2 ++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/AbstractInstanceHandle.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/AbstractInstanceHandle.java index e9e04ef605da8..1225141452304 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/AbstractInstanceHandle.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/AbstractInstanceHandle.java @@ -18,7 +18,7 @@ abstract class AbstractInstanceHandle implements InstanceHandle { private static final AtomicIntegerFieldUpdater DESTROYED_UPDATER = AtomicIntegerFieldUpdater .newUpdater(AbstractInstanceHandle.class, "destroyed"); - private final InjectableBean bean; + protected final InjectableBean bean; private final CreationalContext creationalContext; private final CreationalContext parentCreationalContext; private final Consumer destroyLogic; @@ -56,7 +56,7 @@ public void destroy() { if (isInstanceCreated() && DESTROYED_UPDATER.compareAndSet(this, 0, 1)) { if (destroyLogic != null) { destroyLogic.accept(instanceInternal()); - } else { + } else if (bean != null) { if (bean.getScope().equals(Dependent.class)) { destroyInternal(); } else { diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/EagerInstanceHandle.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/EagerInstanceHandle.java index 1ba2cf08159a6..df2430fcf5505 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/EagerInstanceHandle.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/EagerInstanceHandle.java @@ -8,8 +8,10 @@ import io.quarkus.arc.InstanceHandle; /** + * Instance handle that is initialized eagerly when created. * * @param + * @see LazyInstanceHandle */ class EagerInstanceHandle extends AbstractInstanceHandle { @@ -34,7 +36,7 @@ public static final InstanceHandle unavailable() { @Override protected boolean isInstanceCreated() { - return true; + return instance != null; } @Override diff --git a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/LazyInstanceHandle.java b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/LazyInstanceHandle.java index 891a1c8397257..4be2d86d85e36 100644 --- a/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/LazyInstanceHandle.java +++ b/independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/LazyInstanceHandle.java @@ -8,8 +8,10 @@ import io.quarkus.arc.InjectableBean; /** + * Instance handle that is initialized lazily when first used. * * @param + * @see EagerInstanceHandle */ class LazyInstanceHandle extends AbstractInstanceHandle { From 3a3265d4182529aacb0849a67b93e0e68e06ac99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:25:23 +0000 Subject: [PATCH 08/60] Bump commons-io:commons-io from 2.16.0 to 2.16.1 Bumps commons-io:commons-io from 2.16.0 to 2.16.1. --- updated-dependencies: - dependency-name: commons-io:commons-io dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] (cherry picked from commit 3d70ef11e4882364444260227f099924bfe9041b) --- bom/application/pom.xml | 2 +- independent-projects/bootstrap/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 8439a5d1660e3..0838222f135ac 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -85,7 +85,7 @@ 4.0.2 4.0.5 9.7 - 2.16.0 + 2.16.1 16.0.0.Final 3.0-alpha-2 2.1.0 diff --git a/independent-projects/bootstrap/pom.xml b/independent-projects/bootstrap/pom.xml index 47c5123683b40..b6dc9f02c46c3 100644 --- a/independent-projects/bootstrap/pom.xml +++ b/independent-projects/bootstrap/pom.xml @@ -58,7 +58,7 @@ 4.0.1 2.0.1 1.16.1 - 2.16.0 + 2.16.1 3.14.0 33.1.0-jre 1.0.1 From 845440dd099ecaef01171b683b205001f9384fc2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:32:32 +0000 Subject: [PATCH 09/60] Bump hibernate-orm.version from 6.4.5.Final to 6.4.7.Final Bumps `hibernate-orm.version` from 6.4.5.Final to 6.4.7.Final. Updates `org.hibernate.orm:hibernate-core` from 6.4.5.Final to 6.4.7.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.7/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.5...6.4.7) Updates `org.hibernate.orm:hibernate-graalvm` from 6.4.5.Final to 6.4.7.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.7/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.5...6.4.7) Updates `org.hibernate.orm:hibernate-envers` from 6.4.5.Final to 6.4.7.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.7/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.5...6.4.7) Updates `org.hibernate.orm:hibernate-jpamodelgen` from 6.4.5.Final to 6.4.7.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.7/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.5...6.4.7) Updates `org.hibernate:hibernate-jpamodelgen` from 6.4.5.Final to 6.4.7.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.7/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.5...6.4.7) Updates `org.hibernate.orm:hibernate-community-dialects` from 6.4.5.Final to 6.4.7.Final - [Release notes](https://github.com/hibernate/hibernate-orm/releases) - [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.4.7/changelog.txt) - [Commits](https://github.com/hibernate/hibernate-orm/compare/6.4.5...6.4.7) --- updated-dependencies: - dependency-name: org.hibernate.orm:hibernate-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate.orm:hibernate-graalvm dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate.orm:hibernate-envers dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate.orm:hibernate-jpamodelgen dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate:hibernate-jpamodelgen dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.hibernate.orm:hibernate-community-dialects dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] (cherry picked from commit f390aae60372e5a7688f2d51eb9e11915ef4822f) --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 0838222f135ac..942fc3c1bb363 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -101,7 +101,7 @@ bytebuddy.version (just below), hibernate-orm.version-for-documentation (in docs/pom.xml) and both hibernate-orm.version and antlr.version in build-parent/pom.xml WARNING again for diffs that don't provide enough context: when updating, see above --> - 6.4.5.Final + 6.4.7.Final 1.14.11 6.0.6.Final 2.2.2.Final From 11e771c86e7bab9786a26232592cc089de6bcf61 Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Thu, 25 Apr 2024 14:09:17 +0200 Subject: [PATCH 10/60] Align bootstrap and the rest on the same Slf4j version (cherry picked from commit 7750df985186185a9072f1cbdd89762b0ca27df8) --- bom/application/pom.xml | 6 ------ independent-projects/bootstrap/pom.xml | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 942fc3c1bb363..886d4dc7ae03d 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -116,7 +116,6 @@ 2.2.5.Final 2.2.2.Final 2.2.1.Final - 2.0.6 2.0.0.Final 1.7.0.Final 1.0.1.Final @@ -5064,11 +5063,6 @@ asm-util ${asm.version} - - org.slf4j - slf4j-api - ${slf4j.version} - org.jboss.slf4j slf4j-jboss-logmanager diff --git a/independent-projects/bootstrap/pom.xml b/independent-projects/bootstrap/pom.xml index b6dc9f02c46c3..2cc14b930b89b 100644 --- a/independent-projects/bootstrap/pom.xml +++ b/independent-projects/bootstrap/pom.xml @@ -66,7 +66,7 @@ 1.2.6 3.0.4.Final 1.1.0.Final - 1.7.36 + 2.0.6 23.1.0 2.6.0 2.0 From 61107b6388d6f525069dcfcacc60cfbfc6d3162b Mon Sep 17 00:00:00 2001 From: antonwiens <> Date: Wed, 24 Apr 2024 17:05:56 +0200 Subject: [PATCH 11/60] change abstractjsonmessagebodyreader to use lowercase mediatype for easier and more clear matching (cherry picked from commit b02a75d324ff3a616100f8ca2f8265b2af3f6e8d) --- .../AbstractJsonMessageBodyReader.java | 9 ++-- .../AbstractJsonMessageBodyReaderTest.java | 45 +++++++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 independent-projects/resteasy-reactive/common/runtime/src/test/java/org/jboss/resteasy/reactive/common/providers/serialisers/AbstractJsonMessageBodyReaderTest.java diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/providers/serialisers/AbstractJsonMessageBodyReader.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/providers/serialisers/AbstractJsonMessageBodyReader.java index 50bf2637442e4..d096d04387b9c 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/providers/serialisers/AbstractJsonMessageBodyReader.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/providers/serialisers/AbstractJsonMessageBodyReader.java @@ -20,10 +20,11 @@ protected boolean isReadable(MediaType mediaType, Class type) { if (String.class.equals(type)) { // don't attempt to read plain strings return false; } - String subtype = mediaType.getSubtype(); - boolean isApplicationMediaType = "application".equals(mediaType.getType()); - return (isApplicationMediaType && "json".equalsIgnoreCase(subtype) || subtype.endsWith("+json") - || subtype.equalsIgnoreCase("x-ndjson")) + String subtype = mediaType.getSubtype().toLowerCase(); + final String mainType = mediaType.getType().toLowerCase(); + boolean isApplicationMediaType = "application".equals(mainType); + return (isApplicationMediaType && "json".equals(subtype) || subtype.endsWith("+json") + || "x-ndjson".equals(subtype)) || (mediaType.isWildcardSubtype() && (mediaType.isWildcardType() || isApplicationMediaType)); } } diff --git a/independent-projects/resteasy-reactive/common/runtime/src/test/java/org/jboss/resteasy/reactive/common/providers/serialisers/AbstractJsonMessageBodyReaderTest.java b/independent-projects/resteasy-reactive/common/runtime/src/test/java/org/jboss/resteasy/reactive/common/providers/serialisers/AbstractJsonMessageBodyReaderTest.java new file mode 100644 index 0000000000000..25330dd08f303 --- /dev/null +++ b/independent-projects/resteasy-reactive/common/runtime/src/test/java/org/jboss/resteasy/reactive/common/providers/serialisers/AbstractJsonMessageBodyReaderTest.java @@ -0,0 +1,45 @@ +package org.jboss.resteasy.reactive.common.providers.serialisers; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedMap; + +import org.junit.jupiter.api.Test; + +class AbstractJsonMessageBodyReaderTest { + + class TestReader extends AbstractJsonMessageBodyReader { + @Override + public Object readFrom(Class aClass, Type type, Annotation[] annotations, MediaType mediaType, + MultivaluedMap multivaluedMap, InputStream inputStream) + throws IOException, WebApplicationException { + return null; + } + } + + @Test + void isReadableCaseInsensitive() { + final TestReader testReader = new TestReader(); + assertFalse(testReader.isReadable(new MediaType("application", "jso"), Object.class)); + assertFalse(testReader.isReadable(new MediaType("application", "json+anything"), Object.class)); + assertFalse(testReader.isReadable(new MediaType("test", "json"), Object.class)); + assertTrue(testReader.isReadable(new MediaType("test", "test+json"), Object.class)); + assertTrue(testReader.isReadable(new MediaType("test", "x-ndjson"), Object.class)); + assertTrue(testReader.isReadable(new MediaType("application", "test+json"), Object.class)); + assertTrue(testReader.isReadable(new MediaType("application", "json"), Object.class)); + assertTrue(testReader.isReadable(new MediaType("Application", "Json"), Object.class)); + assertTrue(testReader.isReadable(new MediaType("appliCAtion", "json"), Object.class)); + assertTrue(testReader.isReadable(new MediaType("application", "jSOn"), Object.class)); + assertTrue(testReader.isReadable(new MediaType("application", "test+json"), Object.class)); + assertTrue(testReader.isReadable(new MediaType("application", "x-ndjson"), Object.class)); + assertTrue(testReader.isReadable(new MediaType("applIcation", "x-ndjson"), Object.class)); + } +} From 974b94bd263120ae7edceaeb98a299d73309ea17 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Fri, 26 Apr 2024 11:45:07 +0300 Subject: [PATCH 12/60] Use toString for serializing enum form params Fixes: #40167 (cherry picked from commit 730c2a5cb22cf83b6021f0f7094efd0c85e20db9) --- .../reactive/runtime/RestClientBase.java | 2 +- .../reactive/beanparam/BeanParamTest.java | 32 +++++++++++++++---- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/extensions/resteasy-reactive/rest-client-jaxrs/runtime/src/main/java/io/quarkus/jaxrs/client/reactive/runtime/RestClientBase.java b/extensions/resteasy-reactive/rest-client-jaxrs/runtime/src/main/java/io/quarkus/jaxrs/client/reactive/runtime/RestClientBase.java index 45c8aef084b2c..d7cad534f9c4e 100644 --- a/extensions/resteasy-reactive/rest-client-jaxrs/runtime/src/main/java/io/quarkus/jaxrs/client/reactive/runtime/RestClientBase.java +++ b/extensions/resteasy-reactive/rest-client-jaxrs/runtime/src/main/java/io/quarkus/jaxrs/client/reactive/runtime/RestClientBase.java @@ -144,7 +144,7 @@ public Object convertParam(T value, Class type, Type genericType, Annotat } else { // FIXME: cheating, we should generate a converter for this enum if (value instanceof Enum) { - return ((Enum) value).name(); + return value.toString(); } return value; } diff --git a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/beanparam/BeanParamTest.java b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/beanparam/BeanParamTest.java index 02facb4a25e22..606a79768edd6 100644 --- a/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/beanparam/BeanParamTest.java +++ b/extensions/resteasy-reactive/rest-client/deployment/src/test/java/io/quarkus/rest/client/reactive/beanparam/BeanParamTest.java @@ -37,23 +37,23 @@ void shouldPassPathParamFromBeanParam() { assertThat(client.beanParamWithFields(new MyBeanParamWithFields())) .isEqualTo("restPathDefault/restPathOverridden/pathParam" + "/restHeaderDefault/restHeaderOverridden/headerParam" - + "/restFormDefault/restFormOverridden/formParam" + + "/restFormDefault/test/restFormOverridden/formParam" + "/restCookieDefault/restCookieOverridden/cookieParam" + "/restQueryDefault/restQueryOverridden/queryParam"); assertThat(client.regularParameters( "restPathDefault", "restPathOverridden", "pathParam", "restHeaderDefault", "restHeaderOverridden", "headerParam", "restCookieDefault", "restCookieOverridden", "cookieParam", - "restFormDefault", "restFormOverridden", "formParam", + "restFormDefault", SomeEnum.TEST, "restFormOverridden", "formParam", "restQueryDefault", "restQueryOverridden", "queryParam")) .isEqualTo("restPathDefault/restPathOverridden/pathParam" + "/restHeaderDefault/restHeaderOverridden/headerParam" - + "/restFormDefault/restFormOverridden/formParam" + + "/restFormDefault/test/restFormOverridden/formParam" + "/restCookieDefault/restCookieOverridden/cookieParam" + "/restQueryDefault/restQueryOverridden/queryParam"); assertThat(client.beanParamWithProperties(new MyBeanParamWithProperties())).isEqualTo("null/null/pathParam" + "/null/null/headerParam" - + "/null/null/formParam" + + "/null/null/null/formParam" + "/null/null/cookieParam" + "/null/null/queryParam"); } @@ -78,6 +78,7 @@ String regularParameters(@RestPath String restPathDefault, @CookieParam("cookieParam") String cookieParam, @RestForm String restFormDefault, + @RestForm SomeEnum someEnum, @RestForm("restForm_Overridden") String restFormOverridden, @FormParam("formParam") String formParam, @@ -114,6 +115,8 @@ public static class MyBeanParamWithFields { @RestForm private String restFormDefault = "restFormDefault"; + @RestForm + private SomeEnum someEnum = SomeEnum.TEST; @RestForm("restForm_Overridden") private String restFormOverridden = "restFormOverridden"; @FormParam("formParam") @@ -210,6 +213,7 @@ public String beanParamWithFields(@RestPath String restPathDefault, @RestHeader("restHeader_Overridden") String restHeader_Overridden, @RestHeader("headerParam") String headerParam, @RestForm String restFormDefault, + @RestForm String someEnum, @RestForm String restForm_Overridden, @RestForm String formParam, @RestCookie String restCookieDefault, @@ -220,7 +224,7 @@ public String beanParamWithFields(@RestPath String restPathDefault, @RestQuery String queryParam) { return restPathDefault + "/" + restPath_Overridden + "/" + pathParam + "/" + restHeaderDefault + "/" + restHeader_Overridden + "/" + headerParam - + "/" + restFormDefault + "/" + restForm_Overridden + "/" + formParam + + "/" + restFormDefault + "/" + someEnum + "/" + restForm_Overridden + "/" + formParam + "/" + restCookieDefault + "/" + restCookie_Overridden + "/" + cookieParam + "/" + restQueryDefault + "/" + restQuery_Overridden + "/" + queryParam; } @@ -234,6 +238,7 @@ public String beanParamWithProperties(@RestPath String restPathDefault, @RestHeader("restHeader_Overridden") String restHeader_Overridden, @RestHeader("headerParam") String headerParam, @RestForm String restFormDefault, + @RestForm String someEnum, @RestForm String restForm_Overridden, @RestForm String formParam, @RestCookie String restCookieDefault, @@ -244,9 +249,24 @@ public String beanParamWithProperties(@RestPath String restPathDefault, @RestQuery String queryParam) { return restPathDefault + "/" + restPath_Overridden + "/" + pathParam + "/" + restHeaderDefault + "/" + restHeader_Overridden + "/" + headerParam - + "/" + restFormDefault + "/" + restForm_Overridden + "/" + formParam + + "/" + restFormDefault + "/" + someEnum + "/" + restForm_Overridden + "/" + formParam + "/" + restCookieDefault + "/" + restCookie_Overridden + "/" + cookieParam + "/" + restQueryDefault + "/" + restQuery_Overridden + "/" + queryParam; } } + + public enum SomeEnum { + TEST("test"); + + private final String value; + + SomeEnum(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + } } From c96bcd141f6bbb06a665163eeb06209a16c26fab Mon Sep 17 00:00:00 2001 From: Katia Aresti Date: Thu, 25 Apr 2024 09:30:25 +0200 Subject: [PATCH 13/60] Updates Infinispan to 15.0.2.Final (cherry picked from commit 4647370fea540810db57d43bd8b4c2f048584fb8) --- bom/application/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 886d4dc7ae03d..901304be3484f 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -139,8 +139,8 @@ 1.2.6 2.2 5.10.2 - 15.0.1.Final - 5.0.2.Final + 15.0.2.Final + 5.0.3.Final 3.1.5 4.1.108.Final 1.16.0 From 9eec0a0866ebc20728f0433b3899508c6d5645a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 26 Apr 2024 16:01:52 +0200 Subject: [PATCH 14/60] Improve phrasing in Hibernate documentation In particular, make it clear that the Flyway initialization feature is really just about that: initializing the Flyway config. (cherry picked from commit 24faf1c0cb3fe4ff2ad4861372c347d35ae5dea6) --- docs/src/main/asciidoc/hibernate-orm.adoc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/src/main/asciidoc/hibernate-orm.adoc b/docs/src/main/asciidoc/hibernate-orm.adoc index 3923a512e4327..def7a4f8bf355 100644 --- a/docs/src/main/asciidoc/hibernate-orm.adoc +++ b/docs/src/main/asciidoc/hibernate-orm.adoc @@ -741,8 +741,10 @@ Add the following in your properties file. [[flyway]] == Automatically transitioning to Flyway to Manage Schemas -If you have the xref:flyway.adoc[Flyway extension] installed when running in development mode, Quarkus provides a simple way to turn -your Hibernate ORM auto generated schema into a Flyway migration file. This is intended to make is easy to move from +If you have the xref:flyway.adoc[Flyway extension] installed when running in development mode, +Quarkus provides a simple way to initialize your Flyway configuration +using the schema generated automatically by Hibernate ORM. +This is intended to ease the move from the early development phase, where Hibernate can be used to quickly set up the schema, to the production phase, where Flyway is used to manage schema changes. From bba69af5f56d79e2d36e379e6d39feeada7a13ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 26 Apr 2024 16:03:19 +0200 Subject: [PATCH 15/60] Document how to use Flyway in reactive applications (cherry picked from commit 69ad3fdbe4284628c9cd953c1cb060e27af714c8) --- docs/src/main/asciidoc/datasource.adoc | 14 +++++++++++++ docs/src/main/asciidoc/flyway.adoc | 17 ++++++++++++++++ .../src/main/asciidoc/hibernate-reactive.adoc | 16 +++++++++++++++ .../HibernateReactiveMySQLTestEndpoint.java | 20 +++++++++---------- .../src/main/resources/application.properties | 14 ++++--------- .../mysql/HibernateReactiveMySQLTest.java | 4 ++-- 6 files changed, 62 insertions(+), 23 deletions(-) diff --git a/docs/src/main/asciidoc/datasource.adoc b/docs/src/main/asciidoc/datasource.adoc index e4f0065489314..a56046cb64efd 100644 --- a/docs/src/main/asciidoc/datasource.adoc +++ b/docs/src/main/asciidoc/datasource.adoc @@ -177,6 +177,7 @@ Until now, the configuration has been the same regardless of whether you are usi When you have defined the database kind and the credentials, the rest depends on what type of driver you are using. It is possible to use JDBC and a reactive driver simultaneously. +[[jdbc-datasource]] ==== JDBC datasource JDBC is the most common database connection pattern, typically needed when used in combination with non-reactive Hibernate ORM. @@ -294,6 +295,7 @@ AgroalDataSource defaultDataSource; In the above example, the type is `AgroalDataSource`, a `javax.sql.DataSource` subtype. Because of this, you can also use `javax.sql.DataSource` as the injected type. +[[reactive-datasource]] ==== Reactive datasource Quarkus offers several reactive clients for use with a reactive datasource. @@ -325,10 +327,22 @@ Be aware that setting the pool size too low might cause some requests to time ou For more information about pool size adjustment properties, see the <> section. +[[jdbc-and-reactive-datasources-simultaneously]] ==== JDBC and reactive datasources simultaneously When a JDBC extension - along with Agroal - and a reactive datasource extension handling the given database kind are included, they will both be created by default. +If you want to use them both, +make sure to set both <> and <> configuration, +for example: + +[source,properties] +---- +%prod.quarkus.datasource.reactive.url=postgresql:///your_database +%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/hibernate_orm_test +---- + +If you do not want to have both a JDBC datasource and a reactive datasource created, use the following configuration. * To disable the JDBC datasource explicitly: + [source, properties] diff --git a/docs/src/main/asciidoc/flyway.adoc b/docs/src/main/asciidoc/flyway.adoc index 3544d4361e457..ecdee7ee18944 100644 --- a/docs/src/main/asciidoc/flyway.adoc +++ b/docs/src/main/asciidoc/flyway.adoc @@ -285,6 +285,23 @@ When using Flyway together with Hibernate ORM, you can use the Dev UI to generat You can find more information about this feature in the xref:hibernate-orm.adoc#flyway[Hibernate ORM guide]. +[[reactive-datasources]] +== Flyway and Reactive datasources + +Flyway internally relies on a JDBC datasource, +whereas reactive use cases will rely on xref:reactive-sql-clients.adoc[reactive SQL clients], +either directly or through xref:hibernate-reactive.adoc[Hibernate Reactive]. +This is not a problem in Quarkus, +because xref:datasource.adoc#jdbc-and-reactive-datasources-simultaneously[a single configured datasource can be made available both through reactive clients and JDBC]. + +To use Flyway on a datasource you otherwise access reactively, +simply make sure to configure that datasource +both as xref:datasource.adoc#jdbc-datasource[JDBC] +and xref:datasource.adoc#reactive-datasource[reactive]. +This involves in particular adding dependencies to Quarkus extensions +for both the JDBC driver and the reactive client, +for instance `quarkus-jdbc-postgresql` *and* `quarkus-reactive-pg-client`. + == Flyway on Kubernetes Sometimes, it's helpful not to execute Flyway initialization on each application startup. One such example is when deploying diff --git a/docs/src/main/asciidoc/hibernate-reactive.adoc b/docs/src/main/asciidoc/hibernate-reactive.adoc index 6a3efe3a2b594..52d2f412e0d66 100644 --- a/docs/src/main/asciidoc/hibernate-reactive.adoc +++ b/docs/src/main/asciidoc/hibernate-reactive.adoc @@ -224,6 +224,22 @@ This will inject the `Mutiny.SessionFactory` of the default persistence unit. NOTE: Prior to Quarkus 3.0 it was also possible to inject a `@RequestScoped` bean for `Mutiny.Session`. However, the lifecycle of a reactive session does not fit the lifecycle of the CDI request context. Therefore, this bean is removed in Quarkus 3.0. +[[flyway]] +== Automatically transitioning to Flyway to Manage Schemas + +Hibernate Reactive can be used in the same application as Flyway. +See xref:flyway.adoc#reactive-datasources[this section of the Flyway extension documentation] +for details regarding configuration of Flyway in a reactive application. + +[TIP] +==== +If you have the xref:flyway.adoc[Flyway extension] installed when running in development mode, +Quarkus provides a simple way to initialize your Flyway configuration +using the schema generated automatically by Hibernate Reactive. + +See xref:hibernate-orm.adoc#flyway[the Hibernate ORM guide] for more details. +==== + [[testing]] === Testing diff --git a/integration-tests/hibernate-reactive-mysql-agroal-flyway/src/main/java/io/quarkus/it/hibernate/reactive/mysql/HibernateReactiveMySQLTestEndpoint.java b/integration-tests/hibernate-reactive-mysql-agroal-flyway/src/main/java/io/quarkus/it/hibernate/reactive/mysql/HibernateReactiveMySQLTestEndpoint.java index bbf7070d9cf79..c068c7ef681b1 100644 --- a/integration-tests/hibernate-reactive-mysql-agroal-flyway/src/main/java/io/quarkus/it/hibernate/reactive/mysql/HibernateReactiveMySQLTestEndpoint.java +++ b/integration-tests/hibernate-reactive-mysql-agroal-flyway/src/main/java/io/quarkus/it/hibernate/reactive/mysql/HibernateReactiveMySQLTestEndpoint.java @@ -9,7 +9,6 @@ import org.hibernate.reactive.mutiny.Mutiny; import io.agroal.api.AgroalDataSource; -import io.quarkus.agroal.DataSource; import io.smallrye.mutiny.Uni; import io.vertx.mutiny.mysqlclient.MySQLPool; import io.vertx.mutiny.sqlclient.Row; @@ -28,15 +27,14 @@ public class HibernateReactiveMySQLTestEndpoint { MySQLPool mysqlPool; @Inject - @DataSource("blocking") - AgroalDataSource blockingDS; + AgroalDataSource jdbcDataSource; @GET - @Path("/blockingFind") - public GuineaPig blockingFind() throws SQLException { + @Path("/jdbcFind") + public GuineaPig jdbcFind() throws SQLException { final GuineaPig expectedPig = new GuineaPig(6, "Iola"); - populateDBBlocking(); - return selectBlocking(6); + populateDBJdbc(); + return selectJdbc(6); } @GET @@ -102,8 +100,8 @@ private Uni> populateDB() { .flatMap(junk -> mysqlPool.preparedQuery("INSERT INTO Pig (id, name) VALUES (5, 'Aloi')").execute()); } - private void populateDBBlocking() throws SQLException { - Connection connection = blockingDS.getConnection(); + private void populateDBJdbc() throws SQLException { + Connection connection = jdbcDataSource.getConnection(); connection.prepareStatement("DELETE FROM Pig").execute(); connection.prepareStatement("INSERT INTO Pig (id, name) VALUES (6, 'Iola')").execute(); connection.close(); @@ -121,8 +119,8 @@ private Uni selectNameFromId(Integer id) { }); } - private GuineaPig selectBlocking(Integer id) throws SQLException { - Connection connection = blockingDS.getConnection(); + private GuineaPig selectJdbc(Integer id) throws SQLException { + Connection connection = jdbcDataSource.getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT id, name FROM Pig WHERE id = ?"); statement.setInt(1, id); ResultSet rowSet = statement.executeQuery(); diff --git a/integration-tests/hibernate-reactive-mysql-agroal-flyway/src/main/resources/application.properties b/integration-tests/hibernate-reactive-mysql-agroal-flyway/src/main/resources/application.properties index d48dcb14b0856..227e44e7dda3f 100644 --- a/integration-tests/hibernate-reactive-mysql-agroal-flyway/src/main/resources/application.properties +++ b/integration-tests/hibernate-reactive-mysql-agroal-flyway/src/main/resources/application.properties @@ -1,16 +1,10 @@ -# Reactive datasource config +# Reactive+JDBC datasource config quarkus.datasource.db-kind=mysql quarkus.datasource.username=hibernate_orm_test quarkus.datasource.password=hibernate_orm_test quarkus.datasource.reactive.url=${reactive-mysql.url} - -# Blocking datasource config -quarkus.datasource.blocking.db-kind=mysql -quarkus.datasource.blocking.username=hibernate_orm_test -quarkus.datasource.blocking.password=hibernate_orm_test -quarkus.datasource.blocking.jdbc.url=${mysql.jdbc.url} -quarkus.datasource.blocking.jdbc=true -quarkus.datasource.blocking.jdbc.max-size=1 +quarkus.datasource.jdbc.url=${mysql.jdbc.url} +quarkus.datasource.jdbc.max-size=1 # Hibernate config #quarkus.hibernate-orm.log.sql=true @@ -18,4 +12,4 @@ quarkus.datasource.blocking.jdbc.max-size=1 quarkus.hibernate-orm.database.generation=none # Check that one can use Flyway alongside Hibernate Reactive -quarkus.flyway.blocking.migrate-at-start=true +quarkus.flyway.migrate-at-start=true diff --git a/integration-tests/hibernate-reactive-mysql-agroal-flyway/src/test/java/io/quarkus/it/hibernate/reactive/mysql/HibernateReactiveMySQLTest.java b/integration-tests/hibernate-reactive-mysql-agroal-flyway/src/test/java/io/quarkus/it/hibernate/reactive/mysql/HibernateReactiveMySQLTest.java index da83bb2d53c97..2bdff6cbd7022 100644 --- a/integration-tests/hibernate-reactive-mysql-agroal-flyway/src/test/java/io/quarkus/it/hibernate/reactive/mysql/HibernateReactiveMySQLTest.java +++ b/integration-tests/hibernate-reactive-mysql-agroal-flyway/src/test/java/io/quarkus/it/hibernate/reactive/mysql/HibernateReactiveMySQLTest.java @@ -22,9 +22,9 @@ public void reactiveFindMutiny() { } @Test - public void blockingFind() { + public void jdbcFind() { RestAssured.when() - .get("/tests/blockingFind") + .get("/tests/jdbcFind") .then() .body(is("{\"id\":6,\"name\":\"Iola\"}")); } From 20d3c7425eb40211ddf47825c7cd1ea810686ae6 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 22 Apr 2024 11:35:58 +0300 Subject: [PATCH 16/60] Turn beans from info extension into ApplicationScoped This is done purely to aid testing Closes: #40152 (cherry picked from commit a40c1c2c3f017b01130e0ab9833da6b8662d14fa) --- .../java/io/quarkus/info/deployment/InfoProcessor.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/info/deployment/src/main/java/io/quarkus/info/deployment/InfoProcessor.java b/extensions/info/deployment/src/main/java/io/quarkus/info/deployment/InfoProcessor.java index c8882b07d9f1d..9e62736b27be6 100644 --- a/extensions/info/deployment/src/main/java/io/quarkus/info/deployment/InfoProcessor.java +++ b/extensions/info/deployment/src/main/java/io/quarkus/info/deployment/InfoProcessor.java @@ -13,7 +13,7 @@ import java.util.TimeZone; import java.util.stream.Collectors; -import jakarta.inject.Singleton; +import jakarta.enterprise.context.ApplicationScoped; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; @@ -122,7 +122,7 @@ void gitInfo(InfoBuildTimeConfig config, valuesProducer.produce(new InfoBuildTimeValuesBuildItem("git", data)); beanProducer.produce(SyntheticBeanBuildItem.configure(GitInfo.class) .supplier(recorder.gitInfoSupplier(branch, latestCommitId, latestCommitTime)) - .scope(Singleton.class) + .scope(ApplicationScoped.class) .setRuntimeInit() .done()); } catch (Exception e) { @@ -229,7 +229,7 @@ void buildInfo(CurateOutcomeBuildItem curateOutcomeBuildItem, valuesProducer.produce(new InfoBuildTimeValuesBuildItem("build", data)); beanProducer.produce(SyntheticBeanBuildItem.configure(BuildInfo.class) .supplier(recorder.buildInfoSupplier(group, artifact, version, time, quarkusVersion)) - .scope(Singleton.class) + .scope(ApplicationScoped.class) .setRuntimeInit() .done()); } @@ -251,7 +251,7 @@ void osInfo(InfoRecorder recorder, valuesProducer.produce(new InfoBuildTimeContributorBuildItem(recorder.osInfoContributor())); beanProducer.produce(SyntheticBeanBuildItem.configure(OsInfo.class) .supplier(recorder.osInfoSupplier()) - .scope(Singleton.class) + .scope(ApplicationScoped.class) .setRuntimeInit() .done()); } @@ -264,7 +264,7 @@ void javaInfo(InfoRecorder recorder, valuesProducer.produce(new InfoBuildTimeContributorBuildItem(recorder.javaInfoContributor())); beanProducer.produce(SyntheticBeanBuildItem.configure(JavaInfo.class) .supplier(recorder.javaInfoSupplier()) - .scope(Singleton.class) + .scope(ApplicationScoped.class) .setRuntimeInit() .done()); } From 36d0caa1d3f7aa6d19b0842c2f4e02ab4bddd3da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Apr 2024 22:00:44 +0000 Subject: [PATCH 17/60] Bump org.eclipse.parsson:parsson from 1.1.5 to 1.1.6 Bumps [org.eclipse.parsson:parsson](https://github.com/eclipse-ee4j/parsson) from 1.1.5 to 1.1.6. - [Release notes](https://github.com/eclipse-ee4j/parsson/releases) - [Commits](https://github.com/eclipse-ee4j/parsson/compare/1.1.5...1.1.6) --- updated-dependencies: - dependency-name: org.eclipse.parsson:parsson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] (cherry picked from commit f81e22ebdbce94b095ca0f8f433f3611c4d14d85) --- bom/application/pom.xml | 2 +- independent-projects/resteasy-reactive/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 901304be3484f..68fea431b350c 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -23,7 +23,7 @@ 3.1.7 1.3.2 1 - 1.1.5 + 1.1.6 2.1.5.Final 3.1.2.Final 6.2.7.Final diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml index 873daf8cfadba..ac39b49ea1315 100644 --- a/independent-projects/resteasy-reactive/pom.xml +++ b/independent-projects/resteasy-reactive/pom.xml @@ -41,7 +41,7 @@ 2.1.3 3.1.0 4.0.2 - 1.1.5 + 1.1.6 UTF-8 4.0.1 From d5be2cd42f19919ac11ee0029b0e13cb157e4890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Fri, 12 Apr 2024 19:18:32 +0200 Subject: [PATCH 18/60] Validate Tenant annotation applied before authentication happened (cherry picked from commit 62615a6b33fedbc0f94be96652b744d8ffd3ee39) --- .../io/quarkus/oidc/runtime/OidcRecorder.java | 25 ++++++++ ...JakartaRestResourceHttpPermissionTest.java | 40 ++++++++++-- ...JakartaRestResourceHttpPermissionTest.java | 40 ++++++++++-- .../it/keycloak/TenantEchoResource.java | 13 ++++ .../keycloak/AnnotationBasedTenantTest.java | 62 ++++++++++++++----- 5 files changed, 157 insertions(+), 23 deletions(-) diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java index f67d7f851f41d..0568683eda84a 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java @@ -44,6 +44,7 @@ import io.quarkus.runtime.TlsConfig; import io.quarkus.runtime.annotations.Recorder; import io.quarkus.runtime.configuration.ConfigurationException; +import io.quarkus.security.AuthenticationFailedException; import io.quarkus.security.identity.AuthenticationRequestContext; import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.security.identity.request.TokenAuthenticationRequest; @@ -600,6 +601,30 @@ public Consumer apply(String tenantId) { return new Consumer() { @Override public void accept(RoutingContext routingContext) { + OidcTenantConfig tenantConfig = routingContext.get(OidcTenantConfig.class.getName()); + if (tenantConfig != null) { + // authentication has happened before @Tenant annotation was matched with the HTTP request + String tenantUsedForAuth = tenantConfig.tenantId.orElse(null); + if (tenantId.equals(tenantUsedForAuth)) { + // @Tenant selects the same tenant as already selected + return; + } else { + // @Tenant selects the different tenant than already selected + throw new AuthenticationFailedException( + """ + The '%1$s' selected with the @Tenant annotation must be used to authenticate + the request but it was already authenticated with the '%2$s' tenant. It + can happen if the '%1$s' is selected with an annotation but '%2$s' is + resolved during authentication required by the HTTP Security Policy which + is enforced before the JAX-RS chain is run. In such cases, please set the + 'quarkus.http.auth.permission."permissions".applies-to=JAXRS' to all HTTP + Security Policies which secure the same REST endpoints as the ones + where the '%1$s' tenant is resolved by the '@Tenant' annotation. + """ + .formatted(tenantId, tenantUsedForAuth)); + } + } + LOG.debugf("@Tenant annotation set a '%s' tenant id on the %s request path", tenantId, routingContext.request().path()); routingContext.put(OidcUtils.TENANT_ID_SET_BY_ANNOTATION, tenantId); diff --git a/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/JakartaRestResourceHttpPermissionTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/JakartaRestResourceHttpPermissionTest.java index a8324f72cfc2c..0eb468a1bcdc0 100644 --- a/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/JakartaRestResourceHttpPermissionTest.java +++ b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/JakartaRestResourceHttpPermissionTest.java @@ -12,10 +12,12 @@ import org.jboss.shrinkwrap.api.asset.StringAsset; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.security.test.utils.TestIdentityController; import io.quarkus.security.test.utils.TestIdentityProvider; import io.quarkus.test.QuarkusUnitTest; @@ -40,18 +42,23 @@ public class JakartaRestResourceHttpPermissionTest { "quarkus.http.auth.permission.root.paths=/\n" + "quarkus.http.auth.permission.root.policy=authenticated\n" + "quarkus.http.auth.permission.dot.paths=dot,dot/\n" + - "quarkus.http.auth.permission.dot.policy=authenticated\n"; + "quarkus.http.auth.permission.dot.policy=authenticated\n" + + "quarkus.http.auth.permission.jax-rs.paths=jax-rs\n" + + "quarkus.http.auth.permission.jax-rs.policy=admin-role\n" + + "quarkus.http.auth.policy.admin-role.roles-allowed=admin"; @RegisterExtension static QuarkusUnitTest runner = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar .addClasses(TestIdentityProvider.class, TestIdentityController.class, ApiResource.class, - RootResource.class, PublicResource.class) + RootResource.class, PublicResource.class, JaxRsResource.class) .addAsResource(new StringAsset(APP_PROPS), "application.properties")); @BeforeAll public static void setup() { - TestIdentityController.resetRoles().add("test", "test", "test"); + TestIdentityController.resetRoles() + .add("admin", "admin", "admin") + .add("test", "test", "test"); } @TestHTTPResource @@ -97,6 +104,15 @@ public void testSecuredNotFound(String path) { assurePathAuthenticated(path, 404); } + @Test + public void testJaxRsRolesHttpSecurityPolicy() { + // insufficient role, expected admin + assurePath("/jax-rs", 401); + assurePath("///jax-rs///", 401); + + assurePath("/jax-rs", 200, "admin", true, "admin:admin"); + } + private static String getLastNonEmptySegmentContent(String path) { while (path.endsWith("/") || path.endsWith(".")) { path = path.substring(0, path.length() - 1); @@ -104,6 +120,18 @@ private static String getLastNonEmptySegmentContent(String path) { return path.substring(path.lastIndexOf('/') + 1); } + @Path("jax-rs") + public static class JaxRsResource { + + @Inject + SecurityIdentity identity; + + @GET + public String getPrincipalName() { + return identity.getPrincipal().getName(); + } + } + @Path("/api") public static class ApiResource { @@ -201,13 +229,17 @@ private void assurePathAuthenticated(String path, String body) { } private void assurePath(String path, int expectedStatusCode, String body, boolean auth) { + assurePath(path, expectedStatusCode, body, auth, "test:test"); + } + + private void assurePath(String path, int expectedStatusCode, String body, boolean auth, String credentials) { var httpClient = vertx.createHttpClient(); try { httpClient .request(HttpMethod.GET, url.getPort(), url.getHost(), path) .map(r -> { if (auth) { - r.putHeader("Authorization", "Basic " + encodeBase64URLSafeString("test:test".getBytes())); + r.putHeader("Authorization", "Basic " + encodeBase64URLSafeString(credentials.getBytes())); } return r; }) diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/JakartaRestResourceHttpPermissionTest.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/JakartaRestResourceHttpPermissionTest.java index 220102abd6348..e6ff7c5e18e7b 100644 --- a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/JakartaRestResourceHttpPermissionTest.java +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/JakartaRestResourceHttpPermissionTest.java @@ -14,10 +14,12 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.security.test.utils.TestIdentityController; import io.quarkus.security.test.utils.TestIdentityProvider; import io.quarkus.test.QuarkusUnitTest; @@ -40,19 +42,24 @@ public class JakartaRestResourceHttpPermissionTest { "quarkus.http.auth.permission.root.paths=/\n" + "quarkus.http.auth.permission.root.policy=authenticated\n" + "quarkus.http.auth.permission.fragment.paths=/#stuff,/#stuff/\n" + - "quarkus.http.auth.permission.fragment.policy=authenticated\n"; + "quarkus.http.auth.permission.fragment.policy=authenticated\n" + + "quarkus.http.auth.permission.jax-rs.paths=jax-rs\n" + + "quarkus.http.auth.permission.jax-rs.policy=admin-role\n" + + "quarkus.http.auth.policy.admin-role.roles-allowed=admin"; private static WebClient client; @RegisterExtension static QuarkusUnitTest runner = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar .addClasses(TestIdentityProvider.class, TestIdentityController.class, ApiResource.class, - RootResource.class, PublicResource.class) + RootResource.class, PublicResource.class, JaxRsResource.class) .addAsResource(new StringAsset(APP_PROPS), "application.properties")); @BeforeAll public static void setup() { - TestIdentityController.resetRoles().add("test", "test", "test"); + TestIdentityController.resetRoles() + .add("admin", "admin", "admin") + .add("test", "test", "test"); } @AfterAll @@ -106,6 +113,15 @@ public void testNotSecuredPaths(String path) { assurePathAuthenticated(path); } + @Test + public void testJaxRsRolesHttpSecurityPolicy() { + // insufficient role, expected admin + assurePath("/jax-rs", 401); + assurePath("///jax-rs///", 401); + + assurePath("/jax-rs", 200, "admin", true, "admin"); + } + private static String getLastNonEmptySegmentContent(String path) { while (path.endsWith("/") || path.endsWith(".")) { path = path.substring(0, path.length() - 1); @@ -113,6 +129,18 @@ private static String getLastNonEmptySegmentContent(String path) { return path.substring(path.lastIndexOf('/') + 1); } + @Path("jax-rs") + public static class JaxRsResource { + + @Inject + SecurityIdentity identity; + + @GET + public String getPrincipalName() { + return identity.getPrincipal().getName(); + } + } + @Path("/api") public static class ApiResource { @@ -212,9 +240,13 @@ private void assurePathAuthenticated(String path, String body) { } private void assurePath(String path, int expectedStatusCode, String body, boolean auth) { + assurePath(path, expectedStatusCode, body, auth, "test"); + } + + private void assurePath(String path, int expectedStatusCode, String body, boolean auth, String username) { var req = getClient().get(url.getPort(), url.getHost(), path); if (auth) { - req.basicAuthentication("test", "test"); + req.basicAuthentication(username, username); } var result = req.send(); await().atMost(REQUEST_TIMEOUT).until(result::isComplete); diff --git a/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/TenantEchoResource.java b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/TenantEchoResource.java index 5636556933e8c..4440ddf1a8e4e 100644 --- a/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/TenantEchoResource.java +++ b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/TenantEchoResource.java @@ -54,6 +54,19 @@ public String getHrTenantIdentityAugmentation() { return getTenantInternal(); } + @Path("/http-security-policy-applies-all-diff") + @GET + public String httpSecurityPolicyAppliesAllDiff() { + throw new IllegalStateException("An exception should have been thrown because authentication happened" + + " before Tenant was selected with the @Tenant annotation"); + } + + @Path("/http-security-policy-applies-all-same") + @GET + public String httpSecurityPolicyAppliesAllSame() { + return getTenantInternal(); + } + private String getTenantInternal() { return OidcUtils.TENANT_ID_ATTRIBUTE + "=" + routingContext.get(OidcUtils.TENANT_ID_ATTRIBUTE) + ", static.tenant.id=" + routingContext.get("static.tenant.id") diff --git a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/AnnotationBasedTenantTest.java b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/AnnotationBasedTenantTest.java index a072936710d3e..eac6beda6d99e 100644 --- a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/AnnotationBasedTenantTest.java +++ b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/AnnotationBasedTenantTest.java @@ -28,6 +28,7 @@ public Map getConfigOverrides() { Map.entry("quarkus.oidc.hr.auth-server-url", "http://localhost:8180/auth/realms/quarkus2/"), Map.entry("quarkus.oidc.hr.client-id", "quarkus-app"), Map.entry("quarkus.oidc.hr.credentials.secret", "secret"), + Map.entry("quarkus.oidc.hr.tenant-paths", "/api/tenant-echo/http-security-policy-applies-all-same"), Map.entry("quarkus.oidc.hr.token.audience", "http://hr.service"), Map.entry("quarkus.http.auth.policy.roles1.roles-allowed", "role1"), Map.entry("quarkus.http.auth.policy.roles2.roles-allowed", "role2"), @@ -59,7 +60,11 @@ public Map getConfigOverrides() { Map.entry("quarkus.http.auth.permission.identity-augmentation.paths", "/api/tenant-echo/hr-identity-augmentation"), Map.entry("quarkus.http.auth.permission.identity-augmentation.policy", "roles3"), - Map.entry("quarkus.http.auth.permission.identity-augmentation.applies-to", "JAXRS")); + Map.entry("quarkus.http.auth.permission.identity-augmentation.applies-to", "JAXRS"), + Map.entry("quarkus.http.auth.permission.tenant-annotation-applies-all.paths", + "/api/tenant-echo/http-security-policy-applies-all-diff,/api/tenant-echo/http-security-policy-applies-all-same"), + Map.entry("quarkus.http.auth.permission.tenant-annotation-applies-all.policy", "admin-role"), + Map.entry("quarkus.http.auth.policy.admin-role.roles-allowed", "admin")); } } @@ -204,9 +209,7 @@ public void testClassicHttpSecurityPolicyWithRbac() { token = getTokenWithRole("role1"); RestAssured.given().auth().oauth2(token) .when().get("/api/tenant-echo2/hr-classic-perm-check") - .then().statusCode(200) - .body(Matchers.equalTo(("tenant-id=hr, static.tenant.id=null, name=alice, " - + OidcUtils.TENANT_ID_SET_BY_ANNOTATION + "=hr"))); + .then().statusCode(401); token = getTokenWithRole("wrong-role"); RestAssured.given().auth().oauth2(token) @@ -239,21 +242,18 @@ public void testJaxRsAndClassicHttpSecurityPolicyNoRbac() { token = getTokenWithRole("role2"); RestAssured.given().auth().oauth2(token) .when().get("/api/tenant-echo/hr-classic-and-jaxrs-perm-check") - .then().statusCode(403); + .then().statusCode(401); // roles allowed security check (created for @RolesAllowed) fails over missing role "role3" token = getTokenWithRole("role2", "role1"); RestAssured.given().auth().oauth2(token) .when().get("/api/tenant-echo/hr-classic-and-jaxrs-perm-check") - .then().statusCode(403); + .then().statusCode(401); token = getTokenWithRole("role3", "role2", "role1"); RestAssured.given().auth().oauth2(token) .when().get("/api/tenant-echo/hr-classic-and-jaxrs-perm-check") - .then().statusCode(200) - // static tenant is null as the permission check "combined-part1" happened before @Tenant - .body(Matchers.equalTo(("tenant-id=hr, static.tenant.id=null, name=alice, " - + OidcUtils.TENANT_ID_SET_BY_ANNOTATION + "=hr"))); + .then().statusCode(401); } finally { server.stop(); } @@ -282,15 +282,12 @@ public void testJaxRsAndClassicHttpSecurityPolicyWithRbac() { token = getTokenWithRole("role2"); RestAssured.given().auth().oauth2(token) .when().get("/api/tenant-echo2/hr-classic-and-jaxrs-perm-check") - .then().statusCode(403); + .then().statusCode(401); token = getTokenWithRole("role2", "role1"); RestAssured.given().auth().oauth2(token) .when().get("/api/tenant-echo2/hr-classic-and-jaxrs-perm-check") - .then().statusCode(200) - // static tenant is null as the permission check "combined-part1" happened before @Tenant - .body(Matchers.equalTo(("tenant-id=hr, static.tenant.id=null, name=alice, " - + OidcUtils.TENANT_ID_SET_BY_ANNOTATION + "=hr"))); + .then().statusCode(401); } finally { server.stop(); } @@ -325,6 +322,31 @@ public void testJaxRsIdentityAugmentation() { } } + @Test + public void testPolicyAppliedBeforeTenantAnnotationMatched() { + WiremockTestResource server = new WiremockTestResource(); + server.start(); + try { + // policy applied before @Tenant annotation has been matched and different tenant has been used for auth + // than the one that @Tenant annotation selects + var token = getNonHrTenantAccessToken(Set.of("admin")); + RestAssured.given().auth().oauth2(token) + .when().get("/api/tenant-echo/http-security-policy-applies-all-diff") + .then().statusCode(401); + + // policy applied before @Tenant annotation has been matched and different tenant has been used for auth + // than the one that @Tenant annotation selects + token = getTokenWithRole("admin"); + RestAssured.given().auth().oauth2(token) + .when().get("/api/tenant-echo/http-security-policy-applies-all-same") + .then().statusCode(200) + .body(Matchers + .equalTo("tenant-id=null, static.tenant.id=hr, name=alice, tenant-id-set-by-annotation=null")); + } finally { + server.stop(); + } + } + private static String getTokenWithRole(String... roles) { return Jwt.preferredUserName("alice") .groups(Set.of(roles)) @@ -333,4 +355,14 @@ private static String getTokenWithRole(String... roles) { .keyId("1") .sign("privateKey.jwk"); } + + private String getNonHrTenantAccessToken(Set groups) { + return Jwt.preferredUserName("alice") + .groups(groups) + .issuer("https://server.example.com") + .audience("https://service.example.com") + .jws() + .keyId("1") + .sign(); + } } From c6d5757ba3ab179e0e3baa95b7e7d73fa890364a Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Fri, 26 Apr 2024 16:08:59 +0300 Subject: [PATCH 19/60] Always populate metrics uri in presence of auth failures Closes: #40305 (cherry picked from commit 8694c5ca9ca542638f511b7b06844bbdd188bd09) --- .../ObservabilityIntegrationBuildItem.java | 9 ++ .../deployment/ObservabilityProcessor.java | 14 +- .../deployment/ResteasyReactiveProcessor.java | 8 +- .../runtime/ResteasyReactiveRecorder.java | 8 +- .../ObservabilityIntegrationRecorder.java | 139 +++++++++--------- 5 files changed, 102 insertions(+), 76 deletions(-) create mode 100644 extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ObservabilityIntegrationBuildItem.java diff --git a/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ObservabilityIntegrationBuildItem.java b/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ObservabilityIntegrationBuildItem.java new file mode 100644 index 0000000000000..6db8506c6f418 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ObservabilityIntegrationBuildItem.java @@ -0,0 +1,9 @@ +package io.quarkus.resteasy.reactive.server.deployment; + +import io.quarkus.builder.item.SimpleBuildItem; + +/** + * A marker build item signifying that observability features have been integrated + */ +public final class ObservabilityIntegrationBuildItem extends SimpleBuildItem { +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ObservabilityProcessor.java b/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ObservabilityProcessor.java index 54a8626b5fbea..c6625226dcd1b 100644 --- a/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ObservabilityProcessor.java +++ b/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ObservabilityProcessor.java @@ -12,6 +12,7 @@ import io.quarkus.deployment.Capabilities; import io.quarkus.deployment.Capability; +import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.ExecutionTime; import io.quarkus.deployment.annotations.Record; @@ -42,17 +43,20 @@ public List scan(MethodInfo method, ClassInfo actualEndp @BuildStep @Record(value = ExecutionTime.STATIC_INIT) - FilterBuildItem preAuthFailureFilter(Capabilities capabilities, + void preAuthFailureFilter(Capabilities capabilities, Optional metricsCapability, ObservabilityIntegrationRecorder recorder, - ResteasyReactiveDeploymentBuildItem deployment) { + ResteasyReactiveDeploymentBuildItem deployment, + BuildProducer filterProducer, + BuildProducer observabilityIntegrationProducer) { boolean integrationNeeded = integrationNeeded(capabilities, metricsCapability); if (!integrationNeeded) { - return null; + return; } - return FilterBuildItem.ofPreAuthenticationFailureHandler( - recorder.preAuthFailureHandler(deployment.getDeployment())); + filterProducer.produce(FilterBuildItem.ofPreAuthenticationFailureHandler( + recorder.preAuthFailureHandler(deployment.getDeployment()))); + observabilityIntegrationProducer.produce(new ObservabilityIntegrationBuildItem()); } private boolean integrationNeeded(Capabilities capabilities, diff --git a/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java b/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java index 8fbaa8e3f20db..a6e846be18697 100644 --- a/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java +++ b/extensions/resteasy-reactive/rest/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java @@ -1365,9 +1365,13 @@ private static boolean notFoundCustomExMapper(String builtInExSignature, String @BuildStep @Record(value = ExecutionTime.STATIC_INIT) - public FilterBuildItem addDefaultAuthFailureHandler(ResteasyReactiveRecorder recorder) { + public FilterBuildItem addDefaultAuthFailureHandler(ResteasyReactiveRecorder recorder, + ResteasyReactiveDeploymentBuildItem deployment, + Optional observabilityIntegrationBuildItem) { // replace default auth failure handler added by vertx-http so that our exception mappers can customize response - return new FilterBuildItem(recorder.defaultAuthFailureHandler(), FilterBuildItem.AUTHENTICATION - 1); + return new FilterBuildItem( + recorder.defaultAuthFailureHandler(deployment.getDeployment(), observabilityIntegrationBuildItem.isPresent()), + FilterBuildItem.AUTHENTICATION - 1); } private void checkForDuplicateEndpoint(ResteasyReactiveConfig config, Map> allMethods) { diff --git a/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java index e54d35e3229ec..7d0a6f1231ae1 100644 --- a/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java +++ b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java @@ -53,6 +53,7 @@ import io.quarkus.resteasy.reactive.common.runtime.ArcBeanFactory; import io.quarkus.resteasy.reactive.common.runtime.ArcThreadSetupAction; import io.quarkus.resteasy.reactive.common.runtime.ResteasyReactiveCommonRecorder; +import io.quarkus.resteasy.reactive.server.runtime.observability.ObservabilityIntegrationRecorder; import io.quarkus.runtime.BlockingOperationControl; import io.quarkus.runtime.ExecutorRecorder; import io.quarkus.runtime.LaunchMode; @@ -341,11 +342,16 @@ public ServerSerialisers createServerSerialisers() { return new ServerSerialisers(); } - public Handler defaultAuthFailureHandler() { + public Handler defaultAuthFailureHandler( + RuntimeValue deployment, boolean setTemplatePath) { return new Handler() { @Override public void handle(RoutingContext event) { if (event.get(QuarkusHttpUser.AUTH_FAILURE_HANDLER) instanceof DefaultAuthFailureHandler) { + if (setTemplatePath) { + ObservabilityIntegrationRecorder.setTemplatePath(event, deployment.getValue()); + } + // fail event rather than end it, so it's handled by abort handlers (see #addFailureHandler method) event.put(QuarkusHttpUser.AUTH_FAILURE_HANDLER, new FailingDefaultAuthFailureHandler()); } diff --git a/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityIntegrationRecorder.java b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityIntegrationRecorder.java index 72acb0831b1e6..3e550fa61c9e4 100644 --- a/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityIntegrationRecorder.java +++ b/extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/observability/ObservabilityIntegrationRecorder.java @@ -47,82 +47,85 @@ private boolean shouldHandle(RoutingContext event) { || event.failure() instanceof ForbiddenException || event.failure() instanceof UnauthorizedException; } + }; + } - private void setTemplatePath(RoutingContext rc, Deployment deployment) { - // do what RestInitialHandler does - var initMappers = new RequestMapper<>(deployment.getClassMappers()); - var requestMatch = initMappers.map(getPathWithoutPrefix(rc, deployment)); - var remaining = requestMatch.remaining.isEmpty() ? "/" : requestMatch.remaining; - - var serverRestHandlers = requestMatch.value.handlers; - if (serverRestHandlers == null || serverRestHandlers.length < 1) { - // nothing we can do - return; - } - var firstHandler = serverRestHandlers[0]; - if (!(firstHandler instanceof ClassRoutingHandler)) { - // nothing we can do - return; - } - - var classRoutingHandler = (ClassRoutingHandler) firstHandler; - var mappers = classRoutingHandler.getMappers(); - - var requestMethod = rc.request().method().name(); - - // do what ClassRoutingHandler does - var mapper = mappers.get(requestMethod); - if (mapper == null) { - if (requestMethod.equals(HttpMethod.HEAD) || requestMethod.equals(HttpMethod.OPTIONS)) { - mapper = mappers.get(HttpMethod.GET); - } - if (mapper == null) { - mapper = mappers.get(null); - } - if (mapper == null) { - // can't match the path - return; - } + public static void setTemplatePath(RoutingContext rc, Deployment deployment) { + // do what RestInitialHandler does + var initMappers = new RequestMapper<>(deployment.getClassMappers()); + var requestMatch = initMappers.map(getPathWithoutPrefix(rc, deployment)); + if (requestMatch == null) { + return; + } + var remaining = requestMatch.remaining.isEmpty() ? "/" : requestMatch.remaining; + + var serverRestHandlers = requestMatch.value.handlers; + if (serverRestHandlers == null || serverRestHandlers.length < 1) { + // nothing we can do + return; + } + var firstHandler = serverRestHandlers[0]; + if (!(firstHandler instanceof ClassRoutingHandler)) { + // nothing we can do + return; + } + + var classRoutingHandler = (ClassRoutingHandler) firstHandler; + var mappers = classRoutingHandler.getMappers(); + + var requestMethod = rc.request().method().name(); + + // do what ClassRoutingHandler does + var mapper = mappers.get(requestMethod); + if (mapper == null) { + if (requestMethod.equals(HttpMethod.HEAD) || requestMethod.equals(HttpMethod.OPTIONS)) { + mapper = mappers.get(HttpMethod.GET); + } + if (mapper == null) { + mapper = mappers.get(null); + } + if (mapper == null) { + // can't match the path + return; + } + } + var target = mapper.map(remaining); + if (target == null) { + if (requestMethod.equals(HttpMethod.HEAD)) { + mapper = mappers.get(HttpMethod.GET); + if (mapper != null) { + target = mapper.map(remaining); } - var target = mapper.map(remaining); - if (target == null) { - if (requestMethod.equals(HttpMethod.HEAD)) { - mapper = mappers.get(HttpMethod.GET); - if (mapper != null) { - target = mapper.map(remaining); - } - } + } - if (target == null) { - // can't match the path - return; - } - } + if (target == null) { + // can't match the path + return; + } + } - var templatePath = requestMatch.template.template + target.template.template; - if (templatePath.endsWith("/")) { - templatePath = templatePath.substring(0, templatePath.length() - 1); - } + var templatePath = requestMatch.template.template + target.template.template; + if (templatePath.endsWith("/")) { + templatePath = templatePath.substring(0, templatePath.length() - 1); + } - setUrlPathTemplate(rc, templatePath); - } + setUrlPathTemplate(rc, templatePath); + } - public String getPath(RoutingContext rc) { - return rc.normalizedPath(); - } + private static String getPath(RoutingContext rc) { + return rc.normalizedPath(); + } - public String getPathWithoutPrefix(RoutingContext rc, Deployment deployment) { - String path = getPath(rc); - if (path != null) { - String prefix = deployment.getPrefix(); - if (!prefix.isEmpty()) { - if (path.startsWith(prefix)) { - return path.substring(prefix.length()); - } - } + private static String getPathWithoutPrefix(RoutingContext rc, Deployment deployment) { + String path = getPath(rc); + if (path != null) { + String prefix = deployment.getPrefix(); + if (!prefix.isEmpty()) { + if (path.startsWith(prefix)) { + return path.substring(prefix.length()); } - return path; } - }; + } + return path; } } From 73745f17e92d09d580d3ab866c0c97cb0454fe91 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 29 Apr 2024 08:18:33 +0300 Subject: [PATCH 20/60] Introduce new micrometer and security integration test module (cherry picked from commit dcbcdbdbe4164fda0717e171a56d2b7981cfd5fc) --- integration-tests/micrometer-security/pom.xml | 107 ++++++++++++++++++ .../micrometer/security/SecuredResource.java | 20 ++++ .../src/main/resources/application.properties | 2 + .../security/SecuredResourceTest.java | 32 ++++++ integration-tests/pom.xml | 1 + 5 files changed, 162 insertions(+) create mode 100644 integration-tests/micrometer-security/pom.xml create mode 100644 integration-tests/micrometer-security/src/main/java/io/quarkus/it/micrometer/security/SecuredResource.java create mode 100644 integration-tests/micrometer-security/src/main/resources/application.properties create mode 100644 integration-tests/micrometer-security/src/test/java/io/quarkus/it/micrometer/security/SecuredResourceTest.java diff --git a/integration-tests/micrometer-security/pom.xml b/integration-tests/micrometer-security/pom.xml new file mode 100644 index 0000000000000..9422bf687a856 --- /dev/null +++ b/integration-tests/micrometer-security/pom.xml @@ -0,0 +1,107 @@ + + + 4.0.0 + + + io.quarkus + quarkus-integration-tests-parent + 999-SNAPSHOT + + + quarkus-integration-test-micrometer-security + Quarkus - Integration Tests - Micrometer Security + + + + io.quarkus + quarkus-micrometer-registry-prometheus + + + + + io.quarkus + quarkus-rest-jackson + + + + + io.quarkus + quarkus-security + + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + io.quarkus + quarkus-micrometer-registry-prometheus-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-rest-jackson-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-security-deployment + ${project.version} + pom + test + + + * + * + + + + + org.awaitility + awaitility + test + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + + + diff --git a/integration-tests/micrometer-security/src/main/java/io/quarkus/it/micrometer/security/SecuredResource.java b/integration-tests/micrometer-security/src/main/java/io/quarkus/it/micrometer/security/SecuredResource.java new file mode 100644 index 0000000000000..2b135170ac259 --- /dev/null +++ b/integration-tests/micrometer-security/src/main/java/io/quarkus/it/micrometer/security/SecuredResource.java @@ -0,0 +1,20 @@ +package io.quarkus.it.micrometer.security; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import io.smallrye.mutiny.Uni; + +@Path("/secured") +public class SecuredResource { + + @GET + @Path("/{message}") + @Produces(MediaType.TEXT_PLAIN) + public Uni message(@PathParam("message") String message) { + return Uni.createFrom().item(message); + } +} diff --git a/integration-tests/micrometer-security/src/main/resources/application.properties b/integration-tests/micrometer-security/src/main/resources/application.properties new file mode 100644 index 0000000000000..74f64a800e5bf --- /dev/null +++ b/integration-tests/micrometer-security/src/main/resources/application.properties @@ -0,0 +1,2 @@ +quarkus.http.auth.permission.default.paths=/secured/* +quarkus.http.auth.permission.default.policy=authenticated diff --git a/integration-tests/micrometer-security/src/test/java/io/quarkus/it/micrometer/security/SecuredResourceTest.java b/integration-tests/micrometer-security/src/test/java/io/quarkus/it/micrometer/security/SecuredResourceTest.java new file mode 100644 index 0000000000000..dfed5aab9f601 --- /dev/null +++ b/integration-tests/micrometer-security/src/test/java/io/quarkus/it/micrometer/security/SecuredResourceTest.java @@ -0,0 +1,32 @@ +package io.quarkus.it.micrometer.security; + +import static io.restassured.RestAssured.when; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.not; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +class SecuredResourceTest { + + @Test + void testMetricsForUnauthorizedRequest() { + when().get("/secured/foo") + .then() + .statusCode(403); + + when().get("/q/metrics") + .then() + .statusCode(200) + .body( + allOf( + not(containsString("/secured/foo")), + containsString("/secured/{message}")) + + ); + } + +} diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index a17b168d5cd73..dd30b914547f4 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -345,6 +345,7 @@ elasticsearch-java-client micrometer-mp-metrics micrometer-prometheus + micrometer-security opentelemetry opentelemetry-quickstart opentelemetry-spi From ca2224cea62379ab0a993a2f302b774b15d9c0cb Mon Sep 17 00:00:00 2001 From: Lin Gao Date: Mon, 29 Apr 2024 20:09:19 +0800 Subject: [PATCH 21/60] [issue:40341] [Doc] pseudo code in writing-extensions guide does not match the description Signed-off-by: Lin Gao (cherry picked from commit f1bb65a6c0f0ce29b8557706338ff22651f0c51f) --- docs/src/main/asciidoc/writing-extensions.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/main/asciidoc/writing-extensions.adoc b/docs/src/main/asciidoc/writing-extensions.adoc index 7bf884802e9b4..b9d0f647a2530 100644 --- a/docs/src/main/asciidoc/writing-extensions.adoc +++ b/docs/src/main/asciidoc/writing-extensions.adoc @@ -823,8 +823,8 @@ They represent build items with validation errors that make the build fail. Thes ---- @BuildStep void checkCompatibility(Capabilities capabilities, BuildProducer validationErrors) { - if (capabilities.isMissing(Capability.RESTEASY_REACTIVE) - && capabilities.isMissing(Capability.RESTEASY_CLASSIC)) { + if (capabilities.isPresent(Capability.RESTEASY_REACTIVE) + && capabilities.isPresent(Capability.RESTEASY)) { validationErrors.produce(new ValidationErrorBuildItem( new ConfigurationException("Cannot use both RESTEasy Classic and Reactive extensions at the same time"))); } From 57cb39c999e8d579c0c612a5de4c9552017d2423 Mon Sep 17 00:00:00 2001 From: Jan Martiska Date: Mon, 29 Apr 2024 15:18:12 +0200 Subject: [PATCH 22/60] Upgrade to SmallRye GraphQL 2.8.3 (cherry picked from commit bb5f27c4249f99814fdfd99b740b3fe37598a0d2) --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 68fea431b350c..1a9af5ece3c89 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -55,7 +55,7 @@ 4.1.0 4.0.0 3.10.0 - 2.8.2 + 2.8.3 6.3.0 4.5.1 2.1.0 From 519b1b052bb46b12e566938848254a8bd02206e1 Mon Sep 17 00:00:00 2001 From: Aleksandr Nichiporuk Date: Tue, 30 Apr 2024 12:19:58 +0300 Subject: [PATCH 23/60] Update init-tasks.adoc (cherry picked from commit fa38c1cd07cb4c01689ba4a4b54bace4f1423086) --- docs/src/main/asciidoc/init-tasks.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/init-tasks.adoc b/docs/src/main/asciidoc/init-tasks.adoc index 939ae65a79c29..6c750cc087955 100644 --- a/docs/src/main/asciidoc/init-tasks.adoc +++ b/docs/src/main/asciidoc/init-tasks.adoc @@ -62,7 +62,7 @@ For Liquibase: [source,properties] ---- -quarkus.kubernets.init-tasks.liquibase.enabled=false +quarkus.kubernetes.init-tasks.liquibase.enabled=false ---- For Liquibase Mongodb: From 70d26f174e1164b840171f652a0c37b083105576 Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Fri, 26 Apr 2024 22:17:06 +0100 Subject: [PATCH 24/60] Always register discovered services from SmallRye Config in STATIC INIT (cherry picked from commit 3bc2f38714b55a009ece3bf6fc47568b173405be) --- .../quarkus/deployment/steps/ConfigGenerationBuildStep.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java index a84ccdd1a0fda..130d382c3718b 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/ConfigGenerationBuildStep.java @@ -759,6 +759,12 @@ private static Set staticSafeServices(Set services) { ClassLoader classloader = Thread.currentThread().getContextClassLoader(); Set staticSafe = new HashSet<>(); for (String service : services) { + // SmallRye Config services are always safe, but they cannot be annotated with @StaticInitSafe + if (service.startsWith("io.smallrye.config.")) { + staticSafe.add(service); + continue; + } + try { Class serviceClass = classloader.loadClass(service); if (serviceClass.isAnnotationPresent(StaticInitSafe.class)) { From 875d4f61f2a1cc86400afce31dc114daf7363cd4 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Tue, 30 Apr 2024 17:27:35 +1000 Subject: [PATCH 25/60] Fix servlet error mapping (cherry picked from commit 152453790fbfce46a51bfe81a7d825957c9708d5) --- .../undertow/deployment/UndertowBuildStep.java | 11 +++++++++++ .../undertow/test/ServletWebXmlTestCase.java | 13 ++++++++++++- .../runtime/UndertowDeploymentRecorder.java | 10 ++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/extensions/undertow/deployment/src/main/java/io/quarkus/undertow/deployment/UndertowBuildStep.java b/extensions/undertow/deployment/src/main/java/io/quarkus/undertow/deployment/UndertowBuildStep.java index 415204b0d4fc2..98a1a8dbc3d2d 100644 --- a/extensions/undertow/deployment/src/main/java/io/quarkus/undertow/deployment/UndertowBuildStep.java +++ b/extensions/undertow/deployment/src/main/java/io/quarkus/undertow/deployment/UndertowBuildStep.java @@ -61,6 +61,7 @@ import org.jboss.metadata.web.spec.CookieConfigMetaData; import org.jboss.metadata.web.spec.DispatcherType; import org.jboss.metadata.web.spec.EmptyRoleSemanticType; +import org.jboss.metadata.web.spec.ErrorPageMetaData; import org.jboss.metadata.web.spec.FilterMappingMetaData; import org.jboss.metadata.web.spec.FilterMetaData; import org.jboss.metadata.web.spec.FiltersMetaData; @@ -651,6 +652,16 @@ public ServletDeploymentManagerBuildItem build(List servlets, recorder.addServletContainerInitializer(deployment, (Class) context.classProxy(sci.sciClass), handlesTypes); } + if (webMetaData.getErrorPages() != null) { + for (ErrorPageMetaData errorPage : webMetaData.getErrorPages()) { + if (errorPage.getErrorCode() != null && !errorPage.getErrorCode().isBlank()) { + recorder.addErrorPage(deployment, errorPage.getLocation(), Integer.parseInt(errorPage.getErrorCode())); + } else if (errorPage.getExceptionType() != null && !errorPage.getExceptionType().isBlank()) { + recorder.addErrorPage(deployment, errorPage.getLocation(), + (Class) context.classProxy(errorPage.getExceptionType())); + } + } + } SessionConfigMetaData sessionConfig = webMetaData.getSessionConfig(); if (sessionConfig != null) { if (sessionConfig.getSessionTimeoutSet()) { diff --git a/extensions/undertow/deployment/src/test/java/io/quarkus/undertow/test/ServletWebXmlTestCase.java b/extensions/undertow/deployment/src/test/java/io/quarkus/undertow/test/ServletWebXmlTestCase.java index 30b0e7c7122d6..64ca9d88894fc 100644 --- a/extensions/undertow/deployment/src/test/java/io/quarkus/undertow/test/ServletWebXmlTestCase.java +++ b/extensions/undertow/deployment/src/test/java/io/quarkus/undertow/test/ServletWebXmlTestCase.java @@ -33,7 +33,11 @@ public class ServletWebXmlTestCase { " \n" + " wasm\n" + " application/wasm\n" + - " " + + " \n" + + " \n" + + " 404\n" + + " /mapped\n" + + " \n" + ""; @RegisterExtension @@ -56,4 +60,11 @@ public void testMimeMapping() { .statusCode(200) .contentType(is("application/wasm")); } + + @Test + public void test404Mapping() { + RestAssured.when().get("/missing").then() + .statusCode(404) + .body(is("web xml servlet")); + } } diff --git a/extensions/undertow/runtime/src/main/java/io/quarkus/undertow/runtime/UndertowDeploymentRecorder.java b/extensions/undertow/runtime/src/main/java/io/quarkus/undertow/runtime/UndertowDeploymentRecorder.java index 082431efc6a9e..a0ccfe2a2d83d 100644 --- a/extensions/undertow/runtime/src/main/java/io/quarkus/undertow/runtime/UndertowDeploymentRecorder.java +++ b/extensions/undertow/runtime/src/main/java/io/quarkus/undertow/runtime/UndertowDeploymentRecorder.java @@ -747,6 +747,16 @@ public void setSessionCookieConfig(ServletSessionConfig config, String name, Str } } + public void addErrorPage(RuntimeValue deployment, String location, int errorCode) { + deployment.getValue().addErrorPage(new ErrorPage(location, errorCode)); + + } + + public void addErrorPage(RuntimeValue deployment, String location, + Class exceptionType) { + deployment.getValue().addErrorPage(new ErrorPage(location, exceptionType)); + } + /** * we can't have SecureRandom in the native image heap, so we need to lazy init */ From 7ed1eb00bb5ba58c322b905b2073d7653f039b74 Mon Sep 17 00:00:00 2001 From: "David M. Lloyd" Date: Thu, 25 Apr 2024 07:42:05 -0500 Subject: [PATCH 26/60] Fix accidental config breakage of `quarkus.package.decompiler.*` properties This was missed in #39295. Fixes #40272. (cherry picked from commit 21ce6780012af9033500cdbffc7452ab35efef9c) --- .../configuration/ConfigCompatibility.java | 57 +++++++++++-------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/configuration/ConfigCompatibility.java b/core/deployment/src/main/java/io/quarkus/deployment/configuration/ConfigCompatibility.java index 20b1853ae0c17..8710a0211c4cb 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/configuration/ConfigCompatibility.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/configuration/ConfigCompatibility.java @@ -55,12 +55,18 @@ public final class ConfigCompatibility { ConfigCompatibility::quarkusPackageIncludedOptionalDependencies), entry(List.of("quarkus", "package", "include-dependency-list"), ConfigCompatibility::quarkusPackageIncludeDependencyList), + entry(List.of("quarkus", "package", "decompiler", "version"), + ConfigCompatibility::quarkusPackageDecompilerVersion), + entry(List.of("quarkus", "package", "decompiler", "enabled"), + ConfigCompatibility::quarkusPackageDecompilerEnabled), + entry(List.of("quarkus", "package", "decompiler", "jar-directory"), + ConfigCompatibility::quarkusPackageDecompilerJarDirectory), entry(List.of("quarkus", "package", "vineflower", "version"), - ConfigCompatibility::quarkusPackageVineflowerVersion), + ConfigCompatibility::quarkusPackageDecompilerVersion), entry(List.of("quarkus", "package", "vineflower", "enabled"), - ConfigCompatibility::quarkusPackageVineflowerEnabled), + ConfigCompatibility::quarkusPackageDecompilerEnabled), entry(List.of("quarkus", "package", "vineflower", "jar-directory"), - ConfigCompatibility::quarkusPackageVineflowerJarDirectory), + ConfigCompatibility::quarkusPackageDecompilerJarDirectory), entry(List.of("quarkus", "package", "manifest", "attributes", "*"), ConfigCompatibility::quarkusPackageManifestAttributes), entry(List.of("quarkus", "package", "manifest", "sections", "*", "*"), @@ -102,9 +108,10 @@ public final class ConfigCompatibility { ConfigCompatibility::quarkusPackageJarManifestSections), entry(List.of("quarkus", "package", "jar", "manifest", "add-implementation-entries"), ConfigCompatibility::quarkusPackageJarManifestAddImplementationEntries), - entry(List.of("quarkus", "package", "decompiler", "enabled"), ConfigCompatibility::quarkusPackageDecompilerEnabled), - entry(List.of("quarkus", "package", "decompiler", "jar-directory"), - ConfigCompatibility::quarkusPackageDecompilerJarDirectory)); + entry(List.of("quarkus", "package", "jar", "decompiler", "enabled"), + ConfigCompatibility::quarkusPackageJarDecompilerEnabled), + entry(List.of("quarkus", "package", "jar", "decompiler", "jar-directory"), + ConfigCompatibility::quarkusPackageJarDecompilerJarDirectory)); /** * The interceptor at the front of the chain which handles hiding deprecated properties from the iterator. @@ -271,17 +278,17 @@ private static List quarkusPackageIncludedOptionalDependencies(ConfigSou return List.of("quarkus.package.jar.included-optional-dependencies"); } - private static List quarkusPackageVineflowerVersion(ConfigSourceInterceptorContext ctxt, NameIterator ni) { + private static List quarkusPackageDecompilerVersion(ConfigSourceInterceptorContext ctxt, NameIterator ni) { // always hide this ignored property return List.of(); } - private static List quarkusPackageVineflowerEnabled(ConfigSourceInterceptorContext ctxt, NameIterator ni) { + private static List quarkusPackageDecompilerEnabled(ConfigSourceInterceptorContext ctxt, NameIterator ni) { // simple mapping to a new name return List.of("quarkus.package.decompiler.enabled"); } - private static List quarkusPackageVineflowerJarDirectory(ConfigSourceInterceptorContext ctxt, NameIterator ni) { + private static List quarkusPackageDecompilerJarDirectory(ConfigSourceInterceptorContext ctxt, NameIterator ni) { // simple mapping to a new name return List.of("quarkus.package.decompiler.jar-directory"); } @@ -500,26 +507,30 @@ private static ConfigValue quarkusPackageJarManifestAddImplementationEntries(Con } } - private static ConfigValue quarkusPackageDecompilerEnabled(ConfigSourceInterceptorContext ctxt, NameIterator ni) { - ConfigValue oldVal = ctxt.restart("quarkus.package.vineflower.enabled"); + private static ConfigValue quarkusPackageJarDecompilerEnabled(ConfigSourceInterceptorContext ctxt, NameIterator ni) { + ConfigValue oldVal = ctxt.restart("quarkus.package.decompiler.enabled"); if (oldVal == null) { - // on to the default value - return ctxt.proceed(ni.getName()); - } else { - // map old name to new name - return oldVal.withName(ni.getName()); + oldVal = ctxt.restart("quarkus.package.vineflower.enabled"); + if (oldVal == null) { + // on to the default value + return ctxt.proceed(ni.getName()); + } } + // map old name to new name + return oldVal.withName(ni.getName()); } - private static ConfigValue quarkusPackageDecompilerJarDirectory(ConfigSourceInterceptorContext ctxt, NameIterator ni) { - ConfigValue oldVal = ctxt.restart("quarkus.package.vineflower.jar-directory"); + private static ConfigValue quarkusPackageJarDecompilerJarDirectory(ConfigSourceInterceptorContext ctxt, NameIterator ni) { + ConfigValue oldVal = ctxt.restart("quarkus.package.decompiler.jar-directory"); if (oldVal == null) { - // on to the default value - return ctxt.proceed(ni.getName()); - } else { - // map old name to new name - return oldVal.withName(ni.getName()); + oldVal = ctxt.restart("quarkus.package.vineflower.jar-directory"); + if (oldVal == null) { + // on to the default value + return ctxt.proceed(ni.getName()); + } } + // map old name to new name + return oldVal.withName(ni.getName()); } // utilities From 6f45e378983d6a0aa85c9d57ae4807607636cd27 Mon Sep 17 00:00:00 2001 From: Juan Zuriaga Date: Sun, 21 Apr 2024 19:10:35 +0200 Subject: [PATCH 27/60] Update occupied port detection command Change deprecated busy port detection command with the utility to dump socket statistics. (cherry picked from commit b0eacd49fad71666d4ce3e6352b8dbea0251a5cb) --- .../java/io/quarkus/runtime/ApplicationLifecycleManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/runtime/src/main/java/io/quarkus/runtime/ApplicationLifecycleManager.java b/core/runtime/src/main/java/io/quarkus/runtime/ApplicationLifecycleManager.java index 4c7ed149e1737..a410463dba571 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/ApplicationLifecycleManager.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/ApplicationLifecycleManager.java @@ -183,7 +183,8 @@ public static void run(Application application, Class'."); } From e1abaa7fe1cb193483cc9f2da3667c7fb6c1d92c Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Wed, 1 May 2024 10:13:22 +1000 Subject: [PATCH 28/60] Don't close connection if response is sent Fixes #37323 (cherry picked from commit 23f07ab556adc8980bfc66bc3a14ee8d2771da19) --- .../reactive/server/core/ResteasyReactiveRequestContext.java | 4 +++- .../server/vertx/VertxResteasyReactiveRequestContext.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java index 657599ca1a957..4d288748ef627 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java @@ -640,7 +640,9 @@ protected void handleUnrecoverableError(Throwable throwable) { protected void endResponse() { if (serverResponse().headWritten()) { - serverRequest().closeConnection(); + if (!serverResponse().closed()) { + serverRequest().closeConnection(); + } } else { serverResponse().setStatusCode(500).end(); } diff --git a/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxResteasyReactiveRequestContext.java b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxResteasyReactiveRequestContext.java index 0e02e22bb1b00..d6a097ed92312 100644 --- a/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxResteasyReactiveRequestContext.java +++ b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxResteasyReactiveRequestContext.java @@ -406,7 +406,7 @@ public void removeResponseHeader(String name) { @Override public boolean closed() { - return response.closed(); + return response.ended() || response.closed(); } @Override From f79f9e9f24c353da5e4d5973da64ea1cc1f65ec6 Mon Sep 17 00:00:00 2001 From: Max Rydahl Andersen Date: Wed, 1 May 2024 10:31:43 +0200 Subject: [PATCH 29/60] do not conflate quickstarts with codestarts (cherry picked from commit 9e484f21e0b294ef1d8c1e1f28a42798c899f491) --- docs/src/main/asciidoc/extension-codestart.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/extension-codestart.adoc b/docs/src/main/asciidoc/extension-codestart.adoc index 891a6e61da066..3d5dba084ee1d 100644 --- a/docs/src/main/asciidoc/extension-codestart.adoc +++ b/docs/src/main/asciidoc/extension-codestart.adoc @@ -13,7 +13,7 @@ This guide explains how to create and configure a Quarkus Codestart for an exten == Description -"Extension Codestarts" is the name we give to our Quarkus extension quickstart code generation system. It aims to provide a personalized getting started experience with Quarkus. +"Extension Codestarts" is the name we give to our Quarkus extension "getting started" code generation system. It aims to provide a personalized getting started experience with Quarkus. A Quarkus extension is able to provide one or more well-defined codestarts which will contain the resources and code required/recommended to start using that particular extension. Extension codestarts are applied by default when using the Quarkus tooling (if the chosen extensions contain any): From 79e23a35439a353c2f63b7971f50109c7d944235 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Wed, 1 May 2024 09:24:59 +1000 Subject: [PATCH 30/60] Allow ClassLoader to return multiple resources Previously if the resources were in the same PathTree only one would be returned. Fixes #40371 (cherry picked from commit 9daa467417b84c812ee04da5d909c79d5a729580) --- .../io/quarkus/paths/CachingPathTree.java | 5 ++++ .../io/quarkus/paths/FilteredPathTree.java | 9 ++++++ .../io/quarkus/paths/MultiRootPathTree.java | 20 +++++++++++++ .../main/java/io/quarkus/paths/PathTree.java | 14 +++++++++ .../quarkus/paths/SharedArchivePathTree.java | 5 ++++ .../classloading/ClassPathElement.java | 5 ++++ .../PathTreeClassPathElement.java | 22 ++++++++++++++ .../classloading/QuarkusClassLoader.java | 29 +++++++++++-------- .../io/quarkus/it/extension/my_resource.txt | 0 .../quarkus/it/extension/ClassLoaderTest.java | 29 +++++++++++++++++++ 10 files changed, 126 insertions(+), 12 deletions(-) create mode 100644 integration-tests/test-extension/tests/src/main/resources/io/quarkus/it/extension/my_resource.txt create mode 100644 integration-tests/test-extension/tests/src/test/java/io/quarkus/it/extension/ClassLoaderTest.java diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/CachingPathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/CachingPathTree.java index a2349c3024766..47bde335702f4 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/CachingPathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/CachingPathTree.java @@ -87,6 +87,11 @@ public void accept(String relativePath, Consumer func) { delegate.accept(relativePath, func); } + @Override + public void acceptAll(String relativePath, Consumer func) { + delegate.acceptAll(relativePath, func); + } + @Override public boolean contains(String relativePath) { final LinkedHashMap snapshot = walkSnapshot; diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilteredPathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilteredPathTree.java index 11e125a04fbf1..8265ef59771f1 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilteredPathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilteredPathTree.java @@ -54,6 +54,15 @@ public void accept(String relativePath, Consumer consumer) { } } + @Override + public void acceptAll(String relativePath, Consumer consumer) { + if (!PathFilter.isVisible(filter, relativePath)) { + consumer.accept(null); + } else { + original.acceptAll(relativePath, consumer); + } + } + @Override public boolean contains(String relativePath) { return PathFilter.isVisible(filter, relativePath) && original.contains(relativePath); diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/MultiRootPathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/MultiRootPathTree.java index 4681cec199deb..e09b70ce12168 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/MultiRootPathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/MultiRootPathTree.java @@ -86,6 +86,26 @@ public void accept(PathVisit t) { } } + @Override + public void acceptAll(String relativePath, Consumer func) { + final AtomicBoolean consumed = new AtomicBoolean(); + final Consumer wrapper = new Consumer<>() { + @Override + public void accept(PathVisit t) { + if (t != null) { + func.accept(t); + consumed.set(true); + } + } + }; + for (PathTree tree : trees) { + tree.accept(relativePath, wrapper); + } + if (!consumed.get()) { + func.accept(null); + } + } + @Override public boolean contains(String relativePath) { for (PathTree tree : trees) { diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTree.java index b126db5924d26..5b68db900bc93 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTree.java @@ -134,6 +134,20 @@ default boolean isEmpty() { */ void accept(String relativePath, Consumer consumer); + /** + * Consumes a given path relative to the root of the tree. + * If the path isn't found in the tree, the {@link PathVisit} argument + * passed to the consumer will be {@code null}. + * + * If multiple items match then the consumer will be called multiple times. + * + * @param relativePath relative path to consume + * @param consumer path consumer + */ + default void acceptAll(String relativePath, Consumer consumer) { + accept(relativePath, consumer); + } + /** * Checks whether the tree contains a relative path. * diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/SharedArchivePathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/SharedArchivePathTree.java index b5cd7e9cdd3a8..1206da6cc618c 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/SharedArchivePathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/SharedArchivePathTree.java @@ -158,6 +158,11 @@ public void accept(String relativePath, Consumer consumer) { delegate.accept(relativePath, consumer); } + @Override + public void acceptAll(String relativePath, Consumer consumer) { + delegate.acceptAll(relativePath, consumer); + } + @Override public boolean contains(String relativePath) { return delegate.contains(relativePath); diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/ClassPathElement.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/ClassPathElement.java index 53723350b5476..a76d68963b328 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/ClassPathElement.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/ClassPathElement.java @@ -4,6 +4,7 @@ import java.nio.file.Path; import java.security.ProtectionDomain; import java.util.Collections; +import java.util.List; import java.util.Set; import java.util.function.Function; import java.util.jar.Manifest; @@ -141,4 +142,8 @@ public void close() { } }; + + default List getResources(String name) { + return List.of(getResource(name)); + } } diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/PathTreeClassPathElement.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/PathTreeClassPathElement.java index 17dab87f95563..5d6ca6c91855e 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/PathTreeClassPathElement.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/PathTreeClassPathElement.java @@ -12,8 +12,10 @@ import java.security.CodeSource; import java.security.ProtectionDomain; import java.security.cert.Certificate; +import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; @@ -106,6 +108,26 @@ public ClassPathResource getResource(String name) { return apply(tree -> tree.apply(sanitized, visit -> visit == null ? null : new Resource(visit))); } + @Override + public List getResources(String name) { + final String sanitized = sanitize(name); + final Set resources = this.resources; + if (resources != null && !resources.contains(sanitized)) { + return null; + } + List ret = new ArrayList<>(); + apply(tree -> { + tree.acceptAll(sanitized, visit -> { + if (visit != null) { + ret.add(new Resource(visit)); + + } + }); + return null; + }); + return ret; + } + @Override public T apply(Function func) { lock.readLock().lock(); diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java index e1b20fe85d657..1f1c3bf2c1cdf 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java @@ -11,6 +11,7 @@ import java.sql.Driver; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; @@ -246,27 +247,31 @@ public Enumeration getResources(String unsanitisedName, boolean parentAlrea if (providers != null) { boolean endsWithTrailingSlash = unsanitisedName.endsWith("/"); for (ClassPathElement element : providers) { - ClassPathResource res = element.getResource(name); + Collection resList = element.getResources(name); //if the requested name ends with a trailing / we make sure //that the resource is a directory, and return a URL that ends with a / //this matches the behaviour of URLClassLoader - if (endsWithTrailingSlash) { - if (res.isDirectory()) { - try { - resources.add(new URL(res.getUrl().toString() + "/")); - } catch (MalformedURLException e) { - throw new RuntimeException(e); + for (var res : resList) { + if (endsWithTrailingSlash) { + if (res.isDirectory()) { + try { + resources.add(new URL(res.getUrl().toString() + "/")); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } } + } else { + resources.add(res.getUrl()); } - } else { - resources.add(res.getUrl()); } } } else if (name.isEmpty()) { for (ClassPathElement i : elements) { - ClassPathResource res = i.getResource(""); - if (res != null) { - resources.add(res.getUrl()); + List resList = i.getResources(""); + for (var res : resList) { + if (res != null) { + resources.add(res.getUrl()); + } } } } diff --git a/integration-tests/test-extension/tests/src/main/resources/io/quarkus/it/extension/my_resource.txt b/integration-tests/test-extension/tests/src/main/resources/io/quarkus/it/extension/my_resource.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/integration-tests/test-extension/tests/src/test/java/io/quarkus/it/extension/ClassLoaderTest.java b/integration-tests/test-extension/tests/src/test/java/io/quarkus/it/extension/ClassLoaderTest.java new file mode 100644 index 0000000000000..b863c448676ee --- /dev/null +++ b/integration-tests/test-extension/tests/src/test/java/io/quarkus/it/extension/ClassLoaderTest.java @@ -0,0 +1,29 @@ +package io.quarkus.it.extension; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class ClassLoaderTest { + + @Test + void testClassLoaderResources() throws IOException { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + ArrayList resources = Collections.list(contextClassLoader.getResources("io/quarkus/it/extension")); + Assertions.assertEquals(2, resources.size()); + } + + @Test + void testClassLoaderSingleResource() throws IOException { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + URL resource = contextClassLoader.getResource("io/quarkus/it/extension/my_resource.txt"); + Assertions.assertNotNull(resource); + } +} From 296d776f89ec1cefa50b69ef997b5332b661ec72 Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Wed, 1 May 2024 22:02:56 +0200 Subject: [PATCH 31/60] Fix multi rooted path tree scanning in the Qute processor (cherry picked from commit 195deabfaeaead98db8d779a44313d797d655426) --- .../qute/deployment/QuteProcessor.java | 24 +++++-------------- .../main/java/io/quarkus/paths/PathVisit.java | 10 ++++++++ 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java index f7430ed6b1733..6483c7e8163f5 100644 --- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java +++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java @@ -14,7 +14,6 @@ import java.io.UncheckedIOException; import java.lang.reflect.Modifier; import java.nio.charset.Charset; -import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -2170,7 +2169,7 @@ private void scanPathTree(PathTree pathTree, TemplateRootsBuildItem templateRoot // if template root is found in this tree then walk over its subtree scanTemplateRootSubtree( new FilteredPathTree(pathTree, PathFilter.forIncludes(List.of(templateRoot + "/**"))), - visit.getPath(), watchedPaths, templatePaths, nativeImageResources, config); + visit.getRelativePath(), watchedPaths, templatePaths, nativeImageResources, config); } }); } @@ -3383,7 +3382,7 @@ private static void produceTemplateBuildItems(BuildProducer watchedPaths, BuildProducer templatePaths, BuildProducer nativeImageResources, @@ -3391,30 +3390,19 @@ private void scanTemplateRootSubtree(PathTree pathTree, Path templateRoot, pathTree.walk(visit -> { if (Files.isRegularFile(visit.getPath())) { LOGGER.debugf("Found template: %s", visit.getPath()); - String templatePath = toOsAgnosticPath(templateRoot.relativize(visit.getPath())); + // remove templateRoot + / + final String relativePath = visit.getRelativePath(); + String templatePath = relativePath.substring(templateRoot.length() + 1); if (config.templatePathExclude.matcher(templatePath).matches()) { LOGGER.debugf("Template file excluded: %s", visit.getPath()); return; } produceTemplateBuildItems(templatePaths, watchedPaths, nativeImageResources, - visit.getRelativePath("/"), - templatePath, visit.getPath(), config); + relativePath, templatePath, visit.getPath(), config); } }); } - private static String toOsAgnosticPath(String path, FileSystem fs) { - String separator = fs.getSeparator(); - if (!separator.equals("/")) { - path = path.replace(separator, "/"); - } - return path; - } - - private static String toOsAgnosticPath(Path path) { - return toOsAgnosticPath(path.toString(), path.getFileSystem()); - } - private static boolean isExcluded(TypeCheck check, Iterable> excludes) { for (Predicate exclude : excludes) { if (exclude.test(check)) { diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathVisit.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathVisit.java index 139560a3d40f1..5084405c4bdcf 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathVisit.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathVisit.java @@ -44,6 +44,16 @@ default URL getUrl() { } } + /** + * Path relative to the root of the tree as a string with {@code /} as a path element separator. + * This method calls {@link #getRelativePath(String)} passing {@code /} as an argument. + * + * @return path relative to the root of the tree as a string with {@code /} as a path element separator + */ + default String getRelativePath() { + return getRelativePath("/"); + } + /** * Path relative to the root of the tree as a string with a provided path element separator. * For a {@link PathTree} created for an archive, the returned path will be relative to the root From 2492eff9c474e8634eba12e67affd9a892e27eba Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 1 May 2024 16:49:05 +0300 Subject: [PATCH 32/60] Fix Javadoc of @ClientObjectMapper (cherry picked from commit b8f31ec553634e0ddf25be794eefa699bdced99d) --- .../rest/client/reactive/jackson/ClientObjectMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/resteasy-reactive/rest-client-jackson/runtime/src/main/java/io/quarkus/rest/client/reactive/jackson/ClientObjectMapper.java b/extensions/resteasy-reactive/rest-client-jackson/runtime/src/main/java/io/quarkus/rest/client/reactive/jackson/ClientObjectMapper.java index a5e92124e6f0c..c4030926ecf50 100644 --- a/extensions/resteasy-reactive/rest-client-jackson/runtime/src/main/java/io/quarkus/rest/client/reactive/jackson/ClientObjectMapper.java +++ b/extensions/resteasy-reactive/rest-client-jackson/runtime/src/main/java/io/quarkus/rest/client/reactive/jackson/ClientObjectMapper.java @@ -17,7 +17,7 @@ * *
  * {@code
- * @ClientObjectMapper
+ * @ClientObjectMapper
  * static ObjectMapper objectMapper() {
  *     return new ObjectMapper();
  * }

From 62f1ae27b1ef349ef074ea6766d55ee4733302ae Mon Sep 17 00:00:00 2001
From: George Gastaldi 
Date: Tue, 30 Apr 2024 19:19:03 -0300
Subject: [PATCH 33/60] Avoid classes with incomplete hierarchy in Hibernate
 Validator

(cherry picked from commit 4c575b3a06a8b58f63c4751495d9861928575142)
---
 .../validator/test/ClassHierarchyTest.java    | 63 +++++++++++++++++++
 .../runtime/HibernateValidatorRecorder.java   | 20 ++++++
 2 files changed, 83 insertions(+)
 create mode 100644 extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/ClassHierarchyTest.java

diff --git a/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/ClassHierarchyTest.java b/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/ClassHierarchyTest.java
new file mode 100644
index 0000000000000..a9dd211d283b9
--- /dev/null
+++ b/extensions/hibernate-validator/deployment/src/test/java/io/quarkus/hibernate/validator/test/ClassHierarchyTest.java
@@ -0,0 +1,63 @@
+package io.quarkus.hibernate.validator.test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.AbstractCollection;
+
+import jakarta.inject.Inject;
+import jakarta.validation.Valid;
+import jakarta.validation.Validator;
+
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.ByteArrayAsset;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.test.QuarkusUnitTest;
+import net.bytebuddy.ByteBuddy;
+import net.bytebuddy.dynamic.DynamicType;
+
+public class ClassHierarchyTest {
+
+    @RegisterExtension
+    static final QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(() -> {
+        JavaArchive javaArchive = ShrinkWrap.create(JavaArchive.class)
+                .addClass(Dto.class);
+        // Create an inner class with an incomplete hierarchy
+        try (DynamicType.Unloaded superClass = new ByteBuddy()
+                .subclass(Object.class)
+                .name("SuperClass")
+                .make();
+                DynamicType.Unloaded outerClass = new ByteBuddy()
+                        .subclass(superClass.getTypeDescription())
+                        .name("OuterClass")
+                        .make();
+                DynamicType.Unloaded innerClass = new ByteBuddy()
+                        .subclass(AbstractCollection.class)
+                        .innerTypeOf(outerClass.getTypeDescription())
+                        .name("InnerClass")
+                        .make();
+                DynamicType.Loaded innerLoad = innerClass.load(Thread.currentThread().getContextClassLoader())) {
+            javaArchive.add(new ByteArrayAsset(innerLoad.getBytes()), "InnerClass.class");
+        }
+        return javaArchive;
+    });
+
+    @Inject
+    Validator validator;
+
+    @Test
+    public void doNotFailWhenLoadingIncompleteClassHierarchy() {
+        assertThat(validator).isNotNull();
+    }
+
+    @Valid
+    public static class Dto {
+        String name;
+
+        // InnerClass is a subclass with an incomplete hierarchy
+        @Valid
+        AbstractCollection items;
+    }
+}
diff --git a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorRecorder.java b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorRecorder.java
index 88b808fd14142..40429cc069436 100644
--- a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorRecorder.java
+++ b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/HibernateValidatorRecorder.java
@@ -2,6 +2,7 @@
 
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Set;
@@ -76,6 +77,9 @@ public void created(BeanContainer container) {
                     configuration.localeResolver(localeResolver);
                 }
 
+                // Filter out classes with incomplete hierarchy
+                filterIncompleteClasses(classesToBeValidated);
+
                 configuration.builtinConstraints(detectedBuiltinConstraints)
                         .initializeBeanMetaData(classesToBeValidated)
                         // Locales, Locale ROOT means all locales in this setting.
@@ -188,6 +192,22 @@ public void run() {
                     }
                 });
             }
+
+            /**
+             * Filter out classes with incomplete hierarchy
+             */
+            private void filterIncompleteClasses(Set> classesToBeValidated) {
+                Iterator> iterator = classesToBeValidated.iterator();
+                while (iterator.hasNext()) {
+                    Class clazz = iterator.next();
+                    try {
+                        // This should trigger a NoClassDefFoundError if the class has an incomplete hierarchy
+                        clazz.getCanonicalName();
+                    } catch (NoClassDefFoundError e) {
+                        iterator.remove();
+                    }
+                }
+            }
         };
 
         return beanContainerListener;

From 8897d766e57cb1836eaa3316b124ac230f43b6ef Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 May 2024 21:46:21 +0000
Subject: [PATCH 34/60] Bump org.jboss.logmanager:jboss-logmanager

Bumps [org.jboss.logmanager:jboss-logmanager](https://github.com/jboss-logging/jboss-logmanager) from 3.0.4.Final to 3.0.6.Final.
- [Release notes](https://github.com/jboss-logging/jboss-logmanager/releases)
- [Commits](https://github.com/jboss-logging/jboss-logmanager/compare/3.0.4.Final...3.0.6.Final)

---
updated-dependencies:
- dependency-name: org.jboss.logmanager:jboss-logmanager
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
(cherry picked from commit 8e3ea4912fd4a98d7bc85dc9cb16b1f5c5f50199)
---
 bom/application/pom.xml                        | 2 +-
 independent-projects/bootstrap/pom.xml         | 2 +-
 independent-projects/resteasy-reactive/pom.xml | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/bom/application/pom.xml b/bom/application/pom.xml
index 1a9af5ece3c89..e76266ac23467 100644
--- a/bom/application/pom.xml
+++ b/bom/application/pom.xml
@@ -165,7 +165,7 @@
         4.1.2 
         3.2.0
         4.2.1
-        3.0.4.Final
+        3.0.6.Final
         10.10.0
         3.0.3
         
diff --git a/independent-projects/bootstrap/pom.xml b/independent-projects/bootstrap/pom.xml
index 2cc14b930b89b..4d53ec6dee857 100644
--- a/independent-projects/bootstrap/pom.xml
+++ b/independent-projects/bootstrap/pom.xml
@@ -64,7 +64,7 @@
         1.0.1
         2.8
         1.2.6
-        3.0.4.Final
+        3.0.6.Final
         1.1.0.Final
         2.0.6
         23.1.0
diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml
index ac39b49ea1315..ed1b5b326a45f 100644
--- a/independent-projects/resteasy-reactive/pom.xml
+++ b/independent-projects/resteasy-reactive/pom.xml
@@ -51,7 +51,7 @@
         3.9.6
         3.25.3
         3.5.3.Final
-        3.0.4.Final
+        3.0.6.Final
         2.1.1
         1.8.0
         3.1.0

From 0d78bd7947fb35c2078f0a274abf07a73ad2f362 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 1 May 2024 21:43:10 +0000
Subject: [PATCH 35/60] Bump io.smallrye.beanbag:smallrye-beanbag-bom from
 1.4.0 to 1.4.1

Bumps [io.smallrye.beanbag:smallrye-beanbag-bom](https://github.com/smallrye/smallrye-beanbag) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/smallrye/smallrye-beanbag/releases)
- [Commits](https://github.com/smallrye/smallrye-beanbag/compare/1.4.0...1.4.1)

---
updated-dependencies:
- dependency-name: io.smallrye.beanbag:smallrye-beanbag-bom
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] 
(cherry picked from commit a80b1e2f17d016c62616e52ba8ad0574faedaf6c)
---
 independent-projects/extension-maven-plugin/pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/independent-projects/extension-maven-plugin/pom.xml b/independent-projects/extension-maven-plugin/pom.xml
index 62ed33efb3ff5..ffacc3a198c40 100644
--- a/independent-projects/extension-maven-plugin/pom.xml
+++ b/independent-projects/extension-maven-plugin/pom.xml
@@ -42,7 +42,7 @@
         3.2.1
         3.2.5
         2.17.0
-        1.4.0
+        1.4.1
         5.10.2
     
     

From 53d32957d63eb6acf16317e8a748cc4119de763d Mon Sep 17 00:00:00 2001
From: Phillip Kruger 
Date: Fri, 3 May 2024 10:53:01 +1000
Subject: [PATCH 36/60] Add more clarity around code-block in Dev UI Docs

Signed-off-by: Phillip Kruger 
(cherry picked from commit 852dd85a894b3f9d08285b3aa745f967c0a81b96)
---
 docs/src/main/asciidoc/dev-ui.adoc | 32 ++++++++++++++++++++++++++++--
 1 file changed, 30 insertions(+), 2 deletions(-)

diff --git a/docs/src/main/asciidoc/dev-ui.adoc b/docs/src/main/asciidoc/dev-ui.adoc
index b4365b2ef1aec..e27cb0910d8a1 100644
--- a/docs/src/main/asciidoc/dev-ui.adoc
+++ b/docs/src/main/asciidoc/dev-ui.adoc
@@ -697,7 +697,7 @@ import '@quarkus-webcomponents/codeblock';
 ;
 ----
 
-https://github.com/quarkusio/quarkus/blob/e03a97845738436c69443a591ec4ce88ed04ac91/extensions/kubernetes/vanilla/deployment/src/main/resources/dev-ui/qwc-kubernetes-manifest.js#L99[Example code]
+https://github.com/quarkusio/quarkus/blob/05800d2a74601247a465f91f50d18c4075fb7fe6/extensions/kubernetes/vanilla/deployment/src/main/resources/dev-ui/qwc-kubernetes-manifest.js#L102[Example code]
 
 Or fetching the contents from a URL:
 
@@ -711,7 +711,35 @@ Or fetching the contents from a URL:
 
 ----
 
-https://github.com/quarkusio/quarkus/blob/95c54fa46a6b6f31d69477234486d9359a2a3a4a/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-external-page.js#L116[Example code]
+https://github.com/quarkusio/quarkus/blob/05800d2a74601247a465f91f50d18c4075fb7fe6/extensions/vertx-http/dev-ui-resources/src/main/resources/dev-ui/qwc/qwc-external-page.js#L118[Example code]
+
+To make sure that the code block adopt the correct code-mirror theme (based on the current one in Dev UI), you can do the following:
+
+[source,javascript]
+----
+import { observeState } from 'lit-element-state'; 
+import { themeState } from 'theme-state';
+----
+
+Then change the `extends` to observe state:
+
+[source,javascript]
+----
+extends observeState(LitElement) {
+----
+
+Now you can get the current theme, so add the `theme` property to your code block, example:
+
+[source,html]
+----
+
+ + +
+---- ====== IDE link From af878781098307b2116938332e11e8c6d1d125b6 Mon Sep 17 00:00:00 2001 From: Matej Novotny Date: Mon, 29 Apr 2024 18:52:14 +0200 Subject: [PATCH 37/60] Quarkus REST - reuse CDI request context if it exists (cherry picked from commit 7b0fbd695fef9d0e9db27a0779bbdfc49dc8f337) --- .../common/runtime/ArcThreadSetupAction.java | 5 +++++ .../core/AbstractResteasyReactiveContext.java | 13 +++++++++---- .../resteasy/reactive/spi/ThreadSetupAction.java | 7 +++++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/extensions/resteasy-reactive/rest-common/runtime/src/main/java/io/quarkus/resteasy/reactive/common/runtime/ArcThreadSetupAction.java b/extensions/resteasy-reactive/rest-common/runtime/src/main/java/io/quarkus/resteasy/reactive/common/runtime/ArcThreadSetupAction.java index 4d13a0db03c38..6ff9c39989490 100644 --- a/extensions/resteasy-reactive/rest-common/runtime/src/main/java/io/quarkus/resteasy/reactive/common/runtime/ArcThreadSetupAction.java +++ b/extensions/resteasy-reactive/rest-common/runtime/src/main/java/io/quarkus/resteasy/reactive/common/runtime/ArcThreadSetupAction.java @@ -41,4 +41,9 @@ public void deactivate() { public ThreadState currentState() { return toThreadState(managedContext.getState()); } + + @Override + public boolean isRequestContextActive() { + return managedContext.isActive(); + } } diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/AbstractResteasyReactiveContext.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/AbstractResteasyReactiveContext.java index 705e9e3075605..d78d0af28de6c 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/AbstractResteasyReactiveContext.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/core/AbstractResteasyReactiveContext.java @@ -246,12 +246,17 @@ public void requireCDIRequestScope() { if (requestScopeActivated) { return; } - requestScopeActivated = true; if (isRequestScopeManagementRequired()) { - if (currentRequestScope == null) { - currentRequestScope = requestContext.activateInitial(); + if (requestContext.isRequestContextActive()) { + // req. context is already active, just reuse existing one + currentRequestScope = requestContext.currentState(); } else { - currentRequestScope.activate(); + requestScopeActivated = true; + if (currentRequestScope == null) { + currentRequestScope = requestContext.activateInitial(); + } else { + currentRequestScope.activate(); + } } } else { currentRequestScope = requestContext.currentState(); diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/spi/ThreadSetupAction.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/spi/ThreadSetupAction.java index 912614f2c6939..67c0fdf60727f 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/spi/ThreadSetupAction.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/spi/ThreadSetupAction.java @@ -6,6 +6,8 @@ public interface ThreadSetupAction { ThreadState currentState(); + boolean isRequestContextActive(); + interface ThreadState { void close(); @@ -39,5 +41,10 @@ public void deactivate() { public ThreadState currentState() { return activateInitial(); } + + @Override + public boolean isRequestContextActive() { + return false; + } }; } From c377746aa12c5a42c6ce21b02d700c2d1985164f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Thu, 2 May 2024 16:08:57 +0200 Subject: [PATCH 38/60] Add @TestSecurity NPE test when Reactive Routes are combined with Quarkus REST (cherry picked from commit 864be71f879fe140b59b8b60d7f6d7f0ac340f0c) --- integration-tests/openapi/pom.xml | 5 ++++ .../security/TestSecurityResource.java | 29 +++++++++++++++++++ .../TestSecurityReactiveRoutesTest.java | 26 +++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 integration-tests/openapi/src/main/java/io/quarkus/it/openapi/security/TestSecurityResource.java create mode 100644 integration-tests/openapi/src/test/java/io/quarkus/it/openapi/security/TestSecurityReactiveRoutesTest.java diff --git a/integration-tests/openapi/pom.xml b/integration-tests/openapi/pom.xml index 6f8a6b6250354..b6ad484e08818 100644 --- a/integration-tests/openapi/pom.xml +++ b/integration-tests/openapi/pom.xml @@ -52,6 +52,11 @@ assertj-core test + + io.quarkus + quarkus-test-security + test + diff --git a/integration-tests/openapi/src/main/java/io/quarkus/it/openapi/security/TestSecurityResource.java b/integration-tests/openapi/src/main/java/io/quarkus/it/openapi/security/TestSecurityResource.java new file mode 100644 index 0000000000000..7676535bd3864 --- /dev/null +++ b/integration-tests/openapi/src/main/java/io/quarkus/it/openapi/security/TestSecurityResource.java @@ -0,0 +1,29 @@ +package io.quarkus.it.openapi.security; + +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.SecurityContext; + +import io.quarkus.vertx.web.RouteFilter; +import io.vertx.ext.web.RoutingContext; + +@Path("/security") +public class TestSecurityResource { + + @RolesAllowed("admin") + @GET + @Path("reactive-routes") + public String reactiveRoutes(@Context SecurityContext securityContext) { + return securityContext.getUserPrincipal().getName(); + } + + @RouteFilter(401) + public void doNothing(RoutingContext routingContext) { + // here so that the Reactive Routes extension activates CDI request context + routingContext.response().putHeader("reactive-routes-filter", "true"); + routingContext.next(); + } + +} diff --git a/integration-tests/openapi/src/test/java/io/quarkus/it/openapi/security/TestSecurityReactiveRoutesTest.java b/integration-tests/openapi/src/test/java/io/quarkus/it/openapi/security/TestSecurityReactiveRoutesTest.java new file mode 100644 index 0000000000000..ca6870dbe86ad --- /dev/null +++ b/integration-tests/openapi/src/test/java/io/quarkus/it/openapi/security/TestSecurityReactiveRoutesTest.java @@ -0,0 +1,26 @@ +package io.quarkus.it.openapi.security; + +import static org.hamcrest.Matchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.security.TestSecurity; +import io.restassured.RestAssured; + +@QuarkusTest +@TestHTTPEndpoint(TestSecurityResource.class) +public class TestSecurityReactiveRoutesTest { + + @TestSecurity(user = "Martin", roles = "admin") + @Test + public void testSecurityWithReactiveRoutesAndQuarkusRest() { + RestAssured.get("reactive-routes") + .then() + .statusCode(200) + .header("reactive-routes-filter", is("true")) + .body(is("Martin")); + } + +} From 4842e0b2713dc48be1935576f33964b9baaa19a4 Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Fri, 3 May 2024 14:22:43 +0200 Subject: [PATCH 39/60] Check whether outputDirectory is the root of the file system before using its parent (cherry picked from commit f327ea0a96688bf2a1db4300d95300a107292cbb) --- .../openapi/deployment/SmallRyeOpenApiProcessor.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java index 53f3f86b0fe17..2502f2c710442 100644 --- a/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java +++ b/extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java @@ -12,7 +12,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; @@ -939,14 +938,19 @@ private void storeGeneratedSchema(SmallRyeOpenApiConfig openApiConfig, OutputTar Path outputDirectory = out.getOutputDirectory(); if (!directory.isAbsolute() && outputDirectory != null) { - directory = Paths.get(outputDirectory.getParent().toString(), directory.toString()); + var baseDir = outputDirectory.getParent(); + // check if outputDirectory is the root of the filesystem + if (baseDir == null) { + baseDir = outputDirectory; + } + directory = baseDir.resolve(directory); } if (!Files.exists(directory)) { Files.createDirectories(directory); } - Path file = Paths.get(directory.toString(), "openapi." + format.toString().toLowerCase()); + Path file = directory.resolve("openapi." + format.toString().toLowerCase()); if (!Files.exists(file)) { Files.createFile(file); } From d1322953533b076c6f1017e1fc05b7ac774af629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Sat, 4 May 2024 22:02:35 +0200 Subject: [PATCH 40/60] Tweak UserInfo required autoenablement for OIDC tenants without its endpoint (cherry picked from commit 7c0ffd41306c13fc677da16e5ab3a17f0ea81c5f) --- .../quarkus/oidc/deployment/OidcBuildStep.java | 15 +++++---------- .../test/UserInfoRequiredDetectionTest.java | 17 +++++++++++++++++ .../io/quarkus/oidc/runtime/OidcRecorder.java | 8 +++++++- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildStep.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildStep.java index 9bc2cae847574..a7e4e41dfbd2f 100644 --- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildStep.java +++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/OidcBuildStep.java @@ -218,6 +218,7 @@ private static boolean isTenantIdentityProviderType(InjectionPointInfo ip) { @Record(ExecutionTime.RUNTIME_INIT) @BuildStep public SyntheticBeanBuildItem setup( + BeanRegistrationPhaseBuildItem beanRegistration, OidcConfig config, OidcRecorder recorder, CoreVertxBuildItem vertxBuildItem, @@ -225,7 +226,8 @@ public SyntheticBeanBuildItem setup( // this is required for setup ordering: we need CP set up ContextPropagationInitializedBuildItem cpInitializedBuildItem) { return SyntheticBeanBuildItem.configure(TenantConfigBean.class).unremovable().types(TenantConfigBean.class) - .supplier(recorder.setup(config, vertxBuildItem.getVertx(), tlsConfig)) + .supplier( + recorder.setup(config, vertxBuildItem.getVertx(), tlsConfig, detectUserInfoRequired(beanRegistration))) .destroyer(TenantConfigBean.Destroyer.class) .scope(Singleton.class) // this should have been @ApplicationScoped but fails for some reason .setRuntimeInit() @@ -252,15 +254,8 @@ public void registerTenantResolverInterceptor(Capabilities capabilities, OidcRec } } - @BuildStep - void detectUserInfoRequired(BeanRegistrationPhaseBuildItem beanRegistrationPhaseBuildItem, - BuildProducer runtimeConfigDefaultProducer) { - if (isInjected(beanRegistrationPhaseBuildItem, USER_INFO_NAME, null)) { - runtimeConfigDefaultProducer.produce( - new RunTimeConfigurationDefaultBuildItem("quarkus.oidc.authentication.user-info-required", "true")); - runtimeConfigDefaultProducer.produce( - new RunTimeConfigurationDefaultBuildItem("quarkus.oidc.*.authentication.user-info-required", "true")); - } + private static boolean detectUserInfoRequired(BeanRegistrationPhaseBuildItem beanRegistrationPhaseBuildItem) { + return isInjected(beanRegistrationPhaseBuildItem, USER_INFO_NAME, null); } @BuildStep diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/UserInfoRequiredDetectionTest.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/UserInfoRequiredDetectionTest.java index 10c69b0587693..02a81c23f5610 100644 --- a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/UserInfoRequiredDetectionTest.java +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/UserInfoRequiredDetectionTest.java @@ -37,6 +37,10 @@ public class UserInfoRequiredDetectionTest { quarkus.oidc.named.auth-server-url=${quarkus.oidc.auth-server-url} quarkus.oidc.named.tenant-paths=/user-info/named-tenant quarkus.oidc.named.user-info-path=http://${quarkus.http.host}:${quarkus.http.port}/user-info-endpoint + quarkus.oidc.named-2.auth-server-url=${quarkus.oidc.auth-server-url} + quarkus.oidc.named-2.tenant-paths=/user-info/named-tenant-2 + quarkus.oidc.named-2.discovery-enabled=false + quarkus.oidc.named-2.jwks-path=protocol/openid-connect/certs quarkus.http.auth.proactive=false """), "application.properties")); @@ -53,6 +57,12 @@ public void testNamedTenant() { .body(Matchers.is("alice")); } + @Test + public void testUserInfoNotRequiredWhenMissingUserInfoEndpoint() { + RestAssured.given().auth().oauth2(getAccessToken()).get("/user-info/named-tenant-2").then().statusCode(200) + .body(Matchers.is("false")); + } + private static String getAccessToken() { return new KeycloakTestClient().getAccessToken("alice", "alice", "quarkus-service-app", "secret", List.of("openid")); } @@ -94,6 +104,13 @@ public String getNamedTenantName() { } return userInfo.getPreferredUserName(); } + + @PermissionsAllowed("openid") + @Path("named-tenant-2") + @GET + public boolean getNamed2TenantUserInfoRequired() { + return config.namedTenants.get("named-2").authentication.userInfoRequired.orElse(false); + } } } diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java index 0568683eda84a..9cf4b4177e88d 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java @@ -68,6 +68,7 @@ public class OidcRecorder { private static final Map dynamicTenantsConfig = new ConcurrentHashMap<>(); private static final Set tenantsExpectingServerAvailableEvents = ConcurrentHashMap.newKeySet(); + private static volatile boolean userInfoInjectionPointDetected = false; public Supplier setupTokenCache(OidcConfig config, Supplier vertx) { return new Supplier() { @@ -78,7 +79,9 @@ public DefaultTokenIntrospectionUserInfoCache get() { }; } - public Supplier setup(OidcConfig config, Supplier vertx, TlsConfig tlsConfig) { + public Supplier setup(OidcConfig config, Supplier vertx, TlsConfig tlsConfig, + boolean userInfoInjectionPointDetected) { + OidcRecorder.userInfoInjectionPointDetected = userInfoInjectionPointDetected; final Vertx vertxValue = vertx.get(); String defaultTenantId = config.defaultTenant.getTenantId().orElse(DEFAULT_TENANT_ID); @@ -541,6 +544,9 @@ public Uni apply(OidcConfigurationMetadata metadata, Throwab "The application supports RP-Initiated Logout but the OpenID Provider does not advertise the end_session_endpoint")); } } + if (userInfoInjectionPointDetected && metadata.getUserInfoUri() != null) { + enableUserInfo(oidcConfig); + } if (oidcConfig.authentication.userInfoRequired.orElse(false) && metadata.getUserInfoUri() == null) { client.close(); return Uni.createFrom().failure(new ConfigurationException( From d5d86b6ea516ddb0dff21e58ebc57a75b9f51316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 3 May 2024 15:20:29 +0200 Subject: [PATCH 41/60] Fix DisableLoggingFeature for JBoss Threads/Hibernate/Infinispan/Websocket-client The log level we get in beforeAnalysis may be null, in which case we still want to reset it after analysis. From the javadoc of Logger#getLevel: > Get the log Level that has been specified for this Logger. > The result may be null, which means that this logger's effective level will be inherited from its parent. (cherry picked from commit 554ae55dd9b07a24a18639a015689d5467f2d8fc) --- .../io/quarkus/runtime/graal/DisableLoggingFeature.java | 6 ++---- .../envers/runtime/graal/DisableLoggingFeature.java | 6 ++---- .../hibernate/orm/runtime/graal/DisableLoggingFeature.java | 6 ++---- .../elasticsearch/runtime/graal/DisableLoggingFeature.java | 6 ++---- .../hibernate/validator/runtime/DisableLoggingFeature.java | 6 ++---- .../client/runtime/graal/DisableLoggingFeature.java | 6 ++---- .../websockets/client/runtime/DisableLoggingFeature.java | 6 ++---- 7 files changed, 14 insertions(+), 28 deletions(-) diff --git a/core/runtime/src/main/java/io/quarkus/runtime/graal/DisableLoggingFeature.java b/core/runtime/src/main/java/io/quarkus/runtime/graal/DisableLoggingFeature.java index 955884c00e8ac..2b5565c8015ef 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/graal/DisableLoggingFeature.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/graal/DisableLoggingFeature.java @@ -31,10 +31,8 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { public void afterAnalysis(AfterAnalysisAccess access) { for (String category : CATEGORIES) { Level level = categoryMap.remove(category); - if (level != null) { - Logger logger = Logger.getLogger(category); - logger.setLevel(level); - } + Logger logger = Logger.getLogger(category); + logger.setLevel(level); } } diff --git a/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/runtime/graal/DisableLoggingFeature.java b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/runtime/graal/DisableLoggingFeature.java index de6d22b94c1a6..481398edb364d 100644 --- a/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/runtime/graal/DisableLoggingFeature.java +++ b/extensions/hibernate-envers/runtime/src/main/java/io/quarkus/hibernate/envers/runtime/graal/DisableLoggingFeature.java @@ -32,10 +32,8 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { public void afterAnalysis(AfterAnalysisAccess access) { for (String category : CATEGORIES) { Level level = categoryMap.remove(category); - if (level != null) { - Logger logger = Logger.getLogger(category); - logger.setLevel(level); - } + Logger logger = Logger.getLogger(category); + logger.setLevel(level); } } diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/graal/DisableLoggingFeature.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/graal/DisableLoggingFeature.java index 50acd745f0063..ccd4be97371f3 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/graal/DisableLoggingFeature.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/graal/DisableLoggingFeature.java @@ -34,10 +34,8 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { public void afterAnalysis(AfterAnalysisAccess access) { for (String category : CATEGORIES) { Level level = categoryMap.remove(category); - if (level != null) { - Logger logger = Logger.getLogger(category); - logger.setLevel(level); - } + Logger logger = Logger.getLogger(category); + logger.setLevel(level); } } diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/graal/DisableLoggingFeature.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/graal/DisableLoggingFeature.java index 245bc72fca92d..e3666fda2aed3 100644 --- a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/graal/DisableLoggingFeature.java +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/graal/DisableLoggingFeature.java @@ -31,10 +31,8 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { public void afterAnalysis(AfterAnalysisAccess access) { for (String category : CATEGORIES) { Level level = categoryMap.remove(category); - if (level != null) { - Logger logger = Logger.getLogger(category); - logger.setLevel(level); - } + Logger logger = Logger.getLogger(category); + logger.setLevel(level); } } diff --git a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/DisableLoggingFeature.java b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/DisableLoggingFeature.java index 6d65ddf89463f..64f01c14cfb25 100644 --- a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/DisableLoggingFeature.java +++ b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/DisableLoggingFeature.java @@ -32,10 +32,8 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { public void afterAnalysis(AfterAnalysisAccess access) { for (String category : CATEGORIES) { Level level = categoryMap.remove(category); - if (level != null) { - Logger logger = Logger.getLogger(category); - logger.setLevel(level); - } + Logger logger = Logger.getLogger(category); + logger.setLevel(level); } } diff --git a/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/graal/DisableLoggingFeature.java b/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/graal/DisableLoggingFeature.java index edb073b1e6320..178a83bc3f6fd 100644 --- a/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/graal/DisableLoggingFeature.java +++ b/extensions/infinispan-client/runtime/src/main/java/io/quarkus/infinispan/client/runtime/graal/DisableLoggingFeature.java @@ -31,10 +31,8 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { public void afterAnalysis(AfterAnalysisAccess access) { for (String category : CATEGORIES) { Level level = categoryMap.remove(category); - if (level != null) { - Logger logger = Logger.getLogger(category); - logger.setLevel(level); - } + Logger logger = Logger.getLogger(category); + logger.setLevel(level); } } diff --git a/extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/client/runtime/DisableLoggingFeature.java b/extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/client/runtime/DisableLoggingFeature.java index 022507620961e..b8e723d990eb4 100644 --- a/extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/client/runtime/DisableLoggingFeature.java +++ b/extensions/websockets/client/runtime/src/main/java/io/quarkus/websockets/client/runtime/DisableLoggingFeature.java @@ -31,10 +31,8 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { public void afterAnalysis(AfterAnalysisAccess access) { for (String category : CATEGORIES) { Level level = categoryMap.remove(category); - if (level != null) { - Logger logger = Logger.getLogger(category); - logger.setLevel(level); - } + Logger logger = Logger.getLogger(category); + logger.setLevel(level); } } From a5f2fb3eb4c7c774800a117692de31a9025cdbda Mon Sep 17 00:00:00 2001 From: Ales Justin Date: Sat, 4 May 2024 07:56:15 +0200 Subject: [PATCH 42/60] Ignore non-HTTP_2 requests (cherry picked from commit 92ced67c711367e50f646344fd985b3f2a9046b7) --- .../io/quarkus/grpc/runtime/GrpcServerRecorder.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/GrpcServerRecorder.java b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/GrpcServerRecorder.java index 54e83ead17432..322cf5eb18db7 100644 --- a/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/GrpcServerRecorder.java +++ b/extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/GrpcServerRecorder.java @@ -68,6 +68,8 @@ import io.vertx.core.Handler; import io.vertx.core.Promise; import io.vertx.core.Vertx; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.http.HttpVersion; import io.vertx.ext.web.Route; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; @@ -216,7 +218,13 @@ public void handle(Void unused) { // TODO -- handle Avro, plain text ... when supported / needed private static boolean isGrpc(RoutingContext rc) { - String header = rc.request().getHeader("content-type"); + HttpServerRequest request = rc.request(); + HttpVersion version = request.version(); + if (HttpVersion.HTTP_1_0.equals(version) || HttpVersion.HTTP_1_1.equals(version)) { + LOGGER.debugf("Expecting %s, received %s - not a gRPC request", HttpVersion.HTTP_2, version); + return false; + } + String header = request.getHeader("content-type"); return header != null && GRPC_CONTENT_TYPE.matcher(header.toLowerCase(Locale.ROOT)).matches(); } From d103e47be36d1505198c17b14c94a88f469375f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Sat, 4 May 2024 14:10:27 +0200 Subject: [PATCH 43/60] Build SecurityEventHelper lazily due to static interceptors (cherry picked from commit cca3872844c9ea294a30ac018a29324dc331f982) --- .../RolesAllowedExpressionTest.java | 22 ++++++- .../spi/runtime/SecurityEventHelper.java | 59 +++++++++++++++++++ .../interceptor/SecurityConstrainer.java | 8 +-- 3 files changed, 84 insertions(+), 5 deletions(-) diff --git a/extensions/security/deployment/src/test/java/io/quarkus/security/test/rolesallowed/RolesAllowedExpressionTest.java b/extensions/security/deployment/src/test/java/io/quarkus/security/test/rolesallowed/RolesAllowedExpressionTest.java index f29bc1f0c6f4e..bb63fb075ba5b 100644 --- a/extensions/security/deployment/src/test/java/io/quarkus/security/test/rolesallowed/RolesAllowedExpressionTest.java +++ b/extensions/security/deployment/src/test/java/io/quarkus/security/test/rolesallowed/RolesAllowedExpressionTest.java @@ -12,6 +12,7 @@ import jakarta.inject.Inject; import jakarta.inject.Singleton; +import org.eclipse.microprofile.config.ConfigProvider; import org.jboss.shrinkwrap.api.asset.StringAsset; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -47,7 +48,7 @@ public class RolesAllowedExpressionTest { static final QuarkusUnitTest config = new QuarkusUnitTest() .withApplicationRoot((jar) -> jar .addClasses(RolesAllowedBean.class, IdentityMock.class, - AuthData.class, SecurityTestUtils.class) + AuthData.class, SecurityTestUtils.class, SecuredUtils.class) .addAsResource(new StringAsset(APP_PROPS), "application.properties")); @Inject @@ -97,6 +98,12 @@ public void shouldRestrictAccessToSpecificRole() { new AuthData(Set.of("cn=Administrator,ou=Software,dc=Tester,dc=User"), false, "ldap")); } + @Test + public void testStaticSecuredMethod() { + assertSuccess(SecuredUtils::staticSecuredMethod, "admin", ADMIN); + assertFailureFor(SecuredUtils::staticSecuredMethod, ForbiddenException.class, USER); + } + @Singleton public static class RolesAllowedBean { @@ -153,4 +160,17 @@ public final String ldap() { } + public static class SecuredUtils { + + private SecuredUtils() { + // UTIL CLASS + } + + @RolesAllowed("${sudo}") + public static String staticSecuredMethod() { + return ConfigProvider.getConfig().getValue("sudo", String.class); + } + + } + } diff --git a/extensions/security/runtime-spi/src/main/java/io/quarkus/security/spi/runtime/SecurityEventHelper.java b/extensions/security/runtime-spi/src/main/java/io/quarkus/security/spi/runtime/SecurityEventHelper.java index e9a70973b5661..8907c4e541894 100644 --- a/extensions/security/runtime-spi/src/main/java/io/quarkus/security/spi/runtime/SecurityEventHelper.java +++ b/extensions/security/runtime-spi/src/main/java/io/quarkus/security/spi/runtime/SecurityEventHelper.java @@ -5,6 +5,8 @@ import jakarta.enterprise.event.Event; import jakarta.enterprise.inject.spi.BeanManager; +import org.eclipse.microprofile.config.ConfigProvider; + import io.quarkus.security.identity.SecurityIdentity; public class SecurityEventHelper { @@ -90,4 +92,61 @@ public Map getEventProperties() { return false; } } + + /** + * Creates {@link SecurityEventHelper} initialized on first request. + * This method should only be used when there is a risk the helper will be initialized during the static init phase. + * During the runtime init phase, prefer the constructor. + */ + public static SecurityEventHelper lazilyOf(Event successEvent, + Event failureEvent, S successInstance, F failureInstance, BeanManager beanManager) { + return new SecurityEventHelper<>(successEvent, failureEvent, successInstance, failureInstance, beanManager, true) { + + private volatile Boolean eventsDisabled = null; + + private boolean areEventsDisabled() { + if (eventsDisabled == null) { + synchronized (this) { + if (eventsDisabled == null) { + this.eventsDisabled = !ConfigProvider.getConfig().getValue("quarkus.security.events.enabled", + Boolean.class); + } + } + } + return eventsDisabled; + } + + @Override + public void fireSuccessEvent(S successInstance) { + if (areEventsDisabled()) { + return; + } + super.fireSuccessEvent(successInstance); + } + + @Override + public void fireFailureEvent(F failureInstance) { + if (areEventsDisabled()) { + return; + } + super.fireFailureEvent(failureInstance); + } + + @Override + public boolean fireEventOnSuccess() { + if (areEventsDisabled()) { + return false; + } + return super.fireEventOnSuccess(); + } + + @Override + public boolean fireEventOnFailure() { + if (areEventsDisabled()) { + return false; + } + return super.fireEventOnFailure(); + } + }; + } } diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/SecurityConstrainer.java b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/SecurityConstrainer.java index 3d1f357ad9b77..a8d68b3c23e48 100644 --- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/SecurityConstrainer.java +++ b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/interceptor/SecurityConstrainer.java @@ -14,7 +14,6 @@ import io.quarkus.runtime.BlockingOperationNotAllowedException; import io.quarkus.security.identity.SecurityIdentity; -import io.quarkus.security.runtime.SecurityConfig; import io.quarkus.security.runtime.SecurityIdentityAssociation; import io.quarkus.security.spi.runtime.AuthorizationFailureEvent; import io.quarkus.security.spi.runtime.AuthorizationSuccessEvent; @@ -36,11 +35,12 @@ public class SecurityConstrainer { @Inject SecurityIdentityAssociation identityAssociation; - SecurityConstrainer(SecurityCheckStorage storage, BeanManager beanManager, SecurityConfig securityConfig, + SecurityConstrainer(SecurityCheckStorage storage, BeanManager beanManager, Event authZFailureEvent, Event authZSuccessEvent) { this.storage = storage; - this.securityEventHelper = new SecurityEventHelper<>(authZSuccessEvent, authZFailureEvent, AUTHORIZATION_SUCCESS, - AUTHORIZATION_FAILURE, beanManager, securityConfig.events().enabled()); + // static interceptors are initialized during the static init, therefore we need to initialize the helper lazily + this.securityEventHelper = SecurityEventHelper.lazilyOf(authZSuccessEvent, authZFailureEvent, + AUTHORIZATION_SUCCESS, AUTHORIZATION_FAILURE, beanManager); } public void check(Method method, Object[] parameters) { From c6fe0afe7fc7e1a86cd6065f08e2f7eea0899251 Mon Sep 17 00:00:00 2001 From: Max Rydahl Andersen Date: Mon, 6 May 2024 18:23:42 +0200 Subject: [PATCH 44/60] add missing ! to fix #40195 (cherry picked from commit c89c0968758b3c0fe15df4109a82e94a400f523b) --- docs/src/main/asciidoc/rest.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/rest.adoc b/docs/src/main/asciidoc/rest.adoc index 968213774a87b..7bd89519c38cd 100644 --- a/docs/src/main/asciidoc/rest.adoc +++ b/docs/src/main/asciidoc/rest.adoc @@ -2350,7 +2350,7 @@ class Filters { @ServerRequestFilter public Optional> getFilter(ContainerRequestContext ctx) { // only allow GET methods for now - if(ctx.getMethod().equals(HttpMethod.GET)) { + if(!ctx.getMethod().equals(HttpMethod.GET)) { return Optional.of(RestResponse.status(Response.Status.METHOD_NOT_ALLOWED)); } return Optional.empty(); From c0b0ae8546cbe3613549e8cdb0893ef14ca0a33e Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Tue, 7 May 2024 08:55:52 +0200 Subject: [PATCH 45/60] Label WebSocket Next as Experimental in Documentation This commit updates the quickstart and reference guides for the websocket-next extension to correctly reflect its experimental status. Previously, these guides lacked the appropriate header and warning/information admonition, it was only written in the text of the guides. (cherry picked from commit 181e08ed7db19abcc2bbca8211c789f9eb144e65) --- docs/src/main/asciidoc/websockets-next-reference.adoc | 4 ++-- docs/src/main/asciidoc/websockets-next-tutorial.adoc | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/src/main/asciidoc/websockets-next-reference.adoc b/docs/src/main/asciidoc/websockets-next-reference.adoc index 5387561323b5f..95bdad02b9fd5 100644 --- a/docs/src/main/asciidoc/websockets-next-reference.adoc +++ b/docs/src/main/asciidoc/websockets-next-reference.adoc @@ -12,9 +12,9 @@ include::_attributes.adoc[] :categories: web :topics: web,websockets :extensions: io.quarkus:quarkus-websockets-next +:extension-status: experimental -The `websockets-next` extension provides an experimental API to define _WebSocket_ endpoints declaratively. -The proposed API may change in future releases. +include::{includes}/extension-status.adoc[] == The WebSocket protocol diff --git a/docs/src/main/asciidoc/websockets-next-tutorial.adoc b/docs/src/main/asciidoc/websockets-next-tutorial.adoc index fb5f2ad82e9a2..91a67eda53e3a 100644 --- a/docs/src/main/asciidoc/websockets-next-tutorial.adoc +++ b/docs/src/main/asciidoc/websockets-next-tutorial.adoc @@ -10,11 +10,13 @@ include::_attributes.adoc[] :summary: This guide explains how your Quarkus application can utilize web sockets to create interactive web applications. This guide uses the WebSockets Next extension :topics: web,websockets :extensions: io.quarkus:quarkus-websockets-next +:extension-status: experimental This guide explains how your Quarkus application can utilize web sockets to create interactive web applications. In this guide, we will develop a very simple chat application using web sockets to receive and send messages to the other connected users. -IMPORTANT: The `websockets-next` extension is experimental. The proposal API may change in future releases. +include::{includes}/extension-status.adoc[] + == Prerequisites From 67e16ef12b68931b52c4da79ff09bd1229c3922c Mon Sep 17 00:00:00 2001 From: Ales Justin Date: Wed, 8 May 2024 14:23:05 +0200 Subject: [PATCH 46/60] Remove static (cherry picked from commit f028eb8bbf4c1090e048b233dc2c0b6d029296df) --- docs/src/main/asciidoc/http-reference.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/http-reference.adoc b/docs/src/main/asciidoc/http-reference.adoc index 5201ebbc4df5e..d92ef3e2de8af 100644 --- a/docs/src/main/asciidoc/http-reference.adoc +++ b/docs/src/main/asciidoc/http-reference.adoc @@ -518,7 +518,7 @@ import jakarta.inject.Singleton; import io.quarkus.vertx.http.HttpServerOptionsCustomizer; @Singleton <1> -public static class MyCustomizer implements HttpServerOptionsCustomizer { +public class MyCustomizer implements HttpServerOptionsCustomizer { @Override public void customizeHttpServer(HttpServerOptions options) { <2> From ed6b6747e1589a6ae47d40c942ef32491373201f Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 8 May 2024 17:59:11 +0100 Subject: [PATCH 47/60] Update docs to make it easy to see that the code flow access token fails, update tests (cherry picked from commit f030a3364dc4188943d7988fc21352ab85d41df2) --- ...rity-oidc-bearer-token-authentication.adoc | 6 +++ ...ecurity-oidc-code-flow-authentication.adoc | 11 ++++ ...VerifyInjectedAccessTokenDisabledTest.java | 54 +++++++++++++++++++ .../ProtectedResourceWithJwtAccessToken.java | 31 +++++++++++ .../test/UserInfoRequiredDetectionTest.java | 19 +++++++ ...-injected-access-token-disabled.properties | 5 ++ .../io/quarkus/oidc/OidcTenantConfig.java | 28 ++++++---- .../runtime/CodeAuthenticationMechanism.java | 15 +++++- 8 files changed, 156 insertions(+), 13 deletions(-) create mode 100644 extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowVerifyInjectedAccessTokenDisabledTest.java create mode 100644 extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResourceWithJwtAccessToken.java create mode 100644 extensions/oidc/deployment/src/test/resources/application-verify-injected-access-token-disabled.properties diff --git a/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc b/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc index 4d764c915a9e9..b1c4bd9180b8f 100644 --- a/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc +++ b/docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc @@ -95,6 +95,12 @@ If you must request a UserInfo JSON object from the OIDC `UserInfo` endpoint, se A request is sent to the OIDC provider `UserInfo` endpoint, and an `io.quarkus.oidc.UserInfo` (a simple `javax.json.JsonObject` wrapper) object is created. `io.quarkus.oidc.UserInfo` can be injected or accessed as a `SecurityIdentity` `userinfo` attribute. +`quarkus.oidc.authentication.user-info-required` is automatically enabled if one of these conditions is met: + +- if `quarkus.oidc.roles.source` is set to `userinfo` or `quarkus.oidc.token.verify-access-token-with-user-info` is set to `true` or `quarkus.oidc.authentication.id-token-required` is set to `false`, the current OIDC tenant must support a UserInfo endpoint in these cases. + +- if `io.quarkus.oidc.UserInfo` injection point is detected but only if the current OIDC tenant supports a UserInfo endpoint. + [[config-metadata]] === Configuration metadata diff --git a/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc b/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc index 034cf992124ee..e3f1a15a6f686 100644 --- a/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc +++ b/docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc @@ -483,6 +483,11 @@ public class ProtectedResource { } ---- +[NOTE] +==== +When an authorization code flow access token is injected as `JsonWebToken`, its verification is automatically enabled, in addition to the mandatory ID token verification. If really needed, you can disable this code flow access token verification with `quarkus.oidc.authentication.verify-access-token=false`. +==== + [NOTE] ==== `AccessTokenCredential` is used if the access token issued to the Quarkus `web-app` application is opaque (binary) and cannot be parsed to a `JsonWebToken` or if the inner content is necessary for the application. @@ -501,6 +506,12 @@ Set the `quarkus.oidc.authentication.user-info-required=true` property to reques A request is sent to the OIDC provider `UserInfo` endpoint by using the access token returned with the authorization code grant response, and an `io.quarkus.oidc.UserInfo` (a simple `jakarta.json.JsonObject` wrapper) object is created. `io.quarkus.oidc.UserInfo` can be injected or accessed as a SecurityIdentity `userinfo` attribute. +`quarkus.oidc.authentication.user-info-required` is automatically enabled if one of these conditions is met: + +- if `quarkus.oidc.roles.source` is set to `userinfo` or `quarkus.oidc.token.verify-access-token-with-user-info` is set to `true` or `quarkus.oidc.authentication.id-token-required` is set to `false`, the current OIDC tenant must support a UserInfo endpoint in these cases. + +- if `io.quarkus.oidc.UserInfo` injection point is detected but only if the current OIDC tenant supports a UserInfo endpoint. + [[config-metadata]] ==== Accessing the OIDC configuration information diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowVerifyInjectedAccessTokenDisabledTest.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowVerifyInjectedAccessTokenDisabledTest.java new file mode 100644 index 0000000000000..6d1aaf041f503 --- /dev/null +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowVerifyInjectedAccessTokenDisabledTest.java @@ -0,0 +1,54 @@ +package io.quarkus.oidc.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import com.gargoylesoftware.htmlunit.SilentCssErrorHandler; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlForm; +import com.gargoylesoftware.htmlunit.html.HtmlPage; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.keycloak.server.KeycloakTestResourceLifecycleManager; + +@QuarkusTestResource(KeycloakTestResourceLifecycleManager.class) +public class CodeFlowVerifyInjectedAccessTokenDisabledTest { + + @RegisterExtension + static final QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(ProtectedResourceWithJwtAccessToken.class) + .addAsResource("application-verify-injected-access-token-disabled.properties", "application.properties")); + + @Test + public void testVerifyAccessTokenDisabled() throws IOException, InterruptedException { + try (final WebClient webClient = createWebClient()) { + + HtmlPage page = webClient.getPage("http://localhost:8081/protected"); + + assertEquals("Sign in to quarkus", page.getTitleText()); + + HtmlForm loginForm = page.getForms().get(0); + + loginForm.getInputByName("username").setValueAttribute("alice"); + loginForm.getInputByName("password").setValueAttribute("alice"); + + page = loginForm.getInputByName("login").click(); + + assertEquals("alice:false", page.getBody().asNormalizedText()); + + webClient.getCookieManager().clearCookies(); + } + } + + private WebClient createWebClient() { + WebClient webClient = new WebClient(); + webClient.setCssErrorHandler(new SilentCssErrorHandler()); + return webClient; + } +} diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResourceWithJwtAccessToken.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResourceWithJwtAccessToken.java new file mode 100644 index 0000000000000..dc82b0c8ce510 --- /dev/null +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/ProtectedResourceWithJwtAccessToken.java @@ -0,0 +1,31 @@ +package io.quarkus.oidc.test; + +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.eclipse.microprofile.jwt.JsonWebToken; + +import io.quarkus.oidc.IdToken; +import io.quarkus.oidc.runtime.OidcConfig; +import io.quarkus.security.Authenticated; + +@Path("/protected") +@Authenticated +public class ProtectedResourceWithJwtAccessToken { + + @Inject + @IdToken + JsonWebToken idToken; + + @Inject + JsonWebToken accessToken; + + @Inject + OidcConfig config; + + @GET + public String getName() { + return idToken.getName() + ":" + config.defaultTenant.authentication.verifyAccessToken; + } +} diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/UserInfoRequiredDetectionTest.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/UserInfoRequiredDetectionTest.java index 02a81c23f5610..73633f3bbb4a1 100644 --- a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/UserInfoRequiredDetectionTest.java +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/UserInfoRequiredDetectionTest.java @@ -41,6 +41,12 @@ public class UserInfoRequiredDetectionTest { quarkus.oidc.named-2.tenant-paths=/user-info/named-tenant-2 quarkus.oidc.named-2.discovery-enabled=false quarkus.oidc.named-2.jwks-path=protocol/openid-connect/certs + quarkus.oidc.named-3.auth-server-url=${quarkus.oidc.auth-server-url} + quarkus.oidc.named-3.tenant-paths=/user-info/named-tenant-3 + quarkus.oidc.named-3.discovery-enabled=false + quarkus.oidc.named-3.jwks-path=protocol/openid-connect/certs + quarkus.oidc.named-3.user-info-path=http://${quarkus.http.host}:${quarkus.http.port}/user-info-endpoint + quarkus.oidc.named-3.authentication.user-info-required=false quarkus.http.auth.proactive=false """), "application.properties")); @@ -63,6 +69,12 @@ public void testUserInfoNotRequiredWhenMissingUserInfoEndpoint() { .body(Matchers.is("false")); } + @Test + public void testUserInfoNotRequiredIfDisabledWhenUserInfoEndpointIsPresent() { + RestAssured.given().auth().oauth2(getAccessToken()).get("/user-info/named-tenant-3").then().statusCode(200) + .body(Matchers.is("false")); + } + private static String getAccessToken() { return new KeycloakTestClient().getAccessToken("alice", "alice", "quarkus-service-app", "secret", List.of("openid")); } @@ -111,6 +123,13 @@ public String getNamedTenantName() { public boolean getNamed2TenantUserInfoRequired() { return config.namedTenants.get("named-2").authentication.userInfoRequired.orElse(false); } + + @PermissionsAllowed("openid") + @Path("named-tenant-3") + @GET + public boolean getNamed3TenantUserInfoRequired() { + return config.namedTenants.get("named-3").authentication.userInfoRequired.orElse(false); + } } } diff --git a/extensions/oidc/deployment/src/test/resources/application-verify-injected-access-token-disabled.properties b/extensions/oidc/deployment/src/test/resources/application-verify-injected-access-token-disabled.properties new file mode 100644 index 0000000000000..5f262bf4b7779 --- /dev/null +++ b/extensions/oidc/deployment/src/test/resources/application-verify-injected-access-token-disabled.properties @@ -0,0 +1,5 @@ +quarkus.oidc.auth-server-url=${keycloak.url}/realms/quarkus +quarkus.oidc.client-id=quarkus-web-app +quarkus.oidc.credentials.secret=secret +quarkus.oidc.application-type=web-app +quarkus.oidc.authentication.verify-access-token=false diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcTenantConfig.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcTenantConfig.java index 301293de4d47a..8698275cb67af 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcTenantConfig.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/OidcTenantConfig.java @@ -982,16 +982,18 @@ public enum ResponseMode { /** * Both ID and access tokens are fetched from the OIDC provider as part of the authorization code flow. + *

* ID token is always verified on every user request as the primary token which is used * to represent the principal and extract the roles. - * Access token is not verified by default since it is meant to be propagated to the downstream services. - * The verification of the access token should be enabled if it is injected as a JWT token. - * - * Access tokens obtained as part of the code flow are always verified if `quarkus.oidc.roles.source` - * property is set to `accesstoken` which means the authorization decision is based on the roles extracted from the - * access token. - * - * Bearer access tokens are always verified. + *

+ * Authorization code flow access token is meant to be propagated to downstream services + * and is not verified by default unless `quarkus.oidc.roles.source` property is set to `accesstoken` + * which means the authorization decision is based on the roles extracted from the access token. + *

+ * Authorization code flow access token verification is also enabled if this token is injected as JsonWebToken. + * Set this property to `false` if it is not required. + *

+ * Bearer access token is always verified. */ @ConfigItem(defaultValueDocumentation = "true when access token is injected as the JsonWebToken bean, false otherwise") public boolean verifyAccessToken; @@ -1129,10 +1131,14 @@ public enum ResponseMode { /** * If this property is set to `true`, an OIDC UserInfo endpoint is called. - * This property is enabled if `quarkus.oidc.roles.source` is `userinfo`. - * or `quarkus.oidc.token.verify-access-token-with-user-info` is `true` + *

+ * This property is enabled automatically if `quarkus.oidc.roles.source` is set to `userinfo` + * or `quarkus.oidc.token.verify-access-token-with-user-info` is set to `true` * or `quarkus.oidc.authentication.id-token-required` is set to `false`, - * you do not need to enable this property manually in these cases. + * the current OIDC tenant must support a UserInfo endpoint in these cases. + *

+ * It is also enabled automatically if `io.quarkus.oidc.UserInfo` injection point is detected but only + * if the current OIDC tenant supports a UserInfo endpoint. */ @ConfigItem(defaultValueDocumentation = "true when UserInfo bean is injected, false otherwise") public Optional userInfoRequired = Optional.empty(); diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java index 161fbcd67e884..b23fd118c454c 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java @@ -338,7 +338,7 @@ public Uni apply(Throwable t) { .hasErrorCode(ErrorCodes.EXPIRED); if (!expired) { - LOG.errorf("ID token verification failure: %s", errorMessage(t)); + logAuthenticationError(context, t); return removeSessionCookie(context, configContext.oidcConfig) .replaceWith(Uni.createFrom() .failure(t @@ -837,7 +837,7 @@ public Throwable apply(Throwable tInner) { return tInner; } - LOG.errorf("ID token verification has failed: %s", errorMessage(tInner)); + logAuthenticationError(context, tInner); return new AuthenticationCompletionException(tInner); } }); @@ -846,6 +846,17 @@ public Throwable apply(Throwable tInner) { }); } + private static void logAuthenticationError(RoutingContext context, Throwable t) { + final String errorMessage = errorMessage(t); + final boolean accessTokenFailure = context.get(OidcConstants.ACCESS_TOKEN_VALUE) != null + && context.get(OidcUtils.CODE_ACCESS_TOKEN_RESULT) == null; + if (accessTokenFailure) { + LOG.errorf("Access token verification has failed: %s. ID token has not been verified yet", errorMessage); + } else { + LOG.errorf("ID token verification has failed: %s", errorMessage); + } + } + private static boolean prepareNonceForVerification(RoutingContext context, OidcTenantConfig oidcConfig, CodeAuthenticationStateBean stateBean, String idToken) { if (oidcConfig.authentication.nonceRequired) { From 6a8fd5c3b8f78c1a1926fb197156914cd4cfe886 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 May 2024 21:58:53 +0000 Subject: [PATCH 48/60] Bump com.amazonaws:aws-lambda-java-events from 3.11.4 to 3.11.5 Bumps [com.amazonaws:aws-lambda-java-events](https://github.com/aws/aws-lambda-java-libs) from 3.11.4 to 3.11.5. - [Commits](https://github.com/aws/aws-lambda-java-libs/commits) --- updated-dependencies: - dependency-name: com.amazonaws:aws-lambda-java-events dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] (cherry picked from commit 9e2ed7232d464b05e85e2693f7b7c8f2cb547528) --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index e76266ac23467..d20b9d282a5b0 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -154,7 +154,7 @@ 2.13.13 1.2.3 - 3.11.4 + 3.11.5 2.15.2 3.1.0 1.0.0 From 561da5c88c373c42668bc0370dcb625e30fc02b5 Mon Sep 17 00:00:00 2001 From: "David M. Lloyd" Date: Tue, 7 May 2024 08:06:04 -0500 Subject: [PATCH 49/60] Disable native when dev mode is used Fixes #40495 (cherry picked from commit 5370e591e5684ff782b90f5e5faa9fc968f01bea) --- .../java/io/quarkus/deployment/mutability/ReaugmentTask.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/mutability/ReaugmentTask.java b/core/deployment/src/main/java/io/quarkus/deployment/mutability/ReaugmentTask.java index 6037be04cd1da..1dff627633358 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/mutability/ReaugmentTask.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/mutability/ReaugmentTask.java @@ -55,6 +55,7 @@ public void accept(Path path) { final ApplicationModel existingModel = appModel.getAppModel(appRoot); System.setProperty("quarkus.package.jar.type", "mutable-jar"); + System.setProperty("quarkus.native.enabled", "false"); try (CuratedApplication bootstrap = QuarkusBootstrap.builder() .setAppArtifact(existingModel.getAppArtifact()) .setExistingModel(existingModel) From 339e1d6ef9f12d0b50137f71d4cec533b64a34bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 21:43:54 +0000 Subject: [PATCH 50/60] Bump commons-codec:commons-codec from 1.16.1 to 1.17.0 Bumps [commons-codec:commons-codec](https://github.com/apache/commons-codec) from 1.16.1 to 1.17.0. - [Changelog](https://github.com/apache/commons-codec/blob/master/RELEASE-NOTES.txt) - [Commits](https://github.com/apache/commons-codec/compare/rel/commons-codec-1.16.1...rel/commons-codec-1.17.0) --- updated-dependencies: - dependency-name: commons-codec:commons-codec dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] (cherry picked from commit d6eeddaf401c2681ac33fe026764e074efa83f5f) --- bom/application/pom.xml | 2 +- independent-projects/bootstrap/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index d20b9d282a5b0..21758802c8383 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -95,7 +95,7 @@ 2.17.0 1.0.0.Final 3.14.0 - 1.16.1 + 1.17.0 1.7.0