Skip to content

Commit

Permalink
Resolve Sec. Identity in RESTEasy Reactive when Proactive Auth disabled
Browse files Browse the repository at this point in the history
f ix #23547 for RESTEasy Reactive cases (e.g. the issue reproducer)
  • Loading branch information
michalvavrik committed Jun 29, 2022
1 parent 2ef790c commit 2415439
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import javax.ws.rs.GET;
import javax.ws.rs.Path;

import io.smallrye.common.annotation.NonBlocking;

/**
* @author Michal Szynkiewicz, [email protected]
*/
Expand All @@ -24,4 +26,12 @@ public String admin() {
return "admin";
}

@NonBlocking
@Path("/admin/non-blocking")
@RolesAllowed("admin")
@GET
public String adminNonBlocking() {
return "admin";
}

}
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -40,6 +42,7 @@ public void apply(SecurityIdentity identity, MethodDescription method, Object[]
private volatile InjectableInstance<CurrentIdentityAssociation> currentIdentityAssociation;
private volatile SecurityCheck check;
private volatile AuthorizationController authorizationController;
private Boolean setIdentity;

@Override
public void handle(ResteasyReactiveRequestContext requestContext) throws Exception {
Expand All @@ -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<SecurityIdentity, Object>() {
@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<SecurityIdentity, Object>() {
@Override
public Object apply(SecurityIdentity securityIdentity) {
theCheck.apply(securityIdentity, methodDescription,
requestContext.getParameters());
return null;
}
})
.subscribe().withSubscriber(new UniSubscriber<Object>() {
@Override
public void onSubscribe(UniSubscription subscription) {
Expand Down

0 comments on commit 2415439

Please sign in to comment.