io.quarkus.grpc.auth.GrpcSecurityInterceptor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of quarkus-grpc Show documentation
Show all versions of quarkus-grpc Show documentation
Serve and consume gRPC services
package io.quarkus.grpc.auth;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import jakarta.enterprise.inject.Instance;
import jakarta.enterprise.inject.spi.Prioritized;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import org.jboss.logging.Logger;
import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.quarkus.grpc.GlobalInterceptor;
import io.quarkus.security.AuthenticationFailedException;
import io.quarkus.security.identity.CurrentIdentityAssociation;
import io.quarkus.security.identity.IdentityProviderManager;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.security.identity.request.AuthenticationRequest;
import io.smallrye.mutiny.Uni;
import io.vertx.core.Context;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
/**
* Security interceptor invoking {@link GrpcSecurityMechanism} implementations
*/
@GlobalInterceptor
@Singleton
public final class GrpcSecurityInterceptor implements ServerInterceptor, Prioritized {
private static final Logger log = Logger.getLogger(GrpcSecurityInterceptor.class);
private final IdentityProviderManager identityProviderManager;
private final CurrentIdentityAssociation identityAssociation;
private final AuthExceptionHandlerProvider exceptionHandlerProvider;
private final List securityMechanisms;
private final Map> serviceToBlockingMethods = new HashMap<>();
private boolean hasBlockingMethods = false;
@Inject
public GrpcSecurityInterceptor(
CurrentIdentityAssociation identityAssociation,
IdentityProviderManager identityProviderManager,
Instance securityMechanisms,
Instance exceptionHandlers) {
this.identityAssociation = identityAssociation;
this.identityProviderManager = identityProviderManager;
AuthExceptionHandlerProvider maxPrioHandlerProvider = null;
for (AuthExceptionHandlerProvider handler : exceptionHandlers) {
if (maxPrioHandlerProvider == null || maxPrioHandlerProvider.getPriority() < handler.getPriority()) {
maxPrioHandlerProvider = handler;
}
}
this.exceptionHandlerProvider = maxPrioHandlerProvider;
List mechanisms = new ArrayList<>();
for (GrpcSecurityMechanism securityMechanism : securityMechanisms) {
mechanisms.add(securityMechanism);
}
mechanisms.sort(Comparator.comparing(GrpcSecurityMechanism::getPriority));
this.securityMechanisms = mechanisms;
}
@Override
public ServerCall.Listener interceptCall(ServerCall serverCall,
Metadata metadata, ServerCallHandler serverCallHandler) {
Exception error = null;
for (GrpcSecurityMechanism securityMechanism : securityMechanisms) {
if (securityMechanism.handles(metadata)) {
try {
AuthenticationRequest authenticationRequest = securityMechanism.createAuthenticationRequest(metadata);
Context context = Vertx.currentContext();
boolean onEventLoopThread = Context.isOnEventLoopThread();
final boolean isBlockingMethod;
if (hasBlockingMethods) {
var methods = serviceToBlockingMethods.get(serverCall.getMethodDescriptor().getServiceName());
if (methods != null) {
isBlockingMethod = methods.contains(serverCall.getMethodDescriptor().getFullMethodName());
} else {
isBlockingMethod = false;
}
} else {
isBlockingMethod = false;
}
if (authenticationRequest != null) {
Uni auth = identityProviderManager
.authenticate(authenticationRequest)
.emitOn(new Executor() {
@Override
public void execute(Runnable command) {
if (onEventLoopThread && !isBlockingMethod) {
context.runOnContext(new Handler<>() {
@Override
public void handle(Void event) {
command.run();
}
});
} else {
command.run();
}
}
});
identityAssociation.setIdentity(auth);
error = null;
break;
}
} catch (Exception e) {
error = e;
log.warn("Failed to prepare AuthenticationRequest for a gRPC call", e);
}
}
}
if (error != null) { // if parsing for all security mechanisms failed, let's propagate the last exception
identityAssociation.setIdentity(Uni.createFrom()
.failure(new AuthenticationFailedException("Failed to parse authentication data", error)));
}
ServerCall.Listener listener = serverCallHandler.startCall(serverCall, metadata);
return exceptionHandlerProvider.createHandler(listener, serverCall, metadata);
}
@Override
public int getPriority() {
return Integer.MAX_VALUE - 100;
}
void init(Map> serviceToBlockingMethods) {
this.serviceToBlockingMethods.putAll(serviceToBlockingMethods);
this.hasBlockingMethods = true;
}
}