Skip to content

Commit

Permalink
Merge pull request #40747 from gsmet/3.11.0-backports-1
Browse files Browse the repository at this point in the history
[3.11] 3.11.0 backports 1
  • Loading branch information
gsmet authored May 22, 2024
2 parents 1b42a51 + 1f325e7 commit f11c627
Show file tree
Hide file tree
Showing 65 changed files with 1,374 additions and 200 deletions.
8 changes: 4 additions & 4 deletions bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
<slf4j-jboss-logmanager.version>2.0.0.Final</slf4j-jboss-logmanager.version>
<wildfly-common.version>1.7.0.Final</wildfly-common.version>
<wildfly-client-config.version>1.0.1.Final</wildfly-client-config.version>
<wildfly-elytron.version>2.4.1.Final</wildfly-elytron.version>
<wildfly-elytron.version>2.4.2.Final</wildfly-elytron.version>
<jboss-threads.version>3.6.1.Final</jboss-threads.version>
<vertx.version>4.5.7</vertx.version>
<httpclient.version>4.5.14</httpclient.version>
Expand Down Expand Up @@ -197,16 +197,16 @@
<picocli.version>4.7.6</picocli.version>
<google-cloud-functions.version>1.1.0</google-cloud-functions.version>
<commons-compress.version>1.26.1</commons-compress.version> <!-- Please check with Java Operator SDK / Fabric8 team before updating -->
<commons-text.version>1.11.0</commons-text.version>
<commons-text.version>1.12.0</commons-text.version>
<gson.version>2.10.1</gson.version>
<log4j2-jboss-logmanager.version>1.1.2.Final</log4j2-jboss-logmanager.version>
<log4j2-api.version>2.23.1</log4j2-api.version>
<log4j-jboss-logmanager.version>1.3.0.Final</log4j-jboss-logmanager.version>
<avro.version>1.11.3</avro.version>
<apicurio-registry.version>2.5.10.Final</apicurio-registry.version>
<apicurio-common-rest-client.version>0.1.18.Final</apicurio-common-rest-client.version> <!-- must be the version Apicurio Registry uses -->
<testcontainers.version>1.19.7</testcontainers.version> <!-- Make sure to also update docker-java.version to match its needs -->
<docker-java.version>3.3.5</docker-java.version> <!-- must be the version Testcontainers use -->
<testcontainers.version>1.19.8</testcontainers.version> <!-- Make sure to also update docker-java.version to match its needs -->
<docker-java.version>3.3.6</docker-java.version> <!-- must be the version Testcontainers use -->
<!-- Check the compatibility matrix (https://github.com/opensearch-project/opensearch-testcontainers) before upgrading: -->
<opensearch-testcontainers.version>2.0.0</opensearch-testcontainers.version>
<com.dajudge.kindcontainer>1.4.5</com.dajudge.kindcontainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,11 @@ public Throwable getDeploymentProblem() {
@Override
public void setRemoteProblem(Throwable throwable) {
compileProblem = throwable;
getCompileOutput().setMessage(throwable.getMessage());
if (throwable == null) {
getCompileOutput().setMessage(null);
} else {
getCompileOutput().setMessage(throwable.getMessage());
}
}

private StatusLine getCompileOutput() {
Expand Down Expand Up @@ -561,9 +565,7 @@ public boolean doScan(boolean userInitiated, boolean forceRestart) {
return true;
} else if (!filesChanged.isEmpty()) {
try {
for (Consumer<Set<String>> consumer : noRestartChangesConsumers) {
consumer.accept(filesChanged);
}
notifyExtensions(filesChanged);
hotReloadProblem = null;
getCompileOutput().setMessage(null);
} catch (Throwable t) {
Expand All @@ -585,6 +587,30 @@ public boolean doScan(boolean userInitiated, boolean forceRestart) {
}
}

/**
* This notifies registered extensions of "no-restart" changed files.
*
* @param noRestartChangedFiles the Set of changed files
*/
public void notifyExtensions(Set<String> noRestartChangedFiles) {
if (lastStartIndex == null) {
// we don't notify extensions if the application never started
return;
}
scanLock.lock();
codeGenLock.lock();
try {

for (Consumer<Set<String>> consumer : noRestartChangesConsumers) {
consumer.accept(noRestartChangedFiles);
}
} finally {
scanLock.unlock();
codeGenLock.unlock();
}

}

public boolean instrumentationEnabled() {
if (instrumentationEnabled != null) {
return instrumentationEnabled;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ public BuildCommandArgs prepareAction(String action, BuildOptions buildOptions,

if (buildOptions.buildNative) {
args.add("-Dquarkus.native.enabled=true");
args.add("-Dquarkus.jar.enabled=false");
args.add("-Dquarkus.package.jar.enabled=false");
}
if (buildOptions.skipTests()) {
setSkipTests(args);
Expand Down
2 changes: 1 addition & 1 deletion devtools/gradle/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
id("com.gradle.develocity") version "3.17.3"
id("com.gradle.develocity") version "3.17.4"
}

develocity {
Expand Down
7 changes: 4 additions & 3 deletions docs/src/main/asciidoc/amqp.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,10 @@ Quarkus has built-in capabilities to deal with JSON AMQP messages.
[NOTE]
.@RegisterForReflection
====
The `@RegisterForReflection` annotation instructs Quarkus to include the class (including fields and methods) when building the native executable.
This will be useful later when we run the applications as native executables inside containers.
Without, the native compilation would remove the fields and methods during the dead-code elimination phase.
The `@RegisterForReflection` annotation instructs Quarkus to keep the class, its fields, and methods when creating a native executable.
This is crucial when we later run our applications as native executables within containers.
Without this annotation, the native compilation process would discard the fields and methods during the dead-code elimination phase, which would lead to runtime errors.
More details about the `@RegisterForReflection` annotation can be found on the xref:writing-native-applications-tips.adoc#registerForReflection[native application tips] page.
====

== Sending quote request
Expand Down
1 change: 1 addition & 0 deletions docs/src/main/asciidoc/cache.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1075,3 +1075,4 @@ When you encounter this error, you can easily fix it by adding the following ann
<1> It is an array, so you can register several cache implementations in one go if your configuration requires several of them.

This annotation will register the cache implementation classes for reflection and this will include the classes into the native executable.
More details about the `@RegisterForReflection` annotation can be found on the xref:writing-native-applications-tips.adoc#registerForReflection[native application tips] page.
2 changes: 1 addition & 1 deletion docs/src/main/asciidoc/mongodb.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -701,7 +701,7 @@ Currently, Quarkus doesn't support link:https://docs.mongodb.com/manual/core/sec
====
If you encounter the following error when running your application in native mode: +
`Failed to encode 'MyObject'. Encoding 'myVariable' errored with: Can't find a codec for class org.acme.MyVariable.` +
This means that the `org.acme.MyVariable` class is not known to GraalVM, the remedy is to add the `@RegisterForReflection` annotation to your `MyVariable class`.
This means that the `org.acme.MyVariable` class is not known to GraalVM, the remedy is to add the `@RegisterForReflection` annotation to your `MyVariable` class.
More details about the `@RegisterForReflection` annotation can be found on the xref:writing-native-applications-tips.adoc#registerForReflection[native application tips] page.
====

Expand Down
2 changes: 1 addition & 1 deletion docs/src/main/asciidoc/qute-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2538,7 +2538,7 @@ There are several ways to solve this problem:
** In this case, an optimized value resolver is generated automatically and used at runtime
** This is the preferred solution
* Annotate the model class with <<template_data,`@TemplateData`>> - a specialized value resolver is generated and used at runtime
* Annotate the model class with `@io.quarkus.runtime.annotations.RegisterForReflection` to make the reflection-based value resolver work
* Annotate the model class with `@io.quarkus.runtime.annotations.RegisterForReflection` to make the reflection-based value resolver work. More details about the `@RegisterForReflection` annotation can be found on the xref:writing-native-applications-tips.adoc#registerForReflection[native application tips] page.


[[rest_integration]]
Expand Down
7 changes: 4 additions & 3 deletions docs/src/main/asciidoc/rabbitmq.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,10 @@ Quarkus has built-in capabilities to deal with JSON RabbitMQ messages.
[NOTE]
.@RegisterForReflection
====
The `@RegisterForReflection` annotation instructs Quarkus to include the class (including fields and methods) when building the native executable.
This will be useful later when we run the applications as native executables inside containers.
Without, the native compilation would remove the fields and methods during the dead-code elimination phase.
The `@RegisterForReflection` annotation instructs Quarkus to keep the class, its fields, and methods when creating a native executable.
This is crucial when we later run our applications as native executables within containers.
Without this annotation, the native compilation process would discard the fields and methods during the dead-code elimination phase, which would lead to runtime errors.
More details about the `@RegisterForReflection` annotation can be found on the xref:writing-native-applications-tips.adoc#registerForReflection[native application tips] page.
====

== Sending quote request
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -882,7 +882,7 @@ public class MediaLibraryPermission extends LibraryPermission {
}
----
<1> When building a native executable, the permission class must be registered for reflection unless it is also used in at least one `io.quarkus.security.PermissionsAllowed#name` parameter.
<1> When building a native executable, the permission class must be registered for reflection unless it is also used in at least one `io.quarkus.security.PermissionsAllowed#name` parameter. More details about the `@RegisterForReflection` annotation can be found on the xref:writing-native-applications-tips.adoc#registerForReflection[native application tips] page.
<2> We want to pass the `MediaLibrary` instance to the `LibraryPermission` constructor.

[source,properties]
Expand Down
129 changes: 129 additions & 0 deletions docs/src/main/asciidoc/security-oidc-code-flow-authentication.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ For example, `quarkus.oidc.authentication.redirect-path=/service/callback`, and
If `quarkus.oidc.authentication.redirect-path` is set, but you need the original request URL to be restored after the user is redirected back to a unique callback URL, for example, `http://localhost:8080/service/callback`, set `quarkus.oidc.authentication.restore-path-after-redirect` property to `true`.
This will restore the request URL such as `http://localhost:8080/service/1`.

[[customize-authentication-requests]]
==== Customizing authentication requests

By default, only the `response_type` (set to `code`), `scope` (set to `openid`), `client_id`, `redirect_uri`, and `state` properties are passed as HTTP query parameters to the OIDC provider's authorization endpoint when the user is redirected to it to authenticate.
Expand All @@ -398,6 +399,8 @@ The following example shows how you can work around this issue:
quarkus.oidc.authentication.extra-params.response_mode=query
----

See also the <<oidc-redirect-filters>> section explaining how a custom `OidcRedirectFilter` can be used to customize OIDC redirects, including those to the OIDC authorization endpoint.

==== Customizing the authentication error response

When the user is redirected to the OIDC authorization endpoint to authenticate and, if necessary, authorize the Quarkus application, this redirect request might fail, for example, when an invalid scope is included in the redirect URI.
Expand All @@ -422,6 +425,130 @@ For example, if it is set to '/error' and the current request URI is `https://lo
To prevent the user from being redirected to this page to be re-authenticated, ensure that this error endpoint is a public resource.
====

[[oidc-redirect-filters]]
=== OIDC redirect filters

You can register one or more `io.quarkus.oidc.OidcRedirectFilter` implementations to filter OIDC redirects to OIDC authorization and logout endpoints but also local redirects to custom error and session expired pages. Custom `OidcRedirectFilter` can add additional query parameters, response headers and set new cookies.

For example, the following simple custom `OidcRedirectFilter` adds an additional query parameter and a custom response header for all redirect requests that can be done by Quarkus OIDC:

[source,java]
----
package io.quarkus.it.keycloak;
import jakarta.enterprise.context.ApplicationScoped;
import io.quarkus.arc.Unremovable;
import io.quarkus.oidc.OidcRedirectFilter;
@ApplicationScoped
@Unremovable
public class GlobalOidcRedirectFilter implements OidcRedirectFilter {
@Override
public void filter(OidcRedirectContext context) {
if (context.redirectUri().contains("/session-expired-page")) {
context.additionalQueryParams().add("redirect-filtered", "true,"); <1>
context.routingContext().response().putHeader("Redirect-Filtered", "true"); <2>
}
}
}
----
<1> Add an additional query parameter. Note the queury names and values are URL-encoded by Quarkus OIDC, a `redirect-filtered=true%20C` query parameter is added to the redirect URI in this case.
<2> Add a custom HTTP response header.

See also the <<customize-authentication-requests>> section how to configure additional query parameters for OIDC authorization point.

Custom `OidcRedirectFilter` for local error and session expired pages can also create secure cookies to help with generating such pages.

For example, let's assume you need to redirect the current user whose session has expired to a custom session expired page available at `http://localhost:8080/session-expired-page`. The following custom `OidcRedirectFilter` encrypts the user name in a custom `session_expired` cookie using an OIDC tenant client secret:

[source,java]
----
package io.quarkus.it.keycloak;
import jakarta.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.jwt.Claims;
import io.quarkus.arc.Unremovable;
import io.quarkus.oidc.AuthorizationCodeTokens;
import io.quarkus.oidc.OidcRedirectFilter;
import io.quarkus.oidc.TenantFeature;
import io.quarkus.oidc.runtime.OidcUtils;
import io.smallrye.jwt.build.Jwt;
@ApplicationScoped
@Unremovable
@TenantFeature("tenant-refresh")
public class SessionExpiredOidcRedirectFilter implements OidcRedirectFilter {
@Override
public void filter(OidcRedirectContext context) {
if (context.redirectUri().contains("/session-expired-page")) {
AuthorizationCodeTokens tokens = context.routingContext().get(AuthorizationCodeTokens.class.getName()); <1>
String userName = OidcUtils.decodeJwtContent(tokens.getIdToken()).getString(Claims.preferred_username.name()); <2>
String jwe = Jwt.preferredUserName(userName).jwe()
.encryptWithSecret(context.oidcTenantConfig().credentials.secret.get()); <3>
OidcUtils.createCookie(context.routingContext(), context.oidcTenantConfig(), "session_expired",
jwe + "|" + context.oidcTenantConfig().tenantId.get(), 10); <4>
}
}
}
----
<1> Access `AuthorizationCodeTokens` tokens associated with the now expired session as a `RoutingContext` attribute.
<2> Decode ID token claims and get a user name.
<3> Save the user name in a JWT token encrypted with the current OIDC tenant's client secret.
<4> Create a custom `session_expired` cookie valid for 5 seconds which joins the encrypted token and a tenant id using a "|" separator. Recording a tenant id in a custom cookie can help to generate correct session expired pages in a multi-tenant OIDC setup.

Next, a public JAX-RS resource which generates session expired pages can use this cookie to create a page tailored for this user and the corresponding OIDC tenant, for example:

[source,java]
----
package io.quarkus.it.keycloak;
import jakarta.inject.Inject;
import jakarta.ws.rs.CookieParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.eclipse.microprofile.jwt.Claims;
import org.eclipse.microprofile.jwt.JsonWebToken;
import io.quarkus.oidc.OidcTenantConfig;
import io.quarkus.oidc.runtime.TenantConfigBean;
import io.smallrye.jwt.auth.principal.DefaultJWTParser;
import io.vertx.ext.web.RoutingContext;
@Path("/session-expired-page")
public class SessionExpiredResource {
@Inject
TenantConfigBean tenantConfig; <1>
@GET
public String sessionExpired(@CookieParam("session_expired") String sessionExpired) throws Exception {
// Cookie format: jwt|<tenant id>
String[] pair = sessionExpired.split("\\|"); <2>
OidcTenantConfig oidcConfig = tenantConfig.getStaticTenantsConfig().get(pair[1]).getOidcTenantConfig(); <3>
JsonWebToken jwt = new DefaultJWTParser().decrypt(pair[0], oidcConfig.credentials.secret.get()); <4>
OidcUtils.removeCookie(context, oidcConfig, "session_expired"); <5>
return jwt.getClaim(Claims.preferred_username) + ", your session has expired. "
+ "Please login again at http://localhost:8081/" + oidcConfig.tenantId.get(); <6>
}
}
----
<1> Inject `TenantConfigBean` which can be used to access all the current OIDC tenant configurations.
<2> Split the custom cookie value into 2 parts, first part is the encrypted token, last part is the tenant id.
<3> Get the OIDC tenant configuration.
<4> Decrypt the cookie value using the OIDC tenant's client secret.
<5> Remove the custom cookie.
<6> Use the username in the decrypted token and the tenant id to generate the service expired page response.

=== Accessing authorization data

You can access information about authorization in different ways.
Expand Down Expand Up @@ -1110,6 +1237,8 @@ When the session can not be refreshed, the currently authenticated user is redir
Instead, you can request that the user is redirected to a public, application specific session expired page first. This page informs the user that the session has now expired and advise to re-authenticate by following a link to a secured application welcome page. The user clicks on the link and Quarkus OIDC enforces a redirect to the OIDC provider to re-authenticate. Use `quarkus.oidc.authentication.session-expired-page` relative path property, if you'd like to do it.
For example, setting `quarkus.oidc.authentication.session-expired-page=/session-expired-page` will ensure that the user whose session has expired is redirected to `http://localhost:8080/session-expired-page`, assuming the application is available at `http://localhost:8080`.
See also the <<oidc-redirect-filters>> section explaining how a custom `OidcRedirectFilter` can be used to customize OIDC redirects, including those to the session expired pages.
====


Expand Down
6 changes: 6 additions & 0 deletions docs/src/main/asciidoc/security-overview.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ For guidance on testing Quarkus Security features and ensuring that your Quarkus

== More about security features in Quarkus

=== WebSockets Next security

The `quarkus-websockets-next` extension provides a modern, efficient implementation of the WebSocket API.
It also provides an integration with Quarkus security.
For more information, see the xref:websockets-next-reference.adoc#websocket-next-security[Security] section of the Quarkus "WebSockets Next reference" guide.

[[cross-origin-resource-sharing]]
=== Cross-origin resource sharing

Expand Down
Loading

0 comments on commit f11c627

Please sign in to comment.