ldap3 - LDAP Client Library
ldap3 is a strictly RFC 4510 conforming LDAP V3 pure Python client library. The same codebase runs in Python 2, Python 3, PyPy and PyPy3. It offers a more pythonic way to interact with LDAP servers, including an Abstraction Layer for simplified operations.
Warnings
- gotcha The library was formerly known as `python3-ldap` and was renamed to `ldap3` to avoid confusion with the `python-ldap` library. Users migrating from `python3-ldap` or older `python-ldap` installations should be aware.
- gotcha LDAP protocol strictly uses UTF-8 for string values. While `ldap3` attempts to handle encoding, mismatches between your environment's default encoding and required UTF-8 can lead to issues. Explicit encoding/decoding may be necessary.
- gotcha Different connection strategies (e.g., `SYNC`, `ASYNC`, `RESTARTABLE`, `REUSABLE`, `SAFE_SYNC`, `SAFE_RESTARTABLE`) have different return value semantics. Synchronous strategies (e.g., `SYNC`) typically return booleans for success/failure, while asynchronous strategies (e.g., `ASYNC`) return a `message_id`. Incorrectly handling these return types is a common pitfall.
- gotcha Special characters in user-provided input for LDAP queries (e.g., `*`, `(`, `)`, `\`, NUL) must be properly escaped to prevent syntax errors and security vulnerabilities (like LDAP injection).
- gotcha When using `ldap3` with `pyasn1` versions greater than `0.6.0`, a `DeprecationWarning` regarding `typeMap` vs. `TYPE_MAP` may be triggered. While typically harmless, it can clutter logs. This issue has been addressed in PR #983.
Install
-
pip install ldap3 -
pip install ldap3[gssapi] -
pip install ldap3[winkerberos]
Imports
- Server
from ldap3 import Server
- Connection
from ldap3 import Connection
- Tls
from ldap3 import Tls
- ANONYMOUS
from ldap3 import ANONYMOUS
- STRATEGY_SYNC
from ldap3 import SYNC
Quickstart
import os
from ldap3 import Server, Connection, SYNC, ANONYMOUS, SUBTREE
# Configuration from environment variables for security and flexibility
LDAP_SERVER_URI = os.environ.get('LDAP_SERVER_URI', 'ldap://localhost:389')
LDAP_BIND_DN = os.environ.get('LDAP_BIND_DN', 'cn=admin,dc=example,dc=com')
LDAP_BIND_PASSWORD = os.environ.get('LDAP_BIND_PASSWORD', 'adminpassword')
LDAP_SEARCH_BASE = os.environ.get('LDAP_SEARCH_BASE', 'dc=example,dc=com')
LDAP_SEARCH_FILTER = os.environ.get('LDAP_SEARCH_FILTER', '(objectClass=person)')
LDAP_SEARCH_ATTRIBUTES = os.environ.get('LDAP_SEARCH_ATTRIBUTES', 'cn,mail').split(',')
def ldap_connect_and_search():
try:
# Define the LDAP server
s = Server(LDAP_SERVER_URI)
# Establish a connection. auto_bind=True performs the bind operation immediately.
# authentication=ANONYMOUS can be used if no credentials are required.
# For authenticated bind:
# c = Connection(s, user=LDAP_BIND_DN, password=LDAP_BIND_PASSWORD, client_strategy=SYNC, auto_bind=True)
c = Connection(s, user=LDAP_BIND_DN, password=LDAP_BIND_PASSWORD, client_strategy=SYNC, auto_bind=True, raise_exceptions=True)
print(f"Connection status: {c.bound}")
# Perform a search operation
# search_base: The base DN for the search
# search_filter: The LDAP filter string
# search_scope: The scope of the search (e.g., SUBTREE, BASE, LEVEL)
# attributes: List of attributes to retrieve, or ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES
c.search(LDAP_SEARCH_BASE, LDAP_SEARCH_FILTER, search_scope=SUBTREE, attributes=LDAP_SEARCH_ATTRIBUTES)
# Process the search results
print(f"Found {len(c.entries)} entries:")
for entry in c.entries:
print(f" DN: {entry.entry_dn}")
for attr in LDAP_SEARCH_ATTRIBUTES:
if hasattr(entry, attr):
print(f" {attr}: {getattr(entry, attr).value}")
except Exception as e:
print(f"An LDAP error occurred: {e}")
finally:
if 'c' in locals() and c.bound:
c.unbind()
print("Connection unbound.")
if __name__ == '__main__':
ldap_connect_and_search()