RBAC with OpenAPI and vertx-web-openapi-router #44271
-
In trying to integrate Vert.x's OpenAPI Router into a quarkus application. That router reads an OpenAPI spec and allows routing based on the Routes defined in the JSON or YAML file. This enables Contract first development. I got the routes to load using an App.javapackage com.beyonddemise;
import java.util.List;
import org.jboss.logging.Logger;
import io.quarkus.runtime.StartupEvent;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.enterprise.inject.Instance;
@ApplicationScoped
public class App {
protected static final Logger LOGGER = Logger.getLogger(App.class);
/**
* Initialize the application on startup
*
* @param e StartupEvent triggered by ApplicationScoped
* @param vertx Eventloop runtime
* @param verticles collection of verticles to deploy
*/
public void init(@Observes StartupEvent e, Vertx vertx, Instance<AbstractVerticle> verticles) {
LOGGER.infof("Startup event running %s", this.getClass().getName());
List<Future<String>> loadedVerticles = verticles
.stream()
.map(vertx::deployVerticle)
.toList();
Future.all(loadedVerticles)
.onSuccess(v -> LOGGER.infof("up and running: %S", this.getClass().getName()))
.onFailure(LOGGER::error);
}
} AppRouter.javaimport org.jboss.logging.Logger;
import com.beyonddemise.services.SharedHttpHandlers;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.http.HttpServer;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.openapi.router.OpenAPIRoute;
import io.vertx.ext.web.openapi.router.RouterBuilder;
import io.vertx.openapi.contract.OpenAPIContract;
/**
* Blueprint for a router verticle. Takes care of the basic setup.
* and loads the OpenAPI routes and attaches the handlers to them
* based on the keyword provided like "auth" or "disclosure".
*/
public abstract class AbstractRouterVerticle extends AbstractVerticle {
protected static final Logger LOGGER = Logger.getLogger(AbstractRouterVerticle.class);
@Override
public void start(Promise<Void> startPromise) throws Exception {
LOGGER.infof("%s starting", this.getClass().getName());
HttpServer server = vertx.createHttpServer();
final int port = 8080;
OpenAPIContract.from(vertx, "openapi-spec.json")
.compose(this::loadOpenApiRoutes)
.compose(router -> server.requestHandler(router).listen(port))
.onFailure(startPromise::fail)
.onSuccess(v -> {
LOGGER.infof("Server listening on port %d", port);
startPromise.complete();
});
}
/**
* Loads the OpenAPI routes and attaches the handlers to them.
*
* @param contract OpenAPIContract
* @return Future<Router>
*/
Future<Router> loadOpenApiRoutes(OpenAPIContract contract) {
final RouterBuilder builder = RouterBuilder.create(vertx, contract);
builder.getRoutes().stream()
.forEach(this::loadOneOpenApiRoute);
return Future.succeededFuture(builder.createRouter());
}
void loadOneOpenApiRoute(OpenAPIRoute route) {
route.getOperation().getSecurityRequirements().forEach(securityRequirement -> {
// Here I'm stuck.
});
route.addHandler(
ctx -> ctx.end(String.format("The Operation %s is under construction", route.getOperation().getOperationId())));
}
@Override
public void stop(Promise<Void> stopPromise) throws Exception {
LOGGER.infof("Shutdown %s", this.getClass().getName());
super.stop(stopPromise);
}
} The security requirement can be empty to allow anonymous access or could have 1 or more entries which would serve as alternate options, while providing access to required roles and a list of security definitions. I did read RBAC in Quarkus, which works nicely with Annotations, but I'm stuck how I can transfer that to dynamic code. Would I need to roll my own Pointers are welcome |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments
-
/cc @EricWittmann (openapi), @MikeEdgar (openapi), @phillip-kruger (openapi) |
Beta Was this translation helpful? Give feedback.
-
@Stwissel Hi, in this code,
would you like to attach a security check which will enforce a security constraint specific to this operation ? I think you may have to create an Also CC @michalvavrik |
Beta Was this translation helpful? Give feedback.
-
You create your own HTTP Server, there is no Quarkus HTTP authenticator and therefore, no authentication happens. Considering everything I can see is pure Vert.x, I think you need pure Vert.x solution (better ask Vert.x community how). If you plan to attach your routes to Quarkus HTTP router, like here https://quarkus.io/guides/reactive-routes#using-the-vert-x-web-router, and you give your routes positive priority (0+), metacode, I have no idea what are your security requirements and I don't have opened IDE atm, so you cannot c&p it:
something of that sort. yes, you can use |
Beta Was this translation helpful? Give feedback.
-
@sberyozkin @michalvavrik - Thx for the pointers. In conclusion I have 2 general options:
Both versions need some thought how to communicate roles. OpenAPI 3.0 has scopes only for OICD, OpenAPI 3.1 for any - but scopes are not exactly roles >:). (And apicur.io doesn't do 3.1, hoping that @EricWittmann finds time for it - love his work). I think I'll try the vert.x route to create the OpenAPI router and then hook it into the Quarkus Bean. Thx for the inspiration. Appreciate your time & expertise |
Beta Was this translation helpful? Give feedback.
You create your own HTTP Server, there is no Quarkus HTTP authenticator and therefore, no authentication happens. Considering everything I can see is pure Vert.x, I think you need pure Vert.x solution (better ask Vert.x community how).
If you plan to attach your routes to Quarkus HTTP…