= LDAP authentication configuration
:description: Bonita can be configured to perform user authentication against an LDAP server such as Active Directory, Apache Directory Server, or OpenLDAP.

Bonita can be configured to perform user authentication against an LDAP server such as Active Directory, Apache Directory Server, or OpenLDAP.

[NOTE]
====
For Subscription editions only.
====

*Important notes:*

* This documentation applies to an existing and working Bonita installation (see the xref:bonita-bpm-installation-overview.adoc[installation instructions]).
* In order to have functioning Active Directory/LDAP authentication, the user login (username) must exist both in the LDAP directory and in the Bonita database (user password is checked against the LDAP server but user information is read from Bonita database).  We recommend that you use the xref:ldap-synchronizer.adoc[LDAP synchronizer] to create Bonita users in a Bonita database.

== Overview

This type of configuration relies on a specific implementation of the Bonita Engine authentication service that delegates the actual user name and password verification to a http://docs.oracle.com/javase/8/docs/technotes/guides/security/jaas/JAASRefGuide.html[JAAS] service configured with an http://docs.oracle.com/javase/8/docs/jre/api/security/jaas/spec/com/sun/security/auth/module/LdapLoginModule.html[LDAP
specific Login Module].

== Before you start

In order to configure LDAP successfully, make sure you have the following information:

* LDAP server type: Active Directory (AD), Apache Directory Server, or OpenLDAP
* LDAP server address
* LDAP listening port (e.g. 389 by default)
* Is it possible to build the user distinguished name with user name that the user specifies when logging in? +
For example, if the user name is: `john.smith` and the user DN is: `CN=John	Smith,CN=Users,DC=MyDomain,DC=com`, it's not possible to build the DN dynamically.  But it's possible to do so if the DN is: `uid=john.smith,ou=people,dc=example,dc=com`. +
If it's not possible to build the DN using the user name you will need the following extra information:
 ** The DN of the LDAP entry under which all users are located (e.g. `CN=Users,DC=MyDomain,DC=com`)
 ** The user entry objectClass (the most restrictive one). E.g. usually `user` on AD, `inetOrgPerson`
or `organizationalPerson` for other LDAP servers.
 ** The user entry attribute used for authentication (e.g. `userPrincipalName` on AD, value: `john.smith@mydomain.com`
or `uid` on other LDAP servers, value: `john.smith`)
* Does the LDAP server allow anonymous search?
* Does the LDAP server allow search for all users that can possibly log in?
* If search can only be performed by a limited number of "technical" accounts you will need the user name and password of such an account.
* If SSL security is required, ldaps server address FQDN, port and the certificate if provided

== Create a JAAS configuration file

This section explains how to put together all the LDAP server information you have to create or edit a JAAS configuration file compatible with your JEE application server.

=== Login context name

The JAAS configuration can include one or several login contexts. The Bonita login context must be named `BonitaAuthentication-<TENANT_ID>` (where `<TENANT_ID>` is your tenant id).

=== LdapLoginModule attributes to set

It's important to identify which `LdapLoginModule` attributes you need to set.
This will be at least one of `authIdentity`, `userFilter`, `tryFirstPass`, `java.naming.security.principal` or `java.naming.security.credentials`.
Based on the information described in the "Before you start" section, you can identify which of the following cases applies:

* If you can build the user DN by directly injecting the user name \=> set only the `authIdentity` attribute
* If you cannot build the DN and anonymous search is allowed \=> set only the `userFilter` attribute
* If you cannot build the DN and anonymous search is disallowed and authenticated users can search \=> set the `userFilter` and `authIdentity` attributes
* If you cannot build the DN and anonymous search is disallowed and authenticated users cannot search \=> set the `userFilter`,
`authIdentity`, `tryFirstPass`, `java.naming.security.principal` and `java.naming.security.credentials` attributes

=== Values for LdapLoginModule attributes

In this section we explain how to set `LdapLoginModule` attributes values. (For further information, check the https://docs.oracle.com/javase/8/docs/jre/api/security/jaas/spec/com/sun/security/auth/module/LdapLoginModule.html[`LdapLoginModule` javadoc] page.)

*`userProvider`*: set this to `ldap://<ldap server address>:<ldap server port>/<DN of the LDAP entry under which all users are located>`.

[WARNING]
====
Spaces in the distinguished name (DN) component of the URL must be escaped using the standard mechanism of percent character ('%') followed by two hexadecimal digits: replace them with '%20'. Query components must also be omitted from the URL.
====

For example, if your users are under `CN=Bonita Users,DC=MyDomain,DC=com`: `ldap://localhost:389/CN=Bonita%20Users,DC=MyDomain,DC=com`

*`userFilter`* (only if needed): the value must be a search request that will find your users in the LDAP server. The search request can be for example: `+(&(objectClass=user)(userPrincipalName={USERNAME}@mydomain.com))+`.
Use an LDAP tool (such as Apache Directory Studio) to validate that the request returns the expected result if you replace \{USERNAME} with an actual username.

*`authIdentity`* (only if needed): there are two cases: +
If you can build the user DN, set the attribute value with the user DN and `+{USERNAME}+` tag. For example `+uid={USERNAME},ou=users,dc=example,dc=com+`. +
If you use a `userFilter` and users are allowed to search, set the value with `+{USERNAME}@mydomain.com+` for AD and user the DN (same as above) for other LDAP servers.

*`tryFirstPass`* +
(only if needed): set this to `true`.

*`java.naming.security.principal`* +
(only if needed): specify the username (AD) or the user DN (other LDAP servers) of a user that can perform searches on the server.

*`java.naming.security.credentials`* +
(only if needed): specify the password of a user that can perform searches on the server.

*`useSSL`*
Set this to `true` if SSL is required, `false` otherwise.

=== Create or edit the configuration file for your application server

NOTE: All configuration files are case sensitive. You can find more examples in the <<examples,JAAS configuration files examples>> section of this page.

The JAAS configuration file follows the http://docs.oracle.com/javase/8/docs/api/javax/security/auth/login/Configuration.html[default JVM syntax]. +
Here is an example of JAAS configuration file:

----
BonitaAuthentication-1 {
  com.sun.security.auth.module.LdapLoginModule sufficient
  userProvider="ldap://localhost:389/ou=people,dc=example,dc=com"
  authIdentity="uid={USERNAME},ou=people,dc=example,dc=com"
  useSSL=false;
};
----

We recommend that you name your JAAS configuration file `jaas.cfg` and that you add the file under `<BUNDLE_HOME>/server/conf` folder.

== Configuration steps

=== Changing Bonita authentication service

The default Bonita installation comes with an authentication service implementation based on the Bonita Engine database. In
order to activate Active Directory/LDAP authentication the service implementation needs to be changed. To do this, edit xref:bonita-bpm-platform-setup.adoc[`bonita-tenant-sp-custom.properties`].

You will need to perform following changes:

* Comment out the `authenticationService` line
* Add this new line: `authentication.service.ref.name=jaasAuthenticationService`

=== Configure JAAS

To define the JAAS configuration file location you need to set a JVM property, `java.security.auth.login.config`.
If you're using a xref:custom-deployment.adoc[`custom deployment into existing Tomcat`] installation, you need to edit the `setenv` script provided with Bonita and located in `<BUNDLE_HOME>/server/bin` folder.
If you're using the xref:tomcat-bundle.adoc[`tomcat bundle`] installation, you need to edit the `setenv` script provided with Bonita and located in `<BUNDLE_HOME>/setup/tomcat-templates` folder.

=== For Linux and Mac OS

* Edit this file: `<BUNDLE_HOME>/setup/tomcat-templates/setenv.sh`
* Locate the line that starts: `#SECURITY_OPTS`
* Uncomment this line, i.e. remove the # sign and set property value to: `+${CATALINA_HOME}/conf/jaas.cfg+`
* Locate the line that starts: `CATALINA_OPTS=`
* Add the tag `+${SECURITY_OPTS}+` after the tag `+${PLATFORM_SETUP}+`
* Push into database the changes: `./setup.sh push`

==== For Windows

* Edit this file: `<BUNDLE_HOME>/setup/tomcat-templates/setenv.bat`
* Locate the line that starts: `rem set SECURITY_OPTS`
* Uncomment it, i.e. remove "rem" keyword and set property value to: `%CATALINA_HOME%\conf\jaas.cfg`
* Locate the line that starts: `set CATALINA_OPTS=`
* Add the tag `%SECURITY_OPTS%` after the tag `%PLATFORM_SETUP%`
* Push into database the changes: `.\setup.bat push`

== Configure SSL (optional)

It's possible to allow authentication to ldap over SSL (ldaps).
First of all in the `<BUNDLE_HOME>/server/conf/jaas.cfg` file you should use ldaps FQDN and port, and set `useSSL` to true.
Then the following operations are required:

=== Create the keystore

You should have a certificate from the ldaps server (i.e. certificate.pem).

* Create the following folder:  `+${CATALINA_HOME}/conf/SSL+`.
* Put the certificate.pem file into this folder.
* Move to this directory and create a keystore with the keytool command (keytool is part of JDK):
+
[source,bash]
----
keytool -importcert  -alias yourAlisaName -file certificate.pem -keystore certificateStore.jks
----

* Choose a password for your keystore (here we call it keyStorePassword)
* Answer to the questions and at the end verify that the truststore (i.e. certificateStore.jks) has been created correctly

=== Share the truststore with Tomcat

* Edit this file: `<BUNDLE_HOME>/setup/tomcat-templates/setenv.sh` (Linux) or `<BUNDLE_HOME>/setup/tomcat-templates/setenv.bat` (Windows)
* Add the following line after the one that starts with set `SECURITY_OPTS`:
+
----
Linux: SSL_OPTS="-Djavax.net.ssl.trustStore=pathToTruststore -Djavax.net.ssl.trustStorePassword=keyStorePassword"
Windows: set SSL_OPTS="-Djavax.net.ssl.trustStore=pathToTruststore -Djavax.net.ssl.trustStorePassword=keyStorePassword"
----

* Replace the `pathToTruststore` and `keyStorePassword` with the proper values
* Locate the line that starts with CATALINA_OPTS (Linux) or set CATALINA_OPTS (Windows)
* (Linux) Add the tag `+${SSL_OPTS}+` after the tag `+${INCIDENT_LOG_DIR}+`
* (Windows) Add the tag `%SSL_OPTS%` after the tag `%INCIDENT_LOG_DIR%`

[#examples]

== JAAS configuration files examples

NOTE: Remember to remove the debug flag for production.

NOTE: These examples use the JAAS standard syntax (as used by Tomcat).

=== Active Directory

==== Search allowed for all users

In this example, the user name is john.smith:

----
BonitaAuthentication-1 {
  com.sun.security.auth.module.LdapLoginModule sufficient
  userProvider="ldap://localhost:389/CN=Users,DC=MyDomain,DC=com"
  userFilter="(&(objectClass=user)(userPrincipalName={USERNAME}@mydomain.com))"
  authIdentity="{USERNAME}@mydomain.com"
  debug=true
  useSSL=false;
};
----

In this example, the user name is `john.smith@mydomain.com`:

----
BonitaAuthentication-1 {
  com.sun.security.auth.module.LdapLoginModule sufficient
  userProvider="ldap://localhost:389/CN=Users,DC=MyDomain,DC=com"
  userFilter="(&(objectClass=user)(userPrincipalName={USERNAME}))"
  authIdentity="{USERNAME}"
  debug=true
  useSSL=false;
};
----

==== Search allowed only for a technical users

In this example, the user name is john.smith:

----
BonitaAuthentication-1 {
  com.sun.security.auth.module.LdapLoginModule sufficient
  userProvider="ldap://localhost:389/CN=Users,DC=MyDomain,DC=com"
  userFilter="(&(objectClass=user)(userPrincipalName={USERNAME}@mydomain.com))"
  tryFirstPass=true
  java.naming.security.principal="technical.user@mydomain.com"
  java.naming.security.credentials="technical_user_password"
  debug=true
  useSSL=false;
};
----

=== Other LDAP servers

.Build the user DN using the user name
----
BonitaAuthentication-1 {
  com.sun.security.auth.module.LdapLoginModule sufficient
  userProvider="ldap://localhost:389"
  authIdentity="uid={USERNAME},ou=grenoble,dc=example,dc=com"
  debug=true
  useSSL=false;
};
----

== Known limitations

The Active Directory configuration has been tested in single domain configuration. If you a running with multiple domains it's likely that the user will have to type a username including domain name when logging in.

== Troubleshooting

If necessary, you can edit your configuration to have additional logs (please note, a server restart will be required for the new configuration to be taken into account, though):

* Enable JAAS debug mode by editing your JAAS configuration file (e.g. `<TOMCAT_HOME>/server/conf/jaas.cfg`) and adding the following line: `debug=true`
* Enable java's https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/ReadDebug.html[SSL/TLS connection debug mode] by editing your application server's environment setting file (e.g. `<TOMCAT_HOME>/setup/tomcat-templates/setenv.sh(.bat)`) and adding the following system property to the java options: `-Djavax.net.debug=all`

=== Common error examples

****
*Symptom:* You see the following error in your logs:

[source,log]
----
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed:sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
----

*Possible Solutions:*

* Make sure you have imported the public certificate of the LDAP server into the truststore being used by the application server.
* Make sure the imported certificate has not expired.
* Make sure the certificate has been imported into the correct truststore:
 ** Check whether your application is using a custom truststore. For example, if `-Djavax.net.ssl.trustStore` has been configured, it will override the location of the default truststore.
 ** Else, the default truststore of the JVM is used, namely (beware: you may have multiple JRE/JDKs): `<JAVA_HOME>/jre/lib/security/cacerts`
(In order to add your SSL server's certificate into this truststore, the default password is: `changeit`).
* Check if your Anti-Virus tool has "SSL Scanning" blocking SSL/TLS. If it does, disable this feature or set exceptions for the target addresses.
* Make sure that your LDAP server is indeed reachable at the SSL port. You may try connecting to the LDAPS URL with an external ldap client tool (e.g. ldapsearch).
****

****
*Symptom:* You see the following error in your logs (it may sometimes be necessary to have set the `-Djavax.net.debug=all` system property before hand):

[source,log]
----
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative DNS name matching my.ldap.server found.
----

*Problem:* The enpoint identification (i.e. hostname verification) check is failing to validate the LDAP server's certificate because its subject does not match the hostname specified in the LDAPS url.

*Possible Solutions:*

* Make sure the server name used in the _ldaps://_ address of the LdapLoginModule's _userProvider_ matches that of the LDAP server's certificate.
* Have the LDAP server use a certificate with a subject (or at least a subject alternative name) that matches the server's domain name (so the one used in the ldaps:// addresse of the LdapLoginModule).
* Disable the endpoint identification (note that the endpoint identification is https://www.oracle.com/technetwork/java/javase/8u181-relnotes-4479407.html[enabled by default since java version 1.8.0_181]) in Bonita's application server by adding the following system property to the java options in your application server's environment setting file (e.g. `<TOMCAT_HOME>/setup/tomcat-templates/setenv.sh(.bat)`): `-Dcom.sun.jndi.ldap.object.disableEndpointIdentification=true`
****

****
*Symptom:* The following stacktrace appears in the Bonita server log:

[source,log]
----
javax.security.auth.login.FailedLoginException: Cannot bind to LDAP server
at com.sun.security.auth.module.LdapLoginModule.attemptAuthentication(LdapLoginModule.java:817)
at com.sun.security.auth.module.LdapLoginModule.login(LdapLoginModule.java:565)
...
Caused by: javax.naming.AuthenticationException: [LDAP: error code 49 - 80090308: LdapErr: DSID-XXXXXXXX, comment: AcceptSecurityContext error, data YYY, vZZZZ..]
----

*Problem:* As per LDAP standard, error code 49 means: "Invalid credentials" (i.e. the user credentials presented in the bind operation are not valid). +
When connecting to Active Directory, this error may appear for multiple reasons. The AD-specific error code is the one (`YYY`) after "data" in the error message:

[%autowidth,frame=all,grid=all]
|===
| AD error code | Meaning

| 525
| user not found

| 52e
| invalid credentials

| 530
| not permitted to logon at this time

| 531
| not permitted to logon at this workstation

| 532
| password expired

| 533
| account disabled

| 701
| account expired

| 773
| user must reset password

| 775
| user account locked
|===

*Possible Solutions:*

* *52e*: invalid credentials +
`LDAP: error code 49 - 80090308: LdapErr: DSID-0C0903A9, comment: AcceptSecurityContext error, data 52e, v1db1`. +
This error code can have several root causes:
 ** The user doesn't exist in AD: in the JAAS configuration, verify the user filter and validate it using a tool such as Apache Directory Studio.
 ** The username doesn't include the domain name: in the JAAS configuration, make sure that the `authIdentity` value includes the domain name.
 ** The user password provided is not correct.

* *531*: not permitted to logon at this workstation +
`LDAP: error code 49 - 80090308: LdapErr: DSID-0C090446, comment: AcceptSecurityContext error, data 531, v4563` +
This problem is related to the configuration of Workstation Restrictions in Microsoft Active Directory. This problem does not happen when Workstation Restrictions is disabled on Active Directory. +
In order to get over the error, there seem to be two possible solutions (to be handled by your Active Directory administrator):
 ** Disable the workstations restrictions in AD
 ** Add the hostname of the Active Directory server to "userWorkstations" attribute. (Note: If it is an AD cluster, you need to add each AD server.)
****
