diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 44948bf0ca6b7..2ee4b572e3bb5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -74,6 +74,7 @@ updates: # RX Java 2 - dependency-name: io.reactivex.rxjava2:rxjava # Test dependencies + - dependency-name: net.sourceforge.htmlunit:htmlunit - dependency-name: io.rest-assured:* - dependency-name: org.junit:junit-bom - dependency-name: org.junit.jupiter:* diff --git a/bom/application/pom.xml b/bom/application/pom.xml index a51f41a83f576..ab2df2c0b3d83 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -86,7 +86,7 @@ 22.3.0 ${graal-sdk.version} 1.6.0.Final - 2.14.1 + 2.14.2 1.0.0.Final 3.12.0 1.15 @@ -122,7 +122,7 @@ 2.3.2 2.1.214 42.5.1 - 3.1.1 + 3.1.2 8.0.30 11.2.3.jre11 1.6.7 @@ -131,7 +131,7 @@ 11.5.8.0 1.2.6 4.5.1 - 5.9.1 + 5.9.2 1.5.0 6.14.2 14.0.6.Final @@ -141,7 +141,7 @@ 1.8.0 1.0.3 3.5.0.Final - 1.8.0 + 1.9.0 3.3.2 1.8.0 1.1.8.4 @@ -188,7 +188,7 @@ 0.23.0 1.42.3 2.1 - 4.7.0 + 4.7.1 1.0.4 1.22 1.10.0 diff --git a/build-parent/pom.xml b/build-parent/pom.xml index 7735ec174fae5..cb4600cfb6af7 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -38,7 +38,7 @@ 1.0.0 2.5.7 - 2.40.0 + 2.70.0 3.24.2 2.0.3.Final 5.2.8 diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java index ee4fd9f4100fc..499ca60d7a3cc 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/extension/QuarkusPluginExtension.java @@ -3,7 +3,6 @@ import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Objects; @@ -19,6 +18,7 @@ import org.gradle.api.file.FileCollection; import org.gradle.api.file.RegularFile; import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; import org.gradle.api.tasks.SourceSet; @@ -39,7 +39,7 @@ public class QuarkusPluginExtension { private final Property finalName; - private Map quarkusBuildProperties; + private final MapProperty quarkusBuildProperties; private final SourceSetExtension sourceSetExtension; public QuarkusPluginExtension(Project project) { @@ -49,7 +49,7 @@ public QuarkusPluginExtension(Project project) { finalName.convention(project.provider(() -> String.format("%s-%s", project.getName(), project.getVersion()))); this.sourceSetExtension = new SourceSetExtension(); - quarkusBuildProperties = new HashMap<>(); + this.quarkusBuildProperties = project.getObjects().mapProperty(String.class, String.class); } public void beforeTest(Test task) { @@ -99,7 +99,7 @@ public void beforeTest(Test task) { public String buildNativeRunnerName(final Map taskSystemProps) { Properties properties = new Properties(taskSystemProps.size()); properties.putAll(taskSystemProps); - quarkusBuildProperties.entrySet() + quarkusBuildProperties.get().entrySet() .forEach(buildEntry -> properties.putIfAbsent(buildEntry.getKey(), buildEntry.getValue())); System.getProperties().entrySet() .forEach(propEntry -> properties.putIfAbsent(propEntry.getKey(), propEntry.getValue())); @@ -215,7 +215,7 @@ public Path appJarOrClasses() { return classesDir; } - public Map getQuarkusBuildProperties() { + public MapProperty getQuarkusBuildProperties() { return quarkusBuildProperties; } @@ -223,4 +223,8 @@ public void set(String name, @Nullable String value) { quarkusBuildProperties.put(String.format("quarkus.%s", name), value); } + public void set(String name, Property value) { + quarkusBuildProperties.put(String.format("quarkus.%s", name), value); + } + } diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/GradleLogger.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/GradleLogger.java deleted file mode 100644 index 8acbe9319c964..0000000000000 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/GradleLogger.java +++ /dev/null @@ -1,197 +0,0 @@ -package io.quarkus.gradle.tasks; - -import java.text.MessageFormat; -import java.util.Collections; -import java.util.Map; -import java.util.function.Supplier; - -import org.jboss.logging.Logger; -import org.jboss.logging.LoggerProvider; -import org.wildfly.common.Assert; - -public class GradleLogger implements LoggerProvider { - static final Object[] NO_PARAMS = new Object[0]; - - public static volatile Supplier logSupplier; - - @Override - public Logger getLogger(final String name) { - return new Logger(name) { - @Override - protected void doLog(final Level level, final String loggerClassName, final Object message, - final Object[] parameters, final Throwable thrown) { - final Supplier logSupplier = GradleLogger.logSupplier; - if (logSupplier != null) { - org.gradle.api.logging.Logger log = logSupplier.get(); - String text; - if (parameters == null || parameters.length == 0) { - text = String.valueOf(message); - } else - try { - text = MessageFormat.format(String.valueOf(message), parameters); - } catch (Exception e) { - text = invalidFormat(String.valueOf(message), parameters); - } - doActualLog(log, level, text, thrown); - } - } - - @Override - protected void doLogf(final Level level, final String loggerClassName, final String format, - final Object[] parameters, final Throwable thrown) { - final Supplier logSupplier = GradleLogger.logSupplier; - if (logSupplier != null) { - org.gradle.api.logging.Logger log = logSupplier.get(); - String text; - if (parameters == null) - try { - //noinspection RedundantStringFormatCall - text = String.format(format); - } catch (Exception e) { - text = invalidFormat(format, NO_PARAMS); - } - else - try { - text = String.format(format, (Object[]) parameters); - } catch (Exception e) { - text = invalidFormat(format, parameters); - } - if (!text.startsWith("JBoss Threads version")) { - doActualLog(log, level, text, thrown); - } - } - } - - @Override - public boolean isEnabled(final Level level) { - final Supplier logSupplier = GradleLogger.logSupplier; - if (logSupplier == null) - return false; - org.gradle.api.logging.Logger log = logSupplier.get(); - switch (level) { - case FATAL: - case ERROR: - return log.isErrorEnabled(); - case WARN: - return log.isWarnEnabled(); - case INFO: - return log.isInfoEnabled(); - default: - return log.isDebugEnabled(); - } - } - - void doActualLog(final org.gradle.api.logging.Logger log, final Level level, final String message, - final Throwable thrown) { - //TODO: will fix this in the upcoming version of æsh - // style options are limited unless we crack into jansi ourselves - //buffer.strong("[").project(name).strong("]").a(" ").a(message); - StringBuilder buffer = new StringBuilder(); - buffer.append("[").append(name).append("]").append(" ").append(message); - if (thrown != null) { - switch (level) { - case FATAL: - case ERROR: - log.error(buffer.toString(), thrown); - break; - case WARN: - log.warn(buffer.toString(), thrown); - break; - case INFO: - log.info(buffer.toString(), thrown); - break; - default: - log.debug(buffer.toString(), thrown); - break; - } - } else { - switch (level) { - case FATAL: - case ERROR: - log.error(buffer.toString()); - break; - case WARN: - log.warn(buffer.toString()); - break; - case INFO: - log.info(buffer.toString()); - break; - default: - log.debug(buffer.toString()); - break; - } - } - } - }; - } - - String invalidFormat(final String format, final Object[] parameters) { - final StringBuilder b = new StringBuilder("** invalid format \'" + format + "\'"); - if (parameters != null && parameters.length > 0) { - b.append(" [").append(parameters[0]); - for (int i = 1; i < parameters.length; i++) { - b.append(',').append(parameters[i]); - } - b.append("]"); - } - return b.toString(); - } - - @Override - public void clearMdc() { - } - - @Override - public Object putMdc(final String key, final Object value) { - //throw Assert.unsupported(); - return null; - } - - @Override - public Object getMdc(final String key) { - return null; - } - - @Override - public void removeMdc(final String key) { - } - - @Override - public Map getMdcMap() { - return Collections.emptyMap(); - } - - @Override - public void clearNdc() { - } - - @Override - public String getNdc() { - return ""; - } - - @Override - public int getNdcDepth() { - return 0; - } - - @Override - public String popNdc() { - return ""; - } - - @Override - public String peekNdc() { - return ""; - } - - @Override - public void pushNdc(final String message) { - throw Assert.unsupported(); - } - - @Override - public void setNdcMaxDepth(final int maxDepth) { - } - -} diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuild.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuild.java index 4d98e2e99a3d8..1b3698e4f668a 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuild.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuild.java @@ -256,8 +256,9 @@ private String getPropValueWithPrecedence(final String propName, final java.util } }); } - if (extension().getQuarkusBuildProperties().containsKey(propName)) { - return extension().getQuarkusBuildProperties().get(propName); + Map quarkusBuildProperties = extension().getQuarkusBuildProperties().get(); + if (quarkusBuildProperties.containsKey(propName)) { + return quarkusBuildProperties.get(propName); } else if (applicationProperties.contains(propName)) { return applicationProperties.getProperty(propName); } else if (getQuarkusBuildEnvProperties().containsKey(propName)) { diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusTask.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusTask.java index 254433b5155c3..e16c737d6009f 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusTask.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusTask.java @@ -13,8 +13,6 @@ public abstract class QuarkusTask extends DefaultTask { private QuarkusPluginExtension extension; QuarkusTask(String description) { - GradleLogger.logSupplier = this::getLogger; - setDescription(description); setGroup("quarkus"); } @@ -36,8 +34,9 @@ protected Properties getBuildSystemProperties(ResolvedDependency appArtifact) { realProperties.setProperty(key, (String) value); } } - if (!extension().getQuarkusBuildProperties().isEmpty()) { - extension().getQuarkusBuildProperties().entrySet().stream().filter(entry -> entry.getKey().startsWith("quarkus.")) + Map quarkusBuildProperties = extension().getQuarkusBuildProperties().get(); + if (!quarkusBuildProperties.isEmpty()) { + quarkusBuildProperties.entrySet().stream().filter(entry -> entry.getKey().startsWith("quarkus.")) .forEach(entry -> { realProperties.put(entry.getKey(), entry.getValue()); }); diff --git a/docs/src/main/asciidoc/micrometer.adoc b/docs/src/main/asciidoc/micrometer.adoc index b8fefe6a3e7d5..ee3eb4039b207 100644 --- a/docs/src/main/asciidoc/micrometer.adoc +++ b/docs/src/main/asciidoc/micrometer.adoc @@ -367,11 +367,11 @@ timers, look for `http_server_requests_seconds_count`, `http_server_requests_sec ---- # HELP http_server_requests_seconds # TYPE http_server_requests_seconds summary -http_server_requests_seconds_count{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}",} 1.0 -http_server_requests_seconds_sum{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}",} 0.017385896 +http_server_requests_seconds_count{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}"} 1.0 +http_server_requests_seconds_sum{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}"} 0.017385896 # HELP http_server_requests_seconds_max # TYPE http_server_requests_seconds_max gauge -http_server_requests_seconds_max{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}",} 0.017385896 +http_server_requests_seconds_max{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}"} 0.017385896 # ---- @@ -398,6 +398,11 @@ string. For example, setting `quarkus.micrometer.binder.http-server.match-patterns=/example/prime/[0-9]+=/example/{jellybeans}` would use the value `/example/{jellybeans}` for the uri attribute any time the requested uri matches `/example/prime/[0-9]+`. +.Exported metrics format + +By default, the metrics are exported using the Prometheus format `application/openmetrics-text`, +you can revert to the former format by specifying the `Accept` request header to `plain/text` (`curl -H "Accept: plain/text" localhost:8080/q/metrics/`). + == Using MeterFilter to configure metrics Micrometer uses `MeterFilter` instances to customize the metrics emitted by `MeterRegistry` instances. diff --git a/docs/src/main/asciidoc/resteasy-reactive.adoc b/docs/src/main/asciidoc/resteasy-reactive.adoc index a33ab14484ae5..97549a0a2fc4d 100644 --- a/docs/src/main/asciidoc/resteasy-reactive.adoc +++ b/docs/src/main/asciidoc/resteasy-reactive.adoc @@ -723,6 +723,79 @@ public class Endpoint { } ---- +=== Redirect support + +When handling a `@POST`, `@PUT` or `@DELETE` endpoint, it is common practice to redirect to a `@GET` endpoint after the action has been performed to allow the user to reload the page without triggering the action a second time. +There are multiple ways to achieve this. + +==== Using RestResponse + +Using `RestResponse` as the return type while making sure the proper redirection URI is created can be done as in the following example: + +[source,java] +---- +package org.acme.rest; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.UriInfo; +import org.jboss.resteasy.reactive.RestResponse; + +@Path("/fruits") +public class FruitResource { + + public static class Fruit { + public Long id; + public String name; + public String description; + + public Fruit() { + } + + public Fruit(Long id, String name, String description) { + this.id = id; + this.name = name; + this.description = description; + } + } + + private final Map fruits = new ConcurrentHashMap<>(); + private final AtomicLong ids = new AtomicLong(0); + + + public FruitResource() { + Fruit apple = new Fruit(ids.incrementAndGet(), "Apple", "Winter fruit"); + fruits.put(apple.id, apple); + Fruit pinneapple = new Fruit(ids.incrementAndGet(), "Pineapple", "Tropical fruit"); + fruits.put(pinneapple.id, pinneapple); + } + + // when invoked, this method will result in an HTTP redirect to the GET method that obtains the fruit by id + @POST + public RestResponse add(Fruit fruit, @Context UriInfo uriInfo) { + fruit.id = ids.incrementAndGet(); + fruits.put(fruit.id, fruit); + // seeOther results in an HTTP 303 response with the Location header set to the value of the URI + return RestResponse.seeOther(uriInfo.getAbsolutePathBuilder().path(Long.toString(fruit.id)).build()); + } + + @GET + @Path("{id}") + public Fruit byId(Long id) { + return fruits.get(id); + } +} +---- + +==== Using RedirectException + +Users can also throw `javax.ws.rs.RedirectionException` from a method body to get RESTEasy Reactive to perform the desired redirect. + === Async/reactive support [[reactive]] diff --git a/docs/src/main/asciidoc/telemetry-micrometer-tutorial.adoc b/docs/src/main/asciidoc/telemetry-micrometer-tutorial.adoc index 57cc8b870a00e..5a2d0068d735f 100644 --- a/docs/src/main/asciidoc/telemetry-micrometer-tutorial.adoc +++ b/docs/src/main/asciidoc/telemetry-micrometer-tutorial.adoc @@ -77,16 +77,21 @@ Dimensional labels are added for the request uri, the HTTP method ---- # HELP http_server_requests_seconds # TYPE http_server_requests_seconds summary -http_server_requests_seconds_count{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}",} 2.0 -http_server_requests_seconds_sum{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}",} 0.017385896 +http_server_requests_seconds_count{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}"} 2.0 +http_server_requests_seconds_sum{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}"} 0.017385896 # HELP http_server_requests_seconds_max # TYPE http_server_requests_seconds_max gauge -http_server_requests_seconds_max{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}",} 0.017385896 +http_server_requests_seconds_max{method="GET",outcome="SUCCESS",status="200",uri="/example/prime/{number}"} 0.017385896 # ---- NOTE: Metrics appear lazily, you often won't see any data for your endpoint until it is accessed. +.Exported metrics format + +By default, the metrics are exported using the Prometheus format `application/openmetrics-text`, +you can revert to the former format by specifying the `Accept` request header to `plain/text` (`curl -H "Accept: plain/text" localhost:8080/q/metrics/`). + == Inject the MeterRegistry To register meters, you need a reference to the `MeterRegistry` that is configured and maintained by the Micrometer extension. diff --git a/extensions/funqy/funqy-server-common/deployment/src/main/java/io/quarkus/funqy/deployment/FunctionScannerBuildStep.java b/extensions/funqy/funqy-server-common/deployment/src/main/java/io/quarkus/funqy/deployment/FunctionScannerBuildStep.java index 2b250b7c68540..dcf4096de963a 100644 --- a/extensions/funqy/funqy-server-common/deployment/src/main/java/io/quarkus/funqy/deployment/FunctionScannerBuildStep.java +++ b/extensions/funqy/funqy-server-common/deployment/src/main/java/io/quarkus/funqy/deployment/FunctionScannerBuildStep.java @@ -201,7 +201,8 @@ public ClassVisitor apply(String className, ClassVisitor classVisitor) { public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); - MethodVisitor ctor = visitMethod(Modifier.PUBLIC, "", "()V", null, + MethodVisitor ctor = visitMethod(Modifier.PUBLIC | Opcodes.ACC_SYNTHETIC, "", "()V", + null, null); ctor.visitCode(); ctor.visitVarInsn(Opcodes.ALOAD, 0); diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeDefaultTenantTestCase.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeDefaultTenantTestCase.java index ab820fbd643f0..7262a3cd300fd 100644 --- a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeDefaultTenantTestCase.java +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeDefaultTenantTestCase.java @@ -59,7 +59,7 @@ public void testAccessAndRefreshTokenInjectionDevMode() throws IOException, Inte page = loginForm.getInputByName("login").click(); - assertEquals("alice", page.getBody().asText()); + assertEquals("alice", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeTestCase.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeTestCase.java index 1684c3ee95b83..00e003db3d7da 100644 --- a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeTestCase.java +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/CodeFlowDevModeTestCase.java @@ -51,7 +51,7 @@ public void testAccessAndRefreshTokenInjectionDevMode() throws IOException, Inte // Default tenant is disabled and client secret is wrong HtmlPage page = webClient.getPage("http://localhost:8080/unprotected"); - assertEquals("unprotected", page.getBody().asText()); + assertEquals("unprotected", page.getBody().asNormalizedText()); try { webClient.getPage("http://localhost:8080/protected"); @@ -95,7 +95,7 @@ public void testAccessAndRefreshTokenInjectionDevMode() throws IOException, Inte page = loginForm.getInputByName("login").click(); - assertEquals("alice", page.getBody().asText()); + assertEquals("alice", page.getBody().asNormalizedText()); assertEquals("custom", page.getWebClient().getCookieManager().getCookie("q_session").getValue().split("\\|")[3]); @@ -122,7 +122,7 @@ private void useTenantConfigResolver() throws IOException, InterruptedException page = loginForm.getInputByName("login").click(); - assertEquals("tenant-config-resolver:alice", page.getBody().asText()); + assertEquals("tenant-config-resolver:alice", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } } diff --git a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/SecurityDisabledTestCase.java b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/SecurityDisabledTestCase.java index 003f8063bde6f..4227e587a8a08 100644 --- a/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/SecurityDisabledTestCase.java +++ b/extensions/oidc/deployment/src/test/java/io/quarkus/oidc/test/SecurityDisabledTestCase.java @@ -30,7 +30,7 @@ public void testAccessUnprotectedResource() throws IOException, InterruptedExcep try (final WebClient webClient = createWebClient()) { HtmlPage page = webClient.getPage("http://localhost:8081/unprotected"); - assertEquals("unprotected", page.getBody().asText()); + assertEquals("unprotected", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } } 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 e25c963ca59bd..ebe214e2d7ea3 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 @@ -603,7 +603,7 @@ public static enum Source { public static class Authentication { /** - * SameSite attribute values for the session, state and post logout cookies. + * SameSite attribute values for the session cookie. */ public enum CookieSameSite { STRICT, @@ -767,7 +767,7 @@ public enum ResponseMode { public Optional cookieDomain = Optional.empty(); /** - * SameSite attribute for the session, state and post logout cookies. + * SameSite attribute for the session cookie. */ @ConfigItem(defaultValue = "strict") public CookieSameSite cookieSameSite = CookieSameSite.STRICT; 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 4dd98f1943255..fdf6a4fb4df6a 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 @@ -799,7 +799,7 @@ public Uni apply(Void t) { public Void apply(String cookieValue) { String sessionCookie = createCookie(context, configContext.oidcConfig, getSessionCookieName(configContext.oidcConfig), - cookieValue, sessionMaxAge).getValue(); + cookieValue, sessionMaxAge, true).getValue(); if (sessionCookie.length() >= MAX_COOKIE_VALUE_LENGTH) { LOG.warnf( "Session cookie length for the tenant %s is equal or greater than %d bytes." @@ -914,6 +914,11 @@ private String generatePostLogoutState(RoutingContext context, TenantConfigConte static ServerCookie createCookie(RoutingContext context, OidcTenantConfig oidcConfig, String name, String value, long maxAge) { + return createCookie(context, oidcConfig, name, value, maxAge, false); + } + + static ServerCookie createCookie(RoutingContext context, OidcTenantConfig oidcConfig, + String name, String value, long maxAge, boolean sessionCookie) { ServerCookie cookie = new CookieImpl(name, value); cookie.setHttpOnly(true); cookie.setSecure(oidcConfig.authentication.cookieForceSecure || context.request().isSSL()); @@ -924,7 +929,9 @@ static ServerCookie createCookie(RoutingContext context, OidcTenantConfig oidcCo if (auth.cookieDomain.isPresent()) { cookie.setDomain(auth.getCookieDomain().get()); } - cookie.setSameSite(CookieSameSite.valueOf(auth.cookieSameSite.name())); + if (sessionCookie) { + cookie.setSameSite(CookieSameSite.valueOf(auth.cookieSameSite.name())); + } context.response().addCookie(cookie); return cookie; } diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/CollectionsResource.java b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/CollectionsResource.java index 853e593fb6faa..e24f791173789 100644 --- a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/CollectionsResource.java +++ b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/CollectionsResource.java @@ -3,9 +3,11 @@ import io.quarkus.hibernate.reactive.rest.data.panache.PanacheRepositoryResource; import io.quarkus.rest.data.panache.MethodProperties; import io.quarkus.rest.data.panache.ResourceProperties; +import io.smallrye.mutiny.Uni; @ResourceProperties(hal = true, paged = false, halCollectionName = "item-collections", rolesAllowed = "user") public interface CollectionsResource extends PanacheRepositoryResource { + @MethodProperties(rolesAllowed = "admin") - boolean delete(String name); + Uni delete(String name); } diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/PanacheRepositoryResourcePathCustomisationTest.java b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/PanacheRepositoryResourcePathCustomisationTest.java index d2eee7539c501..276493f06b93f 100644 --- a/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/PanacheRepositoryResourcePathCustomisationTest.java +++ b/extensions/panache/hibernate-reactive-rest-data-panache/deployment/src/test/java/io/quarkus/hibernate/reactive/rest/data/panache/deployment/repository/PanacheRepositoryResourcePathCustomisationTest.java @@ -11,6 +11,7 @@ import io.quarkus.rest.data.panache.MethodProperties; import io.quarkus.rest.data.panache.ResourceProperties; import io.quarkus.test.QuarkusUnitTest; +import io.smallrye.mutiny.Uni; class PanacheRepositoryResourcePathCustomisationTest extends AbstractPathCustomisationTest { @@ -27,18 +28,18 @@ public interface CustomPathCollectionsResource extends PanacheRepositoryResource { @MethodProperties(path = "api") - List list(Page page, Sort sort); + Uni> list(Page page, Sort sort); @MethodProperties(path = "api") - Collection get(String name); + Uni get(String name); @MethodProperties(path = "api") - Collection add(Collection collection); + Uni add(Collection collection); @MethodProperties(path = "api") - Collection update(String name, Collection collection); + Uni update(String name, Collection collection); @MethodProperties(path = "api") - boolean delete(String name); + Uni delete(String name); } } diff --git a/extensions/panache/hibernate-reactive-rest-data-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/PanacheRepositoryResource.java b/extensions/panache/hibernate-reactive-rest-data-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/PanacheRepositoryResource.java index 8fa61c3a2e5ca..27a7edfb55fc9 100644 --- a/extensions/panache/hibernate-reactive-rest-data-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/PanacheRepositoryResource.java +++ b/extensions/panache/hibernate-reactive-rest-data-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/rest/data/panache/PanacheRepositoryResource.java @@ -2,6 +2,7 @@ import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; import io.quarkus.rest.data.panache.MethodProperties; +import io.quarkus.rest.data.panache.ReactiveRestDataResource; import io.quarkus.rest.data.panache.ResourceProperties; import io.quarkus.rest.data.panache.RestDataResource; @@ -18,6 +19,6 @@ * @param ID type of the entity. */ public interface PanacheRepositoryResource, Entity, ID> - extends RestDataResource { + extends ReactiveRestDataResource { } diff --git a/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/RolesAllowedResource.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/RolesAllowedResource.java index 16fc9378d18f5..5c612b4a7cd1e 100644 --- a/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/RolesAllowedResource.java +++ b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/RolesAllowedResource.java @@ -2,15 +2,22 @@ import javax.annotation.security.PermitAll; import javax.annotation.security.RolesAllowed; +import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; +import io.quarkus.security.identity.CurrentIdentityAssociation; + /** * @author Michal Szynkiewicz, michal.l.szynkiewicz@gmail.com */ @Path("/roles") @PermitAll public class RolesAllowedResource { + + @Inject + CurrentIdentityAssociation currentIdentityAssociation; + @GET @RolesAllowed({ "user", "admin" }) public String defaultSecurity() { @@ -24,4 +31,10 @@ public String admin() { return "admin"; } + @Path("/admin/security-identity") + @RolesAllowed("admin") + @GET + public String getSecurityIdentity() { + return currentIdentityAssociation.getIdentity().getPrincipal().getName(); + } } diff --git a/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/SecurityIdentityAugmentorTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/SecurityIdentityAugmentorTest.java new file mode 100644 index 0000000000000..92ceac105166e --- /dev/null +++ b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/security/SecurityIdentityAugmentorTest.java @@ -0,0 +1,67 @@ +package io.quarkus.resteasy.test.security; + +import java.util.List; +import java.util.function.Supplier; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.control.ActivateRequestContext; + +import org.hamcrest.Matchers; +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 io.quarkus.builder.Version; +import io.quarkus.maven.dependency.Dependency; +import io.quarkus.security.identity.AuthenticationRequestContext; +import io.quarkus.security.identity.SecurityIdentity; +import io.quarkus.security.identity.SecurityIdentityAugmentor; +import io.quarkus.security.runtime.QuarkusSecurityIdentity; +import io.quarkus.security.test.utils.TestIdentityController; +import io.quarkus.security.test.utils.TestIdentityProvider; +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; +import io.smallrye.mutiny.Uni; + +public class SecurityIdentityAugmentorTest { + + @RegisterExtension + static QuarkusUnitTest runner = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addAsResource(new StringAsset("quarkus.http.auth.basic=true\n"), "application.properties") + .addClasses(TestIdentityProvider.class, RolesAllowedResource.class, TestIdentityController.class)) + .setForcedDependencies(List.of(Dependency.of("io.quarkus", "quarkus-undertow", Version.getVersion()))); + + @BeforeAll + public static void setupUsers() { + TestIdentityController.resetRoles().add("admin", "admin"); + } + + @Test + public void testSecurityIdentityAugmentor() { + RestAssured.given().auth().basic("admin", "admin").get("/roles/admin/security-identity").then().statusCode(200) + .body(Matchers.is("admin")); + } + + @ApplicationScoped + public static class CustomAugmentor implements SecurityIdentityAugmentor { + + @Override + public Uni augment(SecurityIdentity identity, AuthenticationRequestContext context) { + if (identity.isAnonymous()) { + return Uni.createFrom().item(identity); + } + return context.runBlocking(build(identity)); + } + + @ActivateRequestContext + Supplier build(SecurityIdentity identity) { + QuarkusSecurityIdentity.Builder builder = QuarkusSecurityIdentity.builder(identity); + builder.addRole("admin"); + return builder::build; + } + + } + +} 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 0975c2cce33f4..aabc3a1964e85 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 @@ -564,6 +564,15 @@ public T call(HttpServerExchange exchange, C context) throws Exception { // Not sure what to do here ManagedContext requestContext = beanContainer.requestContext(); if (requestContext.isActive()) { + if (currentVertxRequest.getCurrent() == null && exchange != null + && exchange.getDelegate() instanceof VertxHttpExchange) { + // goal here is to add event to the Vert.X request when Smallrye Context Propagation + // creates fresh instance of request context without the event; we experienced + // the request context activated and terminated by ActivateRequestContextInterceptor + // invoked for the SecurityIdentityAugmentor that was (re)created during permission checks + addEventToVertxRequest(exchange); + } + return action.call(exchange, context); } else if (exchange == null) { requestContext.activate(); @@ -578,9 +587,7 @@ public T call(HttpServerExchange exchange, C context) throws Exception { try { requestContext.activate(existingRequestContext); - VertxHttpExchange delegate = (VertxHttpExchange) exchange.getDelegate(); - RoutingContext rc = (RoutingContext) delegate.getContext(); - currentVertxRequest.setCurrent(rc); + RoutingContext rc = addEventToVertxRequest(exchange); if (association != null) { QuarkusHttpUser existing = (QuarkusHttpUser) rc.user(); @@ -631,6 +638,13 @@ public void onStartAsync(AsyncEvent event) throws IOException { } } } + + private RoutingContext addEventToVertxRequest(HttpServerExchange exchange) { + VertxHttpExchange delegate = (VertxHttpExchange) exchange.getDelegate(); + RoutingContext rc = (RoutingContext) delegate.getContext(); + currentVertxRequest.setCurrent(rc); + return rc; + } }; } }); diff --git a/independent-projects/arc/pom.xml b/independent-projects/arc/pom.xml index a157973981934..4b7e9db488ed1 100644 --- a/independent-projects/arc/pom.xml +++ b/independent-projects/arc/pom.xml @@ -43,7 +43,7 @@ 2.0.2 1.3.3 3.0.5 - 5.9.1 + 5.9.2 3.8.6 3.23.1 3.5.0.Final diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java index 60ca6948c5742..772c7e8b11ab3 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java @@ -913,7 +913,7 @@ public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); - MethodVisitor mv = visitMethod(Modifier.PUBLIC, Methods.INIT, "()V", null, + MethodVisitor mv = visitMethod(Modifier.PUBLIC | Opcodes.ACC_SYNTHETIC, Methods.INIT, "()V", null, null); mv.visitCode(); mv.visitVarInsn(Opcodes.ALOAD, 0); diff --git a/independent-projects/bootstrap/pom.xml b/independent-projects/bootstrap/pom.xml index 05ffd4aa556e1..c7e9b21875bcc 100644 --- a/independent-projects/bootstrap/pom.xml +++ b/independent-projects/bootstrap/pom.xml @@ -42,7 +42,7 @@ 3.23.1 0.9.5 3.5.0.Final - 5.9.1 + 5.9.2 3.8.6 0.3.5 3.6.0 @@ -51,7 +51,7 @@ 3.5.1 4.4.16 1.0.0.Final - 2.14.1 + 2.14.2 1.3.5 2.0.2 1.0 diff --git a/independent-projects/extension-maven-plugin/pom.xml b/independent-projects/extension-maven-plugin/pom.xml index 6597a0a7787d8..18bb872a15986 100644 --- a/independent-projects/extension-maven-plugin/pom.xml +++ b/independent-projects/extension-maven-plugin/pom.xml @@ -35,8 +35,8 @@ 11 3.0.0-M7 1.6.8 - 2.14.1 - 5.9.1 + 2.14.2 + 5.9.2 diff --git a/independent-projects/qute/pom.xml b/independent-projects/qute/pom.xml index 431a5555cf1be..9be201d5d3f16 100644 --- a/independent-projects/qute/pom.xml +++ b/independent-projects/qute/pom.xml @@ -40,14 +40,14 @@ 11 11 11 - 5.9.1 + 5.9.2 3.23.1 3.0.5 1.6.0.Final 3.5.0.Final 3.0.0-M7 1.6.8 - 1.8.0 + 1.9.0 diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/providers/serialisers/FileBodyHandler.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/providers/serialisers/FileBodyHandler.java index e8cc9f3fbcf04..04d1f9fbfa4c9 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/providers/serialisers/FileBodyHandler.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/providers/serialisers/FileBodyHandler.java @@ -10,6 +10,7 @@ import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.nio.file.Files; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; @@ -32,7 +33,7 @@ public boolean isReadable(Class type, Type genericType, Annotation[] annotati public File readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException { - File downloadedFile = File.createTempFile(PREFIX, SUFFIX); + File downloadedFile = Files.createTempFile(PREFIX, SUFFIX).toFile(); if (HeaderUtil.isContentLengthZero(httpHeaders)) { return downloadedFile; } diff --git a/independent-projects/resteasy-reactive/pom.xml b/independent-projects/resteasy-reactive/pom.xml index 51f989ad90ed8..37a22165691b8 100644 --- a/independent-projects/resteasy-reactive/pom.xml +++ b/independent-projects/resteasy-reactive/pom.xml @@ -43,7 +43,7 @@ 2.0.2 3.0.5 1.12.12 - 5.9.1 + 5.9.2 3.8.6 3.23.1 3.5.0.Final @@ -55,13 +55,13 @@ 1.6.8 2.0.1.Final 1.1.6 - 1.8.0 + 1.9.0 1.13.2 4.3.5 4.5.1 1.0.0.Final 2.0.0.Final - 2.14.1 + 2.14.2 1.3.0 2.0.2 1.0.11 diff --git a/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/generation/multipart/MultipartTransformer.java b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/generation/multipart/MultipartTransformer.java index 136a806f84e9c..2fecb582fde29 100644 --- a/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/generation/multipart/MultipartTransformer.java +++ b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/generation/multipart/MultipartTransformer.java @@ -70,7 +70,8 @@ public FieldVisitor visitField(int access, String name, String descriptor, Strin @Override public void visitEnd() { - MethodVisitor injectMethod = visitMethod(Opcodes.ACC_PUBLIC, INJECT_METHOD_NAME, INJECT_METHOD_DESCRIPTOR, null, + MethodVisitor injectMethod = visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, INJECT_METHOD_NAME, + INJECT_METHOD_DESCRIPTOR, null, null); injectMethod.visitParameter("ctx", 0 /* modifiers */); injectMethod.visitCode(); diff --git a/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ClassInjectorTransformer.java b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ClassInjectorTransformer.java index d93ebec5de522..ae69d399728f5 100644 --- a/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ClassInjectorTransformer.java +++ b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/scanning/ClassInjectorTransformer.java @@ -223,7 +223,8 @@ public void visitEnd() { public void visitEnd() { // FIXME: handle setters // FIXME: handle multi fields - MethodVisitor injectMethod = visitMethod(Opcodes.ACC_PUBLIC, INJECT_METHOD_NAME, INJECT_METHOD_DESCRIPTOR, null, + MethodVisitor injectMethod = visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, INJECT_METHOD_NAME, + INJECT_METHOD_DESCRIPTOR, null, null); injectMethod.visitParameter("ctx", 0 /* modifiers */); injectMethod.visitCode(); @@ -333,7 +334,7 @@ public void visitEnd() { if (!seenClassInit && !partTypes.isEmpty()) { // add a class init method for the part types special fields - MethodVisitor mv = super.visitMethod(Opcodes.ACC_STATIC, "", "()V", null, null); + MethodVisitor mv = super.visitMethod(Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, "", "()V", null, null); for (Entry entry : partTypes.entrySet()) { generateMultipartFormStaticInit(mv, entry.getKey(), entry.getValue()); } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/SseBroadcasterImpl.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/SseBroadcasterImpl.java index 1a5b77c30badc..1700f89b43a93 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/SseBroadcasterImpl.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/jaxrs/SseBroadcasterImpl.java @@ -1,5 +1,6 @@ package org.jboss.resteasy.reactive.server.jaxrs; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -37,8 +38,9 @@ public synchronized void onClose(Consumer onClose) { public synchronized void register(SseEventSink sseEventSink) { Objects.requireNonNull(sseEventSink); checkClosed(); - if (sseEventSink instanceof SseEventSinkImpl == false) + if (sseEventSink instanceof SseEventSinkImpl == false) { throw new IllegalArgumentException("Can only work with Quarkus-REST instances: " + sseEventSink); + } ((SseEventSinkImpl) sseEventSink).register(this); sinks.add(sseEventSink); } @@ -50,20 +52,62 @@ public synchronized CompletionStage broadcast(OutboundSseEvent event) { CompletableFuture[] cfs = new CompletableFuture[sinks.size()]; for (int i = 0; i < sinks.size(); i++) { SseEventSink sseEventSink = sinks.get(i); - cfs[i] = sseEventSink.send(event).toCompletableFuture(); + CompletionStage cs; + try { + cs = sseEventSink.send(event).exceptionally((t) -> { + // do not propagate the exception to the returned CF + // apparently, the goal is to close this sink and not report the error + // of the broadcast operation + notifyOnErrorListeners(sseEventSink, t); + return null; + }); + } catch (Exception e) { + // do not propagate the exception to the returned CF + // apparently, the goal is to close this sink and not report the error + // of the broadcast operation + notifyOnErrorListeners(sseEventSink, e); + cs = CompletableFuture.completedFuture(null); + } + cfs[i] = cs.toCompletableFuture(); } return CompletableFuture.allOf(cfs); } + private void notifyOnErrorListeners(SseEventSink eventSink, Throwable throwable) { + // We have to notify close listeners if the SSE event output has been + // closed (either by client closing the connection (IOException) or by + // calling SseEventSink.close() (IllegalStateException) on the server + // side). + if (throwable instanceof IOException || throwable instanceof IllegalStateException) { + notifyOnCloseListeners(eventSink); + } + onErrorListeners.forEach(consumer -> { + consumer.accept(eventSink, throwable); + }); + } + + private void notifyOnCloseListeners(SseEventSink eventSink) { + // First remove the eventSink from the outputQueue to ensure that + // concurrent calls to this method will notify listeners only once for a + // given eventSink instance. + if (sinks.remove(eventSink)) { + onCloseListeners.forEach(consumer -> { + consumer.accept(eventSink); + }); + } + } + private void checkClosed() { - if (isClosed) + if (isClosed) { throw new IllegalStateException("Broadcaster has been closed"); + } } @Override public synchronized void close() { - if (isClosed) + if (isClosed) { return; + } isClosed = true; for (SseEventSink sink : sinks) { // this will in turn fire close events to our listeners diff --git a/independent-projects/tools/pom.xml b/independent-projects/tools/pom.xml index a650cd7361cbd..30fb2f3cec3b1 100644 --- a/independent-projects/tools/pom.xml +++ b/independent-projects/tools/pom.xml @@ -49,9 +49,9 @@ 3.23.1 - 2.14.1 + 2.14.2 2.0.2 - 5.9.1 + 5.9.2 1.22 3.5.0.Final 3.8.6 diff --git a/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java b/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java index 0f4f9ad874da3..67f549bef42b0 100644 --- a/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java +++ b/integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java @@ -55,9 +55,9 @@ public void testCodeFlowNoConsent() throws IOException { .loadWebResponse(new WebRequest(URI.create("http://localhost:8081/index.html").toURL())); verifyLocationHeader(webClient, webResponse.getResponseHeaderValue("location"), null, "web-app", false); - String stateCookieString = webResponse.getResponseHeaderValue("Set-Cookie"); - assertTrue(stateCookieString.startsWith("q_auth_Default_test=")); - assertTrue(stateCookieString.contains("SameSite=Strict")); + Cookie stateCookie = getStateCookie(webClient, null); + assertNotNull(stateCookie); + assertNull(stateCookie.getSameSite()); webClient.getCookieManager().clearCookies(); @@ -86,15 +86,16 @@ public void testCodeFlowNoConsent() throws IOException { assertEquals( client.getAuthServerUrl(), - page.asText()); + page.asNormalizedText()); page = webClient.getPage("http://localhost:8081/web-app/configMetadataScopes"); - assertTrue(page.asText().contains("openid")); - assertTrue(page.asText().contains("profile")); + assertTrue(page.asNormalizedText().contains("openid")); + assertTrue(page.asNormalizedText().contains("profile")); Cookie sessionCookie = getSessionCookie(webClient, null); assertNotNull(sessionCookie); + assertEquals("strict", sessionCookie.getSameSite()); webClient.getCookieManager().clearCookies(); } @@ -157,7 +158,7 @@ public void testCodeFlowScopeErrorWithErrorPage() throws IOException { HtmlPage page = webClient.getPage(URI.create(endpointErrorLocation).toURL()); assertEquals("code: b, error: invalid_scope, error_description: Invalid scopes: unknown", - page.getBody().asText()); + page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } } @@ -176,10 +177,6 @@ public void testCodeFlowForceHttpsRedirectUriAndPkce() throws Exception { verifyLocationHeader(webClient, keycloakUrl, "tenant-https_test", "xforwarded%2Ftenant-https", true); - String stateCookieString = webResponse.getResponseHeaderValue("Set-Cookie"); - assertTrue(stateCookieString.startsWith("q_auth_tenant-https_test=")); - assertTrue(stateCookieString.contains("SameSite=Lax")); - HtmlPage page = webClient.getPage(keycloakUrl); assertEquals("Sign in to quarkus", page.getTitleText()); @@ -195,6 +192,7 @@ public void testCodeFlowForceHttpsRedirectUriAndPkce() throws Exception { String endpointLocation = webResponse.getResponseHeaderValue("location"); Cookie stateCookie = getStateCookie(webClient, "tenant-https_test"); + assertNull(stateCookie.getSameSite()); verifyCodeVerifier(stateCookie, keycloakUrl); assertTrue(endpointLocation.startsWith("https")); @@ -219,9 +217,10 @@ public void testCodeFlowForceHttpsRedirectUriAndPkce() throws Exception { assertNull(endpointLocationWithoutQueryUri.getRawQuery()); page = webClient.getPage(endpointLocationWithoutQueryUri.toURL()); - assertEquals("tenant-https:reauthenticated", page.getBody().asText()); + assertEquals("tenant-https:reauthenticated", page.getBody().asNormalizedText()); Cookie sessionCookie = getSessionCookie(webClient, "tenant-https_test"); assertNotNull(sessionCookie); + assertEquals("lax", sessionCookie.getSameSite()); webClient.getCookieManager().clearCookies(); } } @@ -279,7 +278,7 @@ public void testCodeFlowForceHttpsRedirectUriWithQueryAndPkce() throws Exception JsonObject idToken = OidcUtils.decodeJwtContent(sessionCookie.getValue().split("\\|")[0]); String expiresAt = idToken.getInteger("exp").toString(); page = webClient.getPage(endpointLocationWithoutQueryUri.toURL()); - String response = page.getBody().asText(); + String response = page.getBody().asNormalizedText(); assertTrue( response.startsWith("tenant-https:reauthenticated?code=b&expiresAt=" + expiresAt + "&expiresInDuration=")); Integer duration = Integer.valueOf(response.substring(response.length() - 1)); @@ -430,11 +429,11 @@ public void testRPInitiatedLogout() throws IOException { loginForm.getInputByName("username").setValueAttribute("alice"); loginForm.getInputByName("password").setValueAttribute("alice"); page = loginForm.getInputByName("login").click(); - assertEquals("Tenant Logout, refreshed: false", page.asText()); + assertEquals("Tenant Logout, refreshed: false", page.asNormalizedText()); assertNotNull(getSessionCookie(webClient, "tenant-logout")); page = webClient.getPage("http://localhost:8081/tenant-logout/logout"); - assertTrue(page.asText().contains("You were logged out")); + assertTrue(page.asNormalizedText().contains("You were logged out")); assertNull(getSessionCookie(webClient, "tenant-logout")); page = webClient.getPage("http://localhost:8081/tenant-logout"); @@ -452,7 +451,7 @@ public void testTokenRefresh() throws IOException { loginForm.getInputByName("username").setValueAttribute("alice"); loginForm.getInputByName("password").setValueAttribute("alice"); page = loginForm.getInputByName("login").click(); - assertEquals("Tenant Refresh, refreshed: false", page.asText()); + assertEquals("Tenant Refresh, refreshed: false", page.asNormalizedText()); Cookie sessionCookie = getSessionCookie(webClient, "tenant-refresh"); assertNotNull(sessionCookie); @@ -479,7 +478,7 @@ public Boolean call() throws Exception { // local session refreshed and still valid page = webClient.getPage("http://localhost:8081/tenant-refresh"); - assertEquals("Tenant Refresh, refreshed: false", page.asText()); + assertEquals("Tenant Refresh, refreshed: false", page.asNormalizedText()); assertNotNull(getSessionCookie(webClient, "tenant-refresh")); //wait now so that we reach the refresh timeout @@ -526,7 +525,7 @@ public void testTokenAutoRefresh() throws IOException { loginForm.getInputByName("username").setValueAttribute("alice"); loginForm.getInputByName("password").setValueAttribute("alice"); page = loginForm.getInputByName("login").click(); - assertEquals("Tenant AutoRefresh, refreshed: false", page.asText()); + assertEquals("Tenant AutoRefresh, refreshed: false", page.asNormalizedText()); Cookie sessionCookie = getSessionCookie(webClient, "tenant-autorefresh"); assertNotNull(sessionCookie); @@ -535,7 +534,7 @@ public void testTokenAutoRefresh() throws IOException { // Auto-refresh-interval is 30 secs so every call auto-refreshes the token // Right now the original ID token is still valid but will be auto-refreshed page = webClient.getPage("http://localhost:8081/tenant-autorefresh"); - assertEquals("Tenant AutoRefresh, refreshed: true", page.asText()); + assertEquals("Tenant AutoRefresh, refreshed: true", page.asNormalizedText()); sessionCookie = getSessionCookie(webClient, "tenant-autorefresh"); assertNotNull(sessionCookie); String nextIdToken = getIdToken(sessionCookie); @@ -564,7 +563,7 @@ public void testIdTokenInjection() throws IOException { page = webClient.getPage("http://localhost:8081/web-app"); - assertEquals("alice", page.getBody().asText()); + assertEquals("alice", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } } @@ -585,7 +584,7 @@ public void testIdTokenInjectionWithoutRestoredPath() throws IOException, Interr page = loginForm.getInputByName("login").click(); - assertEquals("callback:alice", page.getBody().asText()); + assertEquals("callback:alice", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } } @@ -621,7 +620,7 @@ public void testIdTokenInjectionJwtMethod() throws IOException, InterruptedExcep assertNull(endpointLocationUri2.getRawQuery()); page = webClient.getPage(endpointLocationUri2.toString()); - assertEquals("callback-jwt:alice", page.getBody().asText()); + assertEquals("callback-jwt:alice", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } } @@ -667,11 +666,11 @@ public void testIdTokenInjectionWithoutRestoredPathDifferentRoot() throws IOExce page = loginForm.getInputByName("login").click(); - assertEquals("web-app2:alice", page.getBody().asText()); + assertEquals("web-app2:alice", page.getBody().asNormalizedText()); page = webClient.getPage("http://localhost:8081/web-app2/name"); - assertEquals("web-app2:alice", page.getBody().asText()); + assertEquals("web-app2:alice", page.getBody().asNormalizedText()); assertNull(getStateCookie(webClient, "tenant-2")); Cookie sessionCookie = getSessionCookie(webClient, "tenant-2"); @@ -735,7 +734,7 @@ public void testAuthenticationCompletionFailedWrongRedirectUri() throws IOExcept loginForm.getInputByName("password").setValueAttribute("alice"); try { page = loginForm.getInputByName("login").click(); - fail("401 status error is expected: " + page.getBody().asText()); + fail("401 status error is expected: " + page.getBody().asNormalizedText()); } catch (FailingHttpStatusCodeException ex) { assertEquals(401, ex.getStatusCode()); assertEquals("http://localhost:8081/web-app/callback-before-wrong-redirect", @@ -764,7 +763,7 @@ public void testAccessTokenInjection() throws IOException { page = webClient.getPage("http://localhost:8081/web-app/access"); - assertEquals("AT injected", page.getBody().asText()); + assertEquals("AT injected", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } } @@ -788,7 +787,7 @@ public void testAccessAndRefreshTokenInjection() throws IOException { page = webClient.getPage("http://localhost:8081/web-app/refresh"); - assertEquals("RT injected", page.getBody().asText()); + assertEquals("RT injected", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } } @@ -808,7 +807,7 @@ public void testAccessAndRefreshTokenInjectionWithoutIndexHtml() throws IOExcept page = loginForm.getInputByName("login").click(); - assertEquals("RT injected", page.getBody().asText()); + assertEquals("RT injected", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } } @@ -827,12 +826,12 @@ public void testDefaultSessionManagerIdTokenOnly() throws IOException, Interrupt loginForm.getInputByName("password").setValueAttribute("alice"); page = loginForm.getInputByName("login").click(); - assertEquals("tenant-idtoken-only:alice", page.getBody().asText()); + assertEquals("tenant-idtoken-only:alice", page.getBody().asNormalizedText()); page = webClient.getPage("http://localhost:8081/web-app/access/tenant-idtoken-only"); - assertEquals("tenant-idtoken-only:no access", page.getBody().asText()); + assertEquals("tenant-idtoken-only:no access", page.getBody().asNormalizedText()); page = webClient.getPage("http://localhost:8081/web-app/refresh/tenant-idtoken-only"); - assertEquals("tenant-idtoken-only:no refresh", page.getBody().asText()); + assertEquals("tenant-idtoken-only:no refresh", page.getBody().asNormalizedText()); Cookie idTokenCookie = getSessionCookie(page.getWebClient(), "tenant-idtoken-only"); checkSingleTokenCookie(idTokenCookie, "ID"); @@ -858,12 +857,12 @@ public void testDefaultSessionManagerIdRefreshTokens() throws IOException, Inter loginForm.getInputByName("password").setValueAttribute("alice"); page = loginForm.getInputByName("login").click(); - assertEquals("tenant-id-refresh-token:alice", page.getBody().asText()); + assertEquals("tenant-id-refresh-token:alice", page.getBody().asNormalizedText()); page = webClient.getPage("http://localhost:8081/web-app/access/tenant-id-refresh-token"); - assertEquals("tenant-id-refresh-token:no access", page.getBody().asText()); + assertEquals("tenant-id-refresh-token:no access", page.getBody().asNormalizedText()); page = webClient.getPage("http://localhost:8081/web-app/refresh/tenant-id-refresh-token"); - assertEquals("tenant-id-refresh-token:RT injected", page.getBody().asText()); + assertEquals("tenant-id-refresh-token:RT injected", page.getBody().asNormalizedText()); Cookie idTokenCookie = getSessionCookie(page.getWebClient(), "tenant-id-refresh-token"); String[] parts = idTokenCookie.getValue().split("\\|"); @@ -894,12 +893,12 @@ public void testDefaultSessionManagerSplitTokens() throws IOException, Interrupt page = loginForm.getInputByName("login").click(); assertEquals("tenant-split-tokens:alice, id token has 5 parts, access token has 5 parts, refresh token has 5 parts", - page.getBody().asText()); + page.getBody().asNormalizedText()); page = webClient.getPage("http://localhost:8081/web-app/access/tenant-split-tokens"); - assertEquals("tenant-split-tokens:AT injected", page.getBody().asText()); + assertEquals("tenant-split-tokens:AT injected", page.getBody().asNormalizedText()); page = webClient.getPage("http://localhost:8081/web-app/refresh/tenant-split-tokens"); - assertEquals("tenant-split-tokens:RT injected", page.getBody().asText()); + assertEquals("tenant-split-tokens:RT injected", page.getBody().asNormalizedText()); Cookie idTokenCookie = getSessionCookie(page.getWebClient(), "tenant-split-tokens"); checkSingleTokenCookie(idTokenCookie, "ID", true); @@ -949,12 +948,12 @@ public void testDefaultSessionManagerIdRefreshSplitTokens() throws IOException, loginForm.getInputByName("password").setValueAttribute("alice"); page = loginForm.getInputByName("login").click(); - assertEquals("tenant-split-id-refresh-token:alice", page.getBody().asText()); + assertEquals("tenant-split-id-refresh-token:alice", page.getBody().asNormalizedText()); page = webClient.getPage("http://localhost:8081/web-app/access/tenant-split-id-refresh-token"); - assertEquals("tenant-split-id-refresh-token:no access", page.getBody().asText()); + assertEquals("tenant-split-id-refresh-token:no access", page.getBody().asNormalizedText()); page = webClient.getPage("http://localhost:8081/web-app/refresh/tenant-split-id-refresh-token"); - assertEquals("tenant-split-id-refresh-token:RT injected", page.getBody().asText()); + assertEquals("tenant-split-id-refresh-token:RT injected", page.getBody().asNormalizedText()); Cookie idTokenCookie = getSessionCookie(page.getWebClient(), "tenant-split-id-refresh-token"); checkSingleTokenCookie(idTokenCookie, "ID"); @@ -1026,7 +1025,8 @@ public void testAccessAndRefreshTokenInjectionWithoutIndexHtmlAndListener() thro page = loginForm.getInputByName("login").click(); - assertEquals("RT injected(event:OIDC_LOGIN,tenantId:tenant-listener,blockingApi:true)", page.getBody().asText()); + assertEquals("RT injected(event:OIDC_LOGIN,tenantId:tenant-listener,blockingApi:true)", + page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } } @@ -1046,7 +1046,7 @@ public void testAccessAndRefreshTokenInjectionWithQuery() throws Exception { page = loginForm.getInputByName("login").click(); - assertEquals("RT injected:aValue", page.getBody().asText()); + assertEquals("RT injected:aValue", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } } @@ -1129,7 +1129,7 @@ public void testCustomLogin() throws Exception { page = loginForm.getInputByName("login").click(); - assertEquals("alice", page.getBody().asText()); + assertEquals("alice", page.getBody().asNormalizedText()); } } diff --git a/integration-tests/oidc-tenancy/src/test/java/io/quarkus/it/keycloak/BearerTokenAuthorizationTest.java b/integration-tests/oidc-tenancy/src/test/java/io/quarkus/it/keycloak/BearerTokenAuthorizationTest.java index 1608749e307e6..4079d17c26ff5 100644 --- a/integration-tests/oidc-tenancy/src/test/java/io/quarkus/it/keycloak/BearerTokenAuthorizationTest.java +++ b/integration-tests/oidc-tenancy/src/test/java/io/quarkus/it/keycloak/BearerTokenAuthorizationTest.java @@ -56,19 +56,19 @@ public void testResolveTenantIdentifierWebApp() throws IOException { // First call after a redirect, tenant-id is initially calculated from the state `q_auth` cookie. // 'reauthenticated' flag is set is because, in fact, it is actually a 2nd call due to // quarkus-oidc doing a final redirect after completing a code flow to drop the redirect OIDC parameters - assertEquals("tenant-web-app:alice:reauthenticated", page.getBody().asText()); + assertEquals("tenant-web-app:alice:reauthenticated", page.getBody().asNormalizedText()); assertNotNull(getSessionCookie(webClient, "tenant-web-app")); assertNull(getStateCookie(webClient, "tenant-web-app")); // Second call after a redirect, tenant-id is calculated from the state `q_session` cookie page = webClient.getPage("http://localhost:8081/tenant/tenant-web-app/api/user/webapp"); - assertEquals("tenant-web-app:alice:reauthenticated", page.getBody().asText()); + assertEquals("tenant-web-app:alice:reauthenticated", page.getBody().asNormalizedText()); assertNotNull(getSessionCookie(webClient, "tenant-web-app")); assertNull(getStateCookie(webClient, "tenant-web-app")); // Local logout page = webClient.getPage("http://localhost:8081/tenant/tenant-web-app/api/user/webapp?logout=true"); - assertEquals("tenant-web-app:alice:reauthenticated:logout", page.getBody().asText()); + assertEquals("tenant-web-app:alice:reauthenticated:logout", page.getBody().asNormalizedText()); assertNull(getSessionCookie(webClient, "tenant-web-app")); assertNull(getStateCookie(webClient, "tenant-web-app")); @@ -98,7 +98,7 @@ public void testResolveTenantIdentifierWebApp2() throws IOException { loginForm.getInputByName("username").setValueAttribute("alice"); loginForm.getInputByName("password").setValueAttribute("alice"); page = loginForm.getInputByName("login").click(); - assertEquals("tenant-web-app2:alice", page.getBody().asText()); + assertEquals("tenant-web-app2:alice", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } } @@ -114,7 +114,7 @@ public void testCodeFlowRefreshTokens() throws IOException, InterruptedException page = loginForm.getInputByName("login").click(); assertEquals("userName: alice, idToken: true, accessToken: true, refreshToken: true", - page.getBody().asText()); + page.getBody().asNormalizedText()); Cookie sessionCookie = getSessionCookie(page.getWebClient(), "tenant-web-app-refresh"); assertNotNull(sessionCookie); @@ -181,7 +181,7 @@ public void testHybridWebApp() throws IOException { loginForm.getInputByName("username").setValueAttribute("alice"); loginForm.getInputByName("password").setValueAttribute("alice"); page = loginForm.getInputByName("login").click(); - assertEquals("alice:web-app", page.getBody().asText()); + assertEquals("alice:web-app", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } } @@ -205,7 +205,7 @@ public void testHybridWebAppService() throws IOException { loginForm.getInputByName("username").setValueAttribute("alice"); loginForm.getInputByName("password").setValueAttribute("alice"); page = loginForm.getInputByName("login").click(); - assertEquals("alice:web-app", page.getBody().asText()); + assertEquals("alice:web-app", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } RestAssured.given().auth().oauth2(getAccessToken("alice", "hybrid")) @@ -228,10 +228,10 @@ public void testResolveTenantIdentifierWebAppNoDiscovery() throws IOException { loginForm.getInputByName("username").setValueAttribute("alice"); loginForm.getInputByName("password").setValueAttribute("alice"); page = loginForm.getInputByName("login").click(); - assertEquals("tenant-web-app-no-discovery:alice", page.getBody().asText()); + assertEquals("tenant-web-app-no-discovery:alice", page.getBody().asNormalizedText()); page = webClient.getPage("http://localhost:8081/tenant/tenant-web-app-no-discovery/api/user/webapp-no-discovery"); - assertEquals("tenant-web-app-no-discovery:alice", page.getBody().asText()); + assertEquals("tenant-web-app-no-discovery:alice", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } } @@ -247,7 +247,7 @@ public void testReAuthenticateWhenSwitchingTenants() throws IOException { loginForm.getInputByName("username").setValueAttribute("alice"); loginForm.getInputByName("password").setValueAttribute("alice"); page = loginForm.getInputByName("login").click(); - assertEquals("tenant-web-app:alice:reauthenticated", page.getBody().asText()); + assertEquals("tenant-web-app:alice:reauthenticated", page.getBody().asNormalizedText()); // tenant-web-app2 page = webClient.getPage("http://localhost:8081/tenant/tenant-web-app2/api/user/webapp2"); assertNull(getStateCookieSavedPath(webClient, "tenant-web-app2")); @@ -256,7 +256,7 @@ public void testReAuthenticateWhenSwitchingTenants() throws IOException { loginForm.getInputByName("username").setValueAttribute("alice"); loginForm.getInputByName("password").setValueAttribute("alice"); page = loginForm.getInputByName("login").click(); - assertEquals("tenant-web-app2:alice", page.getBody().asText()); + assertEquals("tenant-web-app2:alice", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } @@ -568,7 +568,7 @@ public void testResolveTenantIdentifierWebAppDynamic() throws IOException { loginForm.getInputByName("username").setValueAttribute("alice"); loginForm.getInputByName("password").setValueAttribute("alice"); page = loginForm.getInputByName("login").click(); - assertEquals("tenant-web-app-dynamic:alice", page.getBody().asText()); + assertEquals("tenant-web-app-dynamic:alice", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } } diff --git a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java index ce862339115e9..7bbd13ba2e3cb 100644 --- a/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java +++ b/integration-tests/oidc-wiremock/src/test/java/io/quarkus/it/keycloak/CodeFlowAuthorizationTest.java @@ -69,11 +69,11 @@ public void testCodeFlow() throws IOException { page = form.getInputByValue("login").click(); - assertEquals("alice, cache size: 0", page.getBody().asText()); + assertEquals("alice, cache size: 0", page.getBody().asNormalizedText()); assertNotNull(getSessionCookie(webClient, "code-flow")); page = webClient.getPage("http://localhost:8081/code-flow/logout"); - assertEquals("Welcome, clientId: quarkus-web-app", page.getBody().asText()); + assertEquals("Welcome, clientId: quarkus-web-app", page.getBody().asNormalizedText()); assertNull(getSessionCookie(webClient, "code-flow")); // Clear the post logout cookie webClient.getCookieManager().clearCookies(); @@ -97,7 +97,7 @@ private void doTestCodeFlowEncryptedIdToken(String tenant) throws IOException { page = form.getInputByValue("login").click(); - assertEquals("user: alice", page.getBody().asText()); + assertEquals("user: alice", page.getBody().asNormalizedText()); Cookie sessionCookie = getSessionCookie(webClient, tenant); assertNotNull(sessionCookie); // default session cookie format: "idtoken|accesstoken|refreshtoken" @@ -105,7 +105,7 @@ private void doTestCodeFlowEncryptedIdToken(String tenant) throws IOException { // repeat the call with the session cookie containing the encrypted id token page = webClient.getPage("http://localhost:8081/code-flow-encrypted-id-token/" + tenant); - assertEquals("user: alice", page.getBody().asText()); + assertEquals("user: alice", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } @@ -124,12 +124,12 @@ public void testCodeFlowFormPostAndBackChannelLogout() throws IOException { page = form.getInputByValue("login").click(); - assertEquals("alice", page.getBody().asText()); + assertEquals("alice", page.getBody().asNormalizedText()); assertNotNull(getSessionCookie(webClient, "code-flow-form-post")); page = webClient.getPage("http://localhost:8081/code-flow-form-post"); - assertEquals("alice", page.getBody().asText()); + assertEquals("alice", page.getBody().asNormalizedText()); // Session is still active assertNotNull(getSessionCookie(webClient, "code-flow-form-post")); @@ -166,12 +166,12 @@ public void testCodeFlowFormPostAndFrontChannelLogout() throws IOException { page = form.getInputByValue("login").click(); - assertEquals("alice", page.getBody().asText()); + assertEquals("alice", page.getBody().asNormalizedText()); assertNotNull(getSessionCookie(webClient, "code-flow-form-post")); page = webClient.getPage("http://localhost:8081/code-flow-form-post"); - assertEquals("alice", page.getBody().asText()); + assertEquals("alice", page.getBody().asNormalizedText()); // Session is still active Cookie sessionCookie = getSessionCookie(webClient, "code-flow-form-post"); @@ -225,7 +225,7 @@ private void doTestCodeFlowUserInfo(String tenantId, long internalIdTokenLifetim page = form.getInputByValue("login").click(); - assertEquals("alice:alice:alice, cache size: 1", page.getBody().asText()); + assertEquals("alice:alice:alice, cache size: 1", page.getBody().asNormalizedText()); Cookie sessionCookie = getSessionCookie(webClient, tenantId); assertNotNull(sessionCookie); @@ -250,7 +250,7 @@ private void doTestCodeFlowUserInfoCashedInIdToken() throws Exception { page = form.getInputByValue("login").click(); - assertEquals("alice:alice:alice, cache size: 0", page.getBody().asText()); + assertEquals("alice:alice:alice, cache size: 0", page.getBody().asNormalizedText()); Cookie sessionCookie = getSessionCookie(webClient, "code-flow-user-info-github-cached-in-idtoken"); assertNotNull(sessionCookie); @@ -260,7 +260,7 @@ private void doTestCodeFlowUserInfoCashedInIdToken() throws Exception { // refresh Thread.sleep(3000); page = webClient.getPage("http://localhost:8081/code-flow-user-info-github-cached-in-idtoken"); - assertEquals("alice:alice:bob, cache size: 0", page.getBody().asText()); + assertEquals("alice:alice:bob, cache size: 0", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } diff --git a/integration-tests/smallrye-jwt-oidc-webapp/src/test/java/io/quarkus/it/keycloak/SmallRyeJwtOidcWebAppTest.java b/integration-tests/smallrye-jwt-oidc-webapp/src/test/java/io/quarkus/it/keycloak/SmallRyeJwtOidcWebAppTest.java index ac0f1e81522da..54fb9c01b92db 100644 --- a/integration-tests/smallrye-jwt-oidc-webapp/src/test/java/io/quarkus/it/keycloak/SmallRyeJwtOidcWebAppTest.java +++ b/integration-tests/smallrye-jwt-oidc-webapp/src/test/java/io/quarkus/it/keycloak/SmallRyeJwtOidcWebAppTest.java @@ -75,7 +75,7 @@ public void testGetUserNameWithCodeFlow() throws Exception { page = loginForm.getInputByName("login").click(); - assertEquals("alice", page.getBody().asText()); + assertEquals("alice", page.getBody().asNormalizedText()); webClient.getCookieManager().clearCookies(); } }