LDAP
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
- When to use this module
- Architecture
- How a sync run works
- The
dataMappingcontract - Extending the sync
- Configuration
- Tools
- Getting started
- Dependencies
Overview
| Scenario | Behavior |
|---|---|
| User exists in AD but not yet in ADITO | A new ADITO user and its contact are created (onUserMissingInADITO) |
| User exists in both systems | The existing ADITO user and its contact are updated from AD (onUserModified) |
| User no longer exists in AD | The 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
ACTIONbelow). - 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-sideLDAPIPluginXML.jarplugin (registered via theLdapiXmlPluginalias, seealiasDefinition/), 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'ssearch/read/add/replace/remove/createoperations.SearchActionadditionally 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 viaplugin.runWithAlias(...)and returns the raw result.
ActiveDirectory_lib- the sync/business-logic layer, built on top of the protocol layer:LdapImporterdrives the sync loop and exposes four hooks a caller must implement:searchUserInADITO,onUserMissingInADITO,onUserModified,onUserMissingInLdap.ActiveDirectoryUtilsturns mapped LDAP data into SQL statement arrays and into the ADITO user object passed totools.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 fourLdapImporterhooks, and callsprocess(). 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:
- All existing ADITO users (
tools.getStoredUsers()) are collected into a "missing" set, keyed by internal user id. - One LDAP search is run per configured URL/base DN combination. The search requests exactly the
attributes that appear as
dataMappingkeys; 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. - For every AD entry returned:
searchUserInADITO(entry)looks up a matching ADITO user. The reference implementation inactiveDirectoryImport_serverProcesstries, in order:objectGUID(the stable key once a user has been synced once), then login (sAMAccountName), then e-mail - extensible viaactiveDirectorySearchUserCriteria_service.- The entry's attributes are run through
dataMappinginto a{ "<DB-field-or-user-model-path>": { VALUE, ACTION } }map. - No matching user →
onUserMissingInADITO(entry, preparedData)fires and creates thePERSON/CONTACT/COMMUNICATIONrows 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.
- 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 theCONTACTto inactive and disables the user, unless the username is on the ignore list (seeactiveDirectoryIgnoredUsers_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, orBOTH(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. fromFUNCTION) is replaced byDEFAULT_VALUEtoo.FUNCTION(preparedValue, mapping, ldapAttributeObj, ldapAttributeName, aditoKeyValueArray): optional custom transform, e.g. phone-number formatting or deriving roles from group membership.MAPPING: alternative toFUNCTION- 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 forCOMMUNICATION(the latter is driven bycustom.commTypesand 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:
activeDirectoryRoleDN_service: fully replace the base DN (roleDN) AD groups are resolved against.activeDirectoryRoleMapping_service: fully replace the AD-group-to-ADITO-role mapping.activeDirectoryDataMapping_service: add, override or removedataMappingentries.activeDirectoryCommTypes_service: add, override or removecustom.commTypesentries.activeDirectoryIgnoredUsers_service: add usernamesonUserMissingInLdapmust not deactivate (the default list already contains every user with theINTERNAL_TECHNICALrole).activeDirectorySearchUserCriteria_service: add further lookup strategies forsearchUserInADITO, tried in registration order after the built-in ones.
Configuration (project preferences)
| Preference | Description |
|---|---|
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:
- Set the project preferences listed under
Configuration - at minimum
AD.active,AD.URL,AD.Config,AD.Filter,AD.technicalUserDN,AD.technicalUserPassword, andmyCompany.contactID. - 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 ThedataMappingcontract). - Adjust the role mapping for the project's own AD group base DN and AD-group-to-role mapping,
via
activeDirectoryRoleDN_service/activeDirectoryRoleMapping_service. - Add project-specific search criteria or ignored usernames, via
activeDirectorySearchUserCriteria_service/activeDirectoryIgnoredUsers_service, if the defaults (matching by objectGUID/login/e-mail, ignoringINTERNAL_TECHNICALusers) aren't sufficient. - Run
activeDirectoryImport_serverProcessonce 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(seepackage.jsonfor the currently supported version ranges). - ADITO plugin:
LdapiXmlPlugin(Java-side LDAP connector, seealiasDefinition/), accessed throughActiveDirectoryXml_lib.