Synchronize OpenLDAP groups to Active Directory groups
Presentation
It is often needed to synchronize groups from one repository to another.
The difficulty is that the user identifier in the source repository is different from the one in the destination. So simply copying the values of the 'member' attribute is not enough: you need to search for each value in the source member attribute the correct value for the destination. That is where the JNDI utilities of LSC can help you.
Let's go
This tutorial explain how to synchronize groups from an OpenLDAP directory to Active Directory. It should be easy to adapt to your own needs.
Task
Create a task 'group' as a standard LSC task:
<task> <name>group</name> <bean>org.lsc.beans.SimpleBean</bean> ... </task>
Services
We define here a source service that will read OpenLDAP groups, and a destination service that will manage AD groups. OpenLDAP groups are 'groupsOfUniqueNames' with 'uniqueMember' attribute, AD groups are 'group' with 'member attribute. We choose to keep 'cn' as RDN on both sides:
<asyncLdapSourceService> <name>group-source-service</name> <connection reference="openldap" /> <baseDn>ou=groups,dc=example,dc=com</baseDn> <pivotAttributes> <string>cn</string> </pivotAttributes> <fetchedAttributes> <string>cn</string> <string>description</string> <string>uniqueMember</string> </fetchedAttributes> <getAllFilter><![CDATA[(objectClass=groupOfUniqueNames)]]></getAllFilter> <getOneFilter><![CDATA[(&(objectClass=groupOfUniqueNames)(cn={cn}))]]></getOneFilter> <cleanFilter><![CDATA[(&(objectClass=groupOfUniqueNames)(cn={cn}))]]></cleanFilter> <serverType>OpenLDAP</serverType> </asyncLdapSourceService> <ldapDestinationService> <name>group-dst-service</name> <connection reference="ad" /> <baseDn>OU=groups,OU=demo,DC=example,DC=com</baseDn> <pivotAttributes> <string>cn</string> </pivotAttributes> <fetchedAttributes> <string>cn</string> <string>description</string> <string>member</string> <string>objectClass</string> </fetchedAttributes> <getAllFilter><![CDATA[(objectClass=group)]]></getAllFilter> <getOneFilter><![CDATA[(&(objectClass=group)(cn={cn}))]]></getOneFilter> </ldapDestinationService>
Properties
We will now define properties:
<propertiesBasedSyncOptions> ... </propertiesBasedSyncOptions>
Main properties
We define mainIdentifier and conditions:
<mainIdentifier>js:"cn=" + javax.naming.ldap.Rdn.escapeValue(srcBean.getDatasetFirstValueById("cn")) + ",OU=groups,OU=demo,DC=example,DC=com"</mainIdentifier> <defaultDelimiter>;</defaultDelimiter> <defaultPolicy>FORCE</defaultPolicy> <conditions> <create>true</create> <update>true</update> <delete>true</delete> <changeId>true</changeId> </conditions>
Object Class
We force the values of objectClass attribute in AD:
<dataset> <name>objectclass</name> <policy>KEEP</policy> <createValues> <string>"group"</string> <string>"top"</string> </createValues> </dataset>
member
Here is the tricky part: a javascript code that will parse source members and transform them into destination members:
<dataset> <name>member</name> <policy>FORCE</policy> <forceValues> <string> <![CDATA[rjs: var membersSrcDn = srcBean.getDatasetValuesById("uniqueMember"); var membersDstDn = []; for (var i=0; i<membersSrcDn.size(); i++) { var memberSrcDn = membersSrcDn.get(i); var uid = ""; try { uid = srcLdap.attribute(memberSrcDn, "uid").get(0); } catch(e) { continue; } var destDn = ldap.search("ou=users,ou=demo", "(sAMAccountName=" + uid + ")"); if (destDn.size() == 0 || destDn.size() > 1) { continue; } var destMemberDn = destDn.get(0) + "," + ldap.getContextDn(); membersDstDn.push(destMemberDn); } membersDstDn ]]> </string> </forceValues> </dataset>
Some explanations on this script:
- We get DN of members in source in
membersSrcDn
- For each value, a search is done in source LDAP (
srcLdap
) to get its uid - A search is done on destination LDAP (
ldap
) on sAMAccountName to get the DN corresponding to the uid value (sAMAccountName and uid are the pivot values of the user task) - The DN found is computed with context DN (
ldap.getContextDn
) to get the real DN - The DN is inserted into
membersDstDn
array - The
membersDstDn
array is returned to LSC