All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.quarkus.grpc.auth.GrpcSecurityInterceptor Maven / Gradle / Ivy

There is a newer version: 3.15.1
Show newest version
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;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy