Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2.14.3 backports 2 #29687

Merged
merged 7 commits into from
Dec 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<bouncycastle.fips.version>1.0.2.3</bouncycastle.fips.version>
<bouncycastle.tls.fips.version>1.0.12.3</bouncycastle.tls.fips.version>
<findbugs.version>3.0.2</findbugs.version>
<jandex.version>3.0.4</jandex.version>
<jandex.version>3.0.5</jandex.version>
<resteasy.version>4.7.7.Final</resteasy.version>
<opentracing.version>0.33.0</opentracing.version>
<opentracing-jdbc.version>0.2.4</opentracing-jdbc.version>
Expand Down
2 changes: 1 addition & 1 deletion build-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<failsafe-plugin.version>${version.surefire.plugin}</failsafe-plugin.version>

<!-- Jandex versions -->
<jandex.version>3.0.4</jandex.version>
<jandex.version>3.0.5</jandex.version>
<jandex-gradle-plugin.version>1.0.0</jandex-gradle-plugin.version>

<asciidoctorj.version>2.5.7</asciidoctorj.version>
Expand Down
36 changes: 14 additions & 22 deletions docs/src/main/asciidoc/podman.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -71,23 +71,8 @@ This action only needs to be done once.
=== Linux

The Podman package is available in several Linux distributions.
Podman can be used the same way as Docker with the `podman-docker` package.
To install it for your OS, please refer to the https://podman.io/getting-started/installation[Podman installation guide].
Below is the short installation instruction for popular Linux distributions:

==== Fedora

[source,bash]
----
sudo dnf install podman podman-docker docker-compose
----

==== Ubuntu (21.04 and later)

[source,bash]
----
sudo apt install podman podman-docker docker-compose
----
Podman can in most cases be used as an drop-in-replacement for Docker, either with the `podman-docker` package, or using an alias (`alias docker=podman`).
To install it for your Linux OS, please refer to the https://podman.io/getting-started/installation#installing-on-linux[Podman installation guide].

=== Setting DOCKER_HOST on Linux

Expand All @@ -96,17 +81,24 @@ On Linux, the REST API Unix socket is, by default, restricted to only allow the
This prevents someone from using a container to achieve a privilege escalation on the system.
While these restrictions can be softened to allow a special group instead of just root, the recommended approach is to use rootless Podman on Linux.
To use rootless Podman, you need to set a `DOCKER_HOST` environment variable to point to the user-specific socket.
In both cases, you need to start the REST API by enabling the Podman socket service through systemd.

[source]
NOTE: In both cases, you need to start the REST API by enabling the Podman socket service through systemd, or at least by making sure Podman is running as a service.

[source,bash]
----
# Enable the podman socket with Docker REST API (only needs to be done once)
# Example 1: Enable the podman socket with Docker REST API with systemd (only needs to be done once)
systemctl --user enable podman.socket --now
----

[source,bash]
----
# Example 2: Enable the podman socket with Docker REST API on a system where systemd is not running (WSL etc)
podman system service --time=0
----

Then, you can obtain the path of the socket with the following command:

[source]
[source,bash]
----
$ podman info | grep -A2 'remoteSocket'

Expand All @@ -117,7 +109,7 @@ remoteSocket:

Setting the `DOCKER_HOST` environment variable must be done every time or added to the profile:

[source]
[source,bash]
----
export DOCKER_HOST=unix:///path/to/podman.sock <1>
----
Expand Down
33 changes: 32 additions & 1 deletion docs/src/main/asciidoc/security-built-in-authentication.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,38 @@ public class HelloService {

=== How to customize authentication exception responses

By default, the authentication security constraints are enforced before the JAX-RS chain starts.
By default, the authentication security constraints are enforced before the JAX-RS chain starts and only way to handle Quarkus Security authentication exceptions is to provide a failure handler like this one:

[source,java]
----
package io.quarkus.it.keycloak;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;

import io.quarkus.security.AuthenticationFailedException;
import io.vertx.core.Handler;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;

@ApplicationScoped
public class AuthenticationFailedExceptionHandler {

public void init(@Observes Router router) {
router.route().failureHandler(new Handler<RoutingContext>() {
@Override
public void handle(RoutingContext event) {
if (event.failure() instanceof AuthenticationFailedException) {
event.response().setStatusCode(401).end(CUSTOMIZED_RESPONSE);
} else {
event.next();
}
}
});
}
}
----

Disabling the proactive authentication effectively shifts this process to the moment when the JAX-RS chain starts running thus making it possible to use JAX-RS `ExceptionMapper` to capture Quarkus Security authentication exceptions such as `io.quarkus.security.AuthenticationFailedException`, for example:

[source,java]
Expand Down
2 changes: 1 addition & 1 deletion extensions/keycloak-admin-client-reactive/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<artifactId>quarkus-keycloak-admin-client-reactive</artifactId>
<name>Quarkus - Keycloak Admin Client - Reactive - Runtime</name>
<description>Administer a Keycloak Instance</description>
<description>Administer a Keycloak Instance using Reactive</description>

<dependencies>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public void visit(KubernetesListFluent<?> list) {
.withMatchLabels(new HashMap<String, String>())
.endSelector()
.withNewTemplate()
.withNewMetadata()
.endMetadata()
.withNewSpec()
.addNewContainer()
.withName(name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<artifactId>quarkus-hibernate-reactive-rest-data-panache</artifactId>
<name>Quarkus - Hibernate Reactive REST data with Panache - Runtime</name>
<description>Generate JAX-RS resources for your Hibernate Panache entities and repositories</description>
<description>Generate JAX-RS resources for your Hibernate Reactive Panache entities and repositories</description>

<dependencies>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,17 @@ BodyHandlerBuildItem bodyHandler(io.quarkus.vertx.http.deployment.BodyHandlerBui
return new BodyHandlerBuildItem(realOne.getHandler());
}

@BuildStep
@Record(value = ExecutionTime.STATIC_INIT)
public void replaceDefaultAuthFailureHandler(VertxWebRecorder recorder, Capabilities capabilities,
BuildProducer<FilterBuildItem> filterBuildItemBuildProducer) {
if (capabilities.isMissing(Capability.RESTEASY_REACTIVE)) {
// replace default auth failure handler added by vertx-http so that route failure handlers can customize response
filterBuildItemBuildProducer
.produce(new FilterBuildItem(recorder.addAuthFailureHandler(), FilterBuildItem.AUTHENTICATION - 1));
}
}

@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
void addAdditionalRoutes(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.quarkus.vertx.web.failure;

import static javax.ws.rs.core.Response.Status.UNAUTHORIZED;

import java.util.function.Supplier;

import org.hamcrest.Matchers;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.security.AuthenticationCompletionException;
import io.quarkus.security.test.utils.TestIdentityController;
import io.quarkus.security.test.utils.TestIdentityProvider;
import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.vertx.web.Route;
import io.restassured.RestAssured;
import io.restassured.filter.cookie.CookieFilter;
import io.vertx.core.http.HttpServerResponse;

public class AuthCompletionExceptionHandlerTest {

private static final String AUTHENTICATION_COMPLETION_EX = "AuthenticationCompletionException";

@RegisterExtension
static QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(new Supplier<>() {
@Override
public JavaArchive get() {
return ShrinkWrap.create(JavaArchive.class)
.addClasses(TestIdentityProvider.class, TestIdentityController.class,
CustomAuthCompletionExceptionHandler.class)
.addAsResource(new StringAsset("quarkus.http.auth.form.enabled=true\n"), "application.properties");
}
});

@BeforeAll
public static void setup() {
TestIdentityController.resetRoles().add("a d m i n", "a d m i n", "a d m i n");
}

@Test
public void testAuthCompletionExMapper() {
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
RestAssured
.given()
.filter(new CookieFilter())
.redirects().follow(false)
.when()
.formParam("j_username", "a d m i n")
.formParam("j_password", "a d m i n")
.cookie("quarkus-redirect-location", "https://quarkus.io/guides")
.post("/j_security_check")
.then()
.assertThat()
.statusCode(401)
.body(Matchers.equalTo(AUTHENTICATION_COMPLETION_EX));
}

public static final class CustomAuthCompletionExceptionHandler {

@Route(type = Route.HandlerType.FAILURE)
void handle(AuthenticationCompletionException e, HttpServerResponse response) {
response.setStatusCode(UNAUTHORIZED.getStatusCode()).end(AUTHENTICATION_COMPLETION_EX);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig;
import io.quarkus.vertx.http.runtime.HttpCompression;
import io.quarkus.vertx.http.runtime.HttpConfiguration;
import io.quarkus.vertx.http.runtime.security.HttpSecurityRecorder.DefaultAuthFailureHandler;
import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.subscription.UniSubscriber;
Expand Down Expand Up @@ -134,4 +135,26 @@ public void onFailure(Throwable failure) {
};
}

public Handler<RoutingContext> addAuthFailureHandler() {
return new Handler<RoutingContext>() {
@Override
public void handle(RoutingContext event) {
if (event.get(QuarkusHttpUser.AUTH_FAILURE_HANDLER) instanceof DefaultAuthFailureHandler) {
// failing event rather than end it makes it possible to customize response
// QuarkusErrorHandler will send response if the failure is not handled elsewhere
event.put(QuarkusHttpUser.AUTH_FAILURE_HANDLER, new DefaultAuthFailureHandler() {
@Override
protected void proceed(Throwable throwable) {

if (!event.failed()) {
event.fail(throwable);
}
}
});
}
event.next();
}
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT;

import java.util.Optional;
import java.util.function.Consumer;

import io.quarkus.builder.item.SimpleBuildItem;
import io.quarkus.deployment.Capabilities;
Expand All @@ -30,7 +29,6 @@
import io.quarkus.vertx.http.deployment.RouteBuildItem;
import io.quarkus.vertx.http.runtime.VertxHttpRecorder;
import io.vertx.core.Handler;
import io.vertx.ext.web.Route;
import io.vertx.ext.web.RoutingContext;

public class ResteasyStandaloneBuildStep {
Expand Down Expand Up @@ -75,6 +73,7 @@ public void boot(ShutdownContextBuildItem shutdown,
BuildProducer<FeatureBuildItem> feature,
BuildProducer<DefaultRouteBuildItem> defaultRoutes,
BuildProducer<RouteBuildItem> routes,
BuildProducer<FilterBuildItem> filterBuildItemBuildProducer,
CoreVertxBuildItem vertx,
ResteasyStandaloneBuildItem standalone,
Optional<RequireVirtualHttpBuildItem> requireVirtual,
Expand All @@ -92,15 +91,16 @@ public void boot(ShutdownContextBuildItem shutdown,
executorBuildItem.getExecutorProxy(), resteasyVertxConfig);

// failure handler for auth failures that occurred before the handler defined right above started processing the request
final Consumer<Route> addFailureHandler = recorder.addVertxFailureHandler(vertx.getVertx(),
final Handler<RoutingContext> failureHandler = recorder.vertxFailureHandler(vertx.getVertx(),
executorBuildItem.getExecutorProxy(), resteasyVertxConfig);
filterBuildItemBuildProducer.produce(new FilterBuildItem(failureHandler,
VertxHttpRecorder.AFTER_DEFAULT_ROUTE_ORDER_MARK + REST_ROUTE_ORDER_OFFSET, true));

// Exact match for resources matched to the root path
routes.produce(
RouteBuildItem.builder()
.orderedRoute(standalone.deploymentRootPath,
VertxHttpRecorder.AFTER_DEFAULT_ROUTE_ORDER_MARK + REST_ROUTE_ORDER_OFFSET,
addFailureHandler)
VertxHttpRecorder.AFTER_DEFAULT_ROUTE_ORDER_MARK + REST_ROUTE_ORDER_OFFSET)
.handler(handler).build());
String matchPath = standalone.deploymentRootPath;
if (matchPath.endsWith("/")) {
Expand Down