cancel
Showing results for 
Search instead for 
Did you mean: 

Initiator/StartUserId Activiti REST

anjan
Champ in-the-making
Champ in-the-making
Hi, we are using Activiti REST war in our application and we are using custom authentication and it is working perfectly fine.  We can start a process instance…etc.  But when we check the history of a process instance, the "startUserId" value is set to null.

{
            "id": "15001",
            "url": "http://localhost:8080/activiti-rest/service/history/historic-process-instances/15001",
            "businessKey": null,
            "processDefinitionId": "Review:1:2508",
            "processDefinitionUrl": "http://localhost:8080/activiti-rest/service/repository/process-definitions/Review:1:2508",
            "startTime": "2015-04-07T17:16:34.000+05:30",
            "endTime": null,
            "durationInMillis": null,
            "startUserId": null,
            "startActivityId": "start",
            "endActivityId": null,
            "deleteReason": null,
            "superProcessInstanceId": null,
            "variables":
            [
            ],
            "tenantId": ""
        }


Based on my understanding, I need to use the method IdentityService.setAuthenticatedUserId(name) so that the user is set correctly.  Since I am using a custom authentication (as a Filter), how do I get access to IdentityService.  Please let me know, if there is any other way to set this information.
11 REPLIES 11

anjan
Champ in-the-making
Champ in-the-making
In my CustomUserDetailsService, I have added the below code snippet and in the method loadUserByUsername method, I am trying to set the user name.  But I get NPE for identityService.
<code>
@Autowired
private IdentityService identityService;
</code>

trademak
Star Contributor
Star Contributor
Hi,

Can you share the configuration you did to customise the authentication?
The org.activiti.rest.security.BasicAuthenticationProvider class shows how it's implemented by default.

Best regards,

anjan
Champ in-the-making
Champ in-the-making
Hi Tijs, I looked at BasicAuthenticationProvider and added the identityService.  Anyway, here are the changes I made.

In SecurityConfiguration, I added the below line just before the <code>.httpBasic()</code>
<code>
.addFilterBefore(new CustomHeaderAuthenticationFilter(),  BasicAuthenticationFilter.class)
</code>

Here is the CustomHeaderAuthenticationFilter.
<code>
public class CustomHeaderAuthenticationFilter extends
  RequestHeaderAuthenticationFilter {

public CustomHeaderAuthenticationFilter() {
  super();
  setPrincipalRequestHeader("Custom_User");
  setExceptionIfHeaderMissing(false);
  PreAuthenticatedAuthenticationProvider preAuth =  new PreAuthenticatedAuthenticationProvider();
  UserDetailsByNameServiceWrapper userDetails = new UserDetailsByNameServiceWrapper();
  userDetails.setUserDetailsService(new CustomUserDetailsService());
  preAuth.setPreAuthenticatedUserDetailsService(userDetails);
 
  List<AuthenticationProvider> providers = new ArrayList<AuthenticationProvider>(1);
  providers.add(preAuth);
 
  setAuthenticationManager(new ProviderManager(providers));
  setContinueFilterChainOnUnsuccessfulAuthentication(true);
}
}
</code>

And here is CustomUserDetailsService.

<code>
public class CustomUserDetailsService implements UserDetailsService {

@Autowired
private IdentityService identityService;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(1);
        authorities.add(new GrantedAuthorityImpl("ROLE_USER"));
        UserDetails user = new User(username, "", true, true, true, true, authorities);
        identityService.setAuthenticatedUserId(username);
        return user;
}

}
</code>

After successful authentication in our application, we set the "Custom_User" header to the user id and then make a call to Activiti REST.  I am able to successfully make this call.  But as mentioned in my original post, "startUserId" is not set.  So I tried to autowire IdentifyService.  But I am getting NullPointerException at <code>
identityService.setAuthenticatedUserId(username);</code>

Here is the stacktrace:

<code>
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/activiti-rest] threw exception
java.lang.NullPointerException
at my.custom.package.CustomUserDetailsService.loadUserByUsername(CustomUserDetailsService.java:25)
at org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper.loadUserDetails(UserDetailsByNameServiceWrapper.java:51)
at org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider.authenticate(PreAuthenticatedAuthenticationProvider.java:80)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156)
at org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter.doAuthenticate(AbstractPreAuthenticatedProcessingFilter.java:121)
at org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter.doFilter(AbstractPreAuthenticatedProcessingFilter.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:421)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1070)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
</code>

trademak
Star Contributor
Star Contributor
You are creating a new instance of CustomUserDetailsService by doing a new CustomUserDetailsService().
By doing this, you are not using Spring to create the instance and any annotations in it will not be processes. Therefore IdentityService is null.

Best regards,

anjan
Champ in-the-making
Champ in-the-making
I changed the CustomHeaderAuthenticationFilter as below and used @Bean annotator.  But I am still getting the identityService as null.

<code>
@Configuration
public class CustomHeaderAuthenticationFilter extends
  RequestHeaderAuthenticationFilter {

@Bean
public CustomUserDetailsService customUserDetailsService()
{
  return new CustomUserDetailsService();
}

public CustomHeaderAuthenticationFilter() {
  super();
  setPrincipalRequestHeader("Custom_User");
  setExceptionIfHeaderMissing(false);
  PreAuthenticatedAuthenticationProvider preAuth =  new PreAuthenticatedAuthenticationProvider();
  UserDetailsByNameServiceWrapper userDetails = new UserDetailsByNameServiceWrapper();
  userDetails.setUserDetailsService(customUserDetailsService());
  preAuth.setPreAuthenticatedUserDetailsService(userDetails);
 
  List<AuthenticationProvider> providers = new ArrayList<AuthenticationProvider>(1);
  providers.add(preAuth);
 
  setAuthenticationManager(new ProviderManager(providers));
  setContinueFilterChainOnUnsuccessfulAuthentication(true);
}
}
</code>

trademak
Star Contributor
Star Contributor
Is the package of the CustomUserDetailsService class also included in the Spring package scanning? Otherwise it won't be considered for dependency injection.

Best regards,

anjan
Champ in-the-making
Champ in-the-making
I have included the <context:component-scan> (with package) in activiti-custom-context.xml file but identityService is still null.  One question though - I deployed both Activiti Explorer and Activiti REST in Tomcat (both are pointing to the same MySQL database).  Will this have any impact because both Explorer and REST will start a Process Engine?

b_schnarr
Champ in-the-making
Champ in-the-making
I don´t think so. It is a common approach to use a shared database between Explorer and REST. I configured my Custom Filter like this:

Spring Configuration:

<code>
@Bean
public CustomAuthenticationFilter customTokenAuthenticationFilter() {
  return new CustomAuthenticationFilter("/**");
};

@Autowired
CustomAuthenticationFilter customAuthenticationFilter;

.addFilterBefore(customAuthenticationFilter,
      BasicAuthenticationFilter.class).httpBasic();
</code>

In my own filter, I extend <code>AbstractAuthenticationProcessingFilter</code> and import <code>import org.activiti.engine.IdentityService;</code>

After that, I use <code> @Autowired
private IdentityService identityService;</code> and this works perfectly.

anjan
Champ in-the-making
Champ in-the-making
Thanks Ben for the clarification.  I assume, you haven't made any changes to activiti-custom-context.xml file.