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

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();
}
}

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.

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());
}
}

Related

Spring Boot Hibernate Postgresql #Transactional does not rollback [duplicate]

I want to read text data fixtures (CSV files) at the start on my application and put it in my database.
For that, I have created a PopulationService with an initialization method (#PostConstruct annotation).
I also want them to be executed in a single transaction, and hence I added #Transactional on the same method.
However, the #Transactional seems to be ignored :
The transaction is started / stopped at my low level DAO methods.
Do I need to manage the transaction manually then ?
Quote from legacy (closed) Spring forum:
In the #PostConstruct (as with the afterPropertiesSet from the InitializingBean interface) there is no way to ensure that all the post processing is already done, so (indeed) there can be no Transactions. The only way to ensure that that is working is by using a TransactionTemplate.
So if you would like something in your #PostConstruct to be executed within transaction you have to do something like this:
#Service("something")
public class Something {
#Autowired
#Qualifier("transactionManager")
protected PlatformTransactionManager txManager;
#PostConstruct
private void init(){
TransactionTemplate tmpl = new TransactionTemplate(txManager);
tmpl.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
//PUT YOUR CALL TO SERVICE HERE
}
});
}
}
I think #PostConstruct only ensures the preprocessing/injection of your current class is finished. It does not mean that the initialization of the whole application context is finished.
However you can use the spring event system to receive an event when the initialization of the application context is finished:
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
public void onApplicationEvent(ContextRefreshedEvent event) {
// do startup code ..
}
}
See the documentation section Standard and Custom Events for more details.
As an update, from Spring 4.2 the #EventListener annotation allows a cleaner implementation:
#Service
public class InitService {
#Autowired
MyDAO myDAO;
#EventListener(ContextRefreshedEvent.class)
public void onApplicationEvent(ContextRefreshedEvent event) {
event.getApplicationContext().getBean(InitService.class).initialize();
}
#Transactional
public void initialize() {
// use the DAO
}
}
Inject self and call through it the #Transactional method
public class AccountService {
#Autowired
private AccountService self;
#Transactional
public void resetAllAccounts(){
//...
}
#PostConstruct
private void init(){
self.resetAllAccounts();
}
}
For older Spring versions which do not support self-injection, inject BeanFactory and get self as beanFactory.getBean(AccountService.class)
EDIT
It looks like that since this solution has been posted 1.5 years ago developers are still under impression that if a method,
annotated with #Transactional, is called from a #PostContruct-annotated method invoked upon the Bean initialization, it won't be actually executed inside of Spring Transaction, and awkward (obsolete?) solutions get discussed and accepted instead of this very simple and straightforward one and the latter even gets downvoted.
The Doubting Thomases :) are welcome to check out an example Spring Boot application at GitHub which implements the described above solution.
What actually causes, IMHO, the confusion: the call to #Transactional method should be done through a proxied version of a Bean where such method is defined.
When a #Transactional method is called from another Bean, that another Bean usually injects this one and invokes its proxied (e.g. through #Autowired) version of it, and everything is fine.
When a #Transactional method is called from the same Bean directly, through usual Java call, the Spring AOP/Proxy machinery is not involved and the method is not executed inside of Transaction.
When, as in the suggested solution, a #Transactional method is called from the same Bean through self-injected proxy (self field), the situation is basically equivalent to a case 1.
#Platon Serbin's answer didn't work for me. So I kept searching and found the following answer that saved my life. :D
The answer is here No Session Hibernate in #PostConstruct, which I took the liberty to transcribe:
#Service("myService")
#Transactional(readOnly = true)
public class MyServiceImpl implements MyService {
#Autowired
private MyDao myDao;
private CacheList cacheList;
#Autowired
public void MyServiceImpl(PlatformTransactionManager transactionManager) {
this.cacheList = (CacheList) new TransactionTemplate(transactionManager).execute(new TransactionCallback(){
#Override
public Object doInTransaction(TransactionStatus transactionStatus) {
CacheList cacheList = new CacheList();
cacheList.reloadCache(MyServiceImpl.this.myDao.getAllFromServer());
return cacheList;
}
});
}
The transaction part of spring might not be initialized completely at #PostConstruct.
Use a listener to the ContextRefreshedEvent event to ensure, that transactions are available:
#Component
public class YourService
implements ApplicationListener<ContextRefreshedEvent> // <= ensure correct timing!
{
private final YourRepo repo;
public YourService (YourRepo repo) {this.repo = repo;}
#Transactional // <= ensure transaction!
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
repo.doSomethingWithinTransaction();
}
}
Using transactionOperations.execute() in #PostConstruct or in #NoTransaction method both works
#Service
public class ConfigurationService implements ApplicationContextAware {
private static final Logger LOG = LoggerFactory.getLogger(ConfigurationService.class);
private ConfigDAO dao;
private TransactionOperations transactionOperations;
#Autowired
public void setTransactionOperations(TransactionOperations transactionOperations) {
this.transactionOperations = transactionOperations;
}
#Autowired
public void setConfigurationDAO(ConfigDAO dao) {
this.dao = dao;
}
#PostConstruct
public void postConstruct() {
try { transactionOperations.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(final TransactionStatus status) {
ResultSet<Config> configs = dao.queryAll();
}
});
}
catch (Exception ex)
{
LOG.trace(ex.getMessage(), ex);
}
}
#NoTransaction
public void saveConfiguration(final Configuration configuration, final boolean applicationSpecific) {
String name = configuration.getName();
Configuration original = transactionOperations.execute((TransactionCallback<Configuration>) status ->
getConfiguration(configuration.getName(), applicationSpecific, null));
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
}
}

EJB not initializing in Wildfly 9.0.0 using #EJB

I'm trying to migrate from EJB2.x to EJB3.x and i'm using Wildfly 9.0.0.
The old EJB2.x is working in JBoss 4.2.2 and this is how it looks like:
public interface WUFFacadeRemote extends EJBObject {
public ClientData getItems(ClientData data);
public ClientData save(ClientData data);
}
public interface WUFFacadeHome extends EJBHome {
public WUFFacadeRemote create();
}
public class WUFFacade {
public ClientData getItems(ClientData data) {
//code here
}
public ClientData save(ClientData data) {
//code here
}
}
public class WUFAction extends HttpServlet implements IAction {
public void doPost(HttpServletRequest request, HttpServletResponse response) {
...
Object objRef = ic.lookup("java:comp/env/wUF");
com.wuf.WUFFacadeHome home = (com.wuf.WUFFacadeHome) PortableRemoteObject.narrow(objRef, com.wuf.WUFFacadeHome.class);
engine = home.create();
//engine gets the reference, and I can use it normally.
...
}
}
I also have the ejb-jar.xml and it's working. Now, the solution I was thinking to EJB3.x and Wildfly 9.0.0 is as below:
#WebServlet(urlPatterns = "windows/wUF.do", loadOnStartup = 1)
public class WUFAction extends HttpServlet implements IAction {
#EJB
private WUFFacadeRemote engine;
public void doPost(HttpServletRequest request, HttpServletResponse response) {
//Here I should be able to use my engine.
//Wildfly starts and I call the page, engine is not null at this moment,
//but after I call the page again, it becomes null and remains null.
}
}
#Stateless
#Remote(WUFFacadeRemote.class)
public class WUFFacade extends RootFacade implements WUFFacadeRemote, Serializable {
public WUFFacade() { }
#EJB
FUFHome home;
public ClientData getItems(ClientData data) {
//code here
}
public ClientData save(ClientData data) {
//code here
}
private Col load(ClientData data,InitialContext ic) {
//here i'm calling home.
// but home is always null. It was supposed to have the #EJB reference initialized.
//But instead I get a null pointer...
home.findByFilter(loader);
}
}
#Remote(FUFHome.class)
public interface FUFHome {
FUF create(FUFValue fUFValue);
FUF findByPrimaryKey(FUFPK pk);
Collection findByFilter(FacadeLoader loader);
}
public interface WUFFacadeRemote{
public ClientData getItems(ClientData data);
public ClientData save(ClientData data);
}
I don't have ejb-jar.xml anymore, the deploy is sucessfully done and Wildfly starts with no errors. Then the first time I call the page in question, it seems that #EJB is working (Debug is "Proxy for remote EJB StatelessEJBLocator for "bus-facade/WUFFacade", view is interface com.wuf.WUFFacadeRemote, affinity is None"), the value is not null, but for all subsequent calls, my variable is null and I got a NullPointerException.
I really don't know what i'm doing wrong (maybe i'm completely lost), but to me, #EJB should be working correctly like that. What am I missing? Thanks.
As i'm using EJB3.x i'm just using annotations now, (this seems to be ok).
JNDIs:
JNDI bindings for session bean named FUF in deployment
java:global/fumo/bus-entities-fumo/FUF!apyon.components.fumo.fuf.FUF
java:app/bus-entities-fumo/FUF!apyon.components.fumo.fuf.FUF
java:module/FUF!apyon.components.fumo.fuf.FUF
java:global/fumo/bus-entities-fumo/FUF
java:app/bus-entities-fumo/FUF
java:module/FUF
JNDI bindings for session bean named WUFFacade
java:global/fumo/bus-facade-fumo/WUFFacade!apyon.fumo.wuf.WUFFacadeRemote
java:app/bus-facade-fumo/WUFFacade!apyon.fumo.wuf.WUFFacadeRemote
java:module/WUFFacade!apyon.fumo.wuf.WUFFacadeRemote
java:jboss/exported/fumo/bus-facade-fumo/WUFFacade!apyon.fumo.wuf.WUFFacadeRemote
java:global/fumo/bus-facade-fumo/WUFFacade
java:app/bus-facade-fumo/WUFFacade
java:module/WUFFacade
I think I found a possible solution to the problem. I'll still try to find another one, but this is good so far.
After changing to a .war and keeping my other projects in .ears it's working. Maybe the problem was because I have a RootController servlet im my main.ear, which is the starting point of the aplication. The context starts there and then it redirects to fumo.ear (now fumo.war).
For some reason, I always was getting a null in my EJB after entering a page. It was always hapening when I first entered a JSP and tried to call the page again. My solution to this is:
#WebServlet(urlPatterns = "windows/wUF.do", loadOnStartup = 1)
public class WUFAction extends HttpServlet {
private WUFFacadeRemote engine;
public void doGet(HttpServletRequest req, HttpServletResponse resp) {
doPost(req, resp);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) {
if(engine == null) {
InitialContext ic;
try {
ic = new InitialContext();
engine = (WUFFacadeRemote) ic.lookup("java:global/fumo/WUFFacade!fumo.wuf.WUFFacadeRemote");
} catch (NamingException e) {
e.printStackTrace();
}
}
//here I always have the context now.
}
}
And as a .war my structure now looks like this:
So other annotations like #Inject and #EJB are now working. Always when i'm being redirect from a JSP calling a Servlet or some action, I first check if the context is not null, otherwise I lookup it. My #Stateless are working and the #PersistenceContext and #Remote are working too.
#Stateless
public class WUFFacade implements WUFFacadeRemote {
#Inject
private FUFRules rules;
#EJB
private FUFHome home;
private Col load(ClientData data, InitialContext ic) throws InterfaceException {
try {
// home here is nor null anymore.
Collection res = (Collection) home.findByFilter(loader);
...
} catch (InterfaceException e) {
e.printStackTrace();
}
...
return data;
}
}
So I'd like to thank everyone who helped in the thread. It was a good way to understand and see the problem or to find a workaround. As I said, I'll still try the .ear in the future, but as a simplified packaging it definitely works.

Problems when using EntityFilteringFeature and SelectableEntityFilteringFeature with Jersey 2

I'm new to Jersey 2 and JAX-RS, so probably I'm missing something.
What I'm trying to do is a test program to define a coding style in rest services developing.
The test was written in JAVA and uses JERSEY 2.22.2, JDK 1.8.31, MOXY AS JSON Provider.
I defined a Resource with GET methods to support LIST/DETAIL. Due to the size of my POJO, I used some filters and everything was fine.
// 1) First of all I defined the annotation.
#Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#EntityFiltering
public #interface MyDetailView {
public static class Factory extends AnnotationLiteral<MyDetailView>
implements MyDetailView {
private Factory() {
}
public static MyDetailView get() {
return new Factory();
}
}
// 2) Once defined the annotation, I used to
// programmaticaly exclude the list of subItems in the response...
#XmlRootElement
public class MyPojo {
...
//*** THIS SHOULD BE FILTERED IF THE ANNOTATION IS NOT SPECIFIED IN THE RESPONSE ***
#MyDetailView
private List<SubItem> subItems = new ArrayList<SubItem>();
public List<SubItem> getSubItems() {
return subItems;
}
public void setSubItems(List<SubItem> subItems) {
this.subItems = subItems;
}
}
// 3) I registered the EntityFilteringFeature
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
....
register(EntityFilteringFeature.class);
}
// 4) Finally, I wrote the code to include/exclude the subItems
/*
The Resource class has getCollection() and getItem() methods...
getCollection() adds the annotation only if filterStyle="detail"
getItem() always add the annotation
*/
#Path(....)
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class MyResource extends SecuredResource {
//filterStyle -> "detail" means MyDetailAnnotation
#GET
public Response getCollection(
#QueryParam("filterStyle") String filterStyle,
#Context UriInfo uriInfo) {
//THIS CODE AFFECTS THE RESPONSE
boolean detailedResponse = "detail".equals(filterStyle);
Annotation[] responseAnnotations = detailedResponse
? new Annotation[0]
: new Annotation[]{MyDetailView.Factory.get()};
//pojo collection...
MyPagedCollection myCollection = new MyPagedCollection();
//.....
ResponseBuilder builder = Response.ok();
return builder.entity(myCollection, responseAnnotations).build();
}
#GET
#Path("/{id}")
public Response getItem(#PathParam("{id}") String idS, #Context UriInfo uriInfo) {
MyPOJO pojo = ...
Annotation[] responseAnnotations = new Annotation[]{MyDetailView.Factory.get()};
return Response.ok().entity(pojo, responseAnnotations).build();
}
}
After the first test, I tried to use the SelectableEntityFilteringFeature to allow the client to ask for specific fields in the detail, so I changed the ApplicationConfig
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
....
register(EntityFilteringFeature.class);
register(SelectableEntityFilteringFeature.class);
property(SelectableEntityFilteringFeature.QUERY_PARAM_NAME, "fields");
}
and I've add the "fields" QueryParam to the Resource getItem() method...
#GET
#Path("/{id}")
public Response getDetail(#PathParam({id}) String id,
#QueryParam("fields") String fields,
#Context UriInfo uriInfo) {
....
But as long as I registered the SelectableEntityFilteringFeature class, the EntityFilteringFeature class stopped working. I tried to add "fields" parameter to one of the Resource methods, it worked perfectly. But the MyDetailAnnotation was completely useless.
I tried to register it using a DynamicFeature
public class MyDynamicFeature implements DynamicFeature {
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
if ("MyResource".equals(resourceInfo.getResourceClass().getSimpleName())
&& "getItem".equals(resourceInfo.getResourceMethod().getName())) {
//*** IS THE CORRECT WAY TO BIND A FEATURE TO A METHOD? ***
//
context.register(SelectableEntityFilteringFeature.class);
context.property(SelectableEntityFilteringFeature.QUERY_PARAM_NAME, "fields");
}
}
Now the questions:
1) Why registering both the SelectableEntityFilteringFeature feature breaks the EntityFilteringFeature?
2) What is the correct way to bind a feature to a method with the DynamicFeature interface?
Thanks in advance.
This is my first post to Stack Overflow, I hope it was written complaining the rules.
Short answer: you can't. It appears to be a bug as of 2.25.1 and up to 2.26(that I tested with). https://github.com/jersey/jersey/issues/3523
SelectableEntityFilteringFeature implictily registers EntityFilteringFeature (As mentioned here). So I don't see a need to add this.
Since you need Annotation based filtering, you can exclude registering SelectableEntityFilteringFeature.
You can just do,
// Set entity-filtering scope via configuration.
.property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, new Annotation[] {MyDetailView.Factory.get()})
// Register the EntityFilteringFeature.
.register(EntityFilteringFeature.class)
// Further configuration of ResourceConfig.
You can refer to this example for usage and this example for registering the filter.
So you can remove SelectableEntityFilteringFeature and try just the above mentioned way to register it.

How to Inject EPartService

I am developing e4 application. I want to inject EPartService outside the Part and Handler
when i am injecting EPartService then i will get null pointer error
public class DisplayRuntimePart {
#Inject EPartService partService;
private void displayPart(){
MPart part=partService.findPart("com.rcpe4.myproject.part.datapart");
mpart.setVisible(true);
partService.showPart(mpart, PartState.CREATE);
}
}
I am also read this question but till not solve my problem E4 EPartService findPart() throwing java.lang.Null Pointer Exception
Edit
I am inject EPartService in Part class. Class URI in Application.e4xml is bundleclass://com.abc.test/com.abc.test.part.MyPart in this class I am write code as follows.
Class Mypart{
#Inject EPartService prtservice;
#Inject
public MyPart() {
}
#PostConstruct
public void postConstruct(Composite parent) {
parent.setLayout(new FillLayout(SWT.HORIZONTAL));
htmlBrowser = new Browser(parent, SWT.NONE);
}
#PreDestroy
public void preDestroy() {
}
#Focus
public void onFocus() {
}
#Persist
public void save() {
}
public dispalyPart(){
MPart mpart=partService.findPart("com.abc.test.part.datapart"); **Here Getting Null Pointer Exception**
mpart.setVisible(true);
partService.showPart(mpart, PartState.CREATE);
}
}
Eclipse only does direct injection on objects that it 'knows' about - basically objects mentioned in the application model (e4xmi) files or created using something like EPartService.showPart.
If you want to do direct injection on objects that you create then you need to create them using ContextInjectionFactory. For example:
#Inject IEclipseContext context;
...
MyClass myClass = ContextInjectionFactory.make(MyClass.class, context);
you can also do injection on a class created in the normal way with:
ContextInjectionFactory.inject(myClass, context);
(this will not do injection on the constructor).
Note: Since this code is using direct injection you must run it from a class that the Eclipse application model does know about such as a command handler or an MPart.

Multiple ContainerRequestFilter for Jersey

We are planning on using Jersey's reference implementation for our REST APIs. As a prototype effort, I was also playing around with the ContainerRequestFilters and I implemented multiple of them. Is there a way in which we can control the order in which these filters are executed?
The scenario that I am thinking over here is to ensure that the security filter must be the first one to run, and if required establish the SecurityContext and then execute other filters.
Yes you can control this with the javax.annotation.Priority attribute and the default javax.ws.rs.Priorities. For example if you wanted:
Logging filter always runs first
Authentication filter should run next
Authorization filter should run next
Custom filter should always run after others
You could do:
#Priority(Integer.MIN_VALUE)
public class CustomLoggingFilter implements ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
// DO LOGGING HERE, THIS RUNS FIRST
}
}
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
String authHeader = requestContext.getHeaderString(HttpHeaders.WWW_AUTHENTICATE);
// DO AUTHENTICATION HERE, THIS RUNS SECOND
}
}
#Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
String authHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
// DO AUTHORIZATION HERE, THIS RUNS THIRD
}
}
#Priority(Priorities.USER)
public class MyAwesomeStuffFilter implements ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
// DO AWESOME STUFF HERE, THIS RUNS LAST
}
}