Ldaptor: Twisted LDAP Library
Ldaptor is a pure-Python library built on Twisted, implementing LDAP client logic, BER protocol message parsing, filter generation, and LDIF data generation. It also includes command-line utilities for LDAP interactions. The current version, 21.2.0, was released in February 2021, and the project is in a maintenance phase, focusing on bug fixes and compatibility with newer Python and Twisted releases.
Warnings
- breaking Ldaptor version 21.2.0 is the last release to officially support Python 3.5. Future versions, starting with 21.2.1, explicitly drop support for Python 3.5.
- breaking Support for Python 2 was dropped in Ldaptor version 20.1.0. All subsequent versions require Python 3.x.
- gotcha When working with Ldaptor objects that represent LDAP strings (like DNs, filters, or server IPs), it is strongly recommended to use byte strings (e.g., `b'dc=example,dc=com'`) rather than unicode strings, especially for compatibility and protocol-level operations.
- deprecated The `setup.py` file and `sdist` (source distribution) are deprecated for packaging Ldaptor, with `setup.py` planned for removal in a future release. PyPI releases are now managed via GitHub Actions and built with `pep517` for `whl` packages.
- gotcha Prior to version 21.2.0, there were `ModuleNotFoundError` issues related to `cStringIO` in the `ldaptor-ldap2pdns` script, indicating potential compatibility problems with Python 3's `io` module.
- gotcha By default, the `__repr__` method of `ldaptor.protocols.pureldap.LDAPBindRequest` can print the BIND password in logs. This is a security risk for applications logging protocol details.
Install
-
pip install ldaptor
Imports
- LDAPClientCreator
from ldaptor.protocols.ldap import ldapconnector
- LDAPClient
from ldaptor.protocols.ldap import ldapclient
- LDAPEntry
from ldaptor.protocols.ldap import ldapsyntax
- reactor, defer
from twisted.internet import reactor, defer
Quickstart
import os
from twisted.internet import reactor, defer
from ldaptor.protocols.ldap import ldapclient, ldapsyntax, ldapconnector
@defer.inlineCallbacks
def example():
# Note: For production, load sensitive data securely (e.g., from environment variables).
# It is recommended to use byte strings for ldaptor objects.
server_ip = os.environ.get('LDAP_SERVER_IP', '127.0.0.1').encode('utf-8')
basedn = os.environ.get('LDAP_BASE_DN', 'dc=example,dc=com').encode('utf-8')
binddn = os.environ.get('LDAP_BIND_DN', 'cn=admin,dc=example,dc=com').encode('utf-8')
bindpw = os.environ.get('LDAP_BIND_PASSWORD', 'secret').encode('utf-8')
query = os.environ.get('LDAP_QUERY', '(objectClass=*)').encode('utf-8')
# Create an LDAP client creator
c = ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient)
# Define overrides for connecting to the LDAP server
overrides = {basedn: (server_ip, 389)}
# Connect to the LDAP server
client = yield c.connect(basedn, overrides=overrides)
print(f"Connected to LDAP server at {server_ip.decode('utf-8')}")
# Bind to the LDAP server
yield client.bind(binddn, bindpw)
print(f"Bound as {binddn.decode('utf-8')}")
# Perform a search
o = ldapsyntax.LDAPEntry(client, basedn)
results = yield o.search(filterText=query)
print(f"Found {len(results)} entries for query '{query.decode('utf-8')}'")
# Print LDIF for each result
for entry in results:
print(entry.getLDIF())
print("LDAP operations complete.")
if __name__ == '__main__':
df = example()
df.addErrback(lambda err: err.printTraceback())
df.addCallback(lambda _: reactor.stop())
reactor.run()