Skip to main content

A Python utility library for working with Lightweight Directory Access Protocol (LDAP) filters.

Project description

Python LDAP Filter · Latest Version License

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.

Usage

Installation

Install via pip:

pip install ldap-filter

Building a Filter

This 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

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)

Methods:

  • Attribute.present() - Tests if an attribute is present.

    • Output: (attribute=*)
  • Attribute.equal_to(value) - Tests if an attribute is equal to the provided value.

    • Output: (attribute=value)
  • Attribute.contains(value) - Tests if an attribute contains the provided value.

    • Output: (attribute=*value*)
  • Attribute.starts_with(value) - Tests if an attribute starts with the provided value.

    • Output: (attribute=value*)
  • Attribute.ends_with(value) - Tests if an attribute ends with the provided value.

    • Output: (attribute=*value)
  • Attribute.approx(value) - Tests if an attribute is an approximate match to the provided value.

    • Output: (attribute~=value)
  • Attribute.gte(value) - Tests if an attribute is greater than or equal to the provided value.

    • Output: (attribute>=value)
  • Attribute.lte(value) - Tests if an attribute is less than or equal to the provided value.

    • Output: (attribute<=value)
  • Attribute.raw(value) - Allows for a custom filter with escaped value output.

    • Output: (attribute=value)

Logical Methods

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))

Methods:

  • Filter.AND(filt) - Accepts a list of Filter, Attribute, or Group objects.

    • Output: (&(filt=1)(filt=2)..)
  • Filter.OR(filt) - Accepts a list of Filter, Attribute, or Group objects.

    • Output: (|(filt=1)(filt=2)..)
  • Filter.NOT(filt) - Accepts a single Attribute object.

    • Output: (!(filt=1))

Filter Parsing

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)
            ^

Simplifying Filters

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)

Filter Output

There are a few options for getting a string output from your Filter object with optional custom formatting.

Simple String

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))

Beautified String

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)
..)
)

Filter Matching

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))  # False

Unit Tests

In order to run the test suite the pytest library is required. You can install pytest by running:

pip install pytest

To run the unit tests simply type pytest in the projects root directory

Home Page

Project home page is https://github.com/SteveEwell/python-ldap-filter

License

The Python LDAP Filter project is open source software released under the MIT licence. Copyright 2024 Stephen Ewell

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ldap_filter-1.0.0.tar.gz (17.6 kB view hashes)

Uploaded Source

Built Distribution

ldap_filter-1.0.0-py2.py3-none-any.whl (12.9 kB view hashes)

Uploaded Python 2 Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page