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

Run blocking security tasks from Vert.x duplicated context #35072

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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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<T> implements OidcRequestContext<T> {
public Uni<T> runBlocking(Supplier<T> function) {
return Uni.createFrom().deferred(new Supplier<Uni<? extends T>>() {

private final BlockingSecurityExecutor blockingExecutor;

public BlockingTaskRunner() {
this.blockingExecutor = new BlockingSecurityExecutor() {

private volatile BlockingSecurityExecutor delegate = null;

@Override
public Uni<T> 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<UniEmitter<? super T>>() {
@Override
public void accept(UniEmitter<? super T> uniEmitter) {
ExecutorRecorder.getCurrent().execute(new Runnable() {
@Override
public void run() {
try {
uniEmitter.complete(function.get());
} catch (Throwable t) {
uniEmitter.fail(t);
}
}
});
}
});
public <O> Uni<O> executeBlocking(Supplier<? extends O> supplier) {
if (delegate == null) {
delegate = Arc.container().select(BlockingSecurityExecutor.class).get();
}
return delegate.executeBlocking(supplier);
}
});
};
}

public BlockingTaskRunner(BlockingSecurityExecutor blockingExecutor) {
this.blockingExecutor = blockingExecutor;
}

public Uni<T> runBlocking(Supplier<T> function) {
return blockingExecutor.executeBlocking(function);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<String> createTokenStateRequestContext = new BlockingTaskRunner<String>();
private final BlockingTaskRunner<AuthorizationCodeTokens> getTokenStateRequestContext = new BlockingTaskRunner<AuthorizationCodeTokens>();
private final BlockingTaskRunner<String> createTokenStateRequestContext;
private final BlockingTaskRunner<AuthorizationCodeTokens> getTokenStateRequestContext;
private final SecureRandom secureRandom = new SecureRandom();

public CodeAuthenticationMechanism(BlockingSecurityExecutor blockingExecutor) {
this.createTokenStateRequestContext = new BlockingTaskRunner<>(blockingExecutor);
this.getTokenStateRequestContext = new BlockingTaskRunner<>(blockingExecutor);
}

public Uni<SecurityIdentity> authenticate(RoutingContext context,
IdentityProviderManager identityProviderManager, OidcTenantConfig oidcTenantConfig) {
final Map<String, Cookie> cookies = context.request().cookieMap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -64,12 +65,16 @@ public class DefaultTenantConfigResolver {
@ConfigProperty(name = "quarkus.http.proxy.enable-forwarded-prefix")
boolean enableHttpForwardedPrefix;

private final BlockingTaskRunner<OidcTenantConfig> blockingRequestContext = new BlockingTaskRunner<OidcTenantConfig>();
private final BlockingTaskRunner<OidcTenantConfig> blockingRequestContext;

private volatile boolean securityEventObserved;

private ConcurrentHashMap<String, BackChannelLogoutTokenCache> backChannelLogoutTokens = new ConcurrentHashMap<>();

public DefaultTenantConfigResolver(BlockingSecurityExecutor blockingExecutor) {
this.blockingRequestContext = new BlockingTaskRunner<OidcTenantConfig>(blockingExecutor);
}

@PostConstruct
public void verifyResolvers() {
if (tenantConfigResolver.isResolvable() && tenantConfigResolver.isAmbiguous()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -47,12 +47,17 @@ public class OidcIdentityProvider implements IdentityProvider<TokenAuthenticatio
private static final Uni<TokenVerificationResult> 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<Void> uniVoidOidcContext;
private final BlockingTaskRunner<TokenIntrospection> getIntrospectionRequestContext;
private final BlockingTaskRunner<UserInfo> getUserInfoRequestContext;

private BlockingTaskRunner<Void> uniVoidOidcContext = new BlockingTaskRunner<Void>();
private BlockingTaskRunner<TokenIntrospection> getIntrospectionRequestContext = new BlockingTaskRunner<TokenIntrospection>();
private BlockingTaskRunner<UserInfo> getUserInfoRequestContext = new BlockingTaskRunner<UserInfo>();
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<TokenAuthenticationRequest> getRequestType() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -83,8 +82,7 @@ public TenantConfigBean get() {
public Uni<TenantConfigContext> apply(OidcTenantConfig config) {
return createDynamicTenantContext(vertxValue, config, tlsConfig, config.getTenantId().get());
}
},
ExecutorRecorder.getCurrent());
});
}
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -21,8 +20,7 @@ public TenantConfigBean(
Map<String, TenantConfigContext> staticTenantsConfig,
Map<String, TenantConfigContext> dynamicTenantsConfig,
TenantConfigContext defaultTenant,
Function<OidcTenantConfig, Uni<TenantConfigContext>> tenantConfigContextFactory,
Executor blockingExecutor) {
Function<OidcTenantConfig, Uni<TenantConfigContext>> tenantConfigContextFactory) {
this.staticTenantsConfig = staticTenantsConfig;
this.dynamicTenantsConfig = dynamicTenantsConfig;
this.defaultTenant = defaultTenant;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -20,19 +21,32 @@ static BlockingSecurityExecutor createBlockingExecutor(Supplier<Executor> execut
return new BlockingSecurityExecutor() {
@Override
public <T> Uni<T> executeBlocking(Supplier<? extends T> function) {
return Uni.createFrom().emitter(new Consumer<UniEmitter<? super T>>() {
return Uni.createFrom().deferred(new Supplier<Uni<? extends T>>() {
@Override
public void accept(UniEmitter<? super T> uniEmitter) {
executorSupplier.get().execute(new Runnable() {
@Override
public void run() {
try {
uniEmitter.complete(function.get());
} catch (Throwable t) {
uniEmitter.fail(t);
}
public Uni<? extends T> 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<UniEmitter<? super T>>() {
@Override
public void accept(UniEmitter<? super T> uniEmitter) {
executorSupplier.get().execute(new Runnable() {
@Override
public void run() {
try {
uniEmitter.complete(function.get());
} catch (Throwable t) {
uniEmitter.fail(t);
}
}
});
}
});
}
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -38,22 +37,7 @@ public class QuarkusIdentityProviderManagerImpl implements IdentityProviderManag
private final AuthenticationRequestContext blockingRequestContext = new AuthenticationRequestContext() {
@Override
public Uni<SecurityIdentity> runBlocking(Supplier<SecurityIdentity> function) {
return Uni.createFrom().deferred(new Supplier<Uni<? extends SecurityIdentity>>() {
@Override
public Uni<SecurityIdentity> 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);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -31,52 +31,37 @@ abstract class AbstractHttpAuthorizer {
private final IdentityProviderManager identityProviderManager;
private final AuthorizationController controller;
private final List<HttpSecurityPolicy> policies;
private final BlockingSecurityExecutor blockingExecutor;

AbstractHttpAuthorizer(HttpAuthenticator httpAuthenticator, IdentityProviderManager identityProviderManager,
AuthorizationController controller, List<HttpSecurityPolicy> policies) {
AuthorizationController controller, List<HttpSecurityPolicy> 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<HttpSecurityPolicy.CheckResult> runBlocking(RoutingContext context, Uni<SecurityIdentity> identity,
public Uni<HttpSecurityPolicy.CheckResult> runBlocking(RoutingContext context, Uni<SecurityIdentity> identityUni,
BiFunction<RoutingContext, SecurityIdentity, HttpSecurityPolicy.CheckResult> 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<UniEmitter<? super HttpSecurityPolicy.CheckResult>>() {
@Override
public void accept(UniEmitter<? super HttpSecurityPolicy.CheckResult> 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<SecurityIdentity, Uni<? extends HttpSecurityPolicy.CheckResult>>() {
@Override
public Uni<? extends HttpSecurityPolicy.CheckResult> apply(SecurityIdentity identity) {
return blockingExecutor.executeBlocking(new Supplier<HttpSecurityPolicy.CheckResult>() {
@Override
public HttpSecurityPolicy.CheckResult get() {
return function.apply(context, identity);
}
}
});
}
});
} catch (Exception e) {
return Uni.createFrom().failure(e);
}
});
}
});
}
};

Expand Down
Loading