diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/RolesAllowedResource.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/RolesAllowedResource.java index 842e04d7d0ec84..b12389be803a0e 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/RolesAllowedResource.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/RolesAllowedResource.java @@ -5,6 +5,8 @@ import javax.ws.rs.GET; import javax.ws.rs.Path; +import io.smallrye.common.annotation.NonBlocking; + /** * @author Michal Szynkiewicz, michal.l.szynkiewicz@gmail.com */ @@ -24,4 +26,12 @@ public String admin() { return "admin"; } + @NonBlocking + @Path("/admin/non-blocking") + @RolesAllowed("admin") + @GET + public String adminNonBlocking() { + return "admin"; + } + } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/SecurityChecksWithDisabledProactiveAuthTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/SecurityChecksWithDisabledProactiveAuthTest.java new file mode 100644 index 00000000000000..87811a3293cc76 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/SecurityChecksWithDisabledProactiveAuthTest.java @@ -0,0 +1,48 @@ +package io.quarkus.resteasy.reactive.server.test.security; + +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.security.test.utils.TestIdentityController; +import io.quarkus.security.test.utils.TestIdentityProvider; +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class SecurityChecksWithDisabledProactiveAuthTest { + + @RegisterExtension + static final QuarkusUnitTest app = new QuarkusUnitTest() + .overrideConfigKey("quarkus.http.auth.proactive", "false") + .withApplicationRoot((jar) -> jar.addClasses(RolesAllowedResource.class, TestIdentityProvider.class, + TestIdentityController.class)); + + @BeforeAll + public static void setupUsers() { + TestIdentityController.resetRoles() + .add("admin", "admin", "admin") + .add("user", "user", "user"); + } + + @Test + public void test() { + // security identity in 'Quarkus - Security - Runtime' is resolved + RestAssured + .given() + .auth().basic("admin", "admin") + .get("/roles/admin/non-blocking") + .then() + .statusCode(200) + .body(Matchers.is("admin")); + + // security check is applied + RestAssured + .given() + .auth().basic("user", "user") + .get("/roles/admin/non-blocking") + .then() + .statusCode(403); + } + +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/QuarkusResteasyReactiveRequestContext.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/QuarkusResteasyReactiveRequestContext.java index fb47a5faf78e4a..866cab0db461a3 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/QuarkusResteasyReactiveRequestContext.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/QuarkusResteasyReactiveRequestContext.java @@ -17,15 +17,17 @@ public class QuarkusResteasyReactiveRequestContext extends VertxResteasyReactiveRequestContext { + public final boolean isProactiveAuthenticationDisabled; final CurrentIdentityAssociation association; boolean userSetup = false; public QuarkusResteasyReactiveRequestContext(Deployment deployment, ProvidersImpl providers, RoutingContext context, ThreadSetupAction requestContext, ServerRestHandler[] handlerChain, ServerRestHandler[] abortHandlerChain, ClassLoader devModeTccl, - CurrentIdentityAssociation currentIdentityAssociation) { + CurrentIdentityAssociation currentIdentityAssociation, boolean isProactiveAuthenticationDisabled) { super(deployment, providers, context, requestContext, handlerChain, abortHandlerChain, devModeTccl); this.association = currentIdentityAssociation; + this.isProactiveAuthenticationDisabled = isProactiveAuthenticationDisabled; if (VertxContext.isOnDuplicatedContext()) { VertxContextSafetyToggle.setCurrentContextSafe(true); } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java index 1ab27aef29823e..4cdac507e46626 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveRecorder.java @@ -161,7 +161,8 @@ public ResteasyReactiveRequestContext createContext(Deployment deployment, return new QuarkusResteasyReactiveRequestContext(deployment, providers, (RoutingContext) context, requestContext, handlerChain, - abortHandlerChain, launchMode == LaunchMode.DEVELOPMENT ? tccl : null, currentIdentityAssociation); + abortHandlerChain, launchMode == LaunchMode.DEVELOPMENT ? tccl : null, currentIdentityAssociation, + !vertxConfig.auth.proactive); } }; diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityHandler.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityHandler.java index 21078343d33905..08b2cc6b719403 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityHandler.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/EagerSecurityHandler.java @@ -14,12 +14,14 @@ import io.quarkus.arc.Arc; import io.quarkus.arc.InjectableInstance; +import io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext; import io.quarkus.security.identity.CurrentIdentityAssociation; import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.security.spi.runtime.AuthorizationController; import io.quarkus.security.spi.runtime.MethodDescription; import io.quarkus.security.spi.runtime.SecurityCheck; import io.quarkus.security.spi.runtime.SecurityCheckStorage; +import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.subscription.UniSubscriber; import io.smallrye.mutiny.subscription.UniSubscription; @@ -40,6 +42,7 @@ public void apply(SecurityIdentity identity, MethodDescription method, Object[] private volatile InjectableInstance currentIdentityAssociation; private volatile SecurityCheck check; private volatile AuthorizationController authorizationController; + private Boolean setIdentity; @Override public void handle(ResteasyReactiveRequestContext requestContext) throws Exception { @@ -66,18 +69,36 @@ public void handle(ResteasyReactiveRequestContext requestContext) throws Excepti if (!authorizationController.isAuthorizationEnabled()) { return; } + + // if proactive auth is disabled, then accessing SecurityIdentity is a blocking operation + // setting identity here will enable SecurityInterceptors registered in Quarkus Security Deployment to run checks + final boolean setSecurityIdentity; + if (requestContext instanceof QuarkusResteasyReactiveRequestContext) { + setSecurityIdentity = ((QuarkusResteasyReactiveRequestContext) requestContext).isProactiveAuthenticationDisabled; + } else { + setSecurityIdentity = false; + } + requestContext.requireCDIRequestScope(); SecurityCheck theCheck = check; if (!theCheck.isPermitAll()) { requestContext.suspend(); - getCurrentIdentityAssociation().get().getDeferredIdentity().map(new Function() { - @Override - public Object apply(SecurityIdentity securityIdentity) { - theCheck.apply(securityIdentity, methodDescription, - requestContext.getParameters()); - return null; - } - }) + final var currentIdentityAssociation = getCurrentIdentityAssociation().get(); + currentIdentityAssociation.getDeferredIdentity() + .call(securityIdentity -> { + if (setSecurityIdentity && securityIdentity != null) { + currentIdentityAssociation.setIdentity(securityIdentity); + } + return Uni.createFrom().item(securityIdentity); + }) + .map(new Function() { + @Override + public Object apply(SecurityIdentity securityIdentity) { + theCheck.apply(securityIdentity, methodDescription, + requestContext.getParameters()); + return null; + } + }) .subscribe().withSubscriber(new UniSubscriber() { @Override public void onSubscribe(UniSubscription subscription) {