Skip to main content

Sandboxing

Flowable offers configuration options to sandbox a running installation to avoid and / or limit certain functionality.

Here we are going to explain the different mechanism and what each sandboxing mechanism does and how it can be configured.

Repetitions / Loops

v3.14.0+

Using Flowable it is possible to create model repetitions / loops. If you create such a model you are going to have a thread / DB connection that is never going to be closed or if you are using async you will keep creating jobs and the process / case will never end.

This mechanism can be enabled by setting the flowable.sandbox.max-allowed-repetitions property. The value in this property will be used to determine how many times a process / case instance is allowed to go through the same element. The following scenarios are covered:

  • Process loop (in memory or with wait states (e.g. async service task) in between) Process With Loop
  • Multi instance
  • Non-interruptible boundary events
  • Case loop (in memory or with wait states (e.g. async service task) in between) (e.g. modelling repeatable unlimited task)
  • Case plan item transitions (e.g. available -> unavailable -> available)

Strict Expression Mode

v3.15.0+

By default, Flowable exposes all available beans and all methods on those beans during expression evaluation. If you want to enable strict mode (only where specific Flowable beans are exposed and values cannot be set through expressions) then the property flowable.sandbox.expression.strict-mode needs to be set to true. When this is the case the following beans / functions are going to be available:

  • flw.time - exposing the Flowable Time Utils
  • flw.string - exposing the Flowable String Utils
  • flw.math - exposing the Flowable Math Utils
  • flw.locale - exposing the Flowable Locale Utils
  • flw.format - exposing the Flowable Format Utils
  • flw.base64 - exposing the Flowable Base64 Utils
  • flw.io - exposing the Flowable IO Utils
  • flw.setOutput(varName, value) - for setting a variable value in the current scope
  • flw.setTransientOutput(varName, value) - for setting a transient variable value in the current scope
  • flw.setLocalOutput(varName, value) - for setting a transient variable value in the current local scope
  • seq:next(sequenceKey) - for getting the next formatted sequence value
  • seq:nextNumber(sequenceKey) - for getting the next number sequence value
  • json:array() - for creating a JSON array
  • json:arrayWithSize(size) - for creating a JSON array with a specific size
  • json:object() - for creating a JSON object
  • vars:get(varName) - for safe getting a variable
  • vars:getOrDefault(varName, defaultValue) - for safe getting a variable or using the default value if the variable does not exist
  • vars:containsAny(varName, values) - for safe getting checking if a variable contains any of the provided values
  • vars:containsAny(varName, values) - for safe getting checking if a variable contains all the provided values
  • vars:equals(varName, value) - for safe comparing a variable value to a given value
  • vars:notEquals(varName, value) - for safe comparing a variable value to a given value
  • vars:exists(varName) - for checking if a variable with the given name exists
  • vars:isEmpty(varName) - for safe checking if a variable with the given name is empty
  • vars:isNotEmpty(varName) - for safe checking if a variable with the given name is not empty
  • vars:lowerThan(varName, value) - for safe comparing if a variable value is lower than a given value
  • vars:lowerThanOrEquals(varName, value) - for safe comparing if a variable value is lower than or equal to a given value
  • vars:greaterThan(varName, value) - for safe comparing if a variable value is greater than a given value
  • vars:greaterThanOrEquals(varName, value) - for safe comparing if a variable value is greater than or equal to a given value
  • vars:base64(varName) - for safe base64 encoding a variable value with the given name
  • isUserInAnyGroup(userId, groupKeys) - for checking if a user is a member of the given groups
  • isUserInNoGroups(userId, groupKeys) - for checking if a user is not a member of the given groups
  • findUser(userId) - for getting the user information
  • findGroupMemberEmails(groupKeys) - for getting the emails of all the members of the given groups
  • findGroupMemberUserIds(groupKeys) - for getting the user ids of all the members of the given groups

If you want to make your custom beans available in strict expression mode then you need to annotate them with the @AllowedBeanInStrictMode annotation. In order to expose public methods of your beans or types in strict mode you can use the @AllowedInStrictMode annotation. This annotation can be used on a class or a method. When it is on a class then all the public methods of that class will be available in strict mode. If you want to disable a specific method you can use the @NotAllowedInStrictMode annotation.

Max transaction duration

v3.15.0+

In order to limit the max transaction duration, Flowable provides a way to configure the max amount of time a flowable command can be executed. This can be configured using the flowable.sandbox.max-command-duration property. e.g. for a max value of 30 seconds it looks like:

flowable.sandbox.max-command-duration=30s

Rate Limiting

v3.15.0+

Flowable provides a way to rate limit the following:

  • Started Case / Process instances
  • Plan Item instances / Activities started
  • Jobs scheduled / run

Out of the box Flowable provides integration with Bucket4J. To enable this the property flowable.sandbox.rate-limit.type should be set to bucket4j. If you are building your own Flowable application you'll need to add the following dependency

<dependency>
<groupId>com.bucket4j</groupId>
<artifactId>bucket4j-core</artifactId>
<version>${bucket4j.version}</version>
</dependency>

Doing this will enable the in memory single node rate limiting. If you want to have a distributed rate limiting then you'll have to decide which of the available mechanisms from Bucket4J you want to use and expose a bean of type ProxyManager<String>.

The following properties can be used to configure the rate limiting

# This configuration allows starting 10 instances per minute and 100 per hour
# It allows us to protect from spending all the available tokens for an hour within one minute
flowable.sandbox.rate-limit.instance-start[0].capacity=20
flowable.sandbox.rate-limit.instance-start[0].duration=PT1M
flowable.sandbox.rate-limit.instance-start[1].capacity=200
flowable.sandbox.rate-limit.instance-start[1].duration=PT1H

# This configuration allows starting 50 activities / plan item instance per minute and 500 per hour
# It allows us to protect from spending all the available tokens for an hour within one minute
flowable.sandbox.rate-limit.activity-start[0].capacity=100
flowable.sandbox.rate-limit.activity-start[0].duration=PT1M
flowable.sandbox.rate-limit.activity-start[1].capacity=1000
flowable.sandbox.rate-limit.activity-start[1].duration=PT1H

# This configuration allows scheduling 20 jobs per minute and 200 per hour
# It allows us to protect from spending all the available tokens for an hour within one minute
flowable.sandbox.rate-limit.job-schedule[0].capacity=20
flowable.sandbox.rate-limit.job-schedule[0].duration=PT1M
flowable.sandbox.rate-limit.job-schedule[1].capacity=200
flowable.sandbox.rate-limit.job-schedule[1].duration=PT1H

# This configuration allows executing 10 jobs instance per minute and 100 per hour
# It allows us to protect from spending all the available tokens for an hour within one minute
flowable.sandbox.rate-limit.job-execute[0].capacity=10
flowable.sandbox.rate-limit.job-execute[0].duration=PT1M
flowable.sandbox.rate-limit.job-execute[1].capacity=100
flowable.sandbox.rate-limit.job-execute[1].duration=PT1H

Disable Scripting

If you want to disable scripting completely you can set the flowable.sandbox.script.disabled property to true.