Skip to main content

LDAP

Auto-generated

This page is imported automatically from an external repository.

Source project: Open repository

This module keeps ADITO xRM in sync with a Microsoft Active Directory (AD). AD is the leading system: a server process periodically reads its user entries via LDAP and mirrors them into ADITO (typically scheduled nightly). Combined with Kerberos/SSPI, this also lets AD-provisioned users log into the ADITO client without an extra login prompt - the single sign-on itself happens outside this module, LDAP just makes sure the account already exists beforehand.

Table of Contents

Overview

ScenarioBehavior
User exists in AD but not yet in ADITOA new ADITO user and its contact are created (onUserMissingInADITO)
User exists in both systemsThe existing ADITO user and its contact are updated from AD (onUserModified)
User no longer exists in ADThe user is disabled and its contact deactivated, not deleted (onUserMissingInLdap)

Synchronized by default: first/last name, login (sAMAccountName), initial password, email, phone, mobile, calendar ID (userPrincipalName), roles (derived from memberOf), and account status. Every new user is attached to the same, fixed organization/address, configured via myCompany.contactID. Which attributes are synchronized, and to which ADITO field, is fully configurable - see The dataMapping contract.

When to use this module

Good fit when:

  • AD is the central source of truth for user accounts and groups - the module is built specifically for AD/LDAP and does not support other directory services.
  • Users experience regular onboarding/offboarding - automated sync removes manual maintenance in ADITO.
  • ADITO roles should be derived from AD group memberships.

Not a good fit when:

  • Users are managed manually in ADITO with individual profile data - every sync run overwrites locally changed, mapped fields with the AD values again (see ACTION below).
  • There is no network path from the ADITO application server to the LDAP server.
  • External users without AD accounts need to be handled - these must be managed separately, e.g. via activeDirectoryIgnoredUsers_service.

Architecture

activeDirectoryImport_serverProcess configuration, mapping definitions, callback implementations
│ uses

ActiveDirectory_lib sync orchestration, comparison logic, DB/user-model writes
│ uses

ActiveDirectoryXml_lib low-level LDAP API wrapper around the LdapiXmlPlugin

The module is split into two libraries plus one concrete process that ties them together:

  • ActiveDirectoryXml_lib - the LDAP protocol layer. A thin wrapper around the server-side LDAPIPluginXML.jar plugin (registered via the LdapiXmlPlugin alias, see aliasDefinition/), which does the actual talking to AD. This library only builds/parses the XML the plugin expects and returns - it has no knowledge of ADITO's data model. Notable pieces:
    • LoginData: the credentials/URL/authentication-mode fragment attached to every action.
    • Action classes (SearchAction, ReadAction, AddAction, ReplaceAction, RemoveAction, CreateAction) modeling the plugin's search/read/add/replace/remove/create operations. SearchAction additionally carries scope, filter, paging and a result-limit.
    • DNEntry / LDAPAttribute / LDAPIResult: parsed representations of a plugin response.
    • LDAPI.run(): the single choke point that sends an action to the plugin via plugin.runWithAlias(...) and returns the raw result.
  • ActiveDirectory_lib - the sync/business-logic layer, built on top of the protocol layer:
    • LdapImporter drives the sync loop and exposes four hooks a caller must implement: searchUserInADITO, onUserMissingInADITO, onUserModified, onUserMissingInLdap.
    • ActiveDirectoryUtils turns mapped LDAP data into SQL statement arrays and into the ADITO user object passed to tools.insertUser/tools.updateUser.
  • activeDirectoryImport_serverProcess - the concrete, project-facing entry point. It reads the project preferences, defines the attribute mapping (dataMapping) and the communication-type mapping, implements the four LdapImporter hooks, and calls process(). A project might be tempted to customize its behavior with an override modification of this process - but an overridden copy is frozen against future module updates (changes to the original are not automatically merged into it), so prefer extending it via the services described below, which keep working across module updates.

How a sync run works (LdapImporter.process())

activeDirectoryImport_serverProcess is an EXECUTABLE process, so it can be run manually or automated via an ADITO scheduler entry (e.g. a nightly cron schedule). Each run:

  1. All existing ADITO users (tools.getStoredUsers()) are collected into a "missing" set, keyed by internal user id.
  2. One LDAP search is run per configured URL/base DN combination. The search requests exactly the attributes that appear as dataMapping keys; scope, filter, paging and the result limit come from preferences (see below). The importer supports multiple URLs and multiple base DNs, and uses the first URL for which the search across all its base DNs fully succeeds.
  3. For every AD entry returned:
    • searchUserInADITO(entry) looks up a matching ADITO user. The reference implementation in activeDirectoryImport_serverProcess tries, in order: objectGUID (the stable key once a user has been synced once), then login (sAMAccountName), then e-mail - extensible via activeDirectorySearchUserCriteria_service.
    • The entry's attributes are run through dataMapping into a { "<DB-field-or-user-model-path>": { VALUE, ACTION } } map.
    • No matching user → onUserMissingInADITO(entry, preparedData) fires and creates the PERSON/CONTACT/COMMUNICATION rows plus the ADITO user.
    • Matching user found → onUserModified(entry, preparedData, userModel) fires and updates the same records; the user is removed from the "missing" set.
  4. Whatever remains in the "missing" set afterwards - ADITO users that were not found in the AD search result - triggers onUserMissingInLdap(userModel). By default this sets the CONTACT to inactive and disables the user, unless the username is on the ignore list (see activeDirectoryIgnoredUsers_service). ADITO never hard-deletes these datasets, so re-appearing users can simply be re-activated.

The dataMapping contract

dataMapping (built in activeDirectoryImport_serverProcess) is the central piece of configuration: it's keyed by LDAP attribute name (e.g. sAMAccountName, mail, memberOf), and each entry describes how that attribute is turned into ADITO data:

  • FIELD_NAME: target DB column(s), given as "TABLE.COLUMN", or a $virtual.* pseudo-field (see communication types below). The same LDAP value can be written to several targets at once.
  • USER_DATAMODEL: target path(s) in the ADITO user model (tools.PARAMS/tools.TITLE/... style expressions), for data that belongs on the user object rather than a DB row.
  • ACTION: INSERT, UPDATE, or BOTH (default) - controls whether the mapping applies when a user is created, updated, or always.
  • DEFAULT_VALUE: fallback applied whenever the computed value is falsy ("", 0, false, null, undefined) - not only when the LDAP attribute is missing, so a legitimately empty or falsy computed value (e.g. from FUNCTION) is replaced by DEFAULT_VALUE too.
  • FUNCTION(preparedValue, mapping, ldapAttributeObj, ldapAttributeName, aditoKeyValueArray): optional custom transform, e.g. phone-number formatting or deriving roles from group membership.
  • MAPPING: alternative to FUNCTION - a regex-keyed lookup table for translating raw LDAP values.

ActiveDirectoryUtils then turns the resulting { VALUE, ACTION } map into:

  • INSERT/UPDATE statement arrays for PERSON/CONTACT, and for COMMUNICATION (the latter is driven by custom.commTypes and is the one place that actually compares old vs. new values before writing, to avoid redundant updates); and
  • the ADITO user object passed to tools.insertUser/tools.updateUser.

Communication data (phone, mobile, e-mail, ...) is modeled via $virtual.* fields: a dataMapping entry with FIELD_NAME: ["$virtual.PHONE"] produces a value under that key, and a matching custom.commTypes["$virtual.PHONE"] entry (MEDIUM, VALIDATION_TYPE, STANDARD) tells the comm-statement builder which COMMUNICATION medium to write it to. $virtual.* is purely a naming convention connecting the two maps - there is no dedicated field type behind it.

Extending the sync

Rather than editing or overriding activeDirectoryImport_serverProcess directly, a project should extend it via these services, so it keeps receiving future module updates - a direct edit or an override modification instead freezes that copy, since changes to the original are then no longer merged into it. See each service's linked documentation.adoc for the exact contract and examples:

Configuration (project preferences)

PreferenceDescription
AD.active (bool)Master switch; the process throws if this is not set.
AD.URL (string)One or more LDAP server URLs (ldap:// prefix + port), separated by ;. The importer tries them in order and uses the first one that fully succeeds.
AD.Config (string)One or more base DNs to search, separated by ;.
AD.Filter (string)Filter in LDAP filter syntax applied to the search.
AD.technicalUserDN / AD.technicalUserPassword (string)Credentials of the technical user used to bind to AD.
myCompany.contactID (string)CONTACTID of the organization new employees are attached to; the process throws if this is not set or does not resolve to an existing contact.
ldap.authentification.mode (string, default "simple")LDAP security authentication mode.
ldap.sync.scope (string, default "subtree")LDAP search scope (base / one-level / subtree).
ldap.sync.limits (number, default 1000)Maximum number of entries returned per LDAP search.

For AD.Filter, excluding disabled AD accounts is recommended for production setups, e.g. by adding (!(userAccountControl:1.2.840.113556.1.4.803:=2)) to the filter.

The general default LDAP ports are 389, 3268, 3269 and 636 - ADITO's default LDAP port (in AD.URL) should be 636 (LDAPS).

Tools

The free LDAP Browser tool from Softerra lets you browse the structure of an LDAP directory - useful for finding the right base DN(s), group DNs, and attribute names for AD.Config, AD.Filter, roleDN and the dataMapping when setting up a new project.

Getting started

Once the module is added as a project dependency, a project typically needs to:

  1. Set the project preferences listed under Configuration - at minimum AD.active, AD.URL, AD.Config, AD.Filter, AD.technicalUserDN, AD.technicalUserPassword, and myCompany.contactID.
  2. Adjust the field mapping if the project needs fields beyond the module's defaults - additional attributes, different targets, or custom communication types - via activeDirectoryDataMapping_service/activeDirectoryCommTypes_service (see The dataMapping contract).
  3. Adjust the role mapping for the project's own AD group base DN and AD-group-to-role mapping, via activeDirectoryRoleDN_service/activeDirectoryRoleMapping_service.
  4. Add project-specific search criteria or ignored usernames, via activeDirectorySearchUserCriteria_service/activeDirectoryIgnoredUsers_service, if the defaults (matching by objectGUID/login/e-mail, ignoring INTERNAL_TECHNICAL users) aren't sufficient.
  5. Run activeDirectoryImport_serverProcess once manually from the ADITO Designer to verify the setup, then schedule it (e.g. nightly) via an ADITO scheduler entry.

Dependencies

  • Peer modules: @aditosoftware/usermanagement, @aditosoftware/utility, @aditosoftware/contact (see package.json for the currently supported version ranges).
  • ADITO plugin: LdapiXmlPlugin (Java-side LDAP connector, see aliasDefinition/), accessed through ActiveDirectoryXml_lib.