Skip to content

Commit 2c58e9f

Browse files
committed
fix(grpc,security): Run authN and authZ handlers when root path is not "/"
1 parent ddd9c85 commit 2c58e9f

File tree

3 files changed

+84
-8
lines changed

3 files changed

+84
-8
lines changed

extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcServerProcessor.java

+24-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Set;
2222
import java.util.function.Predicate;
2323
import java.util.function.Supplier;
24+
import java.util.stream.Collectors;
2425

2526
import jakarta.enterprise.inject.spi.DeploymentException;
2627
import jakarta.transaction.Transaction;
@@ -90,9 +91,14 @@
9091
import io.quarkus.kubernetes.spi.KubernetesPortBuildItem;
9192
import io.quarkus.netty.deployment.MinNettyAllocatorMaxOrderBuildItem;
9293
import io.quarkus.runtime.LaunchMode;
94+
import io.quarkus.runtime.RuntimeValue;
9395
import io.quarkus.smallrye.health.deployment.spi.HealthBuildItem;
9496
import io.quarkus.vertx.deployment.VertxBuildItem;
97+
import io.quarkus.vertx.http.deployment.FilterBuildItem;
9598
import io.quarkus.vertx.http.deployment.VertxWebRouterBuildItem;
99+
import io.vertx.core.Handler;
100+
import io.vertx.ext.web.Router;
101+
import io.vertx.ext.web.RoutingContext;
96102

97103
public class GrpcServerProcessor {
98104

@@ -683,7 +689,8 @@ ServiceStartBuildItem initializeServer(GrpcServerRecorder recorder,
683689
List<RecorderBeanInitializedBuildItem> orderEnforcer,
684690
LaunchModeBuildItem launchModeBuildItem,
685691
VertxWebRouterBuildItem routerBuildItem,
686-
VertxBuildItem vertx, Capabilities capabilities) {
692+
VertxBuildItem vertx, Capabilities capabilities,
693+
List<FilterBuildItem> filterBuildItems) {
687694

688695
// Build the list of blocking methods per service implementation
689696
Map<String, List<String>> blocking = new HashMap<>();
@@ -702,10 +709,23 @@ ServiceStartBuildItem initializeServer(GrpcServerRecorder recorder,
702709
if (!bindables.isEmpty()
703710
|| (LaunchMode.current() == LaunchMode.DEVELOPMENT && buildTimeConfig.devMode.forceServerStart)) {
704711
//Uses mainrouter when the 'quarkus.http.root-path' is not '/'
705-
recorder.initializeGrpcServer(vertx.getVertx(),
706-
routerBuildItem.getMainRouter() != null ? routerBuildItem.getMainRouter() : routerBuildItem.getHttpRouter(),
712+
Map<Integer, Handler<RoutingContext>> securityHandlers = null;
713+
final RuntimeValue<Router> routerRuntimeValue;
714+
if (routerBuildItem.getMainRouter() != null) {
715+
routerRuntimeValue = routerBuildItem.getMainRouter();
716+
if (capabilities.isPresent(Capability.SECURITY)) {
717+
securityHandlers = filterBuildItems
718+
.stream()
719+
.filter(filter -> filter.getPriority() == FilterBuildItem.AUTHENTICATION
720+
|| filter.getPriority() == FilterBuildItem.AUTHORIZATION)
721+
.collect(Collectors.toMap(f -> f.getPriority() * -1, FilterBuildItem::getHandler));
722+
}
723+
} else {
724+
routerRuntimeValue = routerBuildItem.getHttpRouter();
725+
}
726+
recorder.initializeGrpcServer(vertx.getVertx(), routerRuntimeValue,
707727
config, shutdown, blocking, virtuals, launchModeBuildItem.getLaunchMode(),
708-
capabilities.isPresent(Capability.SECURITY));
728+
capabilities.isPresent(Capability.SECURITY), securityHandlers);
709729
return new ServiceStartBuildItem(GRPC_SERVER);
710730
}
711731
return null;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package io.quarkus.grpc.auth;
2+
3+
import org.junit.jupiter.api.extension.RegisterExtension;
4+
5+
import io.quarkus.test.QuarkusUnitTest;
6+
7+
public class GrpcAuthCustomRootPathTest extends GrpcAuthTestBase {
8+
9+
@RegisterExtension
10+
static final QuarkusUnitTest config = createQuarkusUnitTest("""
11+
quarkus.grpc.server.use-separate-server=false
12+
quarkus.grpc.clients.securityClient.host=localhost
13+
quarkus.grpc.clients.securityClient.port=8081
14+
quarkus.http.root-path=/api
15+
""", true);
16+
17+
}

extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/GrpcServerRecorder.java

+43-4
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import io.quarkus.runtime.ShutdownContext;
6161
import io.quarkus.runtime.annotations.Recorder;
6262
import io.quarkus.vertx.http.runtime.PortSystemProperties;
63+
import io.quarkus.vertx.http.runtime.security.HttpAuthenticator;
6364
import io.quarkus.virtual.threads.VirtualThreadsRecorder;
6465
import io.vertx.core.AbstractVerticle;
6566
import io.vertx.core.AsyncResult;
@@ -99,7 +100,7 @@ public void initializeGrpcServer(RuntimeValue<Vertx> vertxSupplier,
99100
ShutdownContext shutdown,
100101
Map<String, List<String>> blockingMethodsPerService,
101102
Map<String, List<String>> virtualMethodsPerService,
102-
LaunchMode launchMode, boolean securityPresent) {
103+
LaunchMode launchMode, boolean securityPresent, Map<Integer, Handler<RoutingContext>> securityHandlers) {
103104
GrpcContainer grpcContainer = Arc.container().instance(GrpcContainer.class).get();
104105
if (grpcContainer == null) {
105106
throw new IllegalStateException("gRPC not initialized, GrpcContainer not found");
@@ -137,15 +138,16 @@ public void initializeGrpcServer(RuntimeValue<Vertx> vertxSupplier,
137138
}
138139
} else {
139140
buildGrpcServer(vertx, configuration, routerSupplier, shutdown, blockingMethodsPerService, virtualMethodsPerService,
140-
grpcContainer, launchMode, securityPresent);
141+
grpcContainer, launchMode, securityPresent, securityHandlers);
141142
}
142143
}
143144

144145
// TODO -- handle XDS
145146
private void buildGrpcServer(Vertx vertx, GrpcServerConfiguration configuration, RuntimeValue<Router> routerSupplier,
146147
ShutdownContext shutdown, Map<String, List<String>> blockingMethodsPerService,
147148
Map<String, List<String>> virtualMethodsPerService,
148-
GrpcContainer grpcContainer, LaunchMode launchMode, boolean securityPresent) {
149+
GrpcContainer grpcContainer, LaunchMode launchMode, boolean securityPresent,
150+
Map<Integer, Handler<RoutingContext>> securityHandlers) {
149151

150152
GrpcServerOptions options = new GrpcServerOptions();
151153
if (!configuration.maxInboundMessageSize.isEmpty()) {
@@ -193,8 +195,45 @@ private void buildGrpcServer(Vertx vertx, GrpcServerConfiguration configuration,
193195

194196
initHealthStorage();
195197

198+
Router router = routerSupplier.getValue();
199+
if (securityHandlers != null) {
200+
for (Map.Entry<Integer, Handler<RoutingContext>> e : securityHandlers.entrySet()) {
201+
Handler<RoutingContext> handler = e.getValue();
202+
Route route = router.route().order(e.getKey()).handler(new Handler<RoutingContext>() {
203+
@Override
204+
public void handle(RoutingContext ctx) {
205+
if (!isGrpc(ctx)) {
206+
ctx.next();
207+
} else if (ctx.get(HttpAuthenticator.class.getName()) != null) {
208+
// this IF branch shouldn't be invoked with current implementation
209+
// when gRPC is attached to the main router when the root path is not '/'
210+
// because HTTP authenticator and authorizer handlers are not added by default on the main
211+
// router; adding it in case someone made changes without consider this use case
212+
// so that we prevent repeated authentication
213+
ctx.next();
214+
} else {
215+
if (!Context.isOnEventLoopThread()) {
216+
Context capturedVertxContext = Vertx.currentContext();
217+
if (capturedVertxContext != null) {
218+
capturedVertxContext.runOnContext(new Handler<Void>() {
219+
@Override
220+
public void handle(Void unused) {
221+
handler.handle(ctx);
222+
}
223+
});
224+
return;
225+
}
226+
}
227+
handler.handle(ctx);
228+
}
229+
}
230+
});
231+
shutdown.addShutdownTask(route::remove); // remove this route at shutdown, this should reset it
232+
}
233+
}
234+
196235
LOGGER.info("Starting new Quarkus gRPC server (using Vert.x transport)...");
197-
Route route = routerSupplier.getValue().route().handler(ctx -> {
236+
Route route = router.route().handler(ctx -> {
198237
if (!isGrpc(ctx)) {
199238
ctx.next();
200239
} else {

0 commit comments

Comments
 (0)