REST API authorizations
Permissions to access Bonita platform’s REST API resources are handled by a configurable authorizations mechanism.
The Bonita Applications, or any application that uses the Web REST API, enables user to access resources. The set of resources that a user can access is determined by default by the user’s profiles. This authorization mechanism ensures that users can only access the appropriate resources. This means, for example, that a user with only the User profile cannot access REST API resources intended for the Administrator.
The authorization mechanism is a "white list" implementation.
-
a dynamic phase, which can use a script to grant or deny access to a resource,
-
a static phase, which is executed if there is no dynamic configuration for the resource. It checks in the static configuration to see whether the user is authorized to access a given resource or not.
The static phase works the following way:
To access a given resource, a user must have one of the permissions associated with the resource.
These permissions are defined as simple permissions, which are the smallest units of permission, or compound permissions, which are groups of simple permissions.
Upon login, a user is granted a set of permissions. These permissions define the set of resources the user is authorized to access.
Summary
For a new Bonita installation, a basic set of authorization checks is activated by default for the REST API. Here are the key points of the authorization’s configuration :
-
The static check creates an authorization layer that exactly matches the standard Bonita Applications features and profiles.
-
In addition a set of dynamic rules is active by default to set finer grain restrictions mostly based on BPM business rules (process actor mapping, supervisors…).
If you are using the Bonita Applications, you do not need to configure anything.
-
If you want to add extra authorization restrictions based on custom business rules, you can configure custom static permissions or implement your own dynamic rules (Teamwork, Efficiency, Performance and Enterprise editions).
-
If you add a custom page, include a resources=[ list ] in your page.properties to specify which resources your custom page requires the users to have access to.
In Bonita Cloud, to change the properties files and apply Rest Authorization, you just have to open a support ticket and we’ll apply the change on the requested environment for you ! |
Static authorization checking
The static phase uses a set of configuration files:
-
resources-permissions-mapping-*.properties
-
compound-permissions-mapping-*.properties
-
custom-permissions-mapping.properties
These files grant permissions to sets of users based on profile or username. You cannot deny permissions in the configuration files (it’s a "white list" mechanism only), so you must ensure that the default definitions grant the minimum permissions that you want to give to any user.
The default versions of these files are located in setup/platform_conf/initial/tenant_template_portal
.
In order to change the configuration on an installation whose platform has already been initialized, use the platform setup tool to
retrieve the current configuration and update the files in setup/platform_conf/current/tenants/[tenantId]/tenant_portal
.
Then use the tool again to save your changes into the database.
Resources permissions mapping
The resources-permissions-mapping-*.properties
files define the mapping of REST resources to simple permissions.
This tells you what permission can grant you access to a given resource.
For example: GET|identity/user=[organization_visualization,organization_management]
This specifies that a user with the organization_visualization, or organization_management permissions can see information about users.
By default, this file contains a mapping of each Bonita resources to at least one simple permission.
You can modify the file resources-permissions-mapping-custom.properties
to add your own mappings.
For example: GET|identity/user/3=[organization_management]
This specifies that information about the user with id 3 can only be seen by users with the Organization_management permission.
If there are conflicts between permissions, the more restrictive definition overrides the more general. You can see this in the example above, where the specific permission for user 3 overrides the general permission for seeing user information.
Do not modify file |
Compound permissions mapping
The compound-permissions-mapping-*.properties
files define sets of simple permissions that are grouped together into a compound permission.
You can use a compound permission as "shorthand" for a list of simple permissions.
By default, the file compound-permissions-mapping.properties
contains a compound permission that corresponds to each page of the Bonita Applications,
including custom pages.
For example: cusompage_adminUserList=[profile_visualization, process_comment, organization_visualization, tenant_platform_visualization, organization_management]
This specifies the REST API permissions that are granted by the Bonita Administrator User List page that lists all the users in the tenant.
By default, there is a compound permission defined for each page that is installed by default in the Bonita Runtime.
When you install a custom page in the Bonita Administrator Application, if the page declares its resources properly, then a new compound permission will be added in an internal version
of this file (compound-permissions-mapping-internal.properties
). Then all the users being able to access this page (because it is part of an application they have access to) will also be automatically granted the necessary permissions to call the required REST API resources.
Do not modify file |
Custom permissions mapping
The custom-permissions-mapping.properties
file contains custom rules that supplement the resource permissions and compound permissions.
By default, this file is empty, because the compound permissions definitions automatically manage the permissions needed for default and custom profiles, and for default and custom pages.
If you want to override the default behavior, you can add a rule to this file. You can add a simple or compound permission to a profile.
For example, to give users with the User profile the ability to view applications: profile|User=[application_visualization]
You can also assign a permission to a specific user. This is useful if you want to give a named user access to a resource that is not accessible through the user’s profiles.
For example, if the user John.Smith is assigned the User profile, he does not have permission to modify applications.
You can add this specific permission to custom-permissions-mapping.properties
by adding this line: user|John.Smith=[application_management]
This means that in addition to the permissions given to him by the User profile, John.Smith can also manage the installed applications. It does not modify the permissions for any other user.
If you do not use Bonita Applications but still want to manage REST API authorizations, you can do this using the custom-permissions-mapping.properties
file.
To do this, create a custom profile and configure the relevant permissions.
For example, you could create a profile called CustomProcessManager and assign the permissions needed to monitor and manage processes:
profile|MyCustomProfile=[process_visualization, process_management, process_manager_management, custom_process_manager_permission]
In this example, the custom_process_manager_permission
can be defined in the compound-permissions-mapping-custom.properties
file.
Dynamic authorization checking
Default configuration and rules are available in all editions but custom configuration and rules extensibility are only available in Teamwork, Efficiency, Performance and Enterprise editions. |
From Bonita version 2022.1, Dynamic authorization checking is enabled by default in subscription editions. From Bonita version 2024.1, Dynamic authorization checking is also enabled by default in community edition. From Bonita version 2024.2, Dynamic authorization checking is also enabled by default in Access edition. |
For some API resources, the static authorization check may not be specific enough to properly define when the access is granted. In those situations, the static authorization check may be overriden using dynamic checks. A user is then granted a permission only if the dynamic check authorizes it. A dynamic check is implemented as a sequence of conditions, including a Groovy script. This enables you to tailor the permissions needed to access a resource using dynamic information related to processes.
By default, dynamic checks are enabled.
To completely disable dynamic checks, which is not advised, simply either:
-
set the Environment variable
BONITA_RUNTIME_AUTHORIZATION_DYNAMICCHECK_ENABLED=false
-
or add the Java System property
-Dbonita.runtime.authorization.dynamic-check.enabled=false
to yoursetEnv[.sh|.bat]
tomcat startup script.
In community edition
In community and Access editions, a set of dynamic authorization checks is predefined for the resources of the bpm, identity, portal and living APIs.
In subscription editions
In other subscription editions (Teamwork, Efficiency, Performance and Enterprise) a dynamic authorization check for a resource is specified by a line in the file dynamic-permissions-checks-custom.properties
.
The line specifies the checks to be made for a request type for a method.
There can be several terms in the line. Checking stops when the system returns success, indicating that the user is authorized.
For example: POST|bpm/case=[user|william.jobs, user|walter.bates, profile|Administrator, profile|User, check|org.bonitasoft.permissions.CasePermissionRule]
This specifies that a POST
action can be done for a case
resource if the user is william.jobs or walter.bates,
or any user with the Administrator profile, or any user with the User profile, or if the CasePermissionRule grants authorization.
A check
term indicates the name of a class to be called. The class must implement org.bonitasoft.engine.api.permission.PermissionRule
.
This example defines a dynamic check that is made whenever a user makes a GET request for the "bpm/case" resource.
If the script returns true
, the user is authorized. If the script returns false
or any other result (including an error), the user is not authorized.
The dynamic-permissions-checks.properties
file contains a placeholder line for each method and resource. For example:
## CasePermissionRule
#GET|bpm/case=[profile|Administrator, check|org.bonitasoft.permissions.CasePermissionRule]
#POST|bpm/case=[profile|Administrator, check|org.bonitasoft.permissions.CasePermissionRule]
#DELETE|bpm/case=[profile|Administrator, check|org.bonitasoft.permissions.CasePermissionRule]
#GET|bpm/archivedCase=[profile|Administrator, check|org.bonitasoft.permissions.CasePermissionRule]
To disable a single dynamic check for a method and resource, comment out the corresponding line in the file dynamic-permissions-checks-custom.properties
(Teamwork, Efficiency, Performance and Enterprise editions only).
To add a custom dynamic check for a method and resource, add your line in the file dynamic-permissions-checks-custom.properties
following the example above (Teamwork, Efficiency, Performance and Enterprise editions only).
If you specify a condition that calls a Groovy script, you must add the new script:
If the platform has never been started yet:
-
add the script to the
setup/platform_conf/initial/tenant_template_security_scripts
folder -
it will be pushed to database at first run
If the platform has already been started:
-
use the platform setup tool to retrieve the current configuration
-
add the script to the
setup/platform_conf/current/tenants/[tenantId]/tenant_security_scripts
folder -
then use the platform setup tool again to push the new / modified scripts to database
The tenant_security_scripts
folder contains a script sample that can be used to write your own.
Bonita also provides default scripts that should fit common usages. They are packages internally in the binaries, but the
source code is available.
These provided scripts can be used as a base for you own scripts.
If you write your own scripts:
-
make sure you either inherit from an existing rule, or implement the PermissionRule interface, by overriding the isAllowed() method
-
make sure you use the default package declaration at the top of your groovy class (no
package
keyword used) -
make sure this .groovy file is placed in the default directory, under 'initial/tenant_template_security_scripts/' if the platform has never been started, or under 'current/tenants/TENANT_ID/tenant_security_scripts/' if the platform has already been started
Do not modify file |
Example dynamic check script
This script is an example of how to write a dynamic check. It checks two conditions, depending on the method called for a case.
If the method is a POST, which would start a case of a process. the user can only start the case if they are eligible to start the process itself.
If the user action triggers a GET, the user can view the case information only if they are involved in the case.
The Engine API Java method isInvolvedInProcessInstance
is used to check whether the user is involved. For an archived case, the only check possible is whether the user started the case.
import org.bonitasoft.engine.api.*
import org.bonitasoft.engine.api.permission.APICallContext
import org.bonitasoft.engine.api.permission.PermissionRule
import org.bonitasoft.engine.bpm.process.ArchivedProcessInstanceNotFoundException
import org.bonitasoft.engine.identity.User
import org.bonitasoft.engine.identity.UserSearchDescriptor
import org.bonitasoft.engine.search.SearchOptionsBuilder
import org.bonitasoft.engine.search.SearchResult
import org.bonitasoft.engine.session.APISession
import org.json.JSONObject
class CasePermissionRule implements PermissionRule {
@Override
boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) {
long currentUserId = apiSession.getUserId()
if ("GET".equals(apiCallContext.getMethod())) {
return checkGetMethod(apiCallContext, apiAccessor, currentUserId, logger)
} else if ("POST".equals(apiCallContext.getMethod())) {
return checkPostMethod(apiCallContext, apiAccessor, currentUserId, logger)
}
return false
}
private boolean checkPostMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {
def body = apiCallContext.getBodyAsJSON()
def processDefinitionId = body.optLong("processDefinitionId")
if (processDefinitionId <= 0) {
return false;
}
def processAPI = apiAccessor.getProcessAPI()
def identityAPI = apiAccessor.getIdentityAPI()
User user = identityAPI.getUser(currentUserId)
SearchOptionsBuilder searchOptionBuilder = new SearchOptionsBuilder(0, 10)
searchOptionBuilder.filter(UserSearchDescriptor.USER_NAME, user.getUserName())
SearchResult<User> listUsers = processAPI.searchUsersWhoCanStartProcessDefinition(processDefinitionId, searchOptionBuilder.done())
logger.debug("RuleCase : nb Result [" + listUsers.getCount() + "] ?")
def canStart = listUsers.getCount() == 1
logger.debug("RuleCase : User allowed to start? " + canStart)
return canStart
}
private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) {
def processAPI = apiAccessor.getProcessAPI()
def filters = apiCallContext.getFilters()
if (apiCallContext.getResourceId() != null) {
def processInstanceId = Long.valueOf(apiCallContext.getResourceId())
if (apiCallContext.getResourceName().startsWith("archived")) {
//no way to check that the were involved in an archived case, can just show started by
try {
return processAPI.getArchivedProcessInstance(processInstanceId).getStartedBy() == currentUserId
} catch(ArchivedProcessInstanceNotFoundException e) {
logger.debug("archived process not found, "+e.getMessage())
return false
}
} else {
def isInvolved = processAPI.isInvolvedInProcessInstance(currentUserId, processInstanceId)
logger.debug("RuleCase : allowed because get on process that user is involved in")
return isInvolved
}
} else {
def stringUserId = String.valueOf(currentUserId)
if (stringUserId.equals(filters.get("started_by")) || stringUserId.equals(filters.get("user_id")) || stringUserId.equals(filters.get("supervisor_id"))) {
logger.debug("RuleCase : allowed because searching filters contains user id")
return true
}
}
return false
}
}
Initialization
After the application server starts, the first time that one of the configuration files is accessed, the information from all the files is cached in memory for fast access. If you update a file, the changes become active the next time the application server restarts. In your development environment, you can use the debug mode to makes any changes to the configuration files and dynamic check scripts available immediately.
User login
When a user logs in, after the user is authenticated, a map of LoggedUserPermissions
is created.
LoggedUserPermissions
is a combination of the information from compound-permissions-mapping.properties
and
CustomUserPermissionsMapping
that is relevant to the user.
It takes into account all the profiles assigned to the user, not only the current profile, so when you change profile the map does not need to be recreated.
Runtime behavior
At runtime, when a user requests access to a resource, the system checks to see if a dynamic check is defined for this resource. If so, it executes the check, and the result grants or denies the user access to the resource.
If there is no dynamic check for the resource, the system uses the static checks: it uses the information in the ResourceRequiredPermissions
to see what permissions are
needed to access the resource (or page), and checks the LoggedUserPermissions
to see whether the user has the necessary permissions.
If so, the user is authorized.
Otherwise, access is refused.
If access is not authorized, a message is written in the log so that the Administrator is aware that an unauthorized user has tried to gain access.
Note that this level of logging is only available if you set the logging level to FINEST
.
Authorizing access to a custom page
When a new custom page is added, the permissions defined in the page properties are added to the permissions configuration files and the cache. It is not necessary to restart the applications server to activate security for the new custom page. Depending on the permissions that a user of the page already has, it might be necessary to log out and log in again to get access to the new custom page.
If the page declares resources provided by a REST API extension, then the REST API extension must be deployed before the page, otherwise the compound permissions won’t be automatically created when deploying the page, and you will need to redeploy the page after deploying the REST API extension. |
Authorization and custom profiles
When a new custom profile is created, the permissions mappings are updated in the configuration files and in the cache. It is not necessary to restart the application server to activate security for the new custom profile.
Granting permissions to a given resource
If you only develop custom pages and the resources they use are declared properly, no custom permissions should be created. However, you may need to do so if you need to manually grant permissions to a given REST API resource (so that it can be called automatically). In order to do that, you need to:
-
Look into the file
resources-permissions-mapping.properties
for the permissions that grant access to the resource. For example, in order to perform a GET onbpm/task
, I can see that I need the permissionflownode_visualization
(syntax:GET|bpm/task=[flownode_visualization]
) -
Edit the file
custom-permissions-mapping.properties
to give the permissionflownode_visualization
to the required profiles or users. For example, to add the permission to the user walter.bates (username), add the following line :user|walter.bates=[flownode_visualization]
Restricting access to a BDM object or its attributes
Starting with the Bonita Subscription edition, you can use a simpler mechanism to grant or deny access to BDM objects or some of their attributes to specific profiles, using the BDM Access Control feature. It is also possible to protect instances of the BDM objects, using REST API authorizations. For more details see : BDM access control
Activating and deactivating authorization
security-config.properties
contains a Boolean property that specifies whether authorization is activated. To activate authorization, set this property to true
: security.rest.api.authorizations.check.enabled true
To activate authorization, edit security-config.properties
and set the value of the security.rest.api.authorizations.check.enabled
property to true
, then restart the application server.
To deactivate authorization, set the property to false
, then restart the application server.
If you activate authorization, deactivate the HTTP API or it will be used to bypass the authorization settings. To do this, you can:
-
either add a Java system property
-Dhttp.api=false
to filesetEnv.[sh|bat]
inside tomcat bundle (in foldersetup/tomcat_templates/
) -
or set an environment variable
HTTP_API=false
before launching Bonita Tomcat bundle
Running in debug mode
To optimize performance in production, Bonita caches the dynamic check scripts for faster subsequent executions.
If debug mode is activated, Bonita will reload the dynamic check scripts each time they are executed. It allows to rapidly validate your script at development time.
To activate debug mode, edit bonita-platform-sp.properties
and set the value of the bonita.runtime.authorization.dynamic-check.debug
property to true
, then restart the application server.
Then, each time you change a dynamic check script, simply update it in database using Setup tool (./setup.sh push
). The script will be reloaded at next execution (next time you call a URL that matches this dynamic rule).
To deactivate authorization, set the above property to false
, then restart the application server. Debug mode should be deactivated in production, so as not to impact performance.
Troubleshooting
To troubleshoot REST API permissions issues, you need to increase the log level to DEBUG
(or TRACE
for even more logs) for the packages org.bonitasoft.authorization
and com.bonitasoft.authorization
in order requests attempts to be displayed in the log files bonita-*.log
(by default, they are not).
In order to do that in a Tomcat bundle, you need to edit the file `<BUNDLE_HOME>/server/conf/log4j2-loggers.xml.
-
Make sure the following lines are not commented or add them if they are not present :
<Logger level="TRACE" name="org.bonitasoft.engine.authorization"/>
<Logger level="TRACE" name="com.bonitasoft.engine.authorization"/>
In Bonita Studio the loggers to see denied REST resources access are already configured in order to help troubleshooting 403 errors. |
Permissions and resources
You can find the default REST API authorizations in this page.