Hi,

I recently started using LSC and I'm having a problem with commas in DNs. I'm 
syncing from Active Directory 2008 R2 to openLDAP 2.4 and would like to use the 
CN of my users as the first part of the DN. But the problem is that all CNs in 
the Active Directory are in the format "Surname, Forename", so they contain a 
comma.

A simple DN configuration like the following doesn't work at all:
<mainIdentifier>"cn=" + srcBean.getDatasetFirstValueById("cn") + 
",ou=AD,ou=people,dc=bekoag,dc=local"</mainIdentifier>

It results in the following error for each user:

Feb 14 10:13:40 - ERROR - Error while synchronizing ID cn=Pallier, 
Manuel,ou=AD,ou=people,dc=bekoag,dc=local: java.lang.RuntimeException: 
org.apache.directory.shared.ldap.model.exception.LdapInvalidDnException: 
ERR_04196 Unexpected character ',' at position 19. Excpected start of 
attributeType descr.
# Fri Feb 14 10:13:40 CET 2014
dn: cn=Pallier, Manuel,ou=AD,ou=people,dc=bekoag,dc=local
changetype: add

So I've tried the following configuration:
<mainIdentifier>"cn=" + srcBean.getDatasetFirstValueById("cn").replace(",", 
"\\,") + ",ou=AD,ou=people,dc=bekoag,dc=local"</mainIdentifier>

This works for creating the users, but no changes are synchronized afterwards.
If I change the configuration to the following, everything, including updates, 
works fine:
<mainIdentifier>"uid=" + srcBean.getDatasetFirstValueById("sAMAccountName") + 
",ou=AD,ou=people,dc=bekoag,dc=local"</mainIdentifier>

I think this is because the DNs are read differently from Active Directory and 
openLDAP, which then prevents LSC from matching the entries for updates. Here 
is an example output from ldapsearch on Active Directory:

ldapsearch -x -H ldap://adserver -D 'username' -W '(cn=pallier*)'
Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# base <dc=bekoag,dc=local> (default) with scope subtree
# filter: (cn=pallier*)
# requesting: ALL
#
# Pallier\2C Manuel, MA_Produktion, Benutzer, INF, Graz, BEKOAG, bekoag.local
dn: CN=Pallier\, 
Manuel,OU=MA_Produktion,OU=Benutzer,OU=INF,OU=Graz,OU=BEKOAG,DC=bekoag,DC=local
cn: Pallier, Manuel

And here the openLDAP entry that was created by LSC:

ldapsearch -x -H ldap://localhost -D 'cn=Administrator,dc=bekoag,dc=local' -W 
'(cn=pallier*)'
Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# base <dc=bekoag,dc=local> (default) with scope subtree
# filter: (cn=pallier*)
# requesting: ALL
#
# Pallier\2C Manuel, AD, people, bekoag.local
dn: cn=Pallier\2C Manuel,ou=AD,ou=people,dc=bekoag,dc=local
cn: Pallier, Manuel

Note that ldapsearch on Active Directory shows "\2C" for the comma in the 
commented DN, but "\," in the actual DN attribute whereas ldapsearch on 
openLDAP shows "\2C" for both.

If someone has an idea how to solve that problem (other than not using CN in 
the DN) any help would be greatly appreciated.
I've attached my whole configuration file for reference (in the working state 
with uid in the DN, but the problematic mainIdentifier-Tag is also there, just 
commented out).

<?xml version="1.0" ?>
<!-- 
	In the following file, comments are describing each node. Elements are
	referenced through XPath expression, whereas attributes are prefixed with
	'@'

	//lsc Root node of the XML configuration file
	@xmlns XML Schema validation is not ready yet (Reserved for futur use)
	@id optional, added by XML API
	@revision mandatory, used by the Web Administration Interface to version
				this file
	 -->
<lsc xmlns="http://lsc-project.org/XSD/lsc-core-2.0.xsd"; revision="0">

<!--  ./connections Connections list node, must contain at least two connections -->
 
  <connections>

<!--  
	./connection Connection node, include definition of the required parameters.
					Depending on the connection type, properties vary.
		   			Existing class type are : ldapConnection, databaseConnnection
		   			Plugins also provides : nisConnection, jndiExecDstConnection
 -->
    <ldapConnection>
      <name>ad-src-conn</name>
<!--  ./url mandatory, the JNDI URL -->
      <url>ldap://adserver/dc=bekoag,dc=local</url>
<!--  ./username mandatory, the DN to bind with -->
      <username>some user dn</username>
<!--  ./password mandatory, credentials to bind with -->
      <password>some user password</password>
<!--  ./authentication mandatory, must contain either ANONYMOUS, SIMPLE, SASL, GSSAPI or DIGEST_MD5 -->
      <authentication>SIMPLE</authentication>
<!--  ./referral mandatory, must contain either IGNORE, THROUGH, THROW or FOLLOW -->
      <referral>IGNORE</referral>
<!--  ./derefAliases mandatory, must contain either NEVER, SEARCH, FIND, ALWAYS -->
      <derefAliases>NEVER</derefAliases>
<!--  ./version mandatory, must contain either VERSION_2, VERSION_3 -->
      <version>VERSION_3</version>
<!--  ./pageSize optional, specify the paged size when searching -->
      <pageSize>1000</pageSize>
<!--  ./factory mandatory, points to LDAP Context Factory, com.sun.jndi.ldap.LdapCtxFactory for a SUN JDK -->
      <factory>com.sun.jndi.ldap.LdapCtxFactory</factory>
<!--  ./tlsActivated optional, specify if SSL/TLS is activated to connect to the LDAP server -->
      <tlsActivated>false</tlsActivated>
    </ldapConnection>
    
    <ldapConnection>
      <name>openldap-dst-conn</name>
<!--  ./url mandatory, the JNDI URL -->
      <url>ldap://localhost/dc=bekoag,dc=local</url>
<!--  ./username mandatory, the DN to bind with -->
      <username>cn=Administrator,dc=bekoag,dc=local</username>
<!--  ./password mandatory, credentials to bind with -->
      <password>password</password>
<!--  ./authentication mandatory, must contain either ANONYMOUS, SIMPLE, SASL, GSSAPI or DIGEST_MD5 -->
      <authentication>SIMPLE</authentication>
<!--  ./referral mandatory, must contain either IGNORE, THROUGH, THROW or FOLLOW -->
      <referral>IGNORE</referral>
<!--  ./derefAliases mandatory, must contain either NEVER, SEARCH, FIND, ALWAYS -->
      <derefAliases>NEVER</derefAliases>
<!--  ./version mandatory, must contain either VERSION_2, VERSION_3 -->
      <version>VERSION_3</version>
<!--  ./pageSize optional, specify the paged size when searching -->
      <pageSize>-1</pageSize>
<!--  ./factory mandatory, points to LDAP Context Factory, com.sun.jndi.ldap.LdapCtxFactory for a SUN JDK -->
      <factory>com.sun.jndi.ldap.LdapCtxFactory</factory>
<!--  ./tlsActivated optional, specify if SSL/TLS is activated to connect to the LDAP server -->
      <tlsActivated>false</tlsActivated>
    </ldapConnection>
  </connections>

<!--  ./tasks Task list node, must contain at least one task -->
  <tasks>

<!--  ./task Task node, this is the main node, in which synchronization is defined -->
    <task>
<!--  ./name mandatory task node this is the main node, in which synchronization is defined -->
      <name>AdSyncTask</name>
<!--  ./bean optional bean node, default to org.lsc.beans.SimpleBean, define the pivot object used to store datasets and values -->
      <bean>org.lsc.beans.SimpleBean</bean>
<!--  ./sourceService mandatory node containing definition of the source service settings  
			possible builtin types are :
				databaseSourceService, ldapSourceService 
					Plugins also provides: syncreplSourceService, nisSourceService
		-->
      <ldapSourceService>
<!--	name: mandatory, it should contain any value that will uniquely identify this service regarding a task -->
        <name>ad-src-service</name>
<!--	connection: this node should not contain any element, just a "reference" attribute which is used to look for the
	corresponding connection with this name (as sub-element of the connection element) -->
        <connection reference="ad-src-conn" />
<!--	baseDn: mandatory, this value provides the root distinguish name to use to look for entries -->
        <baseDn>ou=INF,ou=Graz,ou=BEKOAG,dc=bekoag,dc=local</baseDn>
<!--	pivotAttributes: mandatory, this list of string values contains all the pivot attributes that are fetched when looking for
	all entries to synchronized and are used to get the right filter to read every single and complete entry to synchronize -->
        <pivotAttributes>
          <string>sAMAccountName</string>
        </pivotAttributes>
<!--	fetchedAttributes: mandatory, this list of string values contains all the attributes that will be read from the source directory -->
        <fetchedAttributes>
          <string>sAMAccountName</string>
          <string>userPrincipalName</string>
          <string>cn</string>
          <string>displayName</string>
          <string>givenName</string>
          <string>sn</string>
          <string>mail</string>
        </fetchedAttributes>
<!--	getAllFilter: mandatory, this filter is used to look for all entries that have to be synchronized
	All filter: First serach in sync/async mode to get all entries to synchronize, returning only the source pivot attribute -->
        <getAllFilter>(&amp;(objectClass=user)(!(objectClass=computer))(sAMAccountName=*)(userPrincipalName=*))</getAllFilter>
<!--	getOneFilter: mandatory, this filter is used to look for a particular entry - the value will be computed to replace the corresponding
	source pivot attributes with their value at runtime
	One filter: After the first search, use the source pivot attribute to get each entry from the source -->
        <getOneFilter>(&amp;(objectClass=user)(sAMAccountName={sAMAccountName}))</getOneFilter>
<!--	cleanFilter: optional, this filter is used to look for a particular entry for the clean phase - the value will be computed to replace
	the corresponding pivot attributes with their value at runtime
	Clean filter : used only in clean mode, use the **destination** pivot to get corresponding entry in source -->
	<cleanFilter>(&amp;(objectClass=user)(sAMAccountName={uid}))</cleanFilter>
      </ldapSourceService>
      
      
<!--  ./destinationService mandatory node containing definition of the source service settings  
			possible builtin types are :
				databaseDestinationService, ldapDestinationService 
					Plugins also provides: jndiExecDstService
		-->
      <ldapDestinationService>
<!-- 	A ldap destination service will have to contain at least a name, a connection reference, a base DN, a filter to list
			entries, a filter to get a particular entry, a list of pivot attributes and a list of fetched attributes -->
        <name>openldap-dst-service</name>
        <connection reference="openldap-dst-conn" />
<!--	./baseDn This mandatory node provide the directory base branch that will be used to look for entries (list and get) -->
        <baseDn>ou=AD,ou=people,dc=bekoag,dc=local</baseDn>
<!--	./pivotAttributes This mandatory node must include string nodes with attributes name that will be used with their values 
			as pivot datasets (used to get the corresponding entry and to identify the counter-part object, here in the source database
			used during the clean phase to delete the corresponding entry if no corresponding object is found)-->
        <pivotAttributes>
          <string>uid</string>
        </pivotAttributes>
<!--	./fetchedAttributes This mandatory node must include string nodes with attributes name that will fill the full object.
			In a LDAP destination service, fetched attributes will be written to the target directory, whereas source provided datasets
			that are not listed their will silently be canceled, i.e. not synchronized with the directory. -->
        <fetchedAttributes>
          <string>objectClass</string>
          <string>uid</string>
          <string>userPassword</string>
          <string>cn</string>
          <string>displayName</string>
          <string>givenName</string>
          <string>sn</string>
          <string>mail</string>
          <string>description</string>
        </fetchedAttributes>
<!--	./getAllFilter This mandatory node must include the filter that will be used to list all target objects. In a LDAP destination service
					this value is used during the clean phase to look for every object that it has a corresponding object in the source database
	All filter: First search in clean mode to get all entries to delete, returning only the destination pivot (see clean filter in source LDAP) -->
        <getAllFilter>(&amp;(objectClass=inetOrgPerson)(uid=*))</getAllFilter>
<!--	./getOneFilter This mandatory node must include the filter that will be used to get a particular entry. In a LDAP destination service
					this value is used during the synchronization phase to get the object - in conjonction with fetchedAttributes to synchronize them
	One filter: Search done with the pivot of the **source** LDAP to match the entry in destination, like is it done in the source with the One Filter from source LDAP -->
        <getOneFilter>(&amp;(objectClass=inetOrgPerson)(uid={sAMAccountName}))</getOneFilter>
      </ldapDestinationService>
      
      
<!--  ./syncOptions This mandatory node describes how to handle the various situations encountered while synchronizing datasets.
			It must contains a main identifier construction rule and a default policy.
			It may contains synchronization conditions, a default delimiter and datasets synchronization rules (attribute nodes) -->
      <propertiesBasedSyncOptions>
<!--    ./mainIdentifier This mandatory node must contain a string Javascript expression that will enforce the object main identifier. -->
        <mainIdentifier>"uid=" + srcBean.getDatasetFirstValueById("sAMAccountName") + ",ou=AD,ou=people,dc=bekoag,dc=local"</mainIdentifier>
<!--    cn can contain commas which seem to be a big problem, even when replaced with .replace(",", "\\,") -->
<!--    <mainIdentifier>"cn=" + srcBean.getDatasetFirstValueById("cn") + ",ou=AD,ou=people,dc=bekoag,dc=local"</mainIdentifier> -->
<!--    ./defaultDelimiter This mandatory node must contain a string Javascript expression that will enforce the object main identifier.-->
        <defaultDelimiter>;</defaultDelimiter>
<!--    ./defaultPolicy This mandatory node must contain a string Javascript expression that will enforce the object main identifier.-->
        <defaultPolicy>FORCE</defaultPolicy>
<!--    ./conditions This optional node may contain one or more of the four node : create, update, delete and changeId -->
        <conditions>
<!--       ./create This optional node may contain a boolean Javascript expression that will indicate whenever a new entry must be created or not -->
        	<create>true</create>
<!--       ./update This optional node may contain a boolean Javascript expression that will indicate whenever a existing entry must be updated or not -->
        	<update>true</update>
<!--       ./delete This optional node may contain a boolean Javascript expression that will indicate whenever a existing entry must be deleted or not -->
        	<delete>true</delete>
<!--       ./changeId This optional node may contain a boolean Javascript expression that will indicate whenever an existing object main identifier must be changed or not -->
        	<changeId>false</changeId>
        </conditions>
<!--    ./dataset This multi-valued node may contain a structure that will describe how to synchronize the corresponding dataset -->
        <dataset>
<!--      ./name Mandatory node containing the dataset name -->
          <name>objectClass</name>
<!--      ./policy Mandatory node containing the policy to apply to this dataset. Contains KEEP, FORCE or MERGE value -->
          <policy>FORCE</policy>
<!--      ./defaultValues Optional node containing a list of string values that will be used if noone is provided by datasource -->
          <defaultValues></defaultValues>
<!--      ./forceValues Optional node containing a list of string values that will be used to force destination service dataset values -->
          <forceValues></forceValues>
<!--      ./createValues Optional node containing a list of string values that will be used to force destination service dataset values when creating object -->
          <createValues>
            <string>"inetOrgPerson"</string>
            <string>"top"</string>
          </createValues>
        </dataset>
        <dataset>
          <name>uid</name>
          <policy>FORCE</policy>
          <defaultValues></defaultValues>
          <forceValues>
            <string>srcBean.getDatasetFirstValueById("sAMAccountName")</string>
	  </forceValues>
          <createValues></createValues>
        </dataset>
        <dataset>
          <name>userPassword</name>
          <policy>FORCE</policy>
          <defaultValues></defaultValues>
          <forceValues>
            <string>"{SASL}" + srcBean.getDatasetFirstValueById("userPrincipalName")</string>
	  </forceValues>
          <createValues></createValues>
        </dataset>
        <dataset>
          <name>description</name>
          <policy>FORCE</policy>
          <defaultValues></defaultValues>
          <forceValues>
            <string>"AD userPrincipalName: " + srcBean.getDatasetFirstValueById("userPrincipalName")</string>
	  </forceValues>
          <createValues></createValues>
        </dataset>
      </propertiesBasedSyncOptions>
    </task>
  </tasks>
<!-- ./security This mandatory node contains the security settings used by LSC -->
  <security>
<!-- ./encryption This optional node contains the encryption settings -->
    <encryption>
<!--  ./keyfile This optional node contains the keyfile location -->
      <keyfile>etc/lsc.key</keyfile>
<!--  ./algorithm This optional node contains the encryption algorithm -->
      <algorithm>AES</algorithm>
<!--  ./strength This optional node contains the algorithm key length -->
      <strength>128</strength>
    </encryption>
  </security>
</lsc>
_______________________________________________________________
Ldap Synchronization Connector (LSC) - http://lsc-project.org

lsc-users mailing list
[email protected]
http://lists.lsc-project.org/listinfo/lsc-users

Reply via email to