Build, generate, and validate LDAP filters
A Python 3 utility library for working with Lightweight Directory Access Protocol (LDAP) filters.
This project is a Python port of the node-ldap-filters project. The filters produced by the library are based on RFC 4515.
Note: This project is currently only compatible with Python 3.4 or higher.
Install via pip:
pip install ldap-filterThis library exposes a number of APIs that allow you to build filters
programmatically. The logical and attribute methods of the Filter
object can be combined in a number of ways to generate filters ranging
from very simple to very complex.
The following is a quick example of how you might build a filter programmatically:
from ldap_filter import Filter
output = Filter.AND([
Filter.attribute('name').equal_to('bob'),
Filter.attribute('mail').ends_with('@example.com'),
Filter.OR([
Filter.attribute('dept').equal_to('accounting'),
Filter.attribute('dept').equal_to('operations')
])
])
print(output.to_string()) # (&(name=bob)(mail=*@example.com)(|(dept=accounting)(dept=operations)))Attribute methods are used to create LDAP attribute filter strings. The
Filter.attribute(name) method returns an Attribute object that
the following filter methods can be applied to.
output = Filter.attribute('name').equal_to('bob') # (name=bob)- Attribute.present() - Tests if an attribute is present.
- Output:
(attribute=*)
- Output:
- Attribute.equal_to(value) - Tests if an attribute is equal to the
provided
value.- Output:
(attribute=value)
- Output:
- Attribute.contains(value) - Tests if an attribute contains the
provided
value.- Output:
(attribute=*value*)
- Output:
- Attribute.starts_with(value) - Tests if an attribute starts with
the provided
value.- Output:
(attribute=value*)
- Output:
- Attribute.ends_with(value) - Tests if an attribute ends with the
provided
value.- Output:
(attribute=*value)
- Output:
- Attribute.approx(value) - Tests if an attribute is an approximate
match to the provided
value.- Output:
(attribute~=value)
- Output:
- Attribute.gte(value) - Tests if an attribute is greater than or
equal to the provided
value.- Output:
(attribute>=value)
- Output:
- Attribute.lte(value) - Tests if an attribute is less than or
equal to the provided
value.- Output:
(attribute<=value)
- Output:
- Attribute.raw(value) - Allows for a custom filter with escaped
valueoutput.- Output:
(attribute=value)
- Output:
Logical methods are used to aggregate simple attribute filters. You can nest as many logical methods as needed to produce complex filters.
output = Filter.OR([
Filter.attribute('name').equal_to('bob'),
Filter.attribute('name').equal_to('bill')
])
print(output) # (|(name=bob)(name=bill))- Filter.AND(filt) - Accepts a list of
Filter,Attribute, orGroupobjects.- Output:
(&(filt=1)(filt=2)..)
- Output:
- Filter.OR(filt) - Accepts a list of
Filter,Attribute, orGroupobjects.- Output:
(|(filt=1)(filt=2)..)
- Output:
- Filter.NOT(filt) - Accepts a single
Attributeobject.- Output:
(!(filt=1))
- Output:
The Filter.parse(input) method can be used to create a Filter
object from an existing LDAP filter. This method can also be used to
determine if a string is a valid LDAP filter or not.
input = '(|(name=bob)(name=bill))'
Filter.parse(input)If an invalid LDAP filter string is passed a ParseError exception
will be thrown.
from ldap_filter import Filter, ParseError
input = '(|(name=bob)name=bill))'
try:
Filter.parse(input)
except ParseError as e:
print(e)Error Output:
Line 1: expected [\x20], [\x09], "\r\n", "\n", '(', ')'
(|(name=bob)name=bill)
^
The Filter.simplify() method can be used to eliminate unnecessary
AND/OR filters that only have one child node.
input = '(&(name=bob))'
complex = Filter.parse(input)
print(complex.simplify()) # (name=bob)There are a few options for getting a string output from your Filter
object with optional custom formatting.
You can get simple filter string by calling the Filter.to_string()
method. The Filter class also implements Python’s __str__
method, allowing you to type cast the Filter object directly to a
string or concatenate with other strings.
output = Filter.AND([
Filter.attribute('name').equal_to('bob'),
Filter.attribute('mail').ends_with('@example.com'),
])
# Filter.to_string() output.
print(output.to_string()) # (&(name=bob)(mail=*@example.com))
# Typecast output.
print(str(output)) # (&(name=bob)(mail=*@example.com))
# String concatenate output
print('LDAP Filter: ' + output) # LDAP Filter: (&(name=bob)(mail=*@example.com))The Filter.to_string() method provides additional formatting options
to produce beautified filter strings.
You can get the default beautified format by passing True to the
Filter.to_string(indent) method
output = Filter.AND([
Filter.attribute('name').equal_to('bob'),
Filter.attribute('mail').ends_with('@example.com'),
Filter.OR([
Filter.attribute('dept').equal_to('accounting'),
Filter.attribute('dept').equal_to('operations')
])
])
print(output.to_string(True))Default Beautified Output:
(&
(name=bob)
(mail=*@example.com)
(|
(dept=accounting)
(dept=operations)
)
)
or you can customize the output by passing the indent and/or
indt_char parameters to Filter.to_string(indent, indt_char). The
indent parameter accepts an integer value while the indt_char
parameter accepts any string or character value.
output = Filter.AND([
Filter.attribute('name').equal_to('bob'),
Filter.attribute('mail').ends_with('@example.com'),
Filter.OR([
Filter.attribute('dept').equal_to('accounting'),
Filter.attribute('dept').equal_to('operations')
])
])
print(output.to_string(2, '.'))Custom Beautified Output:
(& ..(name=bob) ..(mail=*@example.com) ..(| ....(dept=accounting) ....(dept=operations) ..) )
The Filter.match(data) method allows you to evaluate a Python
dictionary with attributes against an LDAP filter. The method will
return True if a match is found or False if there is no match
(or if an attribute matches a NOT exclusion).
filt = Filter.AND([
Filter.attribute('department').equal_to('accounting'),
Filter.NOT(
Filter.attribute('status').equal_to('terminated')
)
])
employee1 = {
'name': 'Bob Smith',
'department': 'Accounting',
'status': 'Active'
}
print(filt.match(employee1)) # True
employee2 = {
'name': 'Jane Brown',
'department': 'Accounting',
'status': 'Terminated'
}
print(filt.match(employee2)) # False
employee3 = {
'name': 'Bob Smith',
'department': 'Marketing',
'status': 'Active'
}
print(filt.match(employee3)) # FalseIn order to run the test suite the pytest library is required. You can install pytest by running:
pip install pytestTo run the unit tests simply type pytest in the projects root
directory
Project home page is https://github.com/SteveEwell/python-ldap-filter
The Python LDAP Filter project is open source software released under the MIT licence. Copyright 2018 Stephen Ewell