ldap3-orm module

This module provides classes for creating object-relational mappings for LDAP data. In particular it provides the base class EntryBase which uses class attributes in order to configure the ORM model and dynamically creates a appropriate constructor with the objective of writing less code and to focus on the configuration.

ORM Modelling

class ldap3_orm.ParamDef(*args, **kwargs)[source]

Hold the definition of a parameter

This class provides a parameter definition which can be used in addition to attributes defined using AttrDef. The parameter definition is used in the same way as for AttrDef except that it is not added the schema.

class ldap3_orm.EntryBase(**kwargs)[source]

Base class for creating object-relational mapping ldap entries.

Configuring ORM models

A mapping can be configured using class attributes of type AttrDef, e.g.:

class User(EntryBase):
    ...
    username = AttrDef("uid")

The class attribute username describes the ldap attribute uid. For each class attribute of type AttrDef a corresponding keyword argument in the constructor will be generated to initialize this attribute. Thus the User has one ldap attribute named uid which has to be set in the constructor e.g. using username="guest". Ldap attributes can be accessed either by sequence, by assignment or as dictionary keys. Keys are not case sensitive.

Ldap attributes are declared mandatory in AttrDef by default. This can be changed either providing a reasonable default value using the default keyword argument or setting mandatory=False.

If the class attribute has the same name as the ldap attribute the latter will be resolved when accessing the attribute on an instance whereas the class attribute will be resolved when accessed on the class. Furthermore all class attributes of type AttrDef will be promoted to OperatorAttrDef in order to support filter expressions.

For more information about ldap attribute access, inherited methods, etc. have a look at Entry.

For more information about filter expressions have a look at ORM Filter Expressions.

Validation of ldap attributes can be configured by passing validate = callable to AttrDef where callable must accept the value which should be assigned to the attribute as argument. The callable must return a boolean allowing or denying the validation or raise an exception.

Attributes

dn

distinguished name, an unique identifier in your ldap tree

Each subclass of this class must define this attribute.

This attribute can be defined as a template using python’s built-in format() function. All class attributes and attributes configured via AttrDef will be expanded. Furthermore the generated DN will be normalized and escaped using the ldap3.utils.dn.safe_dn() function.

object_classes

a set of object classes to which an entry of this class belongs, necessary for creating an new entry in the ldap tree.

Example:

validateuser = lambda value: value.isalpha()


class User(EntryBase):
    dn = "uid={uid},{base_dn}"
    base_dn = "ou=People,dc=example,dc=com"
    username = AttrDef("uid", validate=validateuser)

>>> User(username="guest")
DN: uid=guest,ou=People,dc=example,dc=com
    uid: guest

The distinguished name DN in this example has been initialized with the values of the configured uid ldap attribute and the class attribute base_dn.

The username has been validated using validateuser which accepts only alphabetic characters. Thus the following code will raise TypeError.

>>> User(username="guest42")
TypeError: Validation failed for attribute 'uid'
           and value 'guest42'

In order to support keyword arguments used as a template for the DN it is possible to define parameter definitions using ParamDef in the same way as AttrDef, except that they are not added as ldap attributes.

Example:

class Automount(EntryBase):
    dn = "cn={cn},ou={automap},{base_dn}"
    base_dn = "cn=automount,dc=example,dc=com"
    object_classes = ["top", "automount"]
    autofile = ParamDef("automap", default="auto.master")
    key = AttrDef("cn")
    info = AttrDef("automountInformation")

 >>> Automount(key="/Scratch", info="examplenfs.example.com:/Scratch",
               autofile="auto_nfs")
 DN: cn=/Scratch,ou=auto_nfs,cn=automount,dc=example,dc=com
     automountInformation: examplenfs.example.com:/Scratch
     cn: /Scratch

Automatic ORM Model Creation

ldap3_orm.EntryType(dn, object_classes, schema=None, *args, **kwargs)[source]

Factory for creating ORM models from given object classes.

Creating ORM models automatically

EntryType() dynamically creates new classes derived from EntryBase using the following arguments:

  • dn – distinguished name, an unique identifier in your ldap tree

    This attribute can be defined as a template using python’s built-in format() function. All class attributes and dynamically generated attributes defined by given schema and object_classes will be expanded.

  • object_classes – one or multiple object class(es) which should be

    included in the generated model.

  • schema –

    ldap3_orm.Connection, Server or SchemaInfo which will be used to generate the model from the corresponding schema information.

Furthermore all arguments which can be passed either as a positional argument or as keyword argument to ObjectDef can be passed to this EntryType().

Example:

InetUser = EntryType("uid={uid},ou=People," + config.base_dn,
                     "inetUser", conn)

>>> u = InetUser(uid="guest",
                 userPassword="{SSHA}oKJYPtoC+8mPBn/f47cSK5xWJuap183E")

>>> InetUser
OBJ : inetUser
DN  : uid={uid},ou=People,dc=example,dc=com
MUST:
MAY : inetUserHttpURL, inetUserStatus, memberOf, uid, userPassword

>>> u
DN: uid=guest,ou=People,dc=example,dc=com
    objectClass: inetUser
    uid: guest
    userPassword: {SSHA}oKJYPtoC+8mPBn/f47cSK5xWJuap183E

ORM Filter Expressions

ldap3-orm supports querying LDAP directories without any knowledge about the LDAP filter syntax defined in RFC 4515 simply using ORM models and Python operators. Nevertheless the usual LDAP filter syntax as well as the Simplified Query Language defined in the ldap3 project and ORM Filter Expressions can be used vice versa.

Note

ldap3-orm filter expressions can be used on ldap3_orm.Connection and ldap3_orm.Reader objects. Please ensure that you are importing those classes from the ldap3_orm module and not from ldap3. Both classes inherit from its originating ones and have just been extended to support ORM Filter Expressions.

Assuming we have created the following ORM model for a LDAP user entry:

user.py
from ldap3_orm import EntryBase, AttrDef


class User(EntryBase):
    dn = "uid={uid},{base_dn}"
    base_dn = "ou=People,dc=example,dc=com"
    object_classes = ["top", "inetUser", "inetOrgPerson"]

    username = AttrDef("uid")
    password = AttrDef("userPassword")
    fullname = AttrDef("cn")
    givenname = AttrDef("givenName")
    surname = AttrDef("sn")
    email = AttrDef("mail")

and we have created an active ldap3_orm.Connection object:

>>> from ldap3_orm import ALL_ATTRIBUTES, Connection, ObjectDef, Reader
>>> with Connection("ldap://ldap.example.com", "cn=directory manager",
                    "secret", auto_bind=True) as conn:

We can query for an user entry using his unique username.

>>> search_base = "ou=People,dc=example,dc=com"
>>> conn.search(search_base, User.username == "guest",
                attributes=ALL_ATTRIBUTES)
True
>>> conn.entries
[DN: uid=guest,ou=People,dc=example,dc=com - STATUS: Read - READ TIME:
2018-03-15T14:32:00.369434
    cn: Guest User
    givenName: Guest
    mail: guest.user@example.com
    sn: User
    uid: guest
    userPassword: {SSHA}oKJYPtoC+8mPBn/f47cSK5xWJuap183E
]

We can have a look at the internaly generated LDAP filter.

>>> User.username == "guest"
uid - mandatory: False - filter: (uid=jcnsgast)
>>> print(User.username == "guest")
'(uid=guest)'

We can use ldap3_orm.Reader objects.

>>> r = Reader(conn, ObjectDef("inetOrgPerson", conn), search_base,
               User.username == "guest")
>>> r.query_filter
'(&(objectClass=inetOrgPerson)(uid=guest))'
>>> r.query
(uid=guest)
>>> r.search()
[DN: uid=guest,ou=People,dc=example,dc=com - STATUS: Read - READ TIME:
2018-03-15T14:32:00.369434
>>> r[0]
DN: uid=guest,ou=People,dc=example,dc=com - STATUS: Read - READ TIME:
2018-03-15T14:32:00.369434

Instead of creating our own models we can dynamically create a model from our active connection using either ldap3_orm.ObjectDef

>>> odef = ObjectDef("inetUser", conn)
>>> odef
OBJ : inetUser
AUX : <None>
OID: inetUser (Auxiliary) 2.16.840.1.113730.3.2.130, top (Abstract) 2.5.6.0
MUST: objectClass
MAY : inetUserHttpURL, inetUserStatus, memberOf, uid, userPassword
>>> print(odef.uid == "guest")
'(uid=guest)'

or a class generated from the EntryType factory.

>>> InetUser = EntryType("uid={uid},ou=People," + config.base_dn,
                         "inetUser", conn)
>>> print(InetUser.uid == "guest")
'(uid=guest)'

Operators

The following examples show some of ldap3-orm’s ORM Filter Expression capabilities. We have already seen that we can equate attributes:

>>> print(User.username == "guest")
'(uid=guest)'

ORM Filter Expressions are escaped:

>>> print(User.givenname == "Gu*")
(givenName=Gu\2a)

In order to get all users with its givenname starting with Gu use the startswith operator:

>>> print(User.givenname.startswith("Gu"))
(givenName=Gu*)

For checking endings use the following:

>>> print(User.givenname.endswith("st"))
(givenName=*st)

Expressions can be combined using and &, or | operators:

>>> print(User.givenname.startswith("Chris")
...       & ((User.surname == "Schmitz") | (User.surname == "Maier")))
(&(givenName=Chris*)(|(sn=Schmitz)(sn=Maier)))