Quick tour

To use ldap3 import the library from the ldap3 namespace. You can choose the strategy that the client will use to connect to the server.
here are 5 strategies that can be used for establishing a connection: SYNC, ASYNC, LDIF, RESTARTABLE and REUSABLE.

With synchronous strategy (SYNC, RESTARTABLE) all LDAP operation requests return a boolean: True if they’re successful, False if they fail.

With asynchronous strategies (ASYNC, REUSABLE) all LDAP operation requests (except Bind that returns a boolean) return an integer,
the ‘message_id’ of the request. You can send multiple request without waiting for responses.

You can get each response with the get_response(message_id) method of the Connection object.If you get an exception the response has not yet arrived. A timeout value can be specified (get_response(message_id, timeout = 20)) to set the number of seconds to wait for the response to appear (defaults is 10 seconds).

Library raises an exception in the LDAPExceptionError hierarchy to signal errors, the last exception message is stored in the last_error attribute of the Connection object when available.

After any operation, you’ll find the following attributes populated in the Connection object:

  • result: the result of the last operation (only synchronous strategies)
  • response: the entries found if the last operation is a search operation (only for synchronous strategies)
  • last_error: any error occurred in the last operation
  • bound: True if bound else False
  • listening: True if the socket is listening to the server
  • closed: True if the socket is not open

You can have a LDIF representation of the response of a search with:

connection.response_to_ldif()

or you can save the response to a JSON string:

entries = connection.response_to_json()

or have the response saved to a file in JSON format:

connection.response_to_json('entries-found.json')

Connections

You can create a connection with:

# import class and constants
from ldap3 import Server, Connection, SIMPLE, SYNC, ASYNC, SUBTREE, ALL

# define the server and the connection
s = Server('servername', port = 389, get_info = ALL)  # define an unsecure LDAP server, requesting info on DSE and schema
c = Connection(s, auto_bind = True, client_strategy = SYNC, user='username', password='password', authentication=SIMPLE, check_names=True)
print(s.info) # display info from the DSE. OID are decoded when recognized by the library

# request a few objects from the LDAP server
c.search('o=test','(objectClass=*)', SUBTREE, attributes = ['sn', 'objectClass'])
response = c.response
result = c.result
for r in response:
    print(r['dn'], r['attributes']) # return unicode attributes
    print(r['dn'], r['raw_attributes']) return raw (bytes) attributes
print(result)
c.unbind()

To move from synchronous to asynchronous connection you have just to change the ‘client_strategy’ parameter to ‘ASYNC’ and substitute the response and result assignments with:

response, result = c.get_response(result)

That’s all you have to do to have an asynchronous threaded LDAP client connection.

To get operational attributes (createStamp, modifiedStamp, ...) for response objects add ‘get_operational_attributes = True’ in the search request:

c.search('o=test','(objectClass=*)', SUBTREE, attributes = ['sn', 'objectClass'], get_operational_attributes = True)

After a search operation you can access the connection.entries property, to get the search result in a more object oriented representation:

c.search('o=test','(objectClass=*)', SUBTREE, attributes = ['sn', 'givenName', 'objectClass'], get_operational_attributes = True)
for entry in c.entries:
    print(entry.entry_get_dn())
    print(entry.givenName, entry.sn)

Look at 'Entry' in the 'abstraction layer' chapter for the description of the Entry object)

Connection context manager

Connections respond to the context manager protocol, so you can have automatic open, bind and unbind with the following syntax:

from ldap3 import Server, Connection, SUBTREE
s = Server('servername')
c = Connection(s, user='username', password='password')
with c:
    c.search('o=test','(objectClass=*)', SUBTREE, attributes = ['sn', 'objectClass'])
print(c.response)

or, even shorter:

from ldap3 import Server, Connection, SUBTREE
with Connection(Server('servername'), user = 'username', password = 'password') as c
    c.search('o=test','(objectClass=*)', SUBTREE, attributes = ['sn', 'objectClass'])  # connection is opened, bound, searched and closed
print(c.response)

The Connection object retains its state when entering the context, that is if the connection was closed and unbound it will remain closed and unbound when leaving the context, if the connection was open or bound its state will be restored when exiting the context. Connection is always open and bound while in context.

Using the context manager connections will be opened and bound as you enter the Connection context and will be unbound when you leave the context. Unbind will be tried even if the operations in context raise an exception.

Binding

You can bind (authenticate) to the server with any of the authentication method defined in the LDAP v3 protocol: Anonymous, Simple and SASL.

You can perform an automatic bind with the auto_bind=True parameter of the connection object or performing a bind() operation that returns a boolean to indicate if bind was succcesful.

You can read the result of the bind operation in the ‘result’ attribute of the connection object. If auto_bind is not succesful the library will raise an LDAPBindError exception.

Searching

Search operation is enhanced with a few parameters:

  • get_operational_attributes: when True retrieves the operational (system generated) attributes for each of the result entries.
  • paged_size: if greater than 0 the server returns a simple paged search response with the number of entries specified (LDAP server must conform to RFC2696).
  • paged_cookie: used for subsequent retrieval of additional entries in a simple paged search.
  • paged_criticality: if True the search should fail if simple paged search is not available on the server else a full search is performed.
If the search filter contains the following characters you must use the relevant escape ASCII sequence, as per RFC4515 (section 3):
‘*’ -> ‘\\2A’, ‘(‘ -> ‘\\28’, ‘)’ -> ‘\\29’, ‘\’ -> ‘\\5C’, chr(0) -> ‘\\00’

To search for a binary value you must use the RFC4515 escape ASCII sequence for each byte in the search assertion. You can use the function escape_bytes() in ldap3.utils.conv for properly escape a bytes object:

from ldap3.utils.conv import escape_bytes
guid = b'\xca@\xf2k\x1d\x86\xcaL\xb7\xa2\xca@\xf2k\x1d\x86'
search_filter = '(guid=' + escape_bytes(guid) + ')'
c.search('o=test', search_filter, attributes=['guid'])

search_filter will contain ‘(guid=\ca\40\f2\6b\1d\86\ca\4c\b7\a2\ca\40\f2\6b\1d\86)’ Raw values for the attributes retrieved are stored in the raw_attributes dictonary of the search result entries in c.response. If the schema is read (with get_info=GET_SCHEMA_INFO (or GET_ALL_INFO in the Server object) and check_names is set to True in the Connection object the attributes is populated with the formatted values as specified by the RFCs and the schema syntaxes. Custom formatters can be used to specify how an attribute value must be returned in the ‘attributes’ attribute of the search entry object. A formatter must be a callable that receives a bytes value and return an object. The object will be returned in the ‘attributes’. If the attribute is defined in the schema as ‘multi_value’ the attribute value is returned as a list (even if only a single value is present) else it’s returned as a single value.

Formatted (following the schema and RFC indications) attributes are stored in the attributes dictionary of the search result entries in c.response. This is performed only if the schema is read in the server object and the check_names parameter is set to True else the unicode value is returned.

Attributes key are case insensitive, this means that you can access c.response[0][‘attributes’][‘postalAddress’] or c.response[0][‘attributes’][‘postaladdress’] and get the same values back.