grigouille
grigouille

Reputation: 705

Hibernate Envers : How to inject SecurityContext (REST) in RevisionListener?

I have a REST API (using wildfly 20 with microprofile-jwt) so I would like to audit changes with Hibernate Envers. Unfortunately I can't get my Principal object : the javax.ws.rs.core.SecurityContext is null.

So my question is : how can I inject the SecurityContext in my RevisionListener and get the Principal ?

import java.security.Principal;

import javax.ws.rs.core.Context;
import javax.ws.rs.core.SecurityContext;

import org.hibernate.envers.RevisionListener;

public class CustomRevisionListener implements RevisionListener {

    @Context
    private SecurityContext context;

    @Override
    public void newRevision(Object o) {
        CustomRevEntity e = (CustomRevEntity) o;
        e.setLogin(getUser());
    }

    private String getUser() {
        if(context == null) return "anonymous no context";
        Principal principal = context.getUserPrincipal();
        return principal == null ? "anonymous" : principal.getName();
    }
}

Upvotes: 1

Views: 840

Answers (2)

grigouille
grigouille

Reputation: 705

I received the answer from hibernate forum : https://discourse.hibernate.org/t/how-to-get-username-envers-api-rest/5592

To be able to inject the SecurityContext into the RevisionListner (with @Inject), you need this filter :

@Provider
public class SecurityRequestFilter implements ContainerRequestFilter, ContainerResponseFilter {

    private static final ThreadLocal<SecurityContext> THREAD_LOCAL = new ThreadLocal<>();

    @RequestScoped
    @Produces
    public SecurityContext getSecurityContext() {
        return THREAD_LOCAL.get();
    }

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
            throws IOException {
        THREAD_LOCAL.remove();
    }

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        THREAD_LOCAL.set(requestContext.getSecurityContext());
    }
}

Upvotes: 0

aschoerk
aschoerk

Reputation: 3593

That will not work since the creation of the bean is done outside of the container. If you want to use envers, you would need a way to communicate the principle from beans that are managed by the container to the RevisionListener-bean that is created and managed by the envers-extension.

A way could be to use public static ThreadLocal<Principal> threadlocalPrincipal = new ThreadLocal<>();.

In a bean using JPA or Hibernate, @Context SecurityContext context should work. Before calling any Dao or any other Hibernate-Using method fill the public static Variable using threadlocalPrincipal.set(...). Inside the listener use <Classname of Bean>threadlocalPrincipal.get() to access the current variable set in the current thread.

Make sure to clear the Threadlocal variable afterwards using threadLocalPrincipal.remove() inside a finally block, to avoid memory leaks.

Upvotes: 0

Related Questions