LDAP synchronization

I’m working to revive the LDAP sync extension.

So far I updated the code to get the list of accounts to synchronize. (The group feature is still WIP)

It works as expected, when I put the code on a single page.

However, when I put the code into a scheduler job it throws an exception. Unfortunately I wasn’t able to get any useful information from the logs.

Any suggestions on why the scheduler job fails?

import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.doc.XWikiDocument;
import org.xwiki.contrib.ldap.XWikiLDAPConfig;
import org.xwiki.contrib.ldap.XWikiLDAPUtils;
import org.xwiki.contrib.ldap.XWikiLDAPConnection;
import org.xwiki.contrib.ldap.XWikiLDAPSearchAttribute;

// Constants
//////////////////////////////////////////////////////////////////////

def LDAP_DEFAULT_UID = "sAMAccountName";

// Get configuration elements
//////////////////////////////////////////////////////////////////////

// Get the LDAP configuration instance
def config = XWikiLDAPConfig.getInstance()

// Get the document where to log LDAP synchronization info
def logDocument = xwiki.getDocument("XWiki.LdapSyncLog")

// Get the document where to store the list of accounts that should be synchronized.
def listDocument = xwiki.getDocument("XWiki.LdapAccountsToSync")

// Get the group mapping
def groupsInMapping = config.getGroupMappings().values().flatten().unique()

// Get the property that says if groups in mapping should all be synchronized
def synchronizeGroupMapping = false;

// Get the property that says if a non empty list should be overridden
def overrideNonEmptyList = true;

// Determine if the list is currently empty
def listIsEmpty = listDocument.isNew() || listDocument.content.trim() == ''

// Get the extra groups to synchronize
def extraGroups = "CN=Vollack - Gesamt,OU=StandortuebergreifendeGruppen,DC=intern,DC=vollack,DC=de";

// Constructs the groups to synchronize
def groupsToSynchronize = extraGroups;
if (synchronizeGroupMapping) {
  groupsToSynchronize.addAll(groupsInMapping)
}


def logMessage = """
== Synchronization parameters ==

* Synchronization started at **${new Date().toLocaleString()}**
* Is default groups mapping synchronization active ? ${synchronizeGroupMapping ? "**yes**" : "**no**"} 
* Override non empty list : ${overrideNonEmptyList ? "**yes**" : "**no**"}
* Synchronization list is empty : ${listIsEmpty ? "**yes**" : "**no**"}

== Groups to synchronize

* $groupsToSynchronize

"""

// Do retrieve the list of accounts to sync
//////////////////////////////////////////////////////////////////////

XWikiLDAPConnection connector = new XWikiLDAPConnection();
XWikiLDAPUtils ldapUtils = new XWikiLDAPUtils(connector);

ldapUtils.setUidAttributeName(config.getLDAPParam(XWikiLDAPConfig.PREF_LDAP_UID, LDAP_DEFAULT_UID, xcontext.getContext()));
ldapUtils.setGroupClasses(config.getGroupClasses());
ldapUtils.setGroupMemberFields(config.getGroupMemberFields());
ldapUtils.setBaseDN(config.getLDAPParam("ldap_base_DN", ""));
ldapUtils.setUserSearchFormatString(config.getLDAPParam("ldap_user_search_fmt", "({0}={1})"));

String bindDN = config.getLDAPBindDN();
String password = config.getLDAPBindPassword();

def list = ''

try {
  if (!connector.open(bindDN, password, xcontext.getContext())) {
 logMessage += """
{{error}}
Could not bind to LDAP server. Accounts to synchronize have NOT been retrieved
{{/error}}

"""
  }
  else {
 def allGroupsMembers = [:]

// groupsToSynchronize.each {
//  allGroupsMembers.putAll(ldapUtils.getGroupMembers(it, xcontext.getContext()) ? ldapUtils.getGroupMembers(it, xcontext.getContext()) : [:])
// }

 allGroupsMembers.putAll(ldapUtils.getGroupMembers(extraGroups, xcontext.getContext()) ? ldapUtils.getGroupMembers(extraGroups, xcontext.getContext()) : [:])

 if (allGroupsMembers != null) {
   for (String member : allGroupsMembers.keySet()) {
  String ldapDn = member;
  String ldapUid = allGroupsMembers.get(member);
  list += "${ldapUid}=${ldapDn}\n"
   }
   logMessage += """
{{info}}
Found ${allGroupsMembers.keySet().size()} accounts to check for synchronization, dumped to [[${listDocument.fullName}]].
{{/info}}
"""
 }
 else {
   logMessage += """
{{warning}}
An error occured while getting the list of users to synchronize : **${e.message}**
{{/warning}}
"""
 }

  }

} catch (XWikiException e) {
 logMessage += """
{{error}}
An error occured while getting the list of users to synchronize : **${e.message}**
{{/error}}

"""
}

// Finish : write log file and account list file
//////////////////////////////////////////////////////////////////////

logMessage += """
== Synchronization batches logs ==
"""
 
logDocument.setContent(logMessage)
logDocument.setParent("XWiki.LdapFetchUsersToSync")
logDocument.setTitle("LDAP synchronization log")
logDocument.save()

if (list != '' && (listIsEmpty || overrideNonEmptyList)) {
  listDocument.setContent(list)
  listDocument.setParent("XWiki.LdapFetchUsersToSync")
  listDocument.setTitle("Accounts to synchronize")
  listDocument.save()
}

There is an important difference between a script in a page and a scheduler Groovy script in the meaning of the xcontext variable:

  • in a page: an instance of com.xpn.xwiki.api.Context
  • in a scheduler groovy script: an instance of com.xpn.xwiki.XWikiContext

Thanks for the pointer! It works like a charm now.

I totally overlooked that section of the DevGuide :man_facepalming: