/* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.activiti.ldap; import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; import javax.naming.directory.InitialDirContext; import javax.naming.spi.InitialContextFactory; import org.activiti.engine.cfg.AbstractProcessEngineConfigurator; import org.activiti.engine.cfg.ProcessEngineConfigurator; import org.activiti.engine.identity.Group; import org.activiti.engine.identity.User; import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.activiti.engine.runtime.Clock; import org.activiti.engine.runtime.ClockReader; /** * A {@link ProcessEngineConfigurator} that integrates a LDAP system with the Activiti process engine. * The LDAP system will be consulted primarily for getting user information and in particular * for fetching groups of a user. * * This class is extensible and many methods can be overriden when the default behavior * is not fitting your use case. * * Check the docs (speficifally the setters) to see how this class can be tweaked. * * @author Joram Barrez */ public class LDAPConfigurator extends AbstractProcessEngineConfigurator { /* Server connection params */ protected String server; protected int port; protected String user; protected String password; protected String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory"; protected String securityAuthentication = "simple"; // For parameters like connection pooling settings, etc. protected Map<String, String> customConnectionParameters = new HashMap<String, String>(); // Query configuration protected String baseDn; protected String userBaseDn; protected String groupBaseDn; protected int searchTimeLimit = 0; // Default '0' == wait forever protected String queryUserByUserId; protected String queryGroupsForUser; protected String queryUserByFullNameLike; // Attribute names protected String userIdAttribute; protected String userFirstNameAttribute; protected String userLastNameAttribute; protected String userEmailAttribute; protected String groupIdAttribute; protected String groupNameAttribute; protected String groupTypeAttribute; // Pluggable factories protected LDAPUserManagerFactory ldapUserManagerFactory; protected LDAPGroupManagerFactory ldapGroupManagerFactory; protected LDAPMembershipManagerFactory ldapMembershipManagerFactory; // Pluggable query helper bean protected LDAPQueryBuilder ldapQueryBuilder = new LDAPQueryBuilder(); // Group caching protected int groupCacheSize = -1; protected long groupCacheExpirationTime = 3600000L; // default: one hour // Cache clock private Clock clock; public void beforeInit(ProcessEngineConfigurationImpl processEngineConfiguration) { // Nothing to do } public void configure(ProcessEngineConfigurationImpl processEngineConfiguration) { clock = processEngineConfiguration.getClock(); LDAPUserManagerFactory ldapUserManagerFactory = getLdapUserManagerFactory(); processEngineConfiguration.getSessionFactories().put(ldapUserManagerFactory.getSessionType(), ldapUserManagerFactory); LDAPGroupManagerFactory ldapGroupManagerFactory = getLdapGroupManagerFactory(clock); processEngineConfiguration.getSessionFactories().put(ldapGroupManagerFactory.getSessionType(), ldapGroupManagerFactory); } // Can be overwritten for custom factories ////////////////////////////////////////////////// protected LDAPUserManagerFactory getLdapUserManagerFactory() { if (this.ldapUserManagerFactory != null) { this.ldapUserManagerFactory.setLdapConfigurator(this); return this.ldapUserManagerFactory; } return new LDAPUserManagerFactory(this); } protected LDAPGroupManagerFactory getLdapGroupManagerFactory(ClockReader clockReader) { if (this.ldapGroupManagerFactory != null) { this.ldapGroupManagerFactory.setLdapConfigurator(this); return this.ldapGroupManagerFactory; } return new LDAPGroupManagerFactory(this, clockReader); } protected LDAPMembershipManagerFactory getLdapMembershipManagerFactory() { if (this.ldapMembershipManagerFactory != null) { this.ldapMembershipManagerFactory.setLdapConfigurator(this); } return new LDAPMembershipManagerFactory(this); } // Getters and Setters ////////////////////////////////////////////////// public String getServer() { return server; } /** * The server on which the LDAP system can be reached. * For example 'ldap://localhost:33389'. */ public void setServer(String server) { this.server = server; } public int getPort() { return port; } /** * The port on which the LDAP system is running. */ public void setPort(int port) { this.port = port; } public String getUser() { return user; } /** * The user id that is used to connect to the LDAP system. */ public void setUser(String user) { this.user = user; } public String getPassword() { return password; } /** * The password that is used to connect to the LDAP system. */ public void setPassword(String password) { this.password = password; } public String getInitialContextFactory() { return initialContextFactory; } /** * The {@link InitialContextFactory} name used to connect to the LDAP system. * * By default set to 'com.sun.jndi.ldap.LdapCtxFactory'. */ public void setInitialContextFactory(String initialContextFactory) { this.initialContextFactory = initialContextFactory; } public String getSecurityAuthentication() { return securityAuthentication; } /** * The value that is used for the 'java.naming.security.authentication' property * used to connect to the LDAP system. * * By default set to 'simple'. */ public void setSecurityAuthentication(String securityAuthentication) { this.securityAuthentication = securityAuthentication; } public Map<String, String> getCustomConnectionParameters() { return customConnectionParameters; } /** * Allows to set all LDAP connection parameters which do not have a dedicated setter. * See for example http://docs.oracle.com/javase/tutorial/jndi/ldap/jndi.html for custom * properties. Such properties are for example to configure connection pooling, specific * security settings, etc. * * All the provided parameters will be provided when creating a {@link InitialDirContext}, * ie when a connection to the LDAP system is established. */ public void setCustomConnectionParameters(Map<String, String> customConnectionParameters) { this.customConnectionParameters = customConnectionParameters; } public String getBaseDn() { return baseDn; } /** * The base 'distinguished name' (DN) from which the searches for users and groups are started. * * Use {@link #setUserBaseDn(String)} or {@link #setGroupBaseDn(String)} when needing to * differentiate between user and group base DN. */ public void setBaseDn(String baseDn) { this.baseDn = baseDn; } public String getUserBaseDn() { return userBaseDn; } /** * The base 'distinguished name' (DN) from which the searches for users are started. */ public void setUserBaseDn(String userBaseDn) { this.userBaseDn = userBaseDn; } public String getGroupBaseDn() { return groupBaseDn; } /** * The base 'distinguished name' (DN) from which the searches for groups are started. */ public void setGroupBaseDn(String groupBaseDn) { this.groupBaseDn = groupBaseDn; } public int getSearchTimeLimit() { return searchTimeLimit; } /** * The timeout that is used when doing a search in LDAP. * By default set to '0', which means 'wait forever'. */ public void setSearchTimeLimit(int searchTimeLimit) { this.searchTimeLimit = searchTimeLimit; } public String getQueryUserByUserId() { return queryUserByUserId; } /** * The query that is executed when searching for a user by userId. * * For example: (&(objectClass=inetOrgPerson)(uid={0})) * * Here, all the objects in LDAP with the class 'inetOrgPerson' * and who have the matching 'uid' attribute value will be returned. * * As shown in the example, the user id is injected by the typical * {@link MessageFormat}, ie by using <i>{0}</i> * * If setting the query alone is insufficient for your specific * LDAP setup, you can alternatively plug in a different * {@link LDAPQueryBuilder}, which allows for more customization than * only the query. */ public void setQueryUserByUserId(String queryUserByUserId) { this.queryUserByUserId = queryUserByUserId; } public String getQueryGroupsForUser() { return queryGroupsForUser; } public String getQueryUserByFullNameLike() { return queryUserByFullNameLike; } /** * The query that is executed when searching for a user by full name. * * For example: (&(objectClass=inetOrgPerson)(|({0}=*{1}*)({2}={3}))) * * Here, all the objects in LDAP with the class 'inetOrgPerson' * and who have the matching first name or last name will be returned * * Several things will be injected in the expression: * {0} : the first name attribute * {1} : the search text * {2} : the last name attribute * {3} : the search text * * If setting the query alone is insufficient for your specific * LDAP setup, you can alternatively plug in a different * {@link LDAPQueryBuilder}, which allows for more customization than * only the query. */ public void setQueryUserByFullNameLike(String queryUserByFullNameLike) { this.queryUserByFullNameLike = queryUserByFullNameLike; } /** * The query that is executed when searching for the groups of a specific user. * * For example: (&(objectClass=groupOfUniqueNames)(uniqueMember={0})) * * Here, all the objects in LDAP with the class 'groupOfUniqueNames' * and where the provided DN is a 'uniqueMember' are returned. * * As shown in the example, the user id is injected by the typical * {@link MessageFormat}, ie by using <i>{0}</i> * * If setting the query alone is insufficient for your specific * LDAP setup, you can alternatively plug in a different * {@link LDAPQueryBuilder}, which allows for more customization than * only the query. */ public void setQueryGroupsForUser(String queryGroupsForUser) { this.queryGroupsForUser = queryGroupsForUser; } public String getUserIdAttribute() { return userIdAttribute; } /** * Name of the attribute that matches the user id. * * This property is used when looking for a {@link User} object * and the mapping between the LDAP object and the Activiti {@link User} object * is done. * * This property is optional and is only needed if searching for {@link User} * objects using the Activiti API. */ public void setUserIdAttribute(String userIdAttribute) { this.userIdAttribute = userIdAttribute; } public String getUserFirstNameAttribute() { return userFirstNameAttribute; } /** * Name of the attribute that matches the user first name. * * This property is used when looking for a {@link User} object * and the mapping between the LDAP object and the Activiti {@link User} object * is done. */ public void setUserFirstNameAttribute(String userFirstNameAttribute) { this.userFirstNameAttribute = userFirstNameAttribute; } public String getUserLastNameAttribute() { return userLastNameAttribute; } /** * Name of the attribute that matches the user last name. * * This property is used when looking for a {@link User} object * and the mapping between the LDAP object and the Activiti {@link User} object * is done. */ public void setUserLastNameAttribute(String userLastNameAttribute) { this.userLastNameAttribute = userLastNameAttribute; } public String getUserEmailAttribute() { return userEmailAttribute; } /** * Name of the attribute that matches the user email. * * This property is used when looking for a {@link User} object * and the mapping between the LDAP object and the Activiti {@link User} object * is done. */ public void setUserEmailAttribute(String userEmailAttribute) { this.userEmailAttribute = userEmailAttribute; } public String getGroupIdAttribute() { return groupIdAttribute; } /** * Name of the attribute that matches the group id. * * This property is used when looking for a {@link Group} object * and the mapping between the LDAP object and the Activiti {@link Group} object * is done. */ public void setGroupIdAttribute(String groupIdAttribute) { this.groupIdAttribute = groupIdAttribute; } public String getGroupNameAttribute() { return groupNameAttribute; } /** * Name of the attribute that matches the group name. * * This property is used when looking for a {@link Group} object * and the mapping between the LDAP object and the Activiti {@link Group} object * is done. */ public void setGroupNameAttribute(String groupNameAttribute) { this.groupNameAttribute = groupNameAttribute; } public String getGroupTypeAttribute() { return groupTypeAttribute; } /** * Name of the attribute that matches the group type. * * This property is used when looking for a {@link Group} object * and the mapping between the LDAP object and the Activiti {@link Group} object * is done. */ public void setGroupTypeAttribute(String groupTypeAttribute) { this.groupTypeAttribute = groupTypeAttribute; } /** * Set a custom implementation of the {@link LDAPUserManagerFactory} * if the default implementation is not suitable. */ public void setLdapUserManagerFactory(LDAPUserManagerFactory ldapUserManagerFactory) { this.ldapUserManagerFactory = ldapUserManagerFactory; } /** * Set a custom implementation of the {@link LDAPGroupManagerFactory} * if the default implementation is not suitable. */ public void setLdapGroupManagerFactory(LDAPGroupManagerFactory ldapGroupManagerFactory) { this.ldapGroupManagerFactory = ldapGroupManagerFactory; } /** * Set a custom implementation of the {@link LDAPMembershipManagerFactory} * if the default implementation is not suitable. */ public void setLdapMembershipManagerFactory(LDAPMembershipManagerFactory ldapMembershipManagerFactory) { this.ldapMembershipManagerFactory = ldapMembershipManagerFactory; } /** * Set a custom {@link LDAPQueryBuilder} if the default implementation is not suitable. * The {@link LDAPQueryBuilder} instance is used when the {@link LDAPUserManager} or * {@link LDAPGroupManager} does an actual query against the LDAP system. * * The default implementation uses the properties as set on this instance * such as {@link #setQueryGroupsForUser(String)} and {@link #setQueryUserByUserId(String)}. */ public LDAPQueryBuilder getLdapQueryBuilder() { return ldapQueryBuilder; } public void setLdapQueryBuilder(LDAPQueryBuilder ldapQueryBuilder) { this.ldapQueryBuilder = ldapQueryBuilder; } public int getGroupCacheSize() { return groupCacheSize; } /** * Allows to set the size of the {@link LDAPGroupCache}. * This is an LRU cache that caches groups for users and thus * avoids hitting the LDAP system each time the groups of * a user needs to be known. * * The cache will not be instantiated if the value is less then zero. * By default set to -1, so no caching is done. * * Note that the group cache is instantiated on the {@link LDAPGroupManagerFactory}. * As such, if you have a custom implementation of the {@link LDAPGroupManagerFactory}, * do not forget to add the group cache functionality. */ public void setGroupCacheSize(int groupCacheSize) { this.groupCacheSize = groupCacheSize; } public long getGroupCacheExpirationTime() { return groupCacheExpirationTime; } /** * Sets the expiration time of the {@link LDAPGroupCache} in milliseconds. * When groups for a specific user are fetched, and if the group cache exists (see {@link #setGroupCacheSize(int)}), * the groups will be stored in this cache for the time set in this property. * ie. when the groups were fetched at 00:00 and the expiration time is 30 mins, * any fetch of the groups for that user after 00:30 will not come from the cache, but do * a fetch again from the LDAP system. Likewise, everything group fetch for that user done * between 00:00 - 00:30 will come from the cache. * * By default set to one hour. */ public void setGroupCacheExpirationTime(long groupCacheExpirationTime) { this.groupCacheExpirationTime = groupCacheExpirationTime; } }