While working on a project recently, I stumbled across a new(ish) LDAP Library for Python. I've never had a problem with the old one, but started looking into the new library out of curiosity. This new library is easier to work with, and I encourage anyone using LDAP to give it a try.
This new library is ldap3. It serves the same purpose as the more well known Python-LDAP library, which was an interface to the C LDAP library, from OpenLDAP. Ldap3 is just straight Python, which means all it really needs is Python to run. There's no compiling, no need to have OpenLDAP installed, or anything like that.
Searching is no more or less difficult with ldap3, but dealing with search results is a hundred times better than the old library. It's so much more flexible and more intuitive. Here’s an example showing both libraries:
import ldap import ldap3 from pprint import pprint server_uri = 'ldap://ldap.example.com' search_base = 'ou=users,dc=example,dc=com' search_filter = '(uid=rob)' attrs = ['*'] # Using Python-LDAP connection = ldap.initialize(server_uri) connection.simple_bind_s() results = connection.search_s( search_base, ldap.SCOPE_ONELEVEL, search_filter, attrs, ) connection.unbind() pprint(results) # [('uid=rob,ou=users,dc=example,dc=com', # {'cn': ['Rob McBroom'], # 'displayName': ['Rob McBroom'], # 'gidNumber': ['99999'], # 'givenName': ['Rob'], # 'homeDirectory': ['/home/rob'], # 'homePhone': ['800-555-1212'], # 'host': ['*'], # 'loginShell': ['/bin/zsh'], # 'mail': ['rob@example.com'], # 'objectClass': ['top', 'inetOrgPerson', 'hostObject', 'posixAccount'], # 'sn': ['McBroom'], # 'uid': ['rob'], # 'uidNumber': ['99999']})] # Using ldap3 server = ldap3.Server(server_uri) with ldap3.Connection(server, auto_bind=True) as conn: conn.search(search_base, search_filter, attributes=attrs) pprint(conn.entries) # [DN: uid=rob,ou=users,dc=example,dc=com # cn: Rob McBroom # displayName: Rob McBroom # gidNumber: 99999 # givenName: Rob # homeDirectory: /home/rob # homePhone: 800-555-1212 # host: * # loginShell: /bin/zsh # mail: rob@example.com # objectClass: top # inetOrgPerson # hostObject # posixAccount # sn: McBroom # uid: rob # uidNumber: 99999 # ]
Note what we can do with these results.
>>> rob = conn.entries[0] # property access >>> rob.loginShell loginShell: /bin/zsh # dictionary access >>> rob['loginShell'] loginShell: /bin/zsh # dictionary access is not picky >>> rob['loginshell'] loginShell: /bin/zsh >>> rob['login shell'] loginShell: /bin/zsh >>> rob['Login Shell'] loginShell: /bin/zsh # obtain an attribute's value >>> rob.loginShell.value u'/bin/zsh'
Getting the same value from Python-LDAP would be like this:
>>> rob = results[0] >>> rob[1]['loginShell'][0] '/bin/zsh'
Gross.
Updating existing entries isn't that different between the two libraries, but adding entries is much easier with ldap3. You just need to construct a Python dictionary that looks like the entry you want to end up with.
The traditional Python-LDAP doesn't have Python 3 support, and isn't as easy to contribute to. The maintainer still keeps it in a CVS repository that only they have access to, so anyone who wants to contribute has to email them about the changes, which may or may not be implemented. The new LDAP library works with Python 2 and Python 3 without needing to do anything special. It’s on GitHub so everyone can see it and can open issues, pull requests, etc.
For all the great things that come with ldap3, you may want to stick with the old library if you have an existing very large code base that uses it. Also, things that were working in the traditional library may not work in the new library (e.g. Kerberos and certificate authentication, failover between multiple servers, etc.): I haven't had a chance to test them all yet.
Have you used the new ldap3 library? Have you noticed any issues you're working on? Feel free to reach out to me!