When the first runnable is submitted is an inject ExecutorService, the security Principal is correctly set for that runnable. Each subsequently submitted runnable is given the security principal of the original user instead of keeping the current runnable. My development machine is running Wildfly 8.2 .
I am creating a reporting system for asynchronous processing. I created a service that checks which user created the task and ensures that only that user can start or complete the task. The code for the service is below.
@Stateless
public class ReportingService {
//EE injection security context
@Resource
SessionContext context;
//CDI security Principal
@Inject
Principal principal;
//this method handles getting the username for EE injection or CDI
private String getCurrentUser() {
if (context != null) {
return context.getCallerPrincipal().getName();
}
if (principal != null) {
return principal.getName();
}
return null;
}
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
@Transactional
public void registerTask(String taskId) {
//Create task
//set task.submittedBy = getCurrentUser()
//persist task
//code has been omitted since it is working
}
private void validateCurrentUserRegisteredJob(String taskId) {
String user = //get user that created task with id = id from DB
String currentUser = getCurrentUser();
if (!user.equals(currentUser)) {
throw new EJBAccesException("Current user "+currentUser+" did not register task");
}
}
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
@Transactional
public void startTask(String taskId) {
validateCurrentUserRegisteredJob(taskid);
//retrieve task entity, set start time to now, and merge
}
...
}
Below is my Runnable code
public TaskRunner() implements Runnable {
//CDI principal
@Inject
Principal principal;
@Inject
ReportingService rs;
private taskId;
public void setTaskId() {...}
public void run() {
log.debug("Inside Runner Current User: "+principal.getName());
rs.startTask(taskId);
....
}
}
The following is the code from the Stateless Bean that is called by a REST endpoint that kicks off the process
@Stateless
public ProjectService() {
@Inject
Instance<TaskRunner> taskRunner;
@Inject
ReportingService reportingService;
//ExecutorService that is create from Adam Bien's porcupine project
@Inject
@Dedicated
ExecutorService es;
//method that is called by rest enpoint to kick off
public void performAsynchAction(List<String> taskIds, ...rest of args...) {
taskIds.stream().forEach(t -> {
//registers task with user that made REST call
reportingService.registerTask(t);
TaskRunner runner = taskRunner.get();
runner.setTaskId(t);
log.debug("Created runner. Principal: "+runner.principal.getName());
es.submit(runner);
});
}
}
Here is the chart of the call flow
REST -> ProjectService.performAsynchAction(...)
-> reportingService.registerTask(...)
-> create CDI injected Runnable
-> submit runner to executor service
-> ExecutorService calls Runner.run()
-> rs.startTask(taskId)
I call the Rest end point as user1 for the first time and register tasks: 1-2. They all work as expected and I get the following output in my log.
Created runner. Principal: user1
Created runner. Principal: user1
Inside Runner Current User: user1
Inside Runner Current User: user1
The next time I make the same REST call as user2 and I get the following output in the log
Created runner. Principal: user2
Inside Runner Current User: user1
EJBAccessException Current user user1 did not register task
It appears that the Security Principal of the Runnable is correctly set the first time a Runnable is submitted to the ExecutorService. But for each subsequent Runneable that is submitted to the ExecutorService uses the security Principal of the first submitted runnable. Is this a bug or the intended behavior? Does anyone know of a potential work around?
EDIT: I figure out that the porcupine project I was using to create the ExecutorService was not being managed by the container. Once I switched to a ManagedExecutorService, the SessionContext was being properly propagated.
@Resource(lookup = "java:jboss/ee/concurrency/executor/customExecutor")
private ManagedExecutorService es;
I figured out the issue. I looked into the porcupine code and found out that the ExecutorService was not being managed by the Container. I created a ManagerExecutorService and the SessionContext was then being properly propogated.
@Resource(lookup = "java:jboss/ee/concurrency/executor/customExecutor")
private ManagedExecutorService es_;