diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/BlockingTaskRunner.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/BlockingTaskRunner.java index 601ebc1e72ffb8..70d9e6baaf3590 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/BlockingTaskRunner.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/BlockingTaskRunner.java @@ -1,43 +1,36 @@ package io.quarkus.oidc.runtime; -import java.util.function.Consumer; import java.util.function.Supplier; +import io.quarkus.arc.Arc; import io.quarkus.oidc.OidcRequestContext; -import io.quarkus.runtime.BlockingOperationControl; -import io.quarkus.runtime.ExecutorRecorder; +import io.quarkus.security.spi.runtime.BlockingSecurityExecutor; import io.smallrye.mutiny.Uni; -import io.smallrye.mutiny.subscription.UniEmitter; public class BlockingTaskRunner implements OidcRequestContext { - public Uni runBlocking(Supplier function) { - return Uni.createFrom().deferred(new Supplier>() { + + private final BlockingSecurityExecutor blockingExecutor; + + public BlockingTaskRunner() { + this.blockingExecutor = new BlockingSecurityExecutor() { + + private volatile BlockingSecurityExecutor delegate = null; + @Override - public Uni get() { - if (BlockingOperationControl.isBlockingAllowed()) { - try { - return Uni.createFrom().item(function.get()); - } catch (Throwable t) { - return Uni.createFrom().failure(t); - } - } else { - return Uni.createFrom().emitter(new Consumer>() { - @Override - public void accept(UniEmitter uniEmitter) { - ExecutorRecorder.getCurrent().execute(new Runnable() { - @Override - public void run() { - try { - uniEmitter.complete(function.get()); - } catch (Throwable t) { - uniEmitter.fail(t); - } - } - }); - } - }); + public Uni executeBlocking(Supplier supplier) { + if (delegate == null) { + delegate = Arc.container().select(BlockingSecurityExecutor.class).get(); } + return delegate.executeBlocking(supplier); } - }); + }; + } + + public BlockingTaskRunner(BlockingSecurityExecutor blockingExecutor) { + this.blockingExecutor = blockingExecutor; + } + + public Uni runBlocking(Supplier function) { + return blockingExecutor.executeBlocking(function); } } \ No newline at end of file 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 8d9fcf511023ed..f9e74b1bfe31ca 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 @@ -42,6 +42,7 @@ import io.quarkus.security.AuthenticationRedirectException; import io.quarkus.security.identity.IdentityProviderManager; import io.quarkus.security.identity.SecurityIdentity; +import io.quarkus.security.spi.runtime.BlockingSecurityExecutor; import io.quarkus.vertx.http.runtime.security.ChallengeData; import io.smallrye.jwt.algorithm.KeyEncryptionAlgorithm; import io.smallrye.jwt.build.Jwt; @@ -74,10 +75,15 @@ public class CodeAuthenticationMechanism extends AbstractOidcAuthenticationMecha private static final String INTERNAL_IDTOKEN_HEADER = "internal"; private static final Logger LOG = Logger.getLogger(CodeAuthenticationMechanism.class); - private final BlockingTaskRunner createTokenStateRequestContext = new BlockingTaskRunner(); - private final BlockingTaskRunner getTokenStateRequestContext = new BlockingTaskRunner(); + private final BlockingTaskRunner createTokenStateRequestContext; + private final BlockingTaskRunner getTokenStateRequestContext; private final SecureRandom secureRandom = new SecureRandom(); + public CodeAuthenticationMechanism(BlockingSecurityExecutor blockingExecutor) { + this.createTokenStateRequestContext = new BlockingTaskRunner<>(blockingExecutor); + this.getTokenStateRequestContext = new BlockingTaskRunner<>(blockingExecutor); + } + public Uni authenticate(RoutingContext context, IdentityProviderManager identityProviderManager, OidcTenantConfig oidcTenantConfig) { final Map cookies = context.request().cookieMap(); diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTenantConfigResolver.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTenantConfigResolver.java index 39331278889a1a..784eae19f8c2d9 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTenantConfigResolver.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/DefaultTenantConfigResolver.java @@ -23,6 +23,7 @@ import io.quarkus.oidc.TokenStateManager; import io.quarkus.oidc.UserInfo; import io.quarkus.oidc.UserInfoCache; +import io.quarkus.security.spi.runtime.BlockingSecurityExecutor; import io.smallrye.mutiny.Uni; import io.vertx.ext.web.RoutingContext; @@ -64,12 +65,16 @@ public class DefaultTenantConfigResolver { @ConfigProperty(name = "quarkus.http.proxy.enable-forwarded-prefix") boolean enableHttpForwardedPrefix; - private final BlockingTaskRunner blockingRequestContext = new BlockingTaskRunner(); + private final BlockingTaskRunner blockingRequestContext; private volatile boolean securityEventObserved; private ConcurrentHashMap backChannelLogoutTokens = new ConcurrentHashMap<>(); + public DefaultTenantConfigResolver(BlockingSecurityExecutor blockingExecutor) { + this.blockingRequestContext = new BlockingTaskRunner(blockingExecutor); + } + @PostConstruct public void verifyResolvers() { if (tenantConfigResolver.isResolvable() && tenantConfigResolver.isAmbiguous()) { diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcAuthenticationMechanism.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcAuthenticationMechanism.java index 56b8233e3631dd..edf944566678ba 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcAuthenticationMechanism.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcAuthenticationMechanism.java @@ -14,6 +14,7 @@ import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.security.identity.request.AuthenticationRequest; import io.quarkus.security.identity.request.TokenAuthenticationRequest; +import io.quarkus.security.spi.runtime.BlockingSecurityExecutor; import io.quarkus.vertx.http.runtime.security.ChallengeData; import io.quarkus.vertx.http.runtime.security.HttpAuthenticationMechanism; import io.quarkus.vertx.http.runtime.security.HttpCredentialTransport; @@ -28,11 +29,12 @@ public class OidcAuthenticationMechanism implements HttpAuthenticationMechanism HttpCredentialTransport.Type.AUTHORIZATION_CODE, OidcConstants.CODE_FLOW_CODE); private final BearerAuthenticationMechanism bearerAuth = new BearerAuthenticationMechanism(); - private final CodeAuthenticationMechanism codeAuth = new CodeAuthenticationMechanism(); + private final CodeAuthenticationMechanism codeAuth; private final DefaultTenantConfigResolver resolver; - public OidcAuthenticationMechanism(DefaultTenantConfigResolver resolver) { + public OidcAuthenticationMechanism(DefaultTenantConfigResolver resolver, BlockingSecurityExecutor blockingExecutor) { this.resolver = resolver; + this.codeAuth = new CodeAuthenticationMechanism(blockingExecutor); this.bearerAuth.init(this, resolver); this.codeAuth.init(this, resolver); } diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcIdentityProvider.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcIdentityProvider.java index ece219a5ef0525..e43dc4b6708f77 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcIdentityProvider.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcIdentityProvider.java @@ -9,7 +9,6 @@ import java.util.function.Supplier; import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; import org.eclipse.microprofile.jwt.Claims; import org.jboss.logging.Logger; @@ -31,6 +30,7 @@ import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.security.identity.request.TokenAuthenticationRequest; import io.quarkus.security.runtime.QuarkusSecurityIdentity; +import io.quarkus.security.spi.runtime.BlockingSecurityExecutor; import io.quarkus.vertx.http.runtime.security.HttpSecurityUtils; import io.smallrye.mutiny.Uni; import io.vertx.core.json.JsonObject; @@ -47,12 +47,17 @@ public class OidcIdentityProvider implements IdentityProvider NULL_CODE_ACCESS_TOKEN_UNI = Uni.createFrom().nullItem(); private static final String CODE_ACCESS_TOKEN_RESULT = "code_flow_access_token_result"; - @Inject - DefaultTenantConfigResolver tenantResolver; + private final DefaultTenantConfigResolver tenantResolver; + private final BlockingTaskRunner uniVoidOidcContext; + private final BlockingTaskRunner getIntrospectionRequestContext; + private final BlockingTaskRunner getUserInfoRequestContext; - private BlockingTaskRunner uniVoidOidcContext = new BlockingTaskRunner(); - private BlockingTaskRunner getIntrospectionRequestContext = new BlockingTaskRunner(); - private BlockingTaskRunner getUserInfoRequestContext = new BlockingTaskRunner(); + public OidcIdentityProvider(DefaultTenantConfigResolver tenantResolver, BlockingSecurityExecutor blockingExecutor) { + this.tenantResolver = tenantResolver; + this.uniVoidOidcContext = new BlockingTaskRunner<>(blockingExecutor); + this.getIntrospectionRequestContext = new BlockingTaskRunner<>(blockingExecutor); + this.getUserInfoRequestContext = new BlockingTaskRunner<>(blockingExecutor); + } @Override public Class getRequestType() { diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java index 9536c42f6463a6..854ade5b407881 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcRecorder.java @@ -27,7 +27,6 @@ import io.quarkus.oidc.TenantConfigResolver; import io.quarkus.oidc.common.runtime.OidcCommonConfig; import io.quarkus.oidc.common.runtime.OidcCommonUtils; -import io.quarkus.runtime.ExecutorRecorder; import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.TlsConfig; @@ -83,8 +82,7 @@ public TenantConfigBean get() { public Uni apply(OidcTenantConfig config) { return createDynamicTenantContext(vertxValue, config, tlsConfig, config.getTenantId().get()); } - }, - ExecutorRecorder.getCurrent()); + }); } }; } diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/TenantConfigBean.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/TenantConfigBean.java index c4e401b83dfa7d..535c17814c21ea 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/TenantConfigBean.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/TenantConfigBean.java @@ -1,7 +1,6 @@ package io.quarkus.oidc.runtime; import java.util.Map; -import java.util.concurrent.Executor; import java.util.function.Function; import jakarta.enterprise.context.spi.CreationalContext; @@ -21,8 +20,7 @@ public TenantConfigBean( Map staticTenantsConfig, Map dynamicTenantsConfig, TenantConfigContext defaultTenant, - Function> tenantConfigContextFactory, - Executor blockingExecutor) { + Function> tenantConfigContextFactory) { this.staticTenantsConfig = staticTenantsConfig; this.dynamicTenantsConfig = dynamicTenantsConfig; this.defaultTenant = defaultTenant; diff --git a/extensions/security/runtime-spi/src/main/java/io/quarkus/security/spi/runtime/BlockingSecurityExecutor.java b/extensions/security/runtime-spi/src/main/java/io/quarkus/security/spi/runtime/BlockingSecurityExecutor.java index 50ed7af5839e03..90973f946e3188 100644 --- a/extensions/security/runtime-spi/src/main/java/io/quarkus/security/spi/runtime/BlockingSecurityExecutor.java +++ b/extensions/security/runtime-spi/src/main/java/io/quarkus/security/spi/runtime/BlockingSecurityExecutor.java @@ -4,6 +4,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; +import io.quarkus.runtime.BlockingOperationControl; import io.quarkus.security.identity.AuthenticationRequestContext; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.subscription.UniEmitter; @@ -20,19 +21,32 @@ static BlockingSecurityExecutor createBlockingExecutor(Supplier execut return new BlockingSecurityExecutor() { @Override public Uni executeBlocking(Supplier function) { - return Uni.createFrom().emitter(new Consumer>() { + return Uni.createFrom().deferred(new Supplier>() { @Override - public void accept(UniEmitter uniEmitter) { - executorSupplier.get().execute(new Runnable() { - @Override - public void run() { - try { - uniEmitter.complete(function.get()); - } catch (Throwable t) { - uniEmitter.fail(t); - } + public Uni get() { + if (BlockingOperationControl.isBlockingAllowed()) { + try { + return Uni.createFrom().item(function.get()); + } catch (Throwable t) { + return Uni.createFrom().failure(t); } - }); + } else { + return Uni.createFrom().emitter(new Consumer>() { + @Override + public void accept(UniEmitter uniEmitter) { + executorSupplier.get().execute(new Runnable() { + @Override + public void run() { + try { + uniEmitter.complete(function.get()); + } catch (Throwable t) { + uniEmitter.fail(t); + } + } + }); + } + }); + } } }); } diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/QuarkusIdentityProviderManagerImpl.java b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/QuarkusIdentityProviderManagerImpl.java index 6d72b5dea2eeb1..3179c70c9f734f 100644 --- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/QuarkusIdentityProviderManagerImpl.java +++ b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/QuarkusIdentityProviderManagerImpl.java @@ -13,7 +13,6 @@ import org.jboss.logging.Logger; -import io.quarkus.runtime.BlockingOperationControl; import io.quarkus.security.AuthenticationFailedException; import io.quarkus.security.identity.AuthenticationRequestContext; import io.quarkus.security.identity.IdentityProvider; @@ -38,22 +37,7 @@ public class QuarkusIdentityProviderManagerImpl implements IdentityProviderManag private final AuthenticationRequestContext blockingRequestContext = new AuthenticationRequestContext() { @Override public Uni runBlocking(Supplier function) { - return Uni.createFrom().deferred(new Supplier>() { - @Override - public Uni get() { - if (BlockingOperationControl.isBlockingAllowed()) { - try { - SecurityIdentity result = function.get(); - return Uni.createFrom().item(result); - } catch (Throwable t) { - return Uni.createFrom().failure(t); - } - } else { - return blockingExecutor.executeBlocking(function); - } - } - }); - + return blockingExecutor.executeBlocking(function); } }; diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/AbstractHttpAuthorizer.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/AbstractHttpAuthorizer.java index ce2797f5bfb200..fd194a02b9a77a 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/AbstractHttpAuthorizer.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/AbstractHttpAuthorizer.java @@ -4,18 +4,18 @@ import java.util.List; import java.util.function.BiFunction; import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; import org.jboss.logging.Logger; -import io.quarkus.runtime.BlockingOperationControl; -import io.quarkus.runtime.ExecutorRecorder; import io.quarkus.security.AuthenticationFailedException; import io.quarkus.security.ForbiddenException; import io.quarkus.security.identity.IdentityProviderManager; import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.security.spi.runtime.AuthorizationController; +import io.quarkus.security.spi.runtime.BlockingSecurityExecutor; import io.smallrye.mutiny.Uni; -import io.smallrye.mutiny.subscription.UniEmitter; import io.smallrye.mutiny.subscription.UniSubscriber; import io.smallrye.mutiny.subscription.UniSubscription; import io.vertx.ext.web.RoutingContext; @@ -31,52 +31,37 @@ abstract class AbstractHttpAuthorizer { private final IdentityProviderManager identityProviderManager; private final AuthorizationController controller; private final List policies; + private final BlockingSecurityExecutor blockingExecutor; AbstractHttpAuthorizer(HttpAuthenticator httpAuthenticator, IdentityProviderManager identityProviderManager, - AuthorizationController controller, List policies) { + AuthorizationController controller, List policies, + BlockingSecurityExecutor blockingExecutor) { this.httpAuthenticator = httpAuthenticator; this.identityProviderManager = identityProviderManager; this.controller = controller; this.policies = policies; + this.blockingExecutor = blockingExecutor; } /** * context that allows for running blocking tasks */ - private static final HttpSecurityPolicy.AuthorizationRequestContext CONTEXT = new HttpSecurityPolicy.AuthorizationRequestContext() { + private final HttpSecurityPolicy.AuthorizationRequestContext CONTEXT = new HttpSecurityPolicy.AuthorizationRequestContext() { @Override - public Uni runBlocking(RoutingContext context, Uni identity, + public Uni runBlocking(RoutingContext context, Uni identityUni, BiFunction function) { - if (BlockingOperationControl.isBlockingAllowed()) { - try { - HttpSecurityPolicy.CheckResult res = function.apply(context, identity.await().indefinitely()); - return Uni.createFrom().item(res); - } catch (Throwable t) { - return Uni.createFrom().failure(t); - } - } - try { - return Uni.createFrom().emitter(new Consumer>() { - @Override - public void accept(UniEmitter uniEmitter) { - - ExecutorRecorder.getCurrent().execute(new Runnable() { - @Override - public void run() { - try { - HttpSecurityPolicy.CheckResult val = function.apply(context, - identity.await().indefinitely()); - uniEmitter.complete(val); - } catch (Throwable t) { - uniEmitter.fail(t); + return identityUni + .flatMap(new Function>() { + @Override + public Uni apply(SecurityIdentity identity) { + return blockingExecutor.executeBlocking(new Supplier() { + @Override + public HttpSecurityPolicy.CheckResult get() { + return function.apply(context, identity); } - } - }); - } - }); - } catch (Exception e) { - return Uni.createFrom().failure(e); - } + }); + } + }); } }; diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthorizer.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthorizer.java index 2301a8bad7343b..190a7774888738 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthorizer.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpAuthorizer.java @@ -8,6 +8,7 @@ import io.quarkus.security.identity.IdentityProviderManager; import io.quarkus.security.spi.runtime.AuthorizationController; +import io.quarkus.security.spi.runtime.BlockingSecurityExecutor; /** * Class that is responsible for running the HTTP based permission checks @@ -16,8 +17,9 @@ public class HttpAuthorizer extends AbstractHttpAuthorizer { HttpAuthorizer(HttpAuthenticator httpAuthenticator, IdentityProviderManager identityProviderManager, - AuthorizationController controller, Instance installedPolicies) { - super(httpAuthenticator, identityProviderManager, controller, toList(installedPolicies)); + AuthorizationController controller, Instance installedPolicies, + BlockingSecurityExecutor blockingExecutor) { + super(httpAuthenticator, identityProviderManager, controller, toList(installedPolicies), blockingExecutor); } private static List toList(Instance installedPolicies) { diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/ManagementInterfaceHttpAuthorizer.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/ManagementInterfaceHttpAuthorizer.java index 71b5497fa39e1f..ccf5731e4ca690 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/ManagementInterfaceHttpAuthorizer.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/ManagementInterfaceHttpAuthorizer.java @@ -7,6 +7,7 @@ import io.quarkus.security.identity.IdentityProviderManager; import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.security.spi.runtime.AuthorizationController; +import io.quarkus.security.spi.runtime.BlockingSecurityExecutor; import io.smallrye.mutiny.Uni; import io.vertx.ext.web.RoutingContext; @@ -18,7 +19,8 @@ public class ManagementInterfaceHttpAuthorizer extends AbstractHttpAuthorizer { public ManagementInterfaceHttpAuthorizer(HttpAuthenticator httpAuthenticator, IdentityProviderManager identityProviderManager, - AuthorizationController controller, ManagementPathMatchingHttpSecurityPolicy installedPolicy) { + AuthorizationController controller, ManagementPathMatchingHttpSecurityPolicy installedPolicy, + BlockingSecurityExecutor blockingExecutor) { super(httpAuthenticator, identityProviderManager, controller, List.of(new HttpSecurityPolicy() { @@ -28,6 +30,6 @@ public Uni checkPermission(RoutingContext request, Uni Uni executeBlocking(Supplier supplier) { - Context local = getOrCreateDuplicatedContext(vertx); - setContextSafe(local, true); - return Uni - .createFrom() - .completionStage( - local - .executeBlocking(new Handler>() { - @Override - public void handle(Promise promise) { - promise.complete(supplier.get()); - } - }) - .toCompletionStage()); + if (BlockingOperationControl.isBlockingAllowed()) { + try { + return Uni.createFrom().item(supplier.get()); + } catch (Throwable t) { + return Uni.createFrom().failure(t); + } + } else { + Context local = getOrCreateDuplicatedContext(vertx); + setContextSafe(local, true); + return Uni + .createFrom() + .completionStage( + local + .executeBlocking(new Handler>() { + @Override + public void handle(Promise promise) { + try { + promise.complete(supplier.get()); + } catch (Throwable t) { + promise.fail(t); + } + } + }) + .toCompletionStage()); + } } }