Populating Active Directory from OpenLDAP
Presentation
This article describes the creation of a connector which synchronizes data from OpenLDAP to Active Directory.
It has been done on a Linux machine with LSC v2. Everything will be done through the console.
We suppose that you have LSC installed, and OpenLDAP and AD running.
Configure your connector
Create a configuration directory for your OpenLDAP to AD connector, for example:
# mkdir /etc/lsc/openldap2ad/
You need a logback configuration in this directory:
# cp /etc/lsc/logback.xml /etc/lsc/openldap2ad/
All configuration is done through the configuration file /etc/lsc/openldap2ad/lsc.xml. A sample file is provided, so copy it and edit it: https://github.com/lsc-project/lsc/blob/master/sample/ad/etc-opendj2ad/lsc.xml
All configuration parameters are described in configuration.
Destination LDAP directory connection settings (Active Directory)
Target directory properties:
- Destination directory authentication mode: we choose - simple.
- LDAP server URL: a standard LDAP URI. Do not forget the trailing slash. We choose - ldap://adlds.lsc-project.org:389/(not available online for test!).
- Naming context root DN: the data suffix. We choose - dc=lsc-project,dc=org.
And provide information on the identity used to bind to the target directory:
- Principal DN: for example - cn=LSC,dc=lsc-project,dc=org.
- Password: for example - PassW0RD.
<lsc>
    <connections>
      <ldapConnection>
          <name>AD</name>
          <url>ldap://adlds.lsc-project.org:389/dc=lsc-project,dc=org</url>
          <username>cn=LSC,dc=lsc-project,dc=org</username>
          <password>PassW0RD</password>
          <authentication>SIMPLE</authentication>
To manage Active Directory Paged Results Control (which enables to return more than 1000 entries), add:
<pageSize>1000</pageSize>
Attribute unicodePwd is a binary attribute:
<binaryAttributes><string>unicodePwd</string></binaryAttributes>
And close the XML node :
</ldapConnection>
As we want to act on the password (the attribute unicodePwd), we must use an SSL connection to Active Directory (password modification is not allowed on a clear connection).
The steps are:
- Set and export the CA certificate used in AD (see http://confluence.atlassian.com/display/CROWD/Configuring+an+SSL+Certificate+for+Microsoft+Active+Directory) 
- Import the certificate in the JVM or in your own SSL truststore (see SSL and TLS activation) 
- Use ldaps in the AD URI in - lsc.xml(lsc>connections>ldapConnection>url)
Source LDAP directory settings (OpenLDAP)
Source directory properties:
- Source directory authentication mode: we choose simple bind. 
- LDAP server URL: a standard LDAP URI for our OpenLDAP server. Do not forget the trailing slash. We choose - ldap://localhost:389/.
- Naming context root DN: the data suffix. We choose - dc=lsc-project,dc=org.
- Principal DN: for example - cn=Directory Manager,dc=lsc-project,dc=org.
- Password: for example - secret.
<ldapConnection>
    <name>openldap</name>
    <url>ldap://localhost:389/dc=lsc-project,dc=org</url>
    <username>cn=Directory Manager,dc=lsc-project,dc=org</username>
    <password>secret</password>
    <authentication>SIMPLE</authentication>
</ldapConnection>
Task
You can have multiple tasks in your connector (identified by the lsc>tasks>task value). In this tutorial, we have only one task, named ADuser.
In this task, we define the source service (OpenLDAP) and the destination service (AD):
<tasks>
    <task>
        <name>adUser</name>
        <ldapSourceService>
            <name>openldap-source-service</name>
            <connection reference="openldap" />
            <baseDn>ou=People,dc=lsc-project,dc=org</baseDn>
            <pivotAttributes>
                <string>uid</string>
            </pivotAttributes>
            <fetchedAttributes>
                <string>cn</string>
                <string>description</string>
                <string>givenName</string>
                <string>mail</string>
                <string>sn</string>
                <string>uid</string>
                <string>userpassword</string>
            </fetchedAttributes>
            <getAllFilter><![CDATA[(objectClass=inetOrgPerson)]]></getAllFilter>
            <getOneFilter><![CDATA[(&(objectClass=inetOrgPerson)(uid={uid}))]]></getOneFilter>
            <cleanFilter><![CDATA[(&(objectClass=inetOrgPerson)(uid={sAMAccountName}))]]></cleanFilter>
        </ldapSourceService>
        <ldapDestinationService>
            <name>ad-dst-service</name>
            <connection reference="AD" />
            <baseDn>cn=Users,dc=lsc-project,dc=org</baseDn>
            <pivotAttributes>
                <string>sAMAccountName</string>
            </pivotAttributes>
            <fetchedAttributes>
                <string>cn</string>
                <string>description</string>
                <string>givenName</string>
                <string>mail</string>
                <string>objectclass</string>
                <string>pwdLastSet</string>
                <string>sAMAccountName</string>
                <string>sn</string>
                <string>unicodePwd</string>
                <string>userAccountControl</string>
                <string>userPrincipalName</string>
            </fetchedAttributes>
            <getAllFilter><![CDATA[(objectClass=user)]]></getAllFilter>
            <getOneFilter><![CDATA[(&(objectClass=user)(sAMAccountName={uid}))]]></getOneFilter>
        </ldapDestinationService>
We will also define how the target DN is built. Let’s choose that the target DN is composed from the source’s attribute cn and the destination branch cn=Users.
<lsc>
    <.../>
    <tasks>
        <task>
            <.../>
            <propertiesBasedSyncOptions>
                <mainIdentifier>"cn=" + srcBean.getDatasetFirstValueById("cn") + ",cn=Users,dc=lsc-project,dc=org"</mainIdentifier>
                <.../>
            </propertiesBasedSyncOptions>
        </task>
    </tasks>
Synchronization options
This is the last configuration part, but not the least, because we will now describe all our synchronization rules.
In this tutorial, we plan to use these rules:
| Source attribute | Destination attribute | Rule | 
|---|---|---|
| cn | cn | = | 
| sn | sn | = | 
| uid | uid | = | 
| = | ||
| givenName | givenName | = | 
| description | description | = | 
| objectClass | user/person/organizationalPerson/top | |
| uid | sAMAccountName | = | 
| uid | userPrincipalName | uid + “@lsc-project.org” | 
| userAccountControl | Set as “Normal account” | |
| pwdLastSet | Force password change on creation | |
| unicodePwd | “changeit” | 
These rules are applied through syncoptions:
<lsc>
    <.../>
    <tasks>
        <task>
            <propertiesBasedSyncOptions>
                <mainIdentifier>...</mainIdentifier>
                <defaultDelimiter>;</defaultDelimiter>
                <defaultPolicy>FORCE</defaultPolicy>
                <conditions>
                    <create>true</create>
                    <update>true</update>
                    <delete>true</delete>
                    <changeId>true</changeId>
                </conditions>
                <dataset>
                    <name>objectclass</name>
                    <policy>KEEP</policy>
                    <createValues>
                        <string>"user"</string>
                        <string>"organizationalPerson"</string>
                        <string>"person"</string>
                        <string>"top"</string>
                    </createValues>
                </dataset>
                <dataset>
                    <name>sAMAccountName</name>
                    <policy>KEEP</policy>
                    <createValues>
                        <string>srcBean.getDatasetFirstValueById("uid")</string>
                    </createValues>
                </dataset>
                <dataset>
                    <!-- userPrincipalName = uid + "@lsc-project.org" -->
                    <name>userPrincipalName</name>
                    <policy>FORCE</policy>
                    <forceValues>
                        <string>srcBean.getDatasetFirstValueById("uid") + "@lsc-project.org"</string>
                    </forceValues>
                </dataset>
                <dataset>
                    <name>userAccountControl</name>
                    <policy>KEEP</policy>
                    <createValues>
                       <string>AD.userAccountControlSet( 0, [AD.UAC_SET_NORMAL_ACCOUNT])</string>
                    </createValues>
                </dataset>
                <dataset>
                    <!-- pwdLastSet = 0 to force user to change password on next connection -->
                    <name>pwdLastSet</name>
                    <policy>KEEP</policy>
                    <createValues>
                        <string>"0"</string>
                    </createValues>
                </dataset>
                <dataset>
                    <!-- unicodePwd = "changeit" at creation (requires SSL connection to AD) -->
                    <name>unicodePwd</name>
                    <policy>KEEP</policy>
                    <createValues>
                        <string>AD.getUnicodePwd("changeit")</string>
                    </createValues>
                </dataset>
            </propertiesBasedSyncOptions>
        </task>
    </tasks>
</lsc>
You can test your synchronization in “dry-run” mode (-n), which means no modification is done on the target directory:
# /usr/bin/lsc -f /etc/lsc/openldap2ad -s all -c all -n
If the test is successful, you can run it with modifications applied:
# /usr/bin/lsc -f /etc/lsc/openldap2ad -s all -c all