Hello,

I would like to present in this post, the LDAP servers configuration in Documentum Administrator (DA) and the dm_LDAPSynchronization job.

Creation of LDAP server configuration:

  1. Go to the LDAP Servers node in DA:
  2. Click on the FILE > LDAP Server Configuration menu:

  3. Fill in the informations concerning the LDAP server:
    Warning: the name of LDAP Server Configuration must be identical to domain name.

    In update mode, the password is necessary in order to modify others informations:
  4. Go on to the Sync & Authentication tab:

  5. Go on to the Mapping tab:
  6. Go on to the Failover tab:

LDAP Synchronisation job : dm_LDAPSynchronization:

  1. Go to the Jobs node in DA:
  2. Go on to the Job Properties:
  3. Go on to the Schedule tab:
  4. Go on to the Method tab:
  5. Go on to the SysObject Info tab:

The job dm_LDAPSynchronization creates item of « dm_job_request » type for the jobs :
+ dm_UserRename : « object_name=’UserRename’, job_name=’dm_UserRename’ » to rename users
+ dm_GroupRename : « object_name=’GroupRename’, job_name=’dm_GroupRename’ » to rename groups

DQL: List LDAP being used to authenticate users

  • Source of Users
    select distinct user_source from dm_user;
    --------------
    user_source
    --------------
    LDAP
    inline password
    ''
    
  • OS Users
    select user_login_name, user_login_domain from dm_user where user_source = ' ';
    ------------------------------------------
    user_login_name | user_login_domain
    ------------------------------------------
    DOCUMENTUM |
    dmadmin | MYDCTMSERVER
    dm_superusers |
    dm_superusers_dynamic |
    dm_browse_all |
    dm_browse_all_dynamic |
    dm_retention_managers |
    dm_retention_users |
    ...
    
  • LDAP Users
    select user_login_name, user_login_domain from dm_user where user_source = 'LDAP';
    ------------------------------------------
    user_login_name | user_login_domain
    ------------------------------------------
    myuser1 | my-ad
    myuser2 | my-ad
    myuser3 | my-ad
    myuser4 | my-ad
    myuser5 | my-ad
    myuser6 | my-ad
    ...
    
  • Inline Password Users
    select user_login_name, user_login_domain from dm_user where user_source = 'inline password';
    ------------------------------------------
    user_login_name | user_login_domain
    ------------------------------------------
    mytestuser1 |
    mytestuser2 |
    dmadmin_TEST |
    testread |
    ...
    

 

 

 

 

 

Class for correction the user based on the log of dm_LDAPSynchronization job:

package com.java.lu.business.service.ecm.utils.repair;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.text.MessageFormat;
import java.util.Calendar;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;

import com.documentum.com.DfClientX;
import com.documentum.com.IDfClientX;
import com.documentum.fc.client.DfAuthenticationException;
import com.documentum.fc.client.DfIdentityException;
import com.documentum.fc.client.DfPrincipalException;
import com.documentum.fc.client.DfQuery;
import com.documentum.fc.client.DfServiceException;
import com.documentum.fc.client.IDfClient;
import com.documentum.fc.client.IDfDocbaseMap;
import com.documentum.fc.client.IDfDocument;
import com.documentum.fc.client.IDfQuery;
import com.documentum.fc.client.IDfSession;
import com.documentum.fc.client.IDfSessionManager;
import com.documentum.fc.client.IDfUser;
import com.documentum.fc.common.DfException;
import com.documentum.fc.common.DfLoginInfo;

public class CorrectLDAP {

	private static class User {
		final private String cn;
		private String username;
		private String uniqueId;
		private boolean readUserName;
		private boolean readUniqueId;

		public User(String cn) {
			this.cn = cn;
		}

		public void setUsername(String username) {
			this.username = username;
		}

		public void setUniqueId(String uniqueId) {
			this.uniqueId = uniqueId;
		}

		@Override
		public String toString() {
			return MessageFormat.format("userName:{0} - uniqueId:{1} - cn:{2}", username, uniqueId, cn);
		}
	}

	private final static Pattern CN_NAME_PATTERN = Pattern.compile("User DN: (.*)$");
	private final static Pattern UNIQUE_ID_PATTERN = Pattern.compile("--Unique Id--:$");
	private final static Pattern UNIQUE_ID_SPLITER = Pattern.compile("([^:]*):([a-f0-9]{32})");
	private final static Pattern USER_NAME_PATTERN = Pattern.compile("sAMAccountName:$");
	private final static Pattern VALUE_PATTERN = Pattern.compile("\\t\\t(.*)$");
	private final static Pattern END_PATTERN = Pattern.compile(" [-*]{81}$");

	private User currentUser;

	Map<String, User> map = new TreeMap<>();

	public void processLine(String line) {
		Matcher matcher;
		if ((matcher = CN_NAME_PATTERN.matcher(line)).find()) {
			String cn = matcher.group(1);
			if (!map.containsKey(cn)) {
				currentUser = new User(cn);
			}
		} else if (currentUser != null) {
			if ((matcher = END_PATTERN.matcher(line)).find()) {
				if (!map.containsKey(currentUser.cn)) {
					map.put(currentUser.cn, currentUser);
					currentUser = null;
				}
			} else {
				if ((matcher = USER_NAME_PATTERN.matcher(line)).find()) {
					currentUser.readUserName = true;
				} else if ((matcher = UNIQUE_ID_PATTERN.matcher(line)).find()) {
					currentUser.readUniqueId = true;
				} else if (currentUser.readUserName) {
					currentUser.readUserName = false;
					if ((matcher = VALUE_PATTERN.matcher(line)).find()) {
						currentUser.setUsername(matcher.group(1));
					} else {
						printWriter.println(MessageFormat.format("#Unable to retrieve UserName for {0} : ''{1}''", currentUser.cn, line));
					}
				} else if (currentUser.readUniqueId) {
					currentUser.readUniqueId = false;
					if ((matcher = VALUE_PATTERN.matcher(line)).find()) {
						currentUser.setUniqueId(matcher.group(1));
					} else {
						printWriter.println(MessageFormat.format("#Unable to retrieve UniqueId for {0} : ''{1}''", currentUser.cn, line));
					}
				}
			}
		}
	}

	public void printTable() {
		int i = 0;
		for (Entry<String, User> entry : map.entrySet()) {
			printWriter.println(MessageFormat.format("{0,number,00000} - {1}", ++i, entry.getValue()));
		}
	}

	private final IDfClientX clientx;
	private final IDfClient client;
	private IDfSessionManager sMgr;
	private PrintWriter printWriter;

	private CorrectLDAP() throws DfException {
		this.clientx = new DfClientX();
		this.client = clientx.getLocalClient();
	}

	public CorrectLDAP(String username, String password) throws DfException {
		this();
		IDfSessionManager sMgr = client.newSessionManager();
		sMgr.setIdentity(IDfSessionManager.ALL_DOCBASES, new DfLoginInfo(username, password));
		this.sMgr = sMgr;
		this.printWriter = new PrintWriter(System.out);
	}

	public CorrectLDAP(IDfSessionManager sMgr, PrintWriter printWriter) throws DfException {
		this();
		this.sMgr = sMgr;
		this.printWriter = printWriter;
	}

	public static void main(String[] args) throws IOException, DfException {
		CorrectLDAP correctLDAP = new CorrectLDAP(args[0], args[1]);
		correctLDAP.setUpdateLdapDN(true);
		correctLDAP.setUpdateGlobalUniqueId(true);
		correctLDAP.setUpdateDomain(false);
		correctLDAP.setLaunchUpdate(false);
		correctLDAP.correctLdap();
	}

	public void setUpdateDomain(boolean updateDomain) {
		this.updateDomain = updateDomain;
	}

	public void setUpdateGlobalUniqueId(boolean updateGlobalUniqueId) {
		this.updateGlobalUniqueId = updateGlobalUniqueId;
	}

	public void setUpdateLdapDN(boolean updateLdapDN) {
		this.updateLdapDN = updateLdapDN;
	}

	public void setLaunchUpdate(boolean launchUpdate) {
		this.launchUpdate = launchUpdate;
	}

	private boolean updateLdapDN = true;
	private boolean updateGlobalUniqueId = true;
	private boolean updateDomain = true;
	private boolean launchUpdate = false;

	public void correctLdap() throws DfException, IOException {
		IDfDocbaseMap dfDocbaseMap = client.getDocbaseMap();
		for (int i = 0; i < dfDocbaseMap.getDocbaseCount(); i++) {
			String repository = dfDocbaseMap.getDocbaseName(i);
			correctLdap(repository);
		}
	}

	public void correctLdap(String repository) throws DfIdentityException, DfAuthenticationException, DfPrincipalException, DfServiceException, DfException, IOException {
		IDfSession dfSession = sMgr.newSession(repository);
		try {
			boolean transaction = false;
			boolean noError = false;
			try {
				if (launchUpdate &amp;&amp; !dfSession.isTransactionActive()) {
					dfSession.beginTrans();
					transaction = true;
				}

				currentUser = null;
				map.clear();
				IDfDocument dfDocument = (IDfDocument) dfSession.getObjectByQualification("dm_document WHERE FOLDER('/Temp/Jobs/dm_LDAPSynchronization') ORDER BY r_creation_date DESC ENABLE(RETURN_TOP 1)");
				if (dfDocument != null) {
					printWriter.println("# *******************************");
					printWriter.println("# * " + repository);
					printWriter.println("# *******************************");

					try (InputStream inputStream = dfDocument.getContent(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
						for (String line; (line = bufferedReader.readLine()) != null;) {
							processLine(line);
						}
					}
					boolean foundError = false;
					for (User user : map.values()) {
						IDfUser dfUser = dfSession.getUser(user.username);
						if (dfUser != null) {
							String dql = null;
							if (updateLdapDN &amp;&amp; StringUtils.isNotBlank(user.cn) &amp;&amp; !user.cn.contains("?") &amp;&amp; !dfUser.getString("user_ldap_dn").equals(user.cn)) {
								dql = MessageFormat.format("UPDATE dm_user OBJECTS SET user_ldap_dn = ''{0}'' WHERE user_name = ''{1}'';", StringUtils.replace(user.cn, "'", "''"), StringUtils.replace(user.username, "'", "''"));
							}
							if (updateGlobalUniqueId &amp;&amp; StringUtils.isNotBlank(user.uniqueId) &amp;&amp; !dfUser.getString("user_global_unique_id").equals(user.uniqueId)) {
								Matcher matcher = UNIQUE_ID_SPLITER.matcher(user.uniqueId);
								if (matcher.matches()) {
									if (updateDomain) {
										if (!dfUser.getString("user_global_unique_id").equals(user.uniqueId) || !dfUser.getString("user_login_domain").equals(matcher.group(2))) {
											dql = MessageFormat.format("UPDATE dm_user OBJECTS SET user_global_unique_id = ''{0}'', SET user_login_domain = ''{1}'' WHERE user_name = ''{2}'';", StringUtils.replace(user.uniqueId, "'", "''"), StringUtils.replace(matcher.group(1), "'", "''"), StringUtils.replace(user.username, "'", "''"));
										}
									} else {
										if (!dfUser.getString("user_global_unique_id").endsWith(matcher.group(2))) {
											dql = MessageFormat.format("UPDATE dm_user OBJECTS SET user_global_unique_id = ''{0}:{1}'' WHERE user_name = ''{2}'';", StringUtils.replace(dfUser.getString("user_login_domain"), "'", "''"), StringUtils.replace(matcher.group(2), "'", "''"), StringUtils.replace(user.username, "'", "''"));
										}
									}
								}
							}
							if (dql != null) {
								foundError = true;
								printWriter.print(dql);
								if (launchUpdate) {
									new DfQuery(dql).execute(dfSession, IDfQuery.DF_EXEC_QUERY).close();
									printWriter.print(" => OK");
								}
								printWriter.println();
							}
						}
					}
					
					// relancer le LDAP à une date inférieure
					if (foundError) {
						Calendar calendar = Calendar.getInstance();
						calendar.set(Calendar.MILLISECOND, 0);
						calendar.set(Calendar.SECOND, 0);
						calendar.set(Calendar.MINUTE, 0);
						calendar.set(Calendar.HOUR_OF_DAY, 0);
						calendar.set(Calendar.DAY_OF_MONTH, 1);
						calendar.add(Calendar.MONTH, -1);
						String dql = MessageFormat.format("UPDATE dm_ldap_config OBJECTS SET a_last_run = ''{0,date,yyyyMMddHHmmss.S}Z'' WHERE a_last_run != '' '';", calendar.getTime());
						printWriter.print(dql);
						if (launchUpdate) {
							new DfQuery(dql).execute(dfSession, IDfQuery.DF_EXEC_QUERY).close();
							printWriter.print(" => OK");
						}
						printWriter.println();
					}

					printWriter.println("#=======================================================");
					noError = true;
				}
			} finally {
				if (transaction) {
					if (noError) {
						dfSession.commitTrans();
					} else {
						dfSession.abortTrans();
					}
				}
			}
		} finally {
			sMgr.release(dfSession);
		}
	}
}

Best regards,

Huseyin OZVEREN