Everything is Gray

A place to put stuff.

Complex LDAP Queries With ldapsearch and python-ldap

| Comments

I recently needed to test a complex LDAP query in a Python script I was writing. I realized shortly after beginning to construct the query that I had never done anything more than a simple a=b or a=b* before, (where * is the wildcard). The solution is rather simple, albeit awkward due to the position of the binary operators. You’ll notice in the examples below that the operators are positioned before each operand. So rather than a & b, you would write &(a)(b).

To test the query I used the trusty ldapsearch utility. For example, to find the email addresses of all users in the “Example” OU that 1) have a nickname beginning with “M”, 2) are full time employees, and 3) their department number doesn’t start with “5” or their login shell is /bin/bash, use the following command:

ldapsearch -LLL -x -H ldap://ldap.example.com -b "ou=Example,dc=example,dc=com" '(&(nickname=M*)(employeeType=fulltime)(|(!(departmentNumber=5*))(loginShell=/bin/bash)))' mail | awk '/mail: / { print $2 }'

To perform the same query in Python, try the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import ldap
import sys

LDAP_URI = 'ldap://ldap.example.com'
SEARCH_BASE = 'ou=Example,dc=example,dc=com'
QUERY = '(&(nickname=M*)(employeeType=fulltime)(|(!(departmentNumber=5*))(loginShell=/bin/bash)))'


def ldap_search(ldap_uri, base, query):
  '''
  Perform an LDAP query.
  '''
    emails = []

    try:
        l = ldap.initialize(ldap_uri)
        l.protocol_version = ldap.VERSION3

        search_scope = ldap.SCOPE_SUBTREE
        retrieve_attributes = None

        ldap_result_id = l.search(
            base,
            search_scope,
            query,
            retrieve_attributes
        )
        result_set = []
        while 1:
            result_type, result_data = l.result(ldap_result_id, 0)
            if (result_data == []):
                break
            else:
                if result_type == ldap.RES_SEARCH_ENTRY:
                    result_set.append(result_data)

        if len(result_set) == 0:
            print('No results found.')
            return

        count = 0
        for i in range(len(result_set)):
            for entry in result_set[i]:
                try:
                    email = entry[1]['mail'][0]
                    count += 1
                    emails.append(email)
                except:
                    pass

    except ldap.LDAPError, e:
        print('LDAPError: %s.' % e)

    finally:
        l.unbind_s()
        print(emails)


def main():
    ldap_search(LDAP_URI, SEARCH_BASE, QUERY)


if __name__ == '__main__':
    sys.exit(main())

Happy LDAP'ing.

Comments