LDAP Sinks
Relevant source files
The following files were used as context for generating this wiki page:
LDAP Sinks are specialized components within the flext-target-ldap project responsible for transforming incoming Singer protocol records into LDAP directory entries. They act as the final destination in the data pipeline, handling the mapping of record fields to LDAP attributes, constructing Distinguished Names (DNs), and managing the persistence of data to an LDAP server through a dedicated client.
Sources: src/flext_target_ldap/_models/sinks.py:12-25, src/flext_target_ldap/api.py:65-80
The system employs a hierarchical class structure where a base sink provides common LDAP operations, while specialized subclasses handle specific directory object types such as Users, Groups, and Organizational Units. This architecture ensures consistency in connection management and error handling while allowing for custom transformation logic per stream.
Sources: src/flext_target_ldap/_models/sinks.py:270-515, tests/unit/test_sinks.py:16-25
Sink Architecture and Hierarchy
The LDAP sink implementation follows the flext-core pattern, providing a bridge between the Singer protocol and LDAP-specific requirements. The hierarchy starts with a base interface for protocol compatibility, followed by a functional base class that manages the LDAP client lifecycle.
Class Hierarchy
- FlextTargetLdapSink: The top-level interface for Singer protocol compatibility, providing the
process_recordentry point. - FlextTargetLdapBaseSink: Inherits from
FlextTargetLdapSink. It provides shared logic for client setup, batch processing, DN validation, and entry persistence. - Specialized Sinks: Subclasses of the base sink that implement specific attribute mapping and DN construction logic for different LDAP object types.
The following diagram illustrates the relationship between the different sink classes and the target facade.
Specialized Sink Implementations
Each specialized sink is designed to handle a specific stream name from the Singer catalog, applying distinct transformation rules.
| Sink Class | Stream Name | Default Object Classes | Key Operations |
|---|---|---|---|
UsersSink | users | inetOrgPerson, organizationalPerson, person, top | Maps emails to mail, constructs DN using uid. |
GroupsSink | groups | groupOfNames, top | Maps members to member, constructs DN using cn. |
OUSink | organizational_units | organizationalUnit, top | Constructs DN using ou or name. |
Sources: src/flext_target_ldap/_models/sinks.py:326-515, tests/unit/test_sinks.py:40-100
User Sink Mapping
The FlextTargetLdapUsersSink includes a specific field map for common user attributes. For example, it automatically maps the Singer field emails to the LDAP attribute mail and phone_numbers to telephoneNumber.
Sources: src/flext_target_ldap/_models/sinks.py:330-333, tests/unit/test_sinks.py:165-175
Data Flow and Persistence Logic
The persistence logic follows an "upsert" pattern. When a record is processed, the sink attempts to add a new entry. If the entry already exists and the update_existing_entries setting is enabled, it falls back to a modify operation.
The following sequence diagram demonstrates the internal data flow during record processing within a batch.
Configuration and Validation
Sinks rely on the FlextTargetLdapSettings for connection details and operational behavior. Validation occurs at multiple levels: configuration validation via Pydantic, and entry-level validation before LDAP operations.
Relevant Configuration Keys
| Key | Type | Description |
|---|---|---|
base_dn | String | The root DN under which entries are created. |
update_existing_entries | Boolean | If True, modifies existing entries instead of failing on ADD. |
user_rdn_attribute | String | The attribute used for the Relative DN (default: uid). |
object_classes | List | Default object classes applied to new entries. |
Sources: src/flext_target_ldap/settings.py:100-175, src/flext_target_ldap/_models/sinks.py:250-265
Validation Logic
Before sending data to the FlextTargetLdapClient, the validate_entry method ensures:
- The Distinguished Name (DN) is not empty.
- The attribute dictionary contains data.
- At least one object class is specified.
- If available, the client's
validate_dnutility is invoked to ensure DN syntax correctness.
Sources: src/flext_target_ldap/_models/sinks.py:247-268, tests/unit/test_sinks.py:127-147
Implementation Details: DN Construction
DN construction is a critical function handled differently by each sink subclass. The FlextTargetLdapBaseSink provides a fallback mechanism that looks for dn, id, cn, or name fields in the record. Specialized sinks override this to use specific RDN (Relative Distinguished Name) attributes defined in settings.
# Example of DN construction in UsersSink
# Source: src/flext_target_ldap/_models/sinks.py:343-349
def build_dn(self, record: t.TargetLdap.RecordPayload) -> p.Result[str]:
"""Build DN for user entry."""
rdn_attr = str(self._target.settings.get("user_rdn_attribute", "uid"))
uid = record.get(rdn_attr)
if not uid:
return r[str].fail(f"No value found for RDN attribute '{rdn_attr}'")
base_dn = self._target.settings.get("base_dn", "dc=example,dc=com")
return r[str].ok(f"{rdn_attr}={uid},{base_dn}")
Sources: src/flext_target_ldap/_models/sinks.py:91-110, src/flext_target_ldap/_models/sinks.py:343-349
Conclusion
LDAP Sinks provide a structured and type-safe way to load data into LDAP directories. By leveraging a hierarchical design, the system maintains a clean separation between the Singer protocol handling and the complex attribute mapping and persistence logic required for LDAP. This design allows the project to support standard directory objects like Users and Groups out of the box while remaining extensible for custom schemas through the base sink implementation.