@Scheduled bean in Workflow-Services should access Data services via REST client in presence of DELEGATED authentication mode

Hello,

I have a question regarding non-user-request triggered code in the Workflow-Service application (fullstack template, old WF architecture) . It looks similar to a former question of mine: Approach for dealing with security context in the case of scheduled methods but there are differences.

Our workflow services application has set mgmtp.a12.uaa.authentication.client.rest.authentication-type=DELEGATED which means that in order to use the DataServices RPC client, it reuses (delegates) the UAA token provided by the client, e.g. when a user clicks “start process”.

Now, we want to implement a @Scheduled bean which periodically loads data from another system and wants to create DS documents and start Camunda processes. However, we have trouble to authenticate the DS REST client request in this scenario.

What we tried already is to wrap the REST client calling code with code which sets a security context, like this.

@Component
public class SystemUser {

    private final ModulFUser user = PermissionHelper.createTechnicalUser(AuthConstants.ADMIN,
        List.of(new SimpleGrantedAuthority(AuthConstants.ADMIN)));

    public void execute(final Runnable function) {
        DelegatingSecurityContextRunnable.create(function, createSecurityContext()).run();
    }

    private SecurityContext createSecurityContext() {
        final SecurityContext context = SecurityContextHolder.createEmptyContext();
        context.setAuthentication(new UsernamePasswordAuthenticationToken(user, "", user.getAuthorities()));
        return context;
    }
}

Unfortunately, this does not work:

java.lang.NullPointerException: Cannot invoke "com.mgmtp.a12.uaa.client.rest.auth.AuthorizationData.getAuthenticationTokenType()" because "credentialData" is null
	at com.mgmtp.a12.uaa.client.rest.auth.internal.AuthorizationInterceptor.intercept(AuthorizationInterceptor.java:53)
	at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93)
	at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:77)
	at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
	at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:782)
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:717)

What we tried before is to switch to authentication mode CERTIFICATE which works technically but does not meet our business requirements, because in the standard use case that a user starts a process, we need the user information (who created a document) in DS also.

What is your advice, here?

And a subsequent question: does mgmtp.a12.uaa.authentication.client.rest.authentication-type=DELEGATED apply to both, the DS REST client and the Camunda service REST client? So once we have a solution, will it also work for requests to Camunda?

Thanks in advance,
Rainer.

Hi @rainer-brave-cloud,

The auth setup is rather a UAA question (as you tagged it).

But one info from my side regarding this statement:

What we tried before is to switch to authentication mode CERTIFICATE which works technically but does not meet our business requirements, because in the standard use case that a user starts a process, we need the user information (who created a document) in DS also.

I don’t fully understand the requirement. But we store the process “initiator” (i.e. the username of the user who starts a process) as a process variable in Camunda named initiator. Does this possibly help to fulfill your requirement using CERTIFICATE mode?

Regards,
Peter

This could definitely help. We just tested with CERTIFICATE and observed some anonymization where it had not been the case with DELEGATED. At the moment, I cannot assess all the consequences of changing the authenication method. We had CERTIFICATE in earlier times and I assume there were reasons for switching to DELEGATE. Research is going on :wink:

Thank you, Peter.

Hi Peter,

I did not read your reply carefully enough.
No, the initiator who started the process, is not sufficient for our needs. We have fields for each process step in our document model which is filled with the user which actually started / finished a certain step. This is not the same as the one who started the process. Currently, we retrieve this user data from the Security context like SecurityContextHolder.getContext().getAuthentication().getPrincipal() , when we switch to from UAA type DELEGATED to CERTIFICATE in workflow-service it is always the CERTIFICATE “user”, admin in our case. The same is true for connections from workflow-service to DataServices.

I was about to initialize a second DataServicesClient for my scheduled case which has another postconnector which has … another authenication interceptor and so on. But this is really complex and I had to do the same for the Camunda REST client.

Another idea is to extend your solution and add another field to our document model which is is filled with the current user in a workflow-service-listener before passing the document to Camunda.

Hello,

we finally found a solution which is also based on the approach to wrap the code to be run and setup the authorization as required before.

@Component
@AllArgsConstructor
public class CertificateAuthorizationWrapper {

    private final UAARestClientAutoconfigProperties uaaRestClientProperties;

    public void execute(final Runnable function) {
        DelegatingSecurityContextRunnable.create(function, createSecurityContext()).run();
    }

    private SecurityContext createSecurityContext() {
        final SecurityContext context = SecurityContextHolder.createEmptyContext();
        final AuthorizationDataStore credentialsContext = AuthorizationDataHolder.getCredentialContext();
        final UAACertificateTokenAcquirer tokenAcquirer =
            new UAACertificateTokenAcquirer(uaaRestClientProperties.getRest());
        final AuthorizationData authorizationData = tokenAcquirer.acquireToken();
        credentialsContext.setAuthorizationData(authorizationData);
        return context;
    }
}

Maybe the code is not complete or perfect yet. The authorization data is set in a ThreadLocal, I have to rethink if it makes sense to delete the authorization data after the code has been run.

Preconditions are:

  • Data Services and Camunda (the services your code want to access) has configured CERTIFICATE as one of their autorization methods (mgmtp.a12.uaa.authentication.types=OAUTH2,CERTIFICATE) in our case
  • Workflow-Services has configured a path to a client certificate, e.g. mgmtp.a12.uaa.authentication.client.rest.certificate-resource=classpath:/certs/dataServicesClient.crt

This solution works for release 202206 and older versions.

With 202302 UAA provides a built-in-support for scheduled backend code: GetA12 Login