javaspring-webfluxhibernate-reactive

SQL inspection/injection in Hibernate Reactive


Hibernate allows to inspect every SQL command and possibly returning a different SQL command before it is executed, thankfully to the StatementInspector SPI.

In our (blocking) Spring Web App we are using it to implement multi-tenancy at row level. The user authenticates and in hibernate's inspect method we get user authentication info from the Spring SecurityContextHolder, which we use to change the SQL and implement multi-tenancy. Everything works, as each HTTP request is bound to a specify Thread that will completely fulfill the request.

In a reactive version of the above app this doesn't work. ReactiveSecurityContextHolder returns the asynchronous type Mono and the hibernate's inspect method return a sync type (String).

Is there a similar method like inspect in hibernate reactive to keep the similar multitenancy implementation like in the blocking version, maybe something like Mono<String> inspect(String sql);

I wasn't able to find it.


Solution

  • I've created an issue to keep track of this feature.

    In the meanwhile, in Hibernate Reactive, you could create a wrapper around ReactiveConnection and intercepts all the calls related to the execution of the query.

    For example, you can have:

    public class ConnectionInterceptor implements ReactiveConnection {
       private final ReactiveConnection delegate;
    
       public ConnectionInterceptor(ReactiveConnection delegate) {
          this.delegate = delegate;
       }
    
       public CompletionStage<Void> execute(String sql) {
           String newSql = convert(sql);
           return delegate.execute(newSql);
       }
    
       // ... You need to implement all the other methods running a query
    }
    
    package my.example;
    
    public class MyPool extends DefaultSqlClientPool {
    
       public CompletionStage<ReactiveConnection> getConnection() {
         return super.getConnection().thenApply( ConnectionInterceptor::new );
       }
    
    }
    

    You can register the new pool class using the property hibernate.vertx.pool.class:

    hibernate.vertx.pool.class = my.example.MyPool
    

    This approach will probably not work with Quarkus because it uses it's own pool and there's not a way to override it (as far as I know).