I’m looking for LDAP authentication for the Bonita BPM version 6.5 community (non-subscription) edition. Has anyone been able to do this and can steer me to something that will work?
Have a look at
http://community.bonitasoft.com/answers/ldap-bonita-65#node-24721
Which will guide you to
http://ironman.darthgibus.net/?p=57 (for 5.x but is an indicator)
regards
I was able to get BonitaSoft community edition to talk to our college’s Active Directory server. The code isn’t very clean and I am a VERY much a newbie at java coding. However, here’s what I did:
-
I complied the 6.3.8 version using the instructions mentioned in this thread.
-
I couldn’t successfully compile all the 6.3.8 code for some reason. The portal stuff failed. But the auth code is in the bonita-engine section. Since I was able to get that far, I proceeded to add some LDAP code. I added it to AuthenticationServiceImpl.java, located in the BonitaBPM-build-6-3-8/bonita-engine/services/bonita-authentication/bonita-authentication-api-impl/src/main/java/org/bonitasoft/engine/authentication/impl directory of the source code. The reason I did not write my own java class was because A) I’m a newbie and wanted to write as little as possible, and B) I wanted the ldap code to “fall through” to the standard authentication, in case someone was logging in with an admin, technical, or portal admin account. PLEASE NOTE that this setup requires that the end users in your Bonita organization have usernames that exactly match the SAMAccountname in Active Directory.
-
The easiest-to-use ldap api for java I could find was the Apache Directory LDAP API. So I first wrote my code in Eclipse to ensure that I could talk to my Active Directory server. Since there isn’t an easy “bind as user” function in this api, I first bind as a bind user, then search for the end-users’s name as passed into AuthenticationServiceImpl. The search returns a complete DN for that user, and I bind using that complete DN and the password passed into AuthenticationServiceImpl.
-
I modified the pom.xml located in BonitaBPM-build-6-3-8/bonita-engine/services/bonita-authentication/bonita-authentication-api-impl to include a dependency for the apache ldap API:
org.apache.directory.api
api-all
1.0.0-M30
-
I re-compiled the stuff in bonita-engine until I got everything to work right. I then dropped the new .jar file found in BonitaBPM-build-6-3-8/bonita-engine/bpm/bonita-server/target/ into /opt/BonitaBPMCommunity-6.3.8-Tomcat-6.0.37/webapps/bonita/WEB-INF/lib/, along with all the apache ldap .jar files.
-
Here is AuthenticationServiceImpl, as modified by me:
/**
- Copyright (C) 2011-2012, 2014 BonitaSoft S.A.
- BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble
- This library is free software; you can redistribute it and/or modify it under the terms
- of the GNU Lesser General Public License as published by the Free Software Foundation
- version 2.1 of the License.
- This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public License along with this
- program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
- Floor, Boston, MA 02110-1301, USA.
**/
package org.bonitasoft.engine.authentication.impl;
import java.io.Serializable;
import java.util.Map;
import org.bonitasoft.engine.authentication.AuthenticationConstants;
import org.bonitasoft.engine.authentication.GenericAuthenticationService;
import org.bonitasoft.engine.commons.LogUtil;
import org.bonitasoft.engine.identity.IdentityService;
import org.bonitasoft.engine.identity.SUserNotFoundException;
import org.bonitasoft.engine.identity.model.SUser;
import org.bonitasoft.engine.log.technical.TechnicalLogSeverity;
import org.bonitasoft.engine.log.technical.TechnicalLoggerService;
/**
- Apache LDAP-related imports:
*/
import java.io.IOException;
import org.apache.directory.api.ldap.model.cursor.CursorException;
import org.apache.directory.api.ldap.model.cursor.SearchCursor;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.api.ldap.model.message.Response;
import org.apache.directory.api.ldap.model.message.SearchRequest;
import org.apache.directory.api.ldap.model.message.SearchRequestImpl;
import org.apache.directory.api.ldap.model.message.SearchResultEntry;
import org.apache.directory.api.ldap.model.message.SearchScope;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.ldap.client.api.LdapConnection;
import org.apache.directory.ldap.client.api.LdapNetworkConnection;
/**
-
@author Elias Ricken de Medeiros
-
@author Matthieu Chaffotte
-
@author Hongwen Zang
-
@author Julien Reboul
@author Celine Souchet
*/
public class AuthenticationServiceImpl implements GenericAuthenticationService {private final IdentityService identityService;
private final TechnicalLoggerService logger;
private final String lDAPServer = “put.your.hostname.here”;
private final int lDAPPort = 389;
private final String bindString = “CN=put,OU=your,OU=full,OU=bind,DC=user,DC=dn,DC=here”;
private final String bindPassword = “binduserpasswordgoeshere”;
private final String baseDN = “dc=enduser, dc=base, dc=dn”;
private String userBindDN = “”;public AuthenticationServiceImpl(final IdentityService identityService, final TechnicalLoggerService logger) {
this.identityService = identityService;
this.logger = logger;
}/**
@see org.bonitasoft.engine.authentication.GenericAuthenticationService#checkUserCredentials(java.util.Map)
/
@Override
public String checkUserCredentials(Map<String, Serializable> credentials) {
final String methodName = “checkUserCredentials”;
try {
final String password = String.valueOf(credentials.get(AuthenticationConstants.BASIC_PASSWORD));
final String userName = String.valueOf(credentials.get(AuthenticationConstants.BASIC_USERNAME));
final SUser user = identityService.getUserByUserName(userName);
if (logger.isLoggable(this.getClass(), TechnicalLogSeverity.TRACE)) {
logger.log(this.getClass(), TechnicalLogSeverity.TRACE, LogUtil.getLogBeforeMethod(this.getClass(), methodName));
}
/*
* Ldap code down to the END OF LDAP SECTION is
* Copyright (C) 2015 Snow College
* 150 E. College Ave., Ephraim, UT
* This ldap code is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation
* version 2.1 of the License.
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301, USA.
/
/
* Check via ldap first. If it can’t be contacted, or it fails, check via static database
*/LdapConnection connection = new LdapNetworkConnection(lDAPServer, lDAPPort); try { connection.bind(bindString, bindPassword); SearchRequest req = new SearchRequestImpl(); req.setScope( SearchScope.SUBTREE ); req.addAttributes( "dn" ); req.setTimeLimit( 0 ); req.setBase( new Dn( baseDN ) ); req.setFilter( "(samAccountName=" + userName + ")"); SearchCursor searchCursor = connection.search(req); try { searchCursor.next(); } catch (CursorException c){ System.err.println("failed to search the LDAP cursor for username: " + userName); } // looks like we found the username. Try binding with the provided username and password. try { Response response = searchCursor.get(); // process the SearchResultEntry if ( response instanceof SearchResultEntry ) { org.apache.directory.api.ldap.model.entry.Entry resultEntry = ( ( SearchResultEntry ) response ).getEntry(); userBindDN = resultEntry.toString(); userBindDN = userBindDN.replace("Entry\n",""); userBindDN = userBindDN.replace("dn:",""); userBindDN = userBindDN.trim(); LdapConnection userConnection = new LdapNetworkConnection( lDAPServer, lDAPPort ); try { System.err.println("connecting as: "+ userBindDN); userConnection.bind(userBindDN, password); System.err.println("connection succeeded as " + userBindDN); } catch (LdapException e2) { System.err.println("end user failed to connect as " + userBindDN); } try { userConnection.close(); } catch (IOException e) { System.err.println("unable to close LDAP user connection userConnection for userName: " + userName); } return userName; } } catch (CursorException c) { System.err.println("failed to get a searchCursor for userName: "+userName); } } catch (LdapException e) { System.err.println("failed to bind as the bind user"); }
/**
* END OF LDAP SECTION. Even if an authentication wasn’t successful,
* we still need to try to connect as a non-LDAP (i.e., admin) user:
*/if (identityService.chechCredentials(user, password)) {
if (logger.isLoggable(this.getClass(), TechnicalLogSeverity.TRACE)) {
logger.log(this.getClass(), TechnicalLogSeverity.TRACE, LogUtil.getLogAfterMethod(this.getClass(), methodName));
}
return userName;
}
if (logger.isLoggable(this.getClass(), TechnicalLogSeverity.TRACE)) {
logger.log(this.getClass(), TechnicalLogSeverity.TRACE, LogUtil.getLogAfterMethod(this.getClass(), methodName));
}
} catch (final SUserNotFoundException sunfe) {
if (logger.isLoggable(this.getClass(), TechnicalLogSeverity.TRACE)) {
logger.log(this.getClass(), TechnicalLogSeverity.TRACE, LogUtil.getLogOnExceptionMethod(this.getClass(), methodName, sunfe));
}
}
return null;
}
}
- The code could be improved by making it refer to a .conf file to get the ldap host, the ldap bind user name and password, bind dn, end user dn suffix, etc.
the very good developper
Thanks, Sean. I’m going to try to build something. I think that your instructions at: http://documentation.bonitasoft.com/building-bonita-bpm-source-files-0 for version 6.5 may be need a few corrections:
-
There are lots of git libraries that that appear to have moved away from 1.0.x to 6.1.x
-
The pom.xml’s bonita-integration-tests/bonita-test-utils/bonita-server-test-utils/pom.xml and bonita-integration-tests/pom.xml make reference to jdbc connectors for microsoft and oracle. However, you must download and install them into a local repository, and then make reference to the local repository as follows, in order to get bonita-engine to build:
com.oracle
ojdb6
11.2.0
system
/…your user path…/.m2/repository/com/oracle/ojdbc6/11.2.0/ojdbc6-11.2.0.jar
com.microsoft.jdbc
sqlserver
4.0.2206.100
system
/…your user path…/.m2/repository/com/microsoft/jdbc/sqlserver/4.0.2206.100/sqlserver-4.0.2206.100.jar
I’m a novice at java, but I will continue to post info as I proceed.
Hi, I plan to connect my LDAP to Bonita as well and I’m going to start coding my own connector. Does it work the same for this bpmn sofware version (6.5.2) of are there specific implementation details for each release?
Jordan, I don’t know for sure – I’m going to attempt to write one for 6.5.2. Someone from BonitaSoft would have to answer that. My guess is that they are same for all 6.x versions.
All I can say is WOW, what a wonderful piece of community spirit.
Thank you Phil, for trying it, for getting it working, and finally, for publishing it.
I like you am not a JAVA programmer but have learnt along the way…
In the same spirit I offer the following (Groovy) code that could be modified to easily refer to a properties file for the configuration etc.
In it I also use a include from jasypt.org for encrypted properties file. Please see their website for more details.
The config file must be placed in directory “/webapps/myCompany/properties/” and would be formatted as follows:
ldap.lDAPServer=put.your.hostname.here
ldap.lDAPPort=389
ldap.bindString=CN=put,OU=your,OU=full,OU=bind,DC=user,DC=dn,DC=here
ldap.bindPassword=binduserpasswordgoeshere
ldap.baseDN=dc=enduser, dc=base, dc=dn
ldap.userBindDN=
an encrypted version of the above file could look like:
ldap.lDAPServer=ENC(sknva;uhga;kjgbs41fg)
ldap.lDAPPort=ENC(fghfsdbf7575)
ldap.bindString=ENC(7686fkrhfgis7fgsdhg7htlsghlsrs5ths8yghgliitg78hslghhgl578thsl)
ldap.bindPassword=ENC(zkbfkvuygr7gha7tyafbhdflbas78tyaufh2983208rz.uziluhgzr7gh)
ldap.baseDN=ENC(zjhbvzkvbz7vl;a84th;afubvzlbhtg84h;t;s;uvhbz;rg)
ldap.userBindDN=ENC(sdfdfs)
The Groovy properties code, which is really a subroutine I use to pull text from language files (if you add this to Development->Manage Groovy Scripts you can use it is your scripts to get get from multiple properties files using String myString = myGroovyText.getProperty(“myTextKey”);), **must **be modified to work in the ldap code above:
/**
-
Copyright (C) 2015 Gubernare Ltd.,
-
London, United Kingdom
-
This code is free software; you can redistribute it and/or modify it under the terms
-
of the GNU Lesser General Public License as published by the Free Software Foundation
-
version 2.1 of the License.
-
This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
See the GNU Lesser General Public License for more details.
-
You should have received a copy of the GNU Lesser General Public License along with this
-
program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
-
Floor, Boston, MA 02110-1301, USA.
*/import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.properties.EncryptableProperties;import java.util.regex.Pattern;
import java.io.File;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;def static getPropertyString(String key){
Logger logger= Logger.getLogger(“org.bonitasoft”);
int d = 0;
boolean debug = false; //change to true for logging//preample code - preparation
//get catalina home - can sometimes NOT be set so work it through…
def thisModule = " getPropertyString: “;
String catalinaHome = System.getProperty(“CATALINA_HOME”);
String catalinaPath = System.getProperty(“CATALINA_PATH”);
if(debug){d++; logger.severe(d+thisModule+”: CATALINA_HOME: “+catalinaHome);}
if(debug){d++; logger.severe(d+thisModule+”: CATALINA_PATH: "+catalinaPath);}if(debug){d++; logger.severe(d+thisModule+“: if (catalinaHome == null && catalinaPath == null){”);}
if (catalinaHome == null && catalinaPath == null){if(debug){d++; logger.severe(d+thisModule+": CATASTOPHIC ERROR CATALINA Not found: ");} String strClassPath = System.getProperty("java.class.path"); if(debug){d++; logger.severe(d+thisModule+": strClassPath: "+strClassPath);} //20150201 new version to get to webapps - START if (strClassPath.indexOf(";") != 0 ){ //strClassPath is concatenated and must be reduced to one dir if(debug){d++; logger.severe(d+thisModule+"strClassPath - is concatenated?");} String[] catHome = strClassPath.split(Pattern.quote(";")); for (String singleCatHome : catHome){ if(debug){d++; logger.severe(d+thisModule+"singleCatHome : "+singleCatHome);} if (singleCatHome.indexOf("bin") != 0){ String noBin = singleCatHome.substring(0, singleCatHome.indexOf("bin")); File f = new File(noBin+"webapps"); if(debug){d++; logger.severe(d+thisModule+"noBin (webapps): "+noBin+"webapps");} if (f.exists() && f.isDirectory()) { if(debug){d++; logger.severe(d+thisModule+"singleCatHome Found webapps : "+noBin+"webapps");} catalinaHome = noBin; break; } } } } else{ //strClassPath is not concated and is OK? } //20150201 new version to get to webapps - END
}
else if (catalinaHome == null && catalinaPath != null){
if(debug){d++; logger.severe(d+thisModule+“Set Home = Path”);}
catalinaHome = catalinaPath;
}
else { // we have a CatalinaHome - but is it concatenated?
if(debug){d++; logger.severe(d+thisModule+“CatalinaHome - but is it concatenated?”);}if (catalinaHome.indexOf(";") != 0 ){ //catalinaHome is concatenated and must be reduced to one dir if(debug){d++; logger.severe(d+thisModule+"CatalinaHome - is concatenated?");} String[] catHome = catalinaHome.split(Pattern.quote(";")); for (String singleCatHome : catHome){ if(debug){d++; logger.severe(d+thisModule+"CatalinaHome : "+singleCatHome);} if (singleCatHome.indexOf("webapps") != 0){ if(debug){d++; logger.severe(d+thisModule+"CatalinaHome Found webapps : "+singleCatHome);} catalinaHome = singleCatHome; break; } } } else{ //catalinaHome is not concated and is OK }
}
//set encryption
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
encryptor.setPassword(“myPassword”);
Properties propsEnc = new EncryptableProperties(encryptor);//set locale - defaults
String defaultLang = “en”;
String defCountry = “US”;Locale locale = Locale.getDefault();
String lang = locale.getLanguage();
String country = locale.getCountry();String propertiesDir = “/webapps/myCompany/properties/”;
String fileNameEncrypted = “fileNameEncrypted_”;
String fileNameNotEncrypted = “fileNameNotEncrypted_”;String propertiesFileEncLocale = propertiesDir+fileNameEncrypted+lang+““+country+”.properties";
String propertiesFileEncDefLoc = propertiesDir+fileNameEncrypted+defaultLang+"”+defCountry+“.properties”;
String propertiesFileEnc = propertiesDir+fileNameEncrypted+“.properties”;
String propertiesFileLocale = propertiesDir+fileNameNotEncrypted+lang+““+country+”.properties";
String propertiesFileDefLoc = propertiesDir+fileNameNotEncrypted+defaultLang+"”+defCountry+“.properties”;
String propertiesFile = propertiesDir+fileNameNotEncrypted+“.properties”;try{ //1
try{ //encrypted locale
if(debug){d++; logger.severe(d+thisModule+“: try: “+catalinaHome + propertiesFileEncLocale);}
propsEnc.load(new FileInputStream(new File(catalinaHome + propertiesFileEncLocale)));
}
catch(Exception e0){
try{ //encrypted default
if(debug){d++; logger.severe(d+thisModule+”: try: “+catalinaHome + propertiesFileEncDefLoc);}
propsEnc.load(new FileInputStream(new File(catalinaHome + propertiesFileEncDefLoc)));
}
catch(Exception e1){
try{ //encrypted
if(debug){d++; logger.severe(d+thisModule+”: try: “+catalinaHome + propertiesFileEnc);}
propsEnc.load(new FileInputStream(new File(catalinaHome + propertiesFileEnc)));
}
catch(Exception e2){
try{ // locale
if(debug){d++; logger.severe(d+thisModule+”: try: “+catalinaHome + propertiesFileLocale);}
propsEnc.load(new FileInputStream(new File(catalinaHome + propertiesFileLocale)));
}
catch(Exception e3){
try{ // locale
if(debug){d++; logger.severe(d+thisModule+”: try: “+catalinaHome + propertiesFileDefLoc);}
propsEnc.load(new FileInputStream(new File(catalinaHome + propertiesFileDefLoc)));
}
catch(Exception e4){
try{ // default
if(debug){d++; logger.severe(d+thisModule+”: try: “+catalinaHome + propertiesFile);}
propsEnc.load(new FileInputStream(new File(catalinaHome + propertiesFile)));
}
catch(Exception e5){
logger.severe(thisModule+”: fileNameNotEncrypted.properties Error (E5-0): File Not Found: " + e5.toString());
logger.severe(thisModule+”: fileNameNotEncrypted.properties Error (E5-1): File Not Found: " + +catalinaHome + propertiesFile.toString());
return “Error (e5): fileNameNotEncrypted.properties Error (E5): File Not Found”;
}
}
}
}
}
}try{ if(debug){d++; logger.severe(d+thisModule+": Key: "+key);} return propsEnc.getProperty(key); } catch(Exception ex3){ logger.severe(thisModule+": Error (ex6): Message Not Found: "+key); return "Error (ex6): Message Not Found: "+key; }
}
catch(Exception ex0){
logger.severe(thisModule+": Fatal Error (EX0): File Not Found: " + ex0.toString());
return ": Fatal Error (EX0): File Not Found: ";
}
Hope it helps,
regards
Seán
Thanks Seán. I’ll look at it and see what I can do. It may take me a bit of time to re-post a version that uses a config file – I’m still a slow java coder
Hi,
It would be nice if you could post your project to Github
Regards