toPgConnectOptions(String dataSourceName, DataSou
pgConnectOptions.setReconnectInterval(dataSourceReactiveRuntimeConfig.reconnectInterval().toMillis());
- dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm().ifPresent(
- pgConnectOptions::setHostnameVerificationAlgorithm);
+ var algo = dataSourceReactiveRuntimeConfig.hostnameVerificationAlgorithm();
+ if ("NONE".equalsIgnoreCase(algo)) {
+ pgConnectOptions.setHostnameVerificationAlgorithm("");
+ } else {
+ pgConnectOptions.setHostnameVerificationAlgorithm(algo);
+ }
dataSourceReactiveRuntimeConfig.additionalProperties().forEach(pgConnectOptions::addProperty);
diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/VertxRedisClientFactory.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/VertxRedisClientFactory.java
index 7c89ffd7e8319..e21e438e23998 100644
--- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/VertxRedisClientFactory.java
+++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/VertxRedisClientFactory.java
@@ -116,7 +116,14 @@ private static NetClientOptions toNetClientOptions(RedisClientConfig config) {
tcp.alpn().ifPresent(net::setUseAlpn);
tcp.applicationLayerProtocols().ifPresent(net::setApplicationLayerProtocols);
tcp.connectionTimeout().ifPresent(d -> net.setConnectTimeout((int) d.toMillis()));
- tls.hostnameVerificationAlgorithm().ifPresent(net::setHostnameVerificationAlgorithm);
+
+ String verificationAlgorithm = tls.hostnameVerificationAlgorithm();
+ if ("NONE".equalsIgnoreCase(verificationAlgorithm)) {
+ net.setHostnameVerificationAlgorithm("");
+ } else {
+ net.setHostnameVerificationAlgorithm(verificationAlgorithm);
+ }
+
tcp.idleTimeout().ifPresent(d -> net.setIdleTimeout((int) d.toSeconds()));
tcp.keepAlive().ifPresent(b -> net.setTcpKeepAlive(true));
@@ -163,8 +170,6 @@ private static NetClientOptions toNetClientOptions(RedisClientConfig config) {
tcp.quickAck().ifPresent(net::setTcpQuickAck);
tcp.writeIdleTimeout().ifPresent(d -> net.setWriteIdleTimeout((int) d.toSeconds()));
- tls.hostnameVerificationAlgorithm().ifPresent(net::setHostnameVerificationAlgorithm);
-
return net;
}
diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/config/TlsConfig.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/config/TlsConfig.java
index 44a4126b52752..e8a6c46a75e95 100644
--- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/config/TlsConfig.java
+++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/client/config/TlsConfig.java
@@ -1,7 +1,5 @@
package io.quarkus.redis.runtime.client.config;
-import java.util.Optional;
-
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.vertx.core.runtime.config.JksConfiguration;
import io.quarkus.vertx.core.runtime.config.PemKeyCertConfiguration;
@@ -68,8 +66,12 @@ public interface TlsConfig {
/**
* The hostname verification algorithm to use in case the server's identity should be checked.
- * Should be HTTPS, LDAPS or an empty string.
+ * Should be {@code HTTPS}, {@code LDAPS} or an {@code NONE} (default).
+ *
+ * If set to {@code NONE}, it does not verify the hostname.
+ *
*/
- Optional hostnameVerificationAlgorithm();
+ @WithDefault("NONE")
+ String hostnameVerificationAlgorithm();
}
diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/CustomMessageBodyReaderUsesAnnotationsTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/CustomMessageBodyReaderUsesAnnotationsTest.java
new file mode 100644
index 0000000000000..eab0b5aced44b
--- /dev/null
+++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/CustomMessageBodyReaderUsesAnnotationsTest.java
@@ -0,0 +1,114 @@
+package io.quarkus.rest.client.reactive;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Type;
+import java.net.URI;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.ext.MessageBodyReader;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.test.QuarkusUnitTest;
+import io.quarkus.test.common.http.TestHTTPResource;
+
+public class CustomMessageBodyReaderUsesAnnotationsTest {
+
+ @RegisterExtension
+ static final QuarkusUnitTest TEST = new QuarkusUnitTest();
+
+ @TestHTTPResource
+ URI baseUri;
+
+ private Client client;
+
+ @BeforeEach
+ public void before() {
+ client = QuarkusRestClientBuilder.newBuilder()
+ .baseUri(baseUri)
+ .register(PersonMessageBodyReader.class)
+ .build(Client.class);
+ }
+
+ @Test
+ public void fromAnnotation() {
+ Person person = client.fromAnnotation();
+ assertEquals("from-annotation", person.name());
+ }
+
+ @Test
+ public void unset() {
+ Person person = client.unset();
+ assertEquals("unset", person.name());
+ }
+
+ @Path("test")
+ public interface Client {
+
+ @GET
+ @PersonName("from-annotation")
+ Person fromAnnotation();
+
+ @GET
+ Person unset();
+ }
+
+ @Path("test")
+ public static class Endpoint {
+
+ @GET
+ public Person get() {
+ return new Person("dummy");
+ }
+ }
+
+ public record Person(String name) {
+
+ }
+
+ @Documented
+ @Target({ ElementType.METHOD })
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface PersonName {
+ String value();
+ }
+
+ public static class PersonMessageBodyReader implements MessageBodyReader {
+
+ @Override
+ public boolean isReadable(Class> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+ return type.equals(Person.class);
+ }
+
+ @Override
+ public Person readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType,
+ MultivaluedMap httpHeaders, InputStream entityStream) {
+ PersonName personName = null;
+ if (annotations != null) {
+ for (Annotation annotation : annotations) {
+ if (annotation instanceof PersonName pn) {
+ personName = pn;
+ break;
+ }
+ }
+ }
+ if (personName == null) {
+ return new Person("unset");
+ }
+ return new Person(personName.value());
+ }
+ }
+}
diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/X509IdentityProvider.java b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/X509IdentityProvider.java
index d7bcff7deb67c..63d79961e261b 100644
--- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/X509IdentityProvider.java
+++ b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/X509IdentityProvider.java
@@ -60,7 +60,7 @@ private Set extractRoles(X509Certificate certificate, Map failureUniNonBlocking() {
+ return Uni.createFrom().failure(new BusinessException("boom"));
+ }
+
+ @Query
+ @Blocking
+ public Uni failureUniBlocking() {
+ return Uni.createFrom().failure(new BusinessException("boom"));
+ }
+
+ @Query
+ @NonBlocking
+ public String failureSyncNonBlocking() throws BusinessException {
+ throw new BusinessException("boom");
+ }
+
+ @Query
+ @Blocking
+ public String failureSyncBlocking() throws BusinessException {
+ throw new BusinessException("boom");
+ }
+
@Query
public TestPojo systemserror() {
throw new RuntimeException("Some system problem");
diff --git a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/spi/datafetcher/AbstractAsyncDataFetcher.java b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/spi/datafetcher/AbstractAsyncDataFetcher.java
index e443449ff4580..f5df3abed7ba7 100644
--- a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/spi/datafetcher/AbstractAsyncDataFetcher.java
+++ b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/spi/datafetcher/AbstractAsyncDataFetcher.java
@@ -2,6 +2,7 @@
import java.util.List;
import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutionException;
import jakarta.validation.ConstraintViolationException;
@@ -49,6 +50,9 @@ protected O invokeAndTransform(
deactivate(requestContext);
});
if (throwable != null) {
+ if (throwable instanceof ExecutionException && throwable.getCause() != null) {
+ throwable = throwable.getCause();
+ }
eventEmitter.fireOnDataFetchError(c, throwable);
if (throwable instanceof GraphQLException) {
GraphQLException graphQLException = (GraphQLException) throwable;
diff --git a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java
index eada958ba8648..b5134c2c1ca48 100644
--- a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java
+++ b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java
@@ -81,9 +81,9 @@
import io.smallrye.reactive.messaging.EmitterConfiguration;
import io.smallrye.reactive.messaging.Invoker;
import io.smallrye.reactive.messaging.annotations.Blocking;
-import io.smallrye.reactive.messaging.health.SmallRyeReactiveMessagingLivenessCheck;
-import io.smallrye.reactive.messaging.health.SmallRyeReactiveMessagingReadinessCheck;
-import io.smallrye.reactive.messaging.health.SmallRyeReactiveMessagingStartupCheck;
+import io.smallrye.reactive.messaging.extension.health.SmallRyeReactiveMessagingLivenessCheck;
+import io.smallrye.reactive.messaging.extension.health.SmallRyeReactiveMessagingReadinessCheck;
+import io.smallrye.reactive.messaging.extension.health.SmallRyeReactiveMessagingStartupCheck;
import io.smallrye.reactive.messaging.providers.extension.ChannelConfiguration;
public class SmallRyeReactiveMessagingProcessor {
diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfiguratorBase.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfiguratorBase.java
index 7f56930b8be3a..87cfaaca00bfd 100644
--- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfiguratorBase.java
+++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanConfiguratorBase.java
@@ -72,6 +72,7 @@ protected BeanConfiguratorBase(DotName implClazz) {
*/
public THIS read(BeanConfiguratorBase, ?> base) {
super.read(base);
+ identifier = base.identifier;
types.clear();
types.addAll(base.types);
qualifiers.clear();
diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContext.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContext.java
index 5150d282ca810..4f9af82270835 100644
--- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContext.java
+++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContext.java
@@ -157,6 +157,7 @@ public BootstrapMavenContext(BootstrapMavenContextConfig> config)
this.remoteRepos = config.remoteRepos;
this.remotePluginRepos = config.remotePluginRepos;
this.remoteRepoManager = config.remoteRepoManager;
+ this.settingsDecrypter = config.settingsDecrypter;
this.cliOptions = config.cliOptions;
this.excludeSisuBeanPackages = config.getExcludeSisuBeanPackages();
this.includeSisuBeanPackages = config.getIncludeSisuBeanPackages();
@@ -299,7 +300,7 @@ public List getRemotePluginRepositories() throws BootstrapMave
return remotePluginRepos == null ? remotePluginRepos = resolveRemotePluginRepos() : remotePluginRepos;
}
- private SettingsDecrypter getSettingsDecrypter() {
+ public SettingsDecrypter getSettingsDecrypter() {
if (settingsDecrypter == null) {
initRepoSystemAndManager();
}
diff --git a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContextConfig.java b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContextConfig.java
index 0f44dcc37a8ec..9cef569d8c6f4 100644
--- a/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContextConfig.java
+++ b/independent-projects/bootstrap/maven-resolver/src/main/java/io/quarkus/bootstrap/resolver/maven/BootstrapMavenContextConfig.java
@@ -9,6 +9,7 @@
import java.util.function.Function;
import org.apache.maven.model.Model;
+import org.apache.maven.settings.crypto.SettingsDecrypter;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.impl.RemoteRepositoryManager;
@@ -28,6 +29,7 @@ public class BootstrapMavenContextConfig remoteRepos;
protected List remotePluginRepos;
protected RemoteRepositoryManager remoteRepoManager;
+ protected SettingsDecrypter settingsDecrypter;
protected String alternatePomName;
protected File userSettings;
protected boolean artifactTransferLogging = true;
@@ -190,6 +192,18 @@ public T setRemoteRepositoryManager(RemoteRepositoryManager remoteRepoManager) {
return (T) this;
}
+ /**
+ * Settings decryptor
+ *
+ * @param settingsDecrypter settings decrypter
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ public T setSettingsDecrypter(SettingsDecrypter settingsDecrypter) {
+ this.settingsDecrypter = settingsDecrypter;
+ return (T) this;
+ }
+
/**
* The meaning of this option is equivalent to alternative POM in Maven,
* which can be specified with command line argument '-f'.
diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientResponseCompleteRestHandler.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientResponseCompleteRestHandler.java
index 873bb8fd62d19..36d165192f98b 100644
--- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientResponseCompleteRestHandler.java
+++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/handlers/ClientResponseCompleteRestHandler.java
@@ -76,6 +76,7 @@ public static ResponseImpl mapToResponse(RestClientRequestContext context,
Object fieldValue = context.readEntity(in,
fieldFiller.getFieldType(),
MediaType.valueOf(fieldFiller.getMediaType()),
+ context.getMethodDeclaredAnnotationsSafe(),
// FIXME: we have strings, it wants objects, perhaps there's
// an Object->String conversion too many
(MultivaluedMap) responseContext.getHeaders());
@@ -104,6 +105,7 @@ public static ResponseImpl mapToResponse(RestClientRequestContext context,
Object entity = context.readEntity(entityStream,
context.getResponseType(),
responseContext.getMediaType(),
+ context.getMethodDeclaredAnnotationsSafe(),
// FIXME: we have strings, it wants objects, perhaps there's
// an Object->String conversion too many
(MultivaluedMap) responseContext.getHeaders());
diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/MultiInvoker.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/MultiInvoker.java
index 4459e66000227..235a3937d0605 100644
--- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/MultiInvoker.java
+++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/MultiInvoker.java
@@ -318,7 +318,11 @@ public void handle(Buffer buffer) {
if (start < end) {
ByteArrayInputStream in = new ByteArrayInputStream(bytes, start, end);
- R item = restClientRequestContext.readEntity(in, responseType, mediaType,
+ R item = restClientRequestContext.readEntity(
+ in,
+ responseType,
+ mediaType,
+ restClientRequestContext.getMethodDeclaredAnnotationsSafe(),
response.getMetadata());
multiRequest.emitter.emit(item);
}
@@ -326,7 +330,11 @@ public void handle(Buffer buffer) {
}
} else {
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
- R item = restClientRequestContext.readEntity(in, responseType, mediaType,
+ R item = restClientRequestContext.readEntity(
+ in,
+ responseType,
+ mediaType,
+ restClientRequestContext.getMethodDeclaredAnnotationsSafe(),
response.getMetadata());
multiRequest.emitter.emit(item);
}
@@ -360,7 +368,10 @@ public void handle(Buffer chunk) {
ByteArrayInputStream in = new ByteArrayInputStream(chunk.getBytes());
try {
- R item = restClientRequestContext.readEntity(in, responseType, response.getMediaType(),
+ R item = restClientRequestContext.readEntity(in,
+ responseType,
+ response.getMediaType(),
+ restClientRequestContext.getMethodDeclaredAnnotationsSafe(),
response.getMetadata());
multiRequest.emit(item);
} catch (IOException e) {
diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientRequestContext.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientRequestContext.java
index a2f7e9fba9102..dca9e4fe33bf7 100644
--- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientRequestContext.java
+++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/RestClientRequestContext.java
@@ -167,6 +167,14 @@ public Method getInvokedMethod() {
return null;
}
+ public Annotation[] getMethodDeclaredAnnotationsSafe() {
+ Method invokedMethod = getInvokedMethod();
+ if (invokedMethod != null) {
+ return invokedMethod.getDeclaredAnnotations();
+ }
+ return null;
+ }
+
@Override
protected Throwable unwrapException(Throwable t) {
var res = super.unwrapException(t);
@@ -190,12 +198,14 @@ protected Throwable unwrapException(Throwable t) {
@SuppressWarnings("unchecked")
public T readEntity(InputStream in,
- GenericType responseType, MediaType mediaType,
+ GenericType responseType,
+ MediaType mediaType,
+ Annotation[] annotations,
MultivaluedMap metadata)
throws IOException {
if (in == null)
return null;
- return (T) ClientSerialisers.invokeClientReader(null, responseType.getRawType(), responseType.getType(),
+ return (T) ClientSerialisers.invokeClientReader(annotations, responseType.getRawType(), responseType.getType(),
mediaType, properties, this, metadata, restClient.getClientContext().getSerialisers(), in,
getReaderInterceptors(), configuration);
}
diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml
index 585fea1730e62..f2797fbbf7b36 100644
--- a/independent-projects/resteasy-reactive/pom.xml
+++ b/independent-projects/resteasy-reactive/pom.xml
@@ -61,7 +61,7 @@
3.2.5
2.5.7
2.1.2
- 4.5.3
+ 4.5.4
5.4.0
1.0.0.Final
2.16.1
diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/ResponseBuilderImpl.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/ResponseBuilderImpl.java
index f1a546ff5c846..7b04ab947a610 100644
--- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/ResponseBuilderImpl.java
+++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/ResponseBuilderImpl.java
@@ -28,7 +28,7 @@ public Response.ResponseBuilder location(URI location) {
try {
String host = req.getRequestHost();
int port = -1;
- int index = host.indexOf(":");
+ int index = host.lastIndexOf(":");
if (index > -1) {
port = Integer.parseInt(host.substring(index + 1));
host = host.substring(0, index);
@@ -69,7 +69,7 @@ public Response.ResponseBuilder contentLocation(URI location) {
try {
String host = req.getRequestHost();
int port = -1;
- int index = host.indexOf(":");
+ int index = host.lastIndexOf(":");
if (index > -1) {
port = Integer.parseInt(host.substring(index + 1));
host = host.substring(0, index);
diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/RestResponseBuilderImpl.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/RestResponseBuilderImpl.java
index a62003cbef6a5..43c98757afe21 100644
--- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/RestResponseBuilderImpl.java
+++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/RestResponseBuilderImpl.java
@@ -28,7 +28,7 @@ public RestResponse.ResponseBuilder location(URI location) {
try {
String host = req.getRequestHost();
int port = -1;
- int index = host.indexOf(":");
+ int index = host.lastIndexOf(":");
if (index > -1) {
port = Integer.parseInt(host.substring(index + 1));
host = host.substring(0, index);
@@ -69,7 +69,7 @@ public RestResponse.ResponseBuilder contentLocation(URI location) {
try {
String host = req.getRequestHost();
int port = -1;
- int index = host.indexOf(":");
+ int index = host.lastIndexOf(":");
if (index > -1) {
port = Integer.parseInt(host.substring(index + 1));
host = host.substring(0, index);
diff --git a/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/AdminResource.java b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/AdminResource.java
index 3a6e8c4eb2825..59664d4e21510 100644
--- a/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/AdminResource.java
+++ b/integration-tests/oidc-wiremock/src/main/java/io/quarkus/it/keycloak/AdminResource.java
@@ -61,6 +61,14 @@ public String bearerCertificateFullChain() {
return "granted:" + identity.getRoles();
}
+ @Path("bearer-certificate-full-chain-root-only")
+ @GET
+ @RolesAllowed("admin")
+ @Produces(MediaType.APPLICATION_JSON)
+ public String bearerCertificateFullChainRootOnly() {
+ return "granted:" + identity.getRoles();
+ }
+
@Path("bearer-kid-or-chain")
@GET
@RolesAllowed("admin")
diff --git a/integration-tests/oidc-wiremock/src/main/resources/application.properties b/integration-tests/oidc-wiremock/src/main/resources/application.properties
index f78e4dc9dd018..f775048d5ac31 100644
--- a/integration-tests/oidc-wiremock/src/main/resources/application.properties
+++ b/integration-tests/oidc-wiremock/src/main/resources/application.properties
@@ -104,7 +104,6 @@ quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.credentials.client-sec
quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.credentials.client-secret.method=query
quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.certificate-chain.trust-store-file=truststore.p12
quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.certificate-chain.trust-store-password=storepassword
-quarkus.oidc.code-flow-user-info-github-cached-in-idtoken.certificate-chain.trust-store-cert-alias=fullchain
quarkus.oidc.code-flow-token-introspection.provider=github
@@ -134,7 +133,6 @@ quarkus.oidc.bearer-kid-or-chain.token.audience=https://service.example.com
quarkus.oidc.bearer-kid-or-chain.allow-token-introspection-cache=false
quarkus.oidc.bearer-kid-or-chain.certificate-chain.trust-store-file=truststore.p12
quarkus.oidc.bearer-kid-or-chain.certificate-chain.trust-store-password=storepassword
-quarkus.oidc.bearer-kid-or-chain.certificate-chain.trust-store-cert-alias=fullchain
quarkus.oidc.bearer-id.auth-server-url=${keycloak.url}/realms/quarkus/
quarkus.oidc.bearer-id.client-id=quarkus-app
@@ -156,7 +154,6 @@ quarkus.oidc.bearer-azure.token.lifespan-grace=2147483647
quarkus.oidc.bearer-azure.token.customizer-name=azure-access-token-customizer
quarkus.oidc.bearer-azure.certificate-chain.trust-store-file=truststore.p12
quarkus.oidc.bearer-azure.certificate-chain.trust-store-password=storepassword
-quarkus.oidc.bearer-azure.certificate-chain.trust-store-cert-alias=fullchain
quarkus.oidc.bearer-role-claim-path.auth-server-url=${keycloak.url}/realms/quarkus/
quarkus.oidc.bearer-role-claim-path.client-id=quarkus-app
@@ -173,7 +170,14 @@ quarkus.oidc.bearer-no-introspection.token.allow-jwt-introspection=false
quarkus.oidc.bearer-certificate-full-chain.certificate-chain.trust-store-file=truststore.p12
quarkus.oidc.bearer-certificate-full-chain.certificate-chain.trust-store-password=storepassword
-quarkus.oidc.bearer-certificate-full-chain.certificate-chain.trust-store-cert-alias=fullchain
+
+quarkus.oidc.bearer-certificate-full-chain-root-only.certificate-chain.trust-store-file=truststore-rootcert.p12
+quarkus.oidc.bearer-certificate-full-chain-root-only.certificate-chain.trust-store-password=storepassword
+quarkus.oidc.bearer-certificate-full-chain-root-only.certificate-chain.leaf-certificate-name=www.quarkustest.com
+
+quarkus.oidc.bearer-certificate-full-chain-root-only-wrongcname.certificate-chain.trust-store-file=truststore-rootcert.p12
+quarkus.oidc.bearer-certificate-full-chain-root-only-wrongcname.certificate-chain.trust-store-password=storepassword
+quarkus.oidc.bearer-certificate-full-chain-root-only-wrongcname.certificate-chain.leaf-certificate-name=www.quarkusio.com
quarkus.oidc.bearer-key-without-kid-thumbprint.auth-server-url=${keycloak.url}/realms/quarkus/
quarkus.oidc.bearer-key-without-kid-thumbprint.discovery-enabled=false
diff --git a/integration-tests/oidc-wiremock/src/main/resources/truststore-rootcert.p12 b/integration-tests/oidc-wiremock/src/main/resources/truststore-rootcert.p12
new file mode 100644
index 0000000000000..e6a5a80173a45
Binary files /dev/null and b/integration-tests/oidc-wiremock/src/main/resources/truststore-rootcert.p12 differ
diff --git a/integration-tests/oidc-wiremock/src/main/resources/truststore.p12 b/integration-tests/oidc-wiremock/src/main/resources/truststore.p12
index 81b0be2ede57e..b0c1f8bcb4164 100644
Binary files a/integration-tests/oidc-wiremock/src/main/resources/truststore.p12 and b/integration-tests/oidc-wiremock/src/main/resources/truststore.p12 differ
diff --git a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/BearerTokenAuthorizationTest.java b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/BearerTokenAuthorizationTest.java
index ac7ddb516d975..bee2e7e42f83f 100644
--- a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/BearerTokenAuthorizationTest.java
+++ b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/BearerTokenAuthorizationTest.java
@@ -65,12 +65,12 @@ public void testAccessResourceAzure() throws Exception {
wireMockServer.stubFor(WireMock.get("/auth/azure/jwk")
.withHeader("Authorization", matching("Access token: " + azureToken))
.willReturn(WireMock.aResponse().withBody(azureJwk)));
- // RestAssured.given().auth().oauth2(azureToken)
- // .when().get("/api/admin/bearer-azure")
- // .then()
- // .statusCode(200)
- // .body(Matchers.equalTo(
- // "Name:jondoe@quarkusoidctest.onmicrosoft.com,Issuer:https://sts.windows.net/e7861267-92c5-4a03-bdb2-2d3e491e7831/"));
+ RestAssured.given().auth().oauth2(azureToken)
+ .when().get("/api/admin/bearer-azure")
+ .then()
+ .statusCode(200)
+ .body(Matchers.equalTo(
+ "Name:jondoe@quarkusoidctest.onmicrosoft.com,Issuer:https://sts.windows.net/e7861267-92c5-4a03-bdb2-2d3e491e7831/"));
String accessTokenWithCert = TestUtils.createTokenWithInlinedCertChain("alice-certificate");
@@ -217,7 +217,7 @@ public void testAccessAdminResourceWithFullCertChain() throws Exception {
.then()
.statusCode(401);
- // Send the token with the valid certificates but which are are in the wrong order in the chain
+ // Send the token with the valid certificates but which are in the wrong order in the chain
accessToken = getAccessTokenWithCertChain(
List.of(intermediateCert, subjectCert, rootCert),
subjectPrivateKey);
@@ -246,6 +246,58 @@ public void testAccessAdminResourceWithFullCertChain() throws Exception {
}
+ @Test
+ public void testFullCertChainWithOnlyRootInTruststore() throws Exception {
+ X509Certificate rootCert = KeyUtils.getCertificate(ResourceUtils.readResource("/ca.cert.pem"));
+ X509Certificate intermediateCert = KeyUtils.getCertificate(ResourceUtils.readResource("/intermediate.cert.pem"));
+ X509Certificate subjectCert = KeyUtils.getCertificate(ResourceUtils.readResource("/www.quarkustest.com.cert.pem"));
+ PrivateKey subjectPrivateKey = KeyUtils.readPrivateKey("/www.quarkustest.com.key.pem");
+
+ // Send the token with the valid certificate chain
+ String accessToken = getAccessTokenWithCertChain(
+ List.of(subjectCert, intermediateCert, rootCert),
+ subjectPrivateKey);
+
+ RestAssured.given().auth().oauth2(accessToken)
+ .when().get("/api/admin/bearer-certificate-full-chain-root-only")
+ .then()
+ .statusCode(200)
+ .body(Matchers.containsString("admin"));
+
+ // Send the same token to the service expecting a different leaf certificate name
+ RestAssured.given().auth().oauth2(accessToken)
+ .when().get("/api/admin/bearer-certificate-full-chain-root-only-wrongcname")
+ .then()
+ .statusCode(401);
+
+ // Send the token with the valid certificates but which are in the wrong order in the chain
+ accessToken = getAccessTokenWithCertChain(
+ List.of(intermediateCert, subjectCert, rootCert),
+ subjectPrivateKey);
+ RestAssured.given().auth().oauth2(accessToken)
+ .when().get("/api/admin/bearer-certificate-full-chain-root-only")
+ .then()
+ .statusCode(401);
+
+ // Send the token with the valid certificates but with the intermediate one omitted from the chain
+ accessToken = getAccessTokenWithCertChain(
+ List.of(subjectCert, rootCert),
+ subjectPrivateKey);
+ RestAssured.given().auth().oauth2(accessToken)
+ .when().get("/api/admin/bearer-certificate-full-chain-root-only")
+ .then()
+ .statusCode(401);
+
+ // Send the token with the only the last valid certificate
+ accessToken = getAccessTokenWithCertChain(
+ List.of(subjectCert),
+ subjectPrivateKey);
+ RestAssured.given().auth().oauth2(accessToken)
+ .when().get("/api/admin/bearer-certificate-full-chain-root-only")
+ .then()
+ .statusCode(401);
+ }
+
@Test
public void testAccessAdminResourceWithKidOrChain() throws Exception {
// token with a matching kid, not x5c
diff --git a/test-framework/jacoco/runtime/src/main/java/io/quarkus/jacoco/runtime/JacocoConfig.java b/test-framework/jacoco/runtime/src/main/java/io/quarkus/jacoco/runtime/JacocoConfig.java
index 86aaf74c3f5fb..307c58c6e7fd9 100644
--- a/test-framework/jacoco/runtime/src/main/java/io/quarkus/jacoco/runtime/JacocoConfig.java
+++ b/test-framework/jacoco/runtime/src/main/java/io/quarkus/jacoco/runtime/JacocoConfig.java
@@ -17,8 +17,7 @@ public class JacocoConfig {
public static final String TARGET_JACOCO_REPORT = "target/" + JACOCO_REPORT;
/**
- * Whether or not the jacoco extension is enabled. Disabling it can come in handy when runnig tests in IDEs that do their
- * own jacoco instrumentation, e.g. EclEmma in Eclipse.
+ * Whether or not the jacoco extension is enabled.
*/
@ConfigItem(defaultValue = "true")
public boolean enabled;