Reputation: 705
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
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
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