Multi-Tenant Process Instantiation by REST Call


#1

Hi,

we have the following use case:
We have a process which will be started by an external system by a REST Call. This process is shared by multiple tenants, but by starting it, it should be assigned to a specific tenant depending on a data attribute, that the User Tasks are only seen by this specific tenant.

We saw the documentation about the TenantIDProvider, but we want to avoid to have a callback for each process instantiation when this is only needed by a specific use case.

We implemented our own REST endpoint and were trying this:

runtimeService.createProcessInstanceByKey(processDefinitionKeyToCall).processDefinitionTenantId(tenantId).setVariables(parameters).execute();

Unfortunately, this only seems to work if the process definition is already deployed with this tenant, which will explain the thrown exception:

org.camunda.bpm.engine.exception.NullValueException: no processes deployed with key 'D463EE85-1DAB-4CF2-82B1-9C58035FBDED' and tenant-id 'test': processDefinition is null

Any hints or best practices are appreciated.

Mario


#2

Hello @Mario,

Your findings are accurate, the options would be.

  1. Have the process definition be part of each tenant
  2. Have the process definition have no tenant and be startable by any external system. Then use a process variable,business key, user and/or combination of these to identify which system started the process
  3. Use the TenantIDProvider as your initial approach.

The best practice would be to go for number 3 and then number 1. Number 2 is bit complicated and not very manageable. I understand that having to deal with the callback is probably not very comfortable. However, having to deal with deploying the process definition to each tenant and/or creating an identifier by using a combination of a business key/process variables is definitely more complicated.

You can also create a feature request through our community portal found here https://app.camunda.com/jira/browse/CAM/.

Please let me know if you have any questions :slight_smile:.

Regards,


#3

Hello @patozgg,

thanks for the clarification. We are now going with the TenantIDProvider.

I am currently trying the example from https://docs.camunda.org/manual/latest/user-guide/process-engine/multi-tenancy/#instantiate-a-shared-definition.
Since we are using a shared instance, I was adding the provider as plugin in the bpm-platform.xml:

<!-- plugin enabling tenant Id Provider -->
      <plugin>
        <class>systems.appollo.ams.plugins.CustomTenantIdProvider</class>
      </plugin>

According to the tomcat logs, the provider seems to be recognized by the engine:

13:11:19.956 INFO  {main} [o.c.bpm.engine.cfg] : ENGINE-12003 Plugin 'ProcessApplicationEventListenerPlugin' activated on process engine 'default'
13:11:20.124 INFO  {main} [o.c.bpm.engine.cfg] : ENGINE-12003 Plugin 'systems.appollo.ams.plugins.CustomTenantIdProvider@6221a451' activated on process engine 'default'
13:11:20.174 INFO  {main} [o.c.bpm.engine.cfg] : ENGINE-12003 Plugin 'SpinProcessEnginePlugin' activated on process engine 'default'

But the tenant is never assigned to the shared resource, the tenant ID is always null when starting the process from the camunda cockpit.

Do I have to configure anything else? Below is the implementation of the tenantIDProvider.

Best regards,
Mario

public class CustomTenantIdProvider implements TenantIdProvider, ProcessEnginePlugin {
    private Logger logger = LoggerFactory.getLogger(CustomTenantIdProvider.class.getName());

    @Override
    public String provideTenantIdForProcessInstance(TenantIdProviderProcessInstanceContext tenantIdProviderProcessInstanceContext) {
        return getTenantIdOfCurrentAuthentication();
    }

    @Override
    public String provideTenantIdForCaseInstance(TenantIdProviderCaseInstanceContext tenantIdProviderCaseInstanceContext) {
        return tenantIdProviderCaseInstanceContext.getSuperExecution().getTenantId();
    }

    @Override
    public String provideTenantIdForHistoricDecisionInstance(TenantIdProviderHistoricDecisionInstanceContext tenantIdProviderHistoricDecisionInstanceContext) {
        return tenantIdProviderHistoricDecisionInstanceContext.getExecution().getTenantId();
    }

    protected String getTenantIdOfCurrentAuthentication() {

        IdentityService identityService = Context.getProcessEngineConfiguration().getIdentityService();
        Authentication currentAuthentication = identityService.getCurrentAuthentication();

        if (currentAuthentication != null) {

            List<String> tenantIds = currentAuthentication.getTenantIds();
            if (tenantIds.size() == 1) {
                return tenantIds.get(0);

            } else if (tenantIds.isEmpty()) {
                throw new IllegalStateException("no authenticated tenant");

            } else {
                throw new IllegalStateException("more than one authenticated tenant");
            }

        } else {
            throw new IllegalStateException("no authentication");
        }
    }

    @Override
    public void preInit(ProcessEngineConfigurationImpl processEngineConfiguration) {

    }

    @Override
    public void postInit(ProcessEngineConfigurationImpl processEngineConfiguration) {

    }

    @Override
    public void postProcessEngineBuild(ProcessEngine processEngine) {

    }
}


#4

Hi @patozgg,

no idea?


#5

Hey @Mario,

Sorry, been busy with other tasks. When I get the chance I’ll get back to you.

Regards,


#6

Hello @Mario,

I looked into this and I am also running into the same issue as you. I would recommend posting another post in the forum to see if anybody else has an idea. I will be offline for the next 2 weeks.

Regards,


#7

Thank you, I will do this.