Welcome to Ldaptor’s documentation!

What is Ldaptor

Ldaptor is a pure-Python Twisted library that implements:

  • LDAP client and server logic
  • separately-accessible LDAP and BER protocol message generation/parsing
  • ASCII-format LDAP filter generation and parsing
  • LDIF format data generation

Get it from PyPI, find out what’s new in the Changelog!

Quick Start

LDAP Client Quickstart

from __future__ import print_function
from twisted.internet import reactor, defer
from twisted.internet.endpoints import clientFromString, connectProtocol
from twisted.internet.task import react
from ldaptor.protocols.ldap.ldapclient import LDAPClient
from ldaptor.protocols.ldap.ldapsyntax import LDAPEntry
import sys

@defer.inlineCallbacks
def onConnect(client):
    basedn = 'dc=example,dc=org'
    binddn = 'cn=bob,ou=people,dc=example,dc=org'
    bindpw = 'secret'
    query = '(cn=bob)'
    try:
        yield client.bind(binddn, bindpw)
    except Exception as ex:
        print(ex)
        raise
    o = LDAPEntry(client, basedn)
    results = yield o.search(filterText=query)
    for entry in results:
        print(entry)

def onError(err):
    err.printDetailedTraceback(file=sys.stderr)

def main(reactor):
    endpoint_str = "tcp:host=127.0.0.1:port=8080"
    e = clientFromString(reactor, endpoint_str)
    d = connectProtocol(e, LDAPClient())
    d.addCallback(onConnect)
    d.addErrback(onError)
    return d

react(main)

LDAP Server Quick Start

from twisted.application import service, internet
from twisted.internet.endpoints import serverFromString
from twisted.internet.protocol import ServerFactory
from twisted.python.components import registerAdapter
from twisted.python import log
from ldaptor.inmemory import fromLDIFFile
from ldaptor.interfaces import IConnectedLDAPEntry
from ldaptor.protocols.ldap import distinguishedname
from ldaptor.protocols.ldap.ldapserver import LDAPServer
import tempfile
from cStringIO import StringIO
import sys

LDIF = """\
dn: dc=org
dc: org
objectClass: dcObject

dn: dc=example,dc=org
dc: example
objectClass: dcObject
objectClass: organization

dn: ou=people,dc=example,dc=org
objectClass: organizationalUnit
ou: people

dn: cn=bob,ou=people,dc=example,dc=org
cn: bob
gn: Bob
mail: bob@example.org
objectclass: top
objectclass: person
objectClass: inetOrgPerson
sn: Roberts
userPassword: secret

dn: gn=John+sn=Doe,ou=people,dc=example,dc=org
objectClass: addressbookPerson
gn: John
sn: Doe
street: Back alley
postOfficeBox: 123
postalCode: 54321
postalAddress: Backstreet
st: NY
l: New York City
c: US
userPassword: terces

dn: gn=John+sn=Smith,ou=people, dc=example,dc=org
objectClass: addressbookPerson
gn: John
sn: Smith
telephoneNumber: 555-1234
facsimileTelephoneNumber: 555-1235
description: This is a description that can span multi
 ple lines as long as the non-first lines are inden
 ted in the LDIF.
userPassword: eekretsay

"""


class Tree(object):

    def __init__(self):
        global LDIF
        self.f = StringIO(LDIF)
        d = fromLDIFFile(self.f)
        d.addCallback(self.ldifRead)

    def ldifRead(self, result):
        self.f.close()
        self.db = result

class LDAPServerFactory(ServerFactory):
    protocol = LDAPServer

    def __init__(self, root):
        self.root = root

    def buildProtocol(self, addr):
        proto = self.protocol()
        proto.debug = self.debug
        proto.factory = self
        return proto

if __name__ == '__main__':
    from twisted.internet import reactor
    if len(sys.argv) == 2:
        port = int(sys.argv[1])
    else:
        port = 8080
    # First of all, to show logging info in stdout :
    log.startLogging(sys.stderr)
    # We initialize our tree
    tree = Tree()
    # When the LDAP Server protocol wants to manipulate the DIT, it invokes
    # `root = interfaces.IConnectedLDAPEntry(self.factory)` to get the root
    # of the DIT.  The factory that creates the protocol must therefore
    # be adapted to the IConnectedLDAPEntry interface.
    registerAdapter(
        lambda x: x.root,
        LDAPServerFactory,
        IConnectedLDAPEntry)
    factory = LDAPServerFactory(tree.db)
    factory.debug = True
    application = service.Application("ldaptor-server")
    myService = service.IServiceCollection(application)
    serverEndpointStr = "tcp:{0}".format(port)
    e = serverFromString(reactor, serverEndpointStr)
    d = e.listen(factory)
    reactor.run()

User’s Guide

Introduction to LDAP

Foreword

This text is intended as a quick introduction to the interesting bits of the LDAP protocol, and should be useful whether you are managing an LDAP server, programming something using an LDAP library, or writing an LDAP library yourself. I welcome any feedback you might have.

LDAP Presents a Distributed Tree of Information

Probably the nicest way to get a mental model of LDAP information is to think of a tree with elements both in leaf and non-leaf nodes. Parts of the tree may reside at different LDAP servers.

Tree-like, distributed nature of data stored in LDAP

An organization normally uses their DNS domain name as the root entry for their local LDAP tree. For example, example.com is free to use dc=example,dc=com. The dc stands for domainComponent. An alternative is to identify the organization via geographical location, as in o=Example Inc., c=US, but this is cumbersome as it requires registration to avoid name conflicts. The o stands for organization, c for country. You will also encounter ou, short for organizational unit.

Each node of the tree is called an “LDAP entry”, and can contain multiple attributes in the form of attributeType=value pairs, for example surname=Wiesel. One attributeType may appear multiple times, in effect having multiple values.

One or more of the attributes are chosen as a Relative Distinguished Name or RDN, and will be used to identify the node based on its parent. This means the RDN must be unique among the children of its parent. Listing all the RDNs, separated by commas, from the node to the root, gives us the Distinguished Name or DN of the entry.

  • The RDN of the entry for Jack E. Wiesel is cn=Jack E. Wiesel.
  • The DN is cn=Jack E. Wiesel,ou=Sales,ou=People,dc=example,dc=com.
  • The cn is short for common name.

The RDN of the entry for John Doe consist of two attributes, gn=John and sn=Doe, joined with a plus sign to form gn=John+sn=Doe. gn is short for given name (first name), sn for surname (last name).

Objectclasses and Schemas

A special attributeType of objectClass lists all the objectclasses the LDAP entry manifests. An object class basically lists what attribute types an entry must have, and what optional attribute types it may have. For example, telephone directory entries must have a name and a telephone number, and may have a fax number and street address. objectClass can have multiple values, allowing the same entry to describe e.g. information about a person both for a telephone directory and for UNIX shell login.

An LDAP schema is a part of the configuration of the LDAP server, containing two things: definitions of attribute types and definitions of objectclasses. It is normally stored as ASCII text, but can e.g. be requested from the server over an LDAP connection.

An attribute type definition commonly contains a global identifier for the attribute type (a list of period-separated integers), a list of names for the attribute type, a free-form description and a reference to another attribute type this definition inherits from. It may also contain information about what sort of data the attribute values may contain, how to compare and sort them, how to find substrings in the value, whether the attribute type can have multiple values, etc.

An example attributeType definition:

attributetype ( 2.5.4.4 NAME ( 'sn' 'surname' )
DESC 'RFC2256: last (family) name(s) for which the entity is known by'
SUP name )

An object class definition also commonly contains a global identifier, name, description and inheritance information. It also commonly lists the attribute types entries having this object class must have, and additional attribute types they may have. An entry cannot have attribute types that are not listed as a MUST or MAY by one of the entrys object classes or their parents.

An example objectClass definition:

objectclass ( 2.5.6.6 NAME 'person' DESC 'RFC2256: a person'
SUP top STRUCTURAL MUST ( sn $ cn ) MAY
( userPassword $ telephoneNumber $ seeAlso $ description ) )

There are a lot of pre-existing schemas, standardized in various RFCs. Also, anyone can create their own schemas. The only things you need are access to the LDAP server configuration, and a number reserved for you, which can be achieved by filling a web form.

Object-oriented look at LDAP entries

If you look at LDAP entries from the viewpoint of a programmer accustomed with object oriented programming, you will see a lot of similarities, but also some striking differences.

Writing Things Down: LDIF

There is a standardized way of writing down, in plain text, the contents of LDAP directories, individual entries and even add, delete and modify operations. This format is known as LDIF (LDAP Data Interchange Format) LDAP Data Interchange Format, and it is defined in RFC2849.

The rough format of LDIF is this: there is a paragraph per entry, where paragraphs are separated by blank lines. Each paragraph contains lines in the format keyword:value. Entries start by listing the keyword dn, and their DN, and then list all the attributes and values the entry has. Lines starting with space are appended to the previous line. The whole file starts with the keyword version and value 1.

Note

The actual format is more complex, but this tutorial should allow you to read and write normal LDIF files fluently.

A simple LDAP file with two entries:

version: 1
dn: cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
cn: Barbara Jensen
cn: Barbara J Jensen
cn: Babs Jensen
sn: Jensen
uid: bjensen
telephonenumber: +1 408 555 1212
description: A big sailing fan.

dn: cn=Bjorn Jensen, ou=Accounting, dc=airius, dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
cn: Bjorn Jensen
sn: Jensen
telephonenumber: +1 408 555 1212

A file containing an entry with a folded attribute value, from RFC 2849:

version: 1
dn:cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com
objectclass:top
objectclass:person
objectclass:organizationalPerson
cn:Barbara Jensen
cn:Barbara J Jensen
cn:Babs Jensen
sn:Jensen
uid:bjensen
telephonenumber:+1 408 555 1212
description:Babs is a big sailing fan, and travels extensively in search of perfect sailing conditions.
title:Product Manager, Rod and Reel Division
Searches and Search Filters

The most common LDAP operation is a search, and LDAP is purposefully designed for environments where searches are many times more common than modify operations. In general, LDAP servers index the entries and can effectively search for matches against a reasonably complex criteria among thousands of entries.

An LDAP search takes the following information as input:

  • base DN
  • scope (base, one level, subtree)
  • filter
  • attributes requested

Note

Once again, we are skipping some details for understandability.

Of these, the search filter is clearly the most interesting one. As with LDIF, search filters have a standardized plain text representation, even though they are not transmitted as plain text in the actual protocol.

A search filter is basically a combination of tests an entry must fulfill in order to match the filter. They are always written inside parentheses. A simple example would be

(cn=John Smith)

but the filters can also match against presence, prefix, suffix, substring, rough equality, etc. Multiple matches can be combined freely with and, or and not operators, which are represented by &, | and !, respectively. For example, to match only objects that have objectClass person, where the full name contains the letters a and b in either order, and who don’t have a telephone number listed, we could use the filter

Note

Yes, once again we are skipping details for understandability. See RFC2254 for more.

(&(objectClass=person)(!(telephoneNumber=*))(|(cn=*a*b*)(cn=*b*a*)))

Visualizing an LDAP search filter
Phases of an LDAP Protocol Chat

An average LDAP protocol chat consists of three stages:

  1. Opening the connection
  2. Doing one or more searches
  3. Closing the connection

At the first stage, opening a connection, an LDAP client opens a TCP connection to the LDAP server, either as plain text, encrypted by TLS or starting with plaintext and switching to use TLS with STARTTLS.

The client authenticates itself and/or the user, providing any necessary authentication information. This is called binding. Normally, the connection is not really authenticated, but left as anonymous; the bind message is sent with no user or password information.

Beginning of an LDAP protocol chat

Next, the client sends a search request, containing the base DN for the search, the filter that entries must fulfill to match, and some extra settings discussed above.

The server replies by sending search result entries back, one message per matching entry. If no entry matched or there was an error before the search could even start, the server might not send any entries. Finally, the server sends a message indicating the search is done, and includes information on whether the search was completely successfully, or the error encountered.

A sample LDAP search operation

Note that the client could have sent another search request without waiting for the first search to complete. The order of results from the different search, or when they are completed, is in no way guaranteed.

Multiple search operations pipelined

One important detail we have skimmed over so far is how the LDAP client knows what message the server is replying to. Earlier we avoided this topic just by doing only one thing at a time, but now we have two searches getting their result entries interleaved. Clearly, there must be a mechanism to separate which entries belong to which search request. And exactly such a mechanism exists; each message sent by the client contains a number identifying the request, and the server replies by including the same number in the reply. Now, all the client needs to do is remember which numbers are still in use, and not reuse those. It can internally maintain search state based on these numbers, and process result entries based on them. The client can reuse a number when it is known that no more server replies will be sent using that number; for example, the search done message gives this guarantee.

Finally, when the client no longer wants to talk to the server, it sends a message effectively saying “good bye”. This message is known as unbind. This only means that the state of connection is the same as when connected, before the first bind; that is, it un-authenticates the current user. If the client really wants to close the connection, it will then close the TCP socket.

End of an LDAP protocol chat

Please understand that these were just examples, and in reality protocol chats are often more complicated. For example, one could connect some other protocol servers, say a web servers, authentication mechanism to actually act as an LDAP client, that tries to bind as the user authenticating himself to the web server, with the password given by the user. If this service had no other interest in the contents of LDAP, it would probably immediately after the bind close the connection. But opening and closing TCP connections repeatedly is slow; it is quite likely the authentication mechanism would be changed to keep a single TCP connection alive, and just do repeated binds over the same connection.

Creating a Simple LDAP Application

An LDAP Primer

Entries in an LDAP directory information tree (DIT) are arranged in a hierarchy.

_images/ldap-is-a-tree.png

LDIF is a textual representation of entries in the DIT.

Writing things down, John Doe LDIF:

dn: gn=John+sn=Doe,ou=Research & Development,ou=People,dc=example,dc=com
objectClass: addressbookPerson
gn: John
sn: Doe
street: Back alley
postOfficeBox: 123
postalCode: 54321
postalAddress: Backstreet
st: NY
l: New York City
c: US

Writing things down, John Smith LDIF:

dn: gn=John+sn=Smith,ou=Marketing,ou=People, dc=example,dc=com
objectClass: addressbookPerson
gn: John
sn: Smith
telephoneNumber: 555-1234
facsimileTelephoneNumber: 555-1235
description: This is a description that can span multi
 ple lines as long as the non-first lines are inden
 ted in the LDIF.
Twisted

Twisted is an event-driven networking framework written in Python and licensed under the MIT (Expat) License.

Twisted supports TCP, UDP, SSL/TLS, multicast, Unix sockets, a large number of protocols (including HTTP, NNTP, SSH, IRC, FTP, and others), and much more.

Twisted includes many full-blown applications, such as web, SSH, FTP, DNS and news servers.

Deferreds
  • A promise that a function will at some point have a result.
  • You can attach callback functions to a Deferred.
  • Once it gets a result these callbacks will be called.
  • Also allows you to register a callback for an error, with the default behavior of logging the error.
  • Standard way to handle all sorts of blocking or delayed operations.
Overview of Ldaptor
_images/overview.png
Asynchronous LDAP Clients and Servers

Ldaptor is a set of pure-Python LDAP client and server protocols and libraries..

It is licensed under the MIT (Expat) License.

Following Along with the Examples

If you are following along with the interactive examples, you will need an LDAP directory server to which the example client can connect. A script that creates such a client is available in the section LDAP Server Quick Start. Copy the script to a file quickstart_server.py and run it in another terminal:

$ python quickstart_server.py 10389

Note

Because of the asynchronous nature of Deferreds, a standard interactive Python shell won’t work treat the following examples the way you might expect. That is because the Twisted reator is not running, so connections will never be made and Deferreds will never fire their callback function(s).

If you want to follow along interactively, you can use the following interactive shell that comes with Twisted. It runs a reactor in the background so you can see deferred results:

$ python -m twisted.conch.stdio
Working with Distinguished Names
>>> from ldaptor.protocols.ldap import distinguishedname
>>> dn=distinguishedname.DistinguishedName(
... 'dc=example,dc=com')
>>> dn
DistinguishedName(listOfRDNs=(RelativeDistinguishedName(
attributeTypesAndValues=(LDAPAttributeTypeAndValue(
attributeType='dc', value='example'),)),
RelativeDistinguishedName(attributeTypesAndValues=(
LDAPAttributeTypeAndValue(attributeType='dc', value='com'),))))
>>> str(dn)
'dc=example,dc=com'
Connect to a Directory Asynchronously

Ldaptor contains helper classes to simplify connecting to an LDAP DIT.

>>> from ldaptor.protocols.ldap.ldapclient import LDAPClient
>>> from twisted.internet import reactor
>>> from twisted.internet.endpoints import clientFromString, connectProtocol
>>> e = clientFromString(reactor, "tcp:host=localhost:port=10389")
>>> e
<twisted.internet.endpoints.TCP4ClientEndpoint at 0xb452e0c>
>>> d = connectProtocol(e, LDAPClient())
>>> d
<Deferred at 0x36755a8 current result: <ldaptor.protocols.ldap.ldapclient.LDAPClient instance at 0x36757a0>>
Searching

Once connected to the DIT, an LDAP client can search for entries.

>>> proto = d.result
>>> proto
<ldaptor.protocols.ldap.ldapclient.LDAPClient instance at 0x40619dac>
>>> from ldaptor.protocols.ldap import ldapsyntax
>>> from ldaptor.protocols.ldap import distinguishedname
>>> dn = distinguishedname.DistinguishedName("dc=example,dc=org")
>>> baseEntry = ldapsyntax.LDAPEntry(client=proto, dn=dn)
>>> d2 = baseEntry.search(filterText='(gn=j*)')
>>> results = d2.result
Results

Search results are a list of LDAP entries.

>>> results
[LDAPEntry(dn='gn=John+sn=Smith,ou=People,
dc=example,dc=com', attributes={'description': ['Some text.'],
'facsimileTelephoneNumber': ['555-1235'], 'gn': ['John'],
'objectClass': ['addressbookPerson'], 'sn': ['Smith'],
'telephoneNumber': ['555-1234']}), LDAPEntry(dn=
'gn=John+sn=Doe,ou=People,dc=example,dc=com',
attributes={'c': ['US'], 'givenName': ['John'], 'l': ['New York City'],
'objectClass': ['addressbookPerson'], 'postOfficeBox': ['123'],
'postalAddress': ['Backstreet'], 'postalCode': ['54321'],
'sn': ['Doe'], 'st': ['NY'], 'street': ['Back alley']})]
Results one-by-one

You can inspect individual results in the result list.

>>> results[0]
LDAPEntry(dn=
'gn=John+sn=Smith,ou=People,dc=example,dc=com',
attributes={'description': ['Some text.'],
'facsimileTelephoneNumber': ['555-1235'], 'gn': ['John'],
'objectClass': ['addressbookPerson'], 'sn': ['Smith'],
'telephoneNumber': ['555-1234']})
>>> results[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
IndexError: list index out of range
LDIF output

Search results can be printed as LDIF output. LDIF output can be used by other LDAP tools.

>>> print(results[0])
dn: gn=John+sn=Smith,ou=People,dc=example,dc=com
objectClass: addressbookPerson
description: Some text.
facsimileTelephoneNumber: 555-1235
gn: John
sn: Smith
telephoneNumber: 555-1234
Closing the connection

Unlike an HTTP connection, an LDAP connection persists until the client indicates it is done or the server forcibly terminates the connection (e.g. a TCP socket times out).

>>> proto.unbind()
Access to entry details

LDAP entries have a dictionary-like interface. Attributes are accessed like dictionary keys. The values are always a list of one or more values.

>>> smith = results[0]
>>> print(smith.dn)
gn=John+sn=Smith,ou=People,dc=example,dc=com
>>> smith['gn']
['John']
>>>
Anatomy of an LDAP entry

LDAP entries can “implement” multiple objectClasses.

All objectClasses can inherit zero, one or many objectClasses, just like programming classes.

All objectClasses have a root class, known as top; many object oriented programming languages have a root class, e.g. named Object.

All objectClasses are either STRUCTURAL or AUXILIARY; entries can only implement one STRUCTURAL objectClass.

Lastly, objectClasses of an entry can be changed at will; you only need to take care that the entry has all the MUST attribute types, and no attribute types outside of the ones that are MUST or MAY.

Note

Note that e.g. OpenLDAP doesn’t implement this.

Attributes of an entry closely match attributes of objects in programming languages; however, LDAP attributes may have multiple values.

Search inputs

An example search filter: (cn=John Smith)

A search filter, specifying criteria an entry must fulfill to match.

Scope of the search, either look at the base DN only, only look one level below it, or look at the whole subtree rooted at the base DN.

Size limit of at most how many matching entries to return.

Attributes to return, or none for all attributes the matching entries happen to have.

Our first Python program
#!/usr/bin/python

from twisted.internet import defer
from twisted.internet.task import react
from twisted.internet.endpoints import clientFromString, connectProtocol
from ldaptor import ldapfilter
from ldaptor.protocols.ldap import ldapsyntax
from ldaptor.protocols.ldap.ldapclient import LDAPClient
from ldaptor.protocols.ldap.distinguishedname import DistinguishedName

def search(reactor, endpointStr, base_dn):
    e = clientFromString(reactor, endpointStr)
    d = connectToLDAPEndpoint(e, LDAPClient())

    def _doSearch(proto):
        searchFilter = ldapfilter.parseFilter('(gn=j*)')
        baseEntry = ldapsyntax.LDAPEntry(client=proto, dn=base_dn)
        d = baseEntry.search(filterObject=searchFilter)
        return d

    d.addCallback(_doSearch)
    return d

def main(reactor):
    import sys
    from twisted.python import log
    log.startLogging(sys.stderr, setStdout=0)
    dn =  DistinguishedName('dc=example,dc=org')
    d = search(reactor, 'tcp:host=localhost:port=10389', dn)

    def _show(results):
        for item in results:
            print(item)

    d.addCallback(_show)
    d.addErrback(defer.logError)
    d.addBoth(lambda _: reactor.stop())
    return d

if __name__ == '__main__':
    react(main)
Phases of the protocol chat
  • Open and bind
  • Search (possibly many times)
  • Unbind and close
Opening and binding
_images/chat-bind.png
Doing multiple searches
_images/chat-search-pipeline.png
Unbinding and closing
_images/chat-unbind.png
A complex search filter

An example:

(&(objectClass=person)
    (!(telephoneNumber=*))
    (|(cn=*a*b*)(cn=*b*a*)))
_images/ldapfilter-as-tree.png
Object classes
  1. Special attribute objectClass lists all the objectclasses an LDAP entry manifests.

  2. Objectclass defines
    1. What attributetypes an entry MUST have
    2. What attributetypes an entry MAY have
  3. An entry in a phonebook must have a name and a telephone number, and may have a fax number and street address.

Schema
  1. A configuration file included in the LDAP server configuration.
  2. A combination of attribute type and object class definitions.
  3. Stored as plain text
  4. Can be requested over an LDAP connection
Attribute type

An example:

attributetype ( 2.5.4.4 NAME ( 'sn' 'surname' )
    DESC 'RFC2256: last (family) name(s) for which the entity is known by'
    SUP name )

Can also contain:

  1. content data type
  2. comparison and sort mechanism
  3. substring search mechanism
  4. whether multiple values are allowed
Object class

An example:

objectclass ( 2.5.6.6 NAME 'person'
    DESC 'RFC2256: a person'
    SUP top STRUCTURAL
    MUST ( sn $ cn )
    MAY ( userPassword $ telephoneNumber
    $ seeAlso $ description )
)
Creating schemas
  1. Anyone can create their own schema
  2. Need to be globally unique
  3. But try to use already existing ones
Where to go from here?

Install OpenLDAP: http://www.openldap.org/

Install Ldaptor: https://github.com/twisted/ldaptor

Learn Python: http://www.python.org/

Learn Twisted. Write a client application for a simple protocol. Read the HOWTOs: http://twistedmatrix.com/documents/current/core/howto/clients.html

ldaptor API Reference

Subpackages
ldaptor.protocols package
Subpackages
ldaptor.protocols.ldap package
Subpackages
ldaptor.protocols.ldap.autofill package
Submodules
ldaptor.protocols.ldap.autofill.posixAccount module
class ldaptor.protocols.ldap.autofill.posixAccount.Autofill_posix(baseDN, freeNumberGetter=<function getFreeNumber>)[source]
notify(ldapObject, attributeType)[source]
start(ldapObject)[source]
ldaptor.protocols.ldap.autofill.sambaAccount module
class ldaptor.protocols.ldap.autofill.sambaAccount.Autofill_samba[source]
notify(ldapObject, attributeType)[source]
start(ldapObject)[source]
ldaptor.protocols.ldap.autofill.sambaSamAccount module
class ldaptor.protocols.ldap.autofill.sambaSamAccount.Autofill_samba(domainSID, fixedPrimaryGroupSID=None)[source]
notify(ldapObject, attributeType)[source]
start(ldapObject)[source]
Module contents

LDAP object field value suggestion and autoupdate mechanism.

exception ldaptor.protocols.ldap.autofill.AutofillException[source]

Bases: exceptions.Exception

exception ldaptor.protocols.ldap.autofill.ObjectMissingObjectClassException[source]

Bases: ldaptor.protocols.ldap.autofill.AutofillException

The LDAPEntry is missing an objectClass this autofiller needs to operate.

Submodules
ldaptor.protocols.ldap.distinguishedname module
class ldaptor.protocols.ldap.distinguishedname.DistinguishedName(magic=None, stringValue=None, listOfRDNs=None)[source]

LDAP Distinguished Name.

contains(other)[source]

Does the tree rooted at DN contain or equal the other DN.

getDomainName()[source]
listOfRDNs = None
split()[source]
up()[source]
exception ldaptor.protocols.ldap.distinguishedname.InvalidRelativeDistinguishedName(rdn)[source]

Bases: exceptions.Exception

Invalid relative distinguished name.

class ldaptor.protocols.ldap.distinguishedname.LDAPAttributeTypeAndValue(stringValue=None, attributeType=None, value=None)[source]
attributeType = None
value = None
class ldaptor.protocols.ldap.distinguishedname.RelativeDistinguishedName(magic=None, stringValue=None, attributeTypesAndValues=None)[source]

LDAP Relative Distinguished Name.

attributeTypesAndValues = None
count()[source]
split()[source]
ldaptor.protocols.ldap.distinguishedname.escape(s)[source]
ldaptor.protocols.ldap.distinguishedname.unescape(s)[source]
ldaptor.protocols.ldap.fetchschema module
ldaptor.protocols.ldap.fetchschema.fetch(client, baseObject)[source]
ldaptor.protocols.ldap.ldapclient module

LDAP protocol client

class ldaptor.protocols.ldap.ldapclient.LDAPClient[source]

Bases: twisted.internet.protocol.Protocol

An LDAP client

berdecoder = <LDAPBERDecoderContext_TopLevel identities={0x10: LDAPMessage} fallback=None inherit=<LDAPBERDecoderContext_LDAPMessage identities={0x80: LDAPControls, 0x53: LDAPSearchResultReference} fallback=<LDAPBERDecoderContext identities={0x40: LDAPBindRequest, 0x41: LDAPBindResponse, 0x42: LDAPUnbindRequest, 0x43: LDAPSearchRequest, 0x44: LDAPSearchResultEntry, 0x45: LDAPSearchResultDone, 0x46: LDAPModifyRequest, 0x47: LDAPModifyResponse, 0x48: LDAPAddRequest, 0x49: LDAPAddResponse, 0x4a: LDAPDelRequest, 0x4b: LDAPDelResponse, 0x4c: LDAPModifyDNRequest, 0x4d: LDAPModifyDNResponse, 0x50: LDAPAbandonRequest, 0x83: LDAPReferral, 0x57: LDAPExtendedRequest, 0x58: LDAPExtendedResponse} fallback=<BERDecoderContext identities={0x01: BERBoolean, 0x02: BERInteger, 0x04: BEROctetString, 0x05: BERNull, 0x0a: BEREnumerated, 0x10: BERSequence, 0x11: BERSet} fallback=None inherit=None> inherit=None> inherit=<LDAPBERDecoderContext identities={0x40: LDAPBindRequest, 0x41: LDAPBindResponse, 0x42: LDAPUnbindRequest, 0x43: LDAPSearchRequest, 0x44: LDAPSearchResultEntry, 0x45: LDAPSearchResultDone, 0x46: LDAPModifyRequest, 0x47: LDAPModifyResponse, 0x48: LDAPAddRequest, 0x49: LDAPAddResponse, 0x4a: LDAPDelRequest, 0x4b: LDAPDelResponse, 0x4c: LDAPModifyDNRequest, 0x4d: LDAPModifyDNResponse, 0x50: LDAPAbandonRequest, 0x83: LDAPReferral, 0x57: LDAPExtendedRequest, 0x58: LDAPExtendedResponse} fallback=<BERDecoderContext identities={0x01: BERBoolean, 0x02: BERInteger, 0x04: BEROctetString, 0x05: BERNull, 0x0a: BEREnumerated, 0x10: BERSequence, 0x11: BERSet} fallback=None inherit=None> inherit=None>>>
bind(dn='', auth='')[source]

@depreciated: Use e.bind(auth).

@todo: Remove this method when there are no callers.

connectionLost(reason=<twisted.python.failure.Failure twisted.internet.error.ConnectionDone: Connection was closed cleanly.>)[source]

Called when TCP connection has been lost

connectionMade()[source]

TCP connection has opened

dataReceived(recd)[source]
debug = False
handle(msg)[source]
send(op)[source]

Send an LDAP operation to the server.

@param op: the operation to send

@type op: LDAPProtocolRequest

@return: the response from server

@rtype: Deferred LDAPProtocolResponse

send_multiResponse(op, handler, *args, **kwargs)[source]

Send an LDAP operation to the server, expecting one or more responses.

@param op: the operation to send

@type op: LDAPProtocolRequest

@param handler: a callable that will be called for each response. It should return a boolean, whether this was the final response.

@param args: positional arguments to pass to handler

@param kwargs: keyword arguments to pass to handler

@return: the result from the last handler as a deferred that completes when the last response has been received

@rtype: Deferred LDAPProtocolResponse

send_noResponse(op)[source]

Send an LDAP operation to the server, with no response expected.

@param op: the operation to send @type op: LDAPProtocolRequest

startTLS(ctx=None)[source]

Start Transport Layer Security.

It is the callers responsibility to make sure other things are not happening at the same time.

@todo: server hostname check, see rfc2830 section 3.6.

unbind()[source]
unsolicitedNotification(msg)[source]
exception ldaptor.protocols.ldap.ldapclient.LDAPClientConnectionLostException(message=None)[source]

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

exception ldaptor.protocols.ldap.ldapclient.LDAPStartTLSBusyError(onwire, message=None)[source]

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPOperationsError

ldaptor.protocols.ldap.ldapconnector module
class ldaptor.protocols.ldap.ldapconnector.LDAPClientCreator(reactor, protocolClass, *args, **kwargs)[source]

Bases: twisted.internet.protocol.ClientCreator

connect(dn, overrides=None, bindAddress=None)[source]

Connect to remote host, return Deferred of resulting protocol instance.

connectAnonymously(dn, overrides=None)[source]

Connect to remote host and bind anonymously, return Deferred of resulting protocol instance.

class ldaptor.protocols.ldap.ldapconnector.LDAPConnector(reactor, dn, factory, overrides=None, bindAddress=None)[source]

Bases: twisted.names.srvconnect.SRVConnector

connect()[source]
pickServer()[source]
ldaptor.protocols.ldap.ldapconnector.connectToLDAPEndpoint(reactor, endpointStr, clientProtocol)[source]
ldaptor.protocols.ldap.ldaperrors module
exception ldaptor.protocols.ldap.ldaperrors.LDAPAdminLimitExceeded(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'adminLimitExceeded'
resultCode = 11
exception ldaptor.protocols.ldap.ldaperrors.LDAPAffectsMultipleDSAs(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'affectsMultipleDSAs'
resultCode = 71
exception ldaptor.protocols.ldap.ldaperrors.LDAPAliasDereferencingProblem(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'aliasDereferencingProblem'
resultCode = 36
exception ldaptor.protocols.ldap.ldaperrors.LDAPAliasProblem(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'aliasProblem'
resultCode = 33
exception ldaptor.protocols.ldap.ldaperrors.LDAPAttributeOrValueExists(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'attributeOrValueExists'
resultCode = 20
exception ldaptor.protocols.ldap.ldaperrors.LDAPAuthMethodNotSupported(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'authMethodNotSupported'
resultCode = 7
exception ldaptor.protocols.ldap.ldaperrors.LDAPBusy(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'busy'
resultCode = 51
exception ldaptor.protocols.ldap.ldaperrors.LDAPCompareFalse(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'compareFalse'
resultCode = 5
exception ldaptor.protocols.ldap.ldaperrors.LDAPCompareTrue(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'compareTrue'
resultCode = 6
exception ldaptor.protocols.ldap.ldaperrors.LDAPConfidentialityRequired(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'confidentialityRequired'
resultCode = 13
exception ldaptor.protocols.ldap.ldaperrors.LDAPConstraintViolation(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'constraintViolation'
resultCode = 19
exception ldaptor.protocols.ldap.ldaperrors.LDAPEntryAlreadyExists(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'entryAlreadyExists'
resultCode = 68
exception ldaptor.protocols.ldap.ldaperrors.LDAPException(message=None)[source]

Bases: exceptions.Exception, ldaptor.protocols.ldap.ldaperrors.LDAPResult

message
exception ldaptor.protocols.ldap.ldaperrors.LDAPInappropriateAuthentication(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'inappropriateAuthentication'
resultCode = 48
exception ldaptor.protocols.ldap.ldaperrors.LDAPInappropriateMatching(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'inappropriateMatching'
resultCode = 18
exception ldaptor.protocols.ldap.ldaperrors.LDAPInsufficientAccessRights(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'insufficientAccessRights'
resultCode = 50
exception ldaptor.protocols.ldap.ldaperrors.LDAPInvalidAttributeSyntax(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'invalidAttributeSyntax'
resultCode = 21
exception ldaptor.protocols.ldap.ldaperrors.LDAPInvalidCredentials(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'invalidCredentials'
resultCode = 49
exception ldaptor.protocols.ldap.ldaperrors.LDAPInvalidDNSyntax(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'invalidDNSyntax'
resultCode = 34
exception ldaptor.protocols.ldap.ldaperrors.LDAPLoopDetect(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'loopDetect'
resultCode = 54
exception ldaptor.protocols.ldap.ldaperrors.LDAPNamingViolation(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'namingViolation'
resultCode = 64
exception ldaptor.protocols.ldap.ldaperrors.LDAPNoSuchAttribute(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'noSuchAttribute'
resultCode = 16
exception ldaptor.protocols.ldap.ldaperrors.LDAPNoSuchObject(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'noSuchObject'
resultCode = 32
exception ldaptor.protocols.ldap.ldaperrors.LDAPNotAllowedOnNonLeaf(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'notAllowedOnNonLeaf'
resultCode = 66
exception ldaptor.protocols.ldap.ldaperrors.LDAPNotAllowedOnRDN(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'notAllowedOnRDN'
resultCode = 67
exception ldaptor.protocols.ldap.ldaperrors.LDAPObjectClassModsProhibited(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'objectClassModsProhibited'
resultCode = 69
exception ldaptor.protocols.ldap.ldaperrors.LDAPObjectClassViolation(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'objectClassViolation'
resultCode = 65
exception ldaptor.protocols.ldap.ldaperrors.LDAPOperationsError(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'operationsError'
resultCode = 1
exception ldaptor.protocols.ldap.ldaperrors.LDAPOther(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'other'
resultCode = 80
exception ldaptor.protocols.ldap.ldaperrors.LDAPProtocolError(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'protocolError'
resultCode = 2
exception ldaptor.protocols.ldap.ldaperrors.LDAPReferral(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'referral'
resultCode = 10
class ldaptor.protocols.ldap.ldaperrors.LDAPResult[source]
name = None
resultCode = None
exception ldaptor.protocols.ldap.ldaperrors.LDAPSaslBindInProgress(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'saslBindInProgress'
resultCode = 14
exception ldaptor.protocols.ldap.ldaperrors.LDAPSizeLimitExceeded(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'sizeLimitExceeded'
resultCode = 4
exception ldaptor.protocols.ldap.ldaperrors.LDAPStrongAuthRequired(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'strongAuthRequired'
resultCode = 8
exception ldaptor.protocols.ldap.ldaperrors.LDAPTimeLimitExceeded(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'timeLimitExceeded'
resultCode = 3
exception ldaptor.protocols.ldap.ldaperrors.LDAPUnavailable(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'unavailable'
resultCode = 52
exception ldaptor.protocols.ldap.ldaperrors.LDAPUnavailableCriticalExtension(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'unavailableCriticalExtension'
resultCode = 12
exception ldaptor.protocols.ldap.ldaperrors.LDAPUndefinedAttributeType(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'undefinedAttributeType'
resultCode = 17
exception ldaptor.protocols.ldap.ldaperrors.LDAPUnknownError(resultCode, message=None)[source]

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

resultCode = None
exception ldaptor.protocols.ldap.ldaperrors.LDAPUnwillingToPerform(message=None)

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

name = 'unwillingToPerform'
resultCode = 53
class ldaptor.protocols.ldap.ldaperrors.Success(msg)[source]

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPResult

name = 'success'
resultCode = 0
ldaptor.protocols.ldap.ldaperrors.get(resultCode, errorMessage)[source]

Get an instance of the correct exception for this resultCode.

ldaptor.protocols.ldap.ldaperrors.init(**errors)[source]
ldaptor.protocols.ldap.ldapserver module

LDAP protocol server

class ldaptor.protocols.ldap.ldapserver.BaseLDAPServer[source]

Bases: twisted.internet.protocol.Protocol

berdecoder = <LDAPBERDecoderContext_TopLevel identities={0x10: LDAPMessage} fallback=None inherit=<LDAPBERDecoderContext_LDAPMessage identities={0x80: LDAPControls, 0x53: LDAPSearchResultReference} fallback=<LDAPBERDecoderContext identities={0x40: LDAPBindRequest, 0x41: LDAPBindResponse, 0x42: LDAPUnbindRequest, 0x43: LDAPSearchRequest, 0x44: LDAPSearchResultEntry, 0x45: LDAPSearchResultDone, 0x46: LDAPModifyRequest, 0x47: LDAPModifyResponse, 0x48: LDAPAddRequest, 0x49: LDAPAddResponse, 0x4a: LDAPDelRequest, 0x4b: LDAPDelResponse, 0x4c: LDAPModifyDNRequest, 0x4d: LDAPModifyDNResponse, 0x50: LDAPAbandonRequest, 0x83: LDAPReferral, 0x57: LDAPExtendedRequest, 0x58: LDAPExtendedResponse} fallback=<BERDecoderContext identities={0x01: BERBoolean, 0x02: BERInteger, 0x04: BEROctetString, 0x05: BERNull, 0x0a: BEREnumerated, 0x10: BERSequence, 0x11: BERSet} fallback=None inherit=None> inherit=None> inherit=<LDAPBERDecoderContext identities={0x40: LDAPBindRequest, 0x41: LDAPBindResponse, 0x42: LDAPUnbindRequest, 0x43: LDAPSearchRequest, 0x44: LDAPSearchResultEntry, 0x45: LDAPSearchResultDone, 0x46: LDAPModifyRequest, 0x47: LDAPModifyResponse, 0x48: LDAPAddRequest, 0x49: LDAPAddResponse, 0x4a: LDAPDelRequest, 0x4b: LDAPDelResponse, 0x4c: LDAPModifyDNRequest, 0x4d: LDAPModifyDNResponse, 0x50: LDAPAbandonRequest, 0x83: LDAPReferral, 0x57: LDAPExtendedRequest, 0x58: LDAPExtendedResponse} fallback=<BERDecoderContext identities={0x01: BERBoolean, 0x02: BERInteger, 0x04: BEROctetString, 0x05: BERNull, 0x0a: BEREnumerated, 0x10: BERSequence, 0x11: BERSet} fallback=None inherit=None> inherit=None>>>
checkControls(controls)[source]
connectionLost(reason=<twisted.python.failure.Failure twisted.internet.error.ConnectionDone: Connection was closed cleanly.>)[source]

Called when TCP connection has been lost

connectionMade()[source]

TCP connection has opened

dataReceived(recd)[source]
debug = False
failDefault(resultCode, errorMessage)[source]
handle(msg)[source]
handleUnknown(request, controls, callback)[source]
queue(id, op)[source]
unsolicitedNotification(msg)[source]
class ldaptor.protocols.ldap.ldapserver.LDAPServer[source]

Bases: ldaptor.protocols.ldap.ldapserver.BaseLDAPServer

An LDAP server

boundUser = None
extendedRequest_LDAPPasswordModifyRequest(data, reply)[source]
fail_LDAPAddRequest

alias of LDAPAddResponse

fail_LDAPBindRequest

alias of LDAPBindResponse

fail_LDAPDelRequest

alias of LDAPDelResponse

fail_LDAPExtendedRequest

alias of LDAPExtendedResponse

fail_LDAPModifyDNRequest

alias of LDAPModifyDNResponse

fail_LDAPModifyRequest

alias of LDAPModifyResponse

fail_LDAPSearchRequest

alias of LDAPSearchResultDone

getRootDSE(request, reply)[source]
handle_LDAPAddRequest(request, controls, reply)[source]
handle_LDAPBindRequest(request, controls, reply)[source]
handle_LDAPDelRequest(request, controls, reply)[source]
handle_LDAPExtendedRequest(request, controls, reply)[source]
handle_LDAPModifyDNRequest(request, controls, reply)[source]
handle_LDAPModifyRequest(request, controls, reply)[source]
handle_LDAPSearchRequest(request, controls, reply)[source]
handle_LDAPUnbindRequest(request, controls, reply)[source]
exception ldaptor.protocols.ldap.ldapserver.LDAPServerConnectionLostException(message=None)[source]

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPException

ldaptor.protocols.ldap.ldapsyntax module

Pythonic API for LDAP operations.

exception ldaptor.protocols.ldap.ldapsyntax.CannotRemoveRDNError(key, val=None)[source]

Bases: exceptions.Exception

The attribute to be removed is the RDN for the object and cannot be removed.

exception ldaptor.protocols.ldap.ldapsyntax.DNNotPresentError[source]

Bases: exceptions.Exception

The requested DN cannot be found by the server.

class ldaptor.protocols.ldap.ldapsyntax.JournaledLDAPAttributeSet(ldapObject, *a, **kw)[source]

Bases: ldaptor.attributeset.LDAPAttributeSet

add(value)[source]
clear()[source]
remove(value)[source]
update(sequence)[source]
ldaptor.protocols.ldap.ldapsyntax.LDAPEntry

alias of LDAPEntryWithClient

class ldaptor.protocols.ldap.ldapsyntax.LDAPEntryWithAutoFill(*args, **kwargs)[source]

Bases: ldaptor.protocols.ldap.ldapsyntax.LDAPEntryWithClient

addAutofiller(autoFiller)[source]
journal(journalOperation)[source]
class ldaptor.protocols.ldap.ldapsyntax.LDAPEntryWithClient(client, dn, attributes={}, complete=0)[source]

Bases: ldaptor.entry.EditableLDAPEntry

addChild(rdn, attributes)[source]
bind(password)[source]
buildAttributeSet(key, values)[source]
commit()[source]
delete()[source]
fetch(*attributes)[source]
get(*a, **kw)[source]
has_key(*a, **kw)[source]
items()[source]
journal(journalOperation)[source]

Add a Modification into the list of modifications that need to be flushed to the LDAP server.

Normal callers should not use this, they should use the o[‘foo’]=[‘bar’, ‘baz’] -style API that enforces schema, handles errors and updates the cached data.

keys()[source]
lookup(dn)[source]
move(newDN)[source]
namingContext()[source]
search(filterText=None, filterObject=None, attributes=(), scope=None, derefAliases=None, sizeLimit=0, sizeLimitIsNonFatal=False, timeLimit=0, typesOnly=0, callback=None)[source]
setPassword(newPasswd)[source]
setPasswordMaybe_ExtendedOperation(newPasswd)

Set the password on this object.

@param newPasswd: A string containing the new password.

@return: A Deferred that will complete when the operation is done.

setPasswordMaybe_Samba(newPasswd)[source]

Set the Samba password on this object if it is a sambaSamAccount or sambaAccount.

@param newPasswd: A string containing the new password.

@return: A Deferred that will complete when the operation is done.

setPassword_ExtendedOperation(newPasswd)[source]

Set the password on this object.

@param newPasswd: A string containing the new password.

@return: A Deferred that will complete when the operation is done.

setPassword_Samba(newPasswd, style=None)[source]

Set the Samba password on this object.

@param newPasswd: A string containing the new password.

@param style: one of ‘sambaSamAccount’, ‘sambaAccount’ or None. Specifies the style of samba accounts used. None is default and is the same as ‘sambaSamAccount’.

@return: A Deferred that will complete when the operation is done.

undo()[source]
exception ldaptor.protocols.ldap.ldapsyntax.MatchNotImplemented(op)[source]

Bases: exceptions.NotImplementedError

Match type not implemented

exception ldaptor.protocols.ldap.ldapsyntax.NoContainingNamingContext[source]

Bases: exceptions.Exception

The server contains to LDAP naming context that would contain this object.

exception ldaptor.protocols.ldap.ldapsyntax.ObjectDeletedError[source]

Bases: ldaptor.protocols.ldap.ldapsyntax.ObjectInBadStateError

The LDAP object has already been removed, unable to perform operations on it.

exception ldaptor.protocols.ldap.ldapsyntax.ObjectDirtyError[source]

Bases: ldaptor.protocols.ldap.ldapsyntax.ObjectInBadStateError

The LDAP object has a journal which needs to be committed or undone before this operation.

exception ldaptor.protocols.ldap.ldapsyntax.ObjectInBadStateError[source]

Bases: exceptions.Exception

The LDAP object in in a bad state.

exception ldaptor.protocols.ldap.ldapsyntax.PasswordSetAborted[source]

Bases: exceptions.Exception

Aborted

exception ldaptor.protocols.ldap.ldapsyntax.PasswordSetAggregateError(errors)[source]

Bases: exceptions.Exception

Some of the password plugins failed

ldaptor.protocols.ldap.ldif module

Support for writing a set of directory entries as LDIF. You probably want to use this only indirectly, as in str(LDAPEntry(...)).

TODO support writing modify operations TODO support reading modify operations

TODO implement rest of syntax from RFC2849

ldaptor.protocols.ldap.ldif.asLDIF(dn, attributes)[source]
ldaptor.protocols.ldap.ldif.attributeAsLDIF(attribute, value)[source]
ldaptor.protocols.ldap.ldif.attributeAsLDIF_base64(attribute, value)[source]
ldaptor.protocols.ldap.ldif.base64_encode(s)[source]
ldaptor.protocols.ldap.ldif.containsNonprintable(s)[source]
ldaptor.protocols.ldap.ldif.header()[source]
ldaptor.protocols.ldap.ldif.manyAsLDIF(objects)[source]
ldaptor.protocols.ldap.ldifdelta module
class ldaptor.protocols.ldap.ldifdelta.LDIFDelta[source]

Bases: ldaptor.protocols.ldap.ldifprotocol.LDIF

MOD_SPEC_TO_DELTA = {'add': <class 'ldaptor.delta.Add'>, 'replace': <class 'ldaptor.delta.Replace'>, 'delete': <class 'ldaptor.delta.Delete'>}
state_IN_ADD_ENTRY(line)[source]
state_IN_DELETE(line)[source]
state_IN_MOD_SPEC(line)[source]
state_WAIT_FOR_CHANGETYPE(line)[source]
state_WAIT_FOR_DN(line)[source]
state_WAIT_FOR_MOD_SPEC(line)[source]
exception ldaptor.protocols.ldap.ldifdelta.LDIFDeltaAddMissingAttributesError[source]

Bases: ldaptor.protocols.ldap.ldifprotocol.LDIFParseError

Add operation needs to have atleast one attribute type and value.

exception ldaptor.protocols.ldap.ldifdelta.LDIFDeltaDeleteHasJunkAfterChangeTypeError[source]

Bases: ldaptor.protocols.ldap.ldifprotocol.LDIFParseError

Delete operation takes no attribute types or values.

exception ldaptor.protocols.ldap.ldifdelta.LDIFDeltaMissingChangeTypeError[source]

Bases: ldaptor.protocols.ldap.ldifprotocol.LDIFParseError

LDIF delta entry has no changetype.

exception ldaptor.protocols.ldap.ldifdelta.LDIFDeltaModificationDifferentAttributeTypeError[source]

Bases: ldaptor.protocols.ldap.ldifprotocol.LDIFParseError

The attribute type for the change is not the as in the mod-spec header line.

exception ldaptor.protocols.ldap.ldifdelta.LDIFDeltaModificationMissingEndDashError[source]

Bases: ldaptor.protocols.ldap.ldifprotocol.LDIFParseError

LDIF delta modification has no ending dash.

exception ldaptor.protocols.ldap.ldifdelta.LDIFDeltaUnknownModificationError[source]

Bases: ldaptor.protocols.ldap.ldifprotocol.LDIFParseError

LDIF delta modification has unknown mod-spec.

ldaptor.protocols.ldap.ldifdelta.fromLDIFFile(f)[source]

Read LDIF data from a file.

ldaptor.protocols.ldap.ldifprotocol module
class ldaptor.protocols.ldap.ldifprotocol.LDIF[source]

Bases: object, twisted.protocols.basic.LineReceiver

connectionLost(reason=<twisted.python.failure.Failure twisted.internet.error.ConnectionDone: Connection was closed cleanly.>)[source]
data = None
delimiter = '\n'
dn = None
gotEntry(obj)[source]
lastLine = None
lineReceived(line)[source]
logicalLineReceived(line)[source]
mode = 'HEADER'
parseValue(val)[source]
state_HEADER(line)[source]
state_IN_ENTRY(line)[source]
state_WAIT_FOR_DN(line)[source]
version = None
exception ldaptor.protocols.ldap.ldifprotocol.LDIFEntryStartsWithNonDNError[source]

Bases: ldaptor.protocols.ldap.ldifprotocol.LDIFParseError

LDIF entry starts with a non-DN line

exception ldaptor.protocols.ldap.ldifprotocol.LDIFEntryStartsWithSpaceError[source]

Bases: ldaptor.protocols.ldap.ldifprotocol.LDIFParseError

Invalid LDIF value format

exception ldaptor.protocols.ldap.ldifprotocol.LDIFLineWithoutSemicolonError[source]

Bases: ldaptor.protocols.ldap.ldifprotocol.LDIFParseError

LDIF line without semicolon seen

exception ldaptor.protocols.ldap.ldifprotocol.LDIFParseError[source]

Bases: exceptions.Exception

Error parsing LDIF.

exception ldaptor.protocols.ldap.ldifprotocol.LDIFTruncatedError[source]

Bases: ldaptor.protocols.ldap.ldifprotocol.LDIFParseError

LDIF appears to be truncated

exception ldaptor.protocols.ldap.ldifprotocol.LDIFUnsupportedVersionError[source]

Bases: ldaptor.protocols.ldap.ldifprotocol.LDIFParseError

LDIF version not supported

exception ldaptor.protocols.ldap.ldifprotocol.LDIFVersionNotANumberError[source]

Bases: ldaptor.protocols.ldap.ldifprotocol.LDIFParseError

Non-numeric LDIF version number

ldaptor.protocols.ldap.proxy module

LDAP protocol proxy server

class ldaptor.protocols.ldap.proxy.Proxy(config)[source]

Bases: ldaptor.protocols.ldap.ldapserver.BaseLDAPServer

client = None
connectionLost(reason)[source]
connectionMade()[source]
handleUnknown(request, controls, reply)[source]
handle_LDAPUnbindRequest(request, controls, reply)[source]
protocol

alias of LDAPClient

unbound = False
waitingConnect = []
ldaptor.protocols.ldap.svcbindproxy module
class ldaptor.protocols.ldap.svcbindproxy.ServiceBindingProxy(services=None, fallback=None, *a, **kw)[source]

Bases: ldaptor.protocols.ldap.proxy.Proxy

An LDAP proxy that handles non-anonymous bind requests specially.

BindRequests are intercepted and authentication is attempted against each configured service. This authentication is performed against a separate LDAP entry, found by searching for entries with

  • objectClass: serviceSecurityObject
  • owner: the DN of the original bind attempt
  • cn: the service name.

starting at the identity-base as configured in the config file.

Finally, if the authentication does not succeed against any of the configured services, the proxy can fallback to passing the bind request to the real server.

fail_LDAPBindRequest

alias of LDAPBindResponse

fallback = False
handle_LDAPBindRequest(request, controls, reply)[source]
services = []
timestamp()[source]
Module contents

LDAP protocol logic

Submodules
ldaptor.protocols.pureber module

Pure, simple, BER encoding and decoding

class ldaptor.protocols.pureber.BERBase(tag=None)[source]
identification()[source]
tag = None
class ldaptor.protocols.pureber.BERBoolean(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BERBase

classmethod fromBER(klass, tag, content, berdecoder=None)[source]
tag = 1
class ldaptor.protocols.pureber.BERDecoderContext(fallback=None, inherit=None)[source]
Identities = {1: <class ldaptor.protocols.pureber.BERBoolean at 0x7ff1d754a3f8>, 2: <class ldaptor.protocols.pureber.BERInteger at 0x7ff1d754a2c0>, 4: <class ldaptor.protocols.pureber.BEROctetString at 0x7ff1d754a328>, 5: <class ldaptor.protocols.pureber.BERNull at 0x7ff1d754a390>, 10: <class ldaptor.protocols.pureber.BEREnumerated at 0x7ff1d754a460>, 16: <class 'ldaptor.protocols.pureber.BERSequence'>, 17: <class 'ldaptor.protocols.pureber.BERSet'>}
inherit()[source]
lookup_id(id)[source]
class ldaptor.protocols.pureber.BEREnumerated(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BERInteger

tag = 10
exception ldaptor.protocols.pureber.BERException[source]

Bases: exceptions.Exception

exception ldaptor.protocols.pureber.BERExceptionInsufficientData[source]

Bases: exceptions.Exception

class ldaptor.protocols.pureber.BERInteger(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BERBase

classmethod fromBER(klass, tag, content, berdecoder=None)[source]
tag = 2
value = None
class ldaptor.protocols.pureber.BERNull(tag=None)[source]

Bases: ldaptor.protocols.pureber.BERBase

classmethod fromBER(klass, tag, content, berdecoder=None)[source]
tag = 5
class ldaptor.protocols.pureber.BEROctetString(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BERBase

classmethod fromBER(klass, tag, content, berdecoder=None)[source]
tag = 4
value = None
class ldaptor.protocols.pureber.BERSequence(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BERStructured, UserList.UserList

classmethod fromBER(klass, tag, content, berdecoder=None)[source]
tag = 16
class ldaptor.protocols.pureber.BERSequenceOf(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BERSequence

class ldaptor.protocols.pureber.BERSet(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BERSequence

tag = 17
class ldaptor.protocols.pureber.BERStructured(tag=None)[source]

Bases: ldaptor.protocols.pureber.BERBase

identification()[source]
exception ldaptor.protocols.pureber.UnknownBERTag(tag, context)[source]

Bases: exceptions.Exception

ldaptor.protocols.pureber.ber2int(e, signed=True)[source]
ldaptor.protocols.pureber.berDecodeLength(m, offset=0)[source]

Return a tuple of (length, lengthLength). m must be atleast one byte long.

ldaptor.protocols.pureber.berDecodeMultiple(content, berdecoder) → [objects][source]

Decodes everything in content and returns a list of decoded objects.

All of content will be decoded, and content must contain complete BER objects.

ldaptor.protocols.pureber.berDecodeObject(context, string) -> (berobject, bytesUsed)[source]

berobject may be None.

ldaptor.protocols.pureber.int2ber(i, signed=True)[source]
ldaptor.protocols.pureber.int2berlen(i)[source]
ldaptor.protocols.pureber.need(buf, n)[source]
ldaptor.protocols.pureldap module

LDAP protocol message conversion; no application logic here.

class ldaptor.protocols.pureldap.LDAPAbandonRequest(value=None, id=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPProtocolRequest, ldaptor.protocols.pureldap.LDAPInteger

needs_answer = 0
tag = 80
class ldaptor.protocols.pureldap.LDAPAddRequest(entry=None, attributes=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPProtocolRequest, ldaptor.protocols.pureber.BERSequence

classmethod fromBER(klass, tag, content, berdecoder=None)[source]
tag = 72
class ldaptor.protocols.pureldap.LDAPAddResponse(resultCode=None, matchedDN=None, errorMessage=None, referral=None, serverSaslCreds=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPResult

tag = 73
class ldaptor.protocols.pureldap.LDAPAssertionValue(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BEROctetString

class ldaptor.protocols.pureldap.LDAPAttributeDescription(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BEROctetString

class ldaptor.protocols.pureldap.LDAPAttributeValue(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BEROctetString

class ldaptor.protocols.pureldap.LDAPAttributeValueAssertion(attributeDesc=None, assertionValue=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BERSequence

classmethod fromBER(klass, tag, content, berdecoder=None)[source]
class ldaptor.protocols.pureldap.LDAPBERDecoderContext(fallback=None, inherit=None)[source]

Bases: ldaptor.protocols.pureber.BERDecoderContext

Identities = {64: <class 'ldaptor.protocols.pureldap.LDAPBindRequest'>, 65: <class 'ldaptor.protocols.pureldap.LDAPBindResponse'>, 66: <class ldaptor.protocols.pureldap.LDAPUnbindRequest at 0x7ff1d754a8d8>, 67: <class 'ldaptor.protocols.pureldap.LDAPSearchRequest'>, 68: <class 'ldaptor.protocols.pureldap.LDAPSearchResultEntry'>, 69: <class 'ldaptor.protocols.pureldap.LDAPSearchResultDone'>, 70: <class 'ldaptor.protocols.pureldap.LDAPModifyRequest'>, 71: <class 'ldaptor.protocols.pureldap.LDAPModifyResponse'>, 72: <class 'ldaptor.protocols.pureldap.LDAPAddRequest'>, 73: <class 'ldaptor.protocols.pureldap.LDAPAddResponse'>, 74: <class ldaptor.protocols.pureldap.LDAPDelRequest at 0x7ff1d7576188>, 75: <class 'ldaptor.protocols.pureldap.LDAPDelResponse'>, 76: <class 'ldaptor.protocols.pureldap.LDAPModifyDNRequest'>, 77: <class 'ldaptor.protocols.pureldap.LDAPModifyDNResponse'>, 80: <class ldaptor.protocols.pureldap.LDAPAbandonRequest at 0x7ff1d75762c0>, 131: <class 'ldaptor.protocols.pureldap.LDAPReferral'>, 87: <class 'ldaptor.protocols.pureldap.LDAPExtendedRequest'>, 88: <class 'ldaptor.protocols.pureldap.LDAPExtendedResponse'>}
class ldaptor.protocols.pureldap.LDAPBERDecoderContext_BindResponse(fallback=None, inherit=None)[source]

Bases: ldaptor.protocols.pureber.BERDecoderContext

Identities = {135: <class ldaptor.protocols.pureldap.LDAPBindResponse_serverSaslCreds at 0x7ff1d754a808>}
class ldaptor.protocols.pureldap.LDAPBERDecoderContext_Filter(fallback=None, inherit=None)[source]

Bases: ldaptor.protocols.pureber.BERDecoderContext

Identities = {128: <class 'ldaptor.protocols.pureldap.LDAPFilter_and'>, 129: <class 'ldaptor.protocols.pureldap.LDAPFilter_or'>, 130: <class ldaptor.protocols.pureldap.LDAPFilter_not at 0x7ff1d754aa10>, 131: <class 'ldaptor.protocols.pureldap.LDAPFilter_equalityMatch'>, 132: <class 'ldaptor.protocols.pureldap.LDAPFilter_substrings'>, 133: <class 'ldaptor.protocols.pureldap.LDAPFilter_greaterOrEqual'>, 134: <class 'ldaptor.protocols.pureldap.LDAPFilter_lessOrEqual'>, 135: <class ldaptor.protocols.pureldap.LDAPFilter_present at 0x7ff1d754ac18>, 136: <class 'ldaptor.protocols.pureldap.LDAPFilter_approxMatch'>, 137: <class 'ldaptor.protocols.pureldap.LDAPFilter_extensibleMatch'>}
class ldaptor.protocols.pureldap.LDAPBERDecoderContext_Filter_substrings(fallback=None, inherit=None)[source]

Bases: ldaptor.protocols.pureber.BERDecoderContext

Identities = {128: <class ldaptor.protocols.pureldap.LDAPFilter_substrings_initial at 0x7ff1d754aa78>, 129: <class ldaptor.protocols.pureldap.LDAPFilter_substrings_any at 0x7ff1d754aae0>, 130: <class ldaptor.protocols.pureldap.LDAPFilter_substrings_final at 0x7ff1d754ab48>}
class ldaptor.protocols.pureldap.LDAPBERDecoderContext_LDAPBindRequest(fallback=None, inherit=None)[source]

Bases: ldaptor.protocols.pureber.BERDecoderContext

Identities = {128: <class ldaptor.protocols.pureber.BEROctetString at 0x7ff1d754a328>, 131: <class 'ldaptor.protocols.pureber.BERSequence'>}
class ldaptor.protocols.pureldap.LDAPBERDecoderContext_LDAPControls(fallback=None, inherit=None)[source]

Bases: ldaptor.protocols.pureber.BERDecoderContext

Identities = {16: <class 'ldaptor.protocols.pureldap.LDAPControl'>}
class ldaptor.protocols.pureldap.LDAPBERDecoderContext_LDAPExtendedRequest(fallback=None, inherit=None)[source]

Bases: ldaptor.protocols.pureber.BERDecoderContext

Identities = {128: <class ldaptor.protocols.pureber.BEROctetString at 0x7ff1d754a328>, 129: <class ldaptor.protocols.pureber.BEROctetString at 0x7ff1d754a328>}
class ldaptor.protocols.pureldap.LDAPBERDecoderContext_LDAPExtendedResponse(fallback=None, inherit=None)[source]

Bases: ldaptor.protocols.pureber.BERDecoderContext

Identities = {138: <class ldaptor.protocols.pureldap.LDAPResponseName at 0x7ff1d7576390>, 139: <class ldaptor.protocols.pureldap.LDAPResponse at 0x7ff1d75763f8>}
class ldaptor.protocols.pureldap.LDAPBERDecoderContext_LDAPMessage(fallback=None, inherit=None)[source]

Bases: ldaptor.protocols.pureber.BERDecoderContext

Identities = {128: <class 'ldaptor.protocols.pureldap.LDAPControls'>, 83: <class ldaptor.protocols.pureldap.LDAPSearchResultReference at 0x7ff1d754a7a0>}
class ldaptor.protocols.pureldap.LDAPBERDecoderContext_LDAPPasswordModifyRequest(fallback=None, inherit=None)[source]

Bases: ldaptor.protocols.pureber.BERDecoderContext

Identities = {128: <class ldaptor.protocols.pureldap.LDAPPasswordModifyRequest_userIdentity at 0x7ff1d75764c8>, 129: <class ldaptor.protocols.pureldap.LDAPPasswordModifyRequest_oldPasswd at 0x7ff1d7576530>, 130: <class ldaptor.protocols.pureldap.LDAPPasswordModifyRequest_newPasswd at 0x7ff1d7576598>}
class ldaptor.protocols.pureldap.LDAPBERDecoderContext_MatchingRuleAssertion(fallback=None, inherit=None)[source]

Bases: ldaptor.protocols.pureber.BERDecoderContext

Identities = {129: <class ldaptor.protocols.pureldap.LDAPMatchingRuleAssertion_matchingRule at 0x7ff1d754ad50>, 130: <class ldaptor.protocols.pureldap.LDAPMatchingRuleAssertion_type at 0x7ff1d754adb8>, 131: <class ldaptor.protocols.pureldap.LDAPMatchingRuleAssertion_matchValue at 0x7ff1d754ae20>, 132: <class ldaptor.protocols.pureldap.LDAPMatchingRuleAssertion_dnAttributes at 0x7ff1d754ae88>}
class ldaptor.protocols.pureldap.LDAPBERDecoderContext_ModifyDNRequest(fallback=None, inherit=None)[source]

Bases: ldaptor.protocols.pureber.BERDecoderContext

Identities = {128: <class ldaptor.protocols.pureldap.LDAPModifyDNResponse_newSuperior at 0x7ff1d75761f0>}
class ldaptor.protocols.pureldap.LDAPBERDecoderContext_TopLevel(fallback=None, inherit=None)[source]

Bases: ldaptor.protocols.pureber.BERDecoderContext

Identities = {16: <class 'ldaptor.protocols.pureldap.LDAPMessage'>}
class ldaptor.protocols.pureldap.LDAPBindRequest(version=None, dn=None, auth=None, tag=None, sasl=False)[source]

Bases: ldaptor.protocols.pureldap.LDAPProtocolRequest, ldaptor.protocols.pureber.BERSequence

classmethod fromBER(klass, tag, content, berdecoder=None)[source]
tag = 64
class ldaptor.protocols.pureldap.LDAPBindResponse(resultCode=None, matchedDN=None, errorMessage=None, referral=None, serverSaslCreds=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPResult

errorMessage = None
classmethod fromBER(klass, tag, content, berdecoder=None)[source]
matchedDN = None
referral = None
resultCode = None
serverSaslCreds = None
tag = 65
class ldaptor.protocols.pureldap.LDAPBindResponse_serverSaslCreds(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BEROctetString

tag = 135
class ldaptor.protocols.pureldap.LDAPControl(controlType, criticality=None, controlValue=None, id=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BERSequence

controlValue = None
criticality = None
classmethod fromBER(klass, tag, content, berdecoder=None)[source]
class ldaptor.protocols.pureldap.LDAPControls(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BERSequence

classmethod fromBER(klass, tag, content, berdecoder=None)[source]
tag = 128
class ldaptor.protocols.pureldap.LDAPDelRequest(value=None, entry=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPProtocolRequest, ldaptor.protocols.pureldap.LDAPString

tag = 74
class ldaptor.protocols.pureldap.LDAPDelResponse(resultCode=None, matchedDN=None, errorMessage=None, referral=None, serverSaslCreds=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPResult

tag = 75
class ldaptor.protocols.pureldap.LDAPExtendedRequest(requestName=None, requestValue=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPProtocolRequest, ldaptor.protocols.pureber.BERSequence

classmethod fromBER(klass, tag, content, berdecoder=None)[source]
requestName = None
requestValue = None
tag = 87
class ldaptor.protocols.pureldap.LDAPExtendedResponse(resultCode=None, matchedDN=None, errorMessage=None, referral=None, serverSaslCreds=None, responseName=None, response=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPResult

classmethod fromBER(klass, tag, content, berdecoder=None)[source]
response = None
responseName = None
tag = 88
class ldaptor.protocols.pureldap.LDAPFilter(tag=None)[source]

Bases: ldaptor.protocols.pureber.BERStructured

class ldaptor.protocols.pureldap.LDAPFilterSet(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BERSet

classmethod fromBER(klass, tag, content, berdecoder=None)[source]
class ldaptor.protocols.pureldap.LDAPFilter_and(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPFilterSet

asText()[source]
tag = 128
class ldaptor.protocols.pureldap.LDAPFilter_approxMatch(attributeDesc=None, assertionValue=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPAttributeValueAssertion

asText()[source]
tag = 136
class ldaptor.protocols.pureldap.LDAPFilter_equalityMatch(attributeDesc=None, assertionValue=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPAttributeValueAssertion

asText()[source]
tag = 131
class ldaptor.protocols.pureldap.LDAPFilter_extensibleMatch(matchingRule=None, type=None, matchValue=None, dnAttributes=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPMatchingRuleAssertion

tag = 137
class ldaptor.protocols.pureldap.LDAPFilter_greaterOrEqual(attributeDesc=None, assertionValue=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPAttributeValueAssertion

asText()[source]
tag = 133
class ldaptor.protocols.pureldap.LDAPFilter_lessOrEqual(attributeDesc=None, assertionValue=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPAttributeValueAssertion

asText()[source]
tag = 134
class ldaptor.protocols.pureldap.LDAPFilter_not(value, tag=130)[source]

Bases: ldaptor.protocols.pureldap.LDAPFilter

asText()[source]
classmethod fromBER(klass, tag, content, berdecoder=None)[source]
tag = 130
class ldaptor.protocols.pureldap.LDAPFilter_or(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPFilterSet

asText()[source]
tag = 129
class ldaptor.protocols.pureldap.LDAPFilter_present(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPAttributeDescription

asText()[source]
tag = 135
class ldaptor.protocols.pureldap.LDAPFilter_substrings(type=None, substrings=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BERSequence

asText()[source]
classmethod fromBER(klass, tag, content, berdecoder=None)[source]
tag = 132
class ldaptor.protocols.pureldap.LDAPFilter_substrings_any(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPString

asText()[source]
tag = 129
class ldaptor.protocols.pureldap.LDAPFilter_substrings_final(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPString

asText()[source]
tag = 130
class ldaptor.protocols.pureldap.LDAPFilter_substrings_initial(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPString

asText()[source]
tag = 128
class ldaptor.protocols.pureldap.LDAPInteger(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BERInteger

class ldaptor.protocols.pureldap.LDAPMatchingRuleAssertion(matchingRule=None, type=None, matchValue=None, dnAttributes=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BERSequence

dnAttributes = None
classmethod fromBER(klass, tag, content, berdecoder=None)[source]
matchValue = None
matchingRule = None
type = None
class ldaptor.protocols.pureldap.LDAPMatchingRuleAssertion_dnAttributes(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BERBoolean

tag = 132
class ldaptor.protocols.pureldap.LDAPMatchingRuleAssertion_matchValue(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPAssertionValue

tag = 131
class ldaptor.protocols.pureldap.LDAPMatchingRuleAssertion_matchingRule(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPMatchingRuleId

tag = 129
class ldaptor.protocols.pureldap.LDAPMatchingRuleAssertion_type(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPAttributeDescription

tag = 130
class ldaptor.protocols.pureldap.LDAPMatchingRuleId(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPString

class ldaptor.protocols.pureldap.LDAPMessage(value=None, controls=None, id=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BERSequence

classmethod fromBER(klass, tag, content, berdecoder=None)[source]
id = None
value = None
class ldaptor.protocols.pureldap.LDAPModifyDNRequest(entry, newrdn, deleteoldrdn, newSuperior=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPProtocolRequest, ldaptor.protocols.pureber.BERSequence

deleteoldrdn = None
entry = None
classmethod fromBER(klass, tag, content, berdecoder=None)[source]
newSuperior = None
newrdn = None
tag = 76
class ldaptor.protocols.pureldap.LDAPModifyDNResponse(resultCode=None, matchedDN=None, errorMessage=None, referral=None, serverSaslCreds=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPResult

tag = 77
class ldaptor.protocols.pureldap.LDAPModifyDNResponse_newSuperior(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPString

tag = 128
class ldaptor.protocols.pureldap.LDAPModifyRequest(object=None, modification=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPProtocolRequest, ldaptor.protocols.pureber.BERSequence

classmethod fromBER(klass, tag, content, berdecoder=None)[source]
modification = None
object = None
tag = 70
class ldaptor.protocols.pureldap.LDAPModifyResponse(resultCode=None, matchedDN=None, errorMessage=None, referral=None, serverSaslCreds=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPResult

tag = 71
class ldaptor.protocols.pureldap.LDAPOID(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BEROctetString

class ldaptor.protocols.pureldap.LDAPPasswordModifyRequest(requestName=None, userIdentity=None, oldPasswd=None, newPasswd=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPExtendedRequest

oid = '1.3.6.1.4.1.4203.1.11.1'
class ldaptor.protocols.pureldap.LDAPPasswordModifyRequest_newPasswd(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BEROctetString

tag = 130
class ldaptor.protocols.pureldap.LDAPPasswordModifyRequest_oldPasswd(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BEROctetString

tag = 129
class ldaptor.protocols.pureldap.LDAPPasswordModifyRequest_userIdentity(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BEROctetString

tag = 128
class ldaptor.protocols.pureldap.LDAPProtocolOp[source]
class ldaptor.protocols.pureldap.LDAPProtocolRequest[source]

Bases: ldaptor.protocols.pureldap.LDAPProtocolOp

needs_answer = 1
class ldaptor.protocols.pureldap.LDAPProtocolResponse[source]

Bases: ldaptor.protocols.pureldap.LDAPProtocolOp

class ldaptor.protocols.pureldap.LDAPReferral(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BERSequence

tag = 131
class ldaptor.protocols.pureldap.LDAPResponse(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BEROctetString

tag = 139
class ldaptor.protocols.pureldap.LDAPResponseName(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPOID

tag = 138
class ldaptor.protocols.pureldap.LDAPResult(resultCode=None, matchedDN=None, errorMessage=None, referral=None, serverSaslCreds=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPProtocolResponse, ldaptor.protocols.pureber.BERSequence

classmethod fromBER(klass, tag, content, berdecoder=None)[source]
class ldaptor.protocols.pureldap.LDAPSearchRequest(baseObject=None, scope=None, derefAliases=None, sizeLimit=None, timeLimit=None, typesOnly=None, filter=None, attributes=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPProtocolRequest, ldaptor.protocols.pureber.BERSequence

attributes = []
baseObject = ''
derefAliases = 0
filter = LDAPFilter_present(value='objectClass')
classmethod fromBER(klass, tag, content, berdecoder=None)[source]
scope = 2
sizeLimit = 0
tag = 67
timeLimit = 0
typesOnly = 0
class ldaptor.protocols.pureldap.LDAPSearchResultDone(resultCode=None, matchedDN=None, errorMessage=None, referral=None, serverSaslCreds=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPResult

tag = 69
class ldaptor.protocols.pureldap.LDAPSearchResultEntry(objectName, attributes, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPProtocolResponse, ldaptor.protocols.pureber.BERSequence

classmethod fromBER(klass, tag, content, berdecoder=None)[source]
tag = 68
class ldaptor.protocols.pureldap.LDAPSearchResultReference[source]

Bases: ldaptor.protocols.pureldap.LDAPProtocolResponse

classmethod fromBER(tag, content, berdecoder=None)[source]
tag = 83
class ldaptor.protocols.pureldap.LDAPStartTLSRequest(requestName=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPExtendedRequest

Request to start Transport Layer Security. See RFC 2830 for details.

oid = '1.3.6.1.4.1.1466.20037'
class ldaptor.protocols.pureldap.LDAPStartTLSResponse(resultCode=None, matchedDN=None, errorMessage=None, referral=None, serverSaslCreds=None, responseName=None, response=None, tag=None)[source]

Bases: ldaptor.protocols.pureldap.LDAPExtendedResponse

Response to start Transport Layer Security. See RFC 4511 section 4.14.2 for details.

oid = '1.3.6.1.4.1.1466.20037'
class ldaptor.protocols.pureldap.LDAPString(value=None, tag=None)[source]

Bases: ldaptor.protocols.pureber.BEROctetString

class ldaptor.protocols.pureldap.LDAPUnbindRequest(*args, **kwargs)[source]

Bases: ldaptor.protocols.pureldap.LDAPProtocolRequest, ldaptor.protocols.pureber.BERNull

needs_answer = 0
tag = 66
ldaptor.protocols.pureldap.alloc_ldap_message_id()[source]
ldaptor.protocols.pureldap.escape(s)[source]
Module contents
ldaptor.samba package
Submodules
ldaptor.samba.smbpassword module
ldaptor.samba.smbpassword.lmhash(password='')

Generates lanman password hash for a given password.

Note that the author thinks LanMan hashes should be banished from the face of the earth.

ldaptor.samba.smbpassword.lmhash_locked(password='')[source]

Generates a lanman password hash that matches no password.

Note that the author thinks LanMan hashes should be banished from the face of the earth.

ldaptor.samba.smbpassword.nthash(password='')[source]

Generates nt md4 password hash for a given password.

Module contents
Submodules
ldaptor.attributeset module
class ldaptor.attributeset.LDAPAttributeSet(key, *a, **kw)[source]

Bases: set

copy()[source]
difference(other)[source]
intersection(other)[source]
symmetric_difference(other)[source]
union(other)[source]
ldaptor.checkers module
class ldaptor.checkers.LDAPBindingChecker(cfg)[source]

The avatarID returned is an LDAPEntry.

credentialInterfaces = (<InterfaceClass twisted.cred.credentials.IUsernamePassword>,)
requestAvatarId(credentials)[source]
ldaptor.checkers.makeFilter(name, template=None)[source]
ldaptor.compat module
ldaptor.compat.print_(*args, **kwds)[source]

The new-style print function.

ldaptor.compat.callable(object) → bool

Return whether the object is callable (i.e., some kind of function). Note that classes are callable, as are instances with a __call__() method.

ldaptor.compat.base_string_types

alias of basestring

ldaptor.compat.u(s)[source]
ldaptor.compat.b(s)[source]
class ldaptor.compat.unicode(object='') → unicode object

Bases: basestring

unicode(string[, encoding[, errors]]) -> unicode object

Create a new Unicode object from the given encoded string. encoding defaults to the current default string encoding. errors can be ‘strict’, ‘replace’ or ‘ignore’ and defaults to ‘strict’.

capitalize() → unicode

Return a capitalized version of S, i.e. make the first character have upper case and the rest lower case.

center(width[, fillchar]) → unicode

Return S centered in a Unicode string of length width. Padding is done using the specified fill character (default is a space)

count(sub[, start[, end]]) → int

Return the number of non-overlapping occurrences of substring sub in Unicode string S[start:end]. Optional arguments start and end are interpreted as in slice notation.

decode([encoding[, errors]]) → string or unicode

Decodes S using the codec registered for encoding. encoding defaults to the default encoding. errors may be given to set a different error handling scheme. Default is ‘strict’ meaning that encoding errors raise a UnicodeDecodeError. Other possible values are ‘ignore’ and ‘replace’ as well as any other name registered with codecs.register_error that is able to handle UnicodeDecodeErrors.

encode([encoding[, errors]]) → string or unicode

Encodes S using the codec registered for encoding. encoding defaults to the default encoding. errors may be given to set a different error handling scheme. Default is ‘strict’ meaning that encoding errors raise a UnicodeEncodeError. Other possible values are ‘ignore’, ‘replace’ and ‘xmlcharrefreplace’ as well as any other name registered with codecs.register_error that can handle UnicodeEncodeErrors.

endswith(suffix[, start[, end]]) → bool

Return True if S ends with the specified suffix, False otherwise. With optional start, test S beginning at that position. With optional end, stop comparing S at that position. suffix can also be a tuple of strings to try.

expandtabs([tabsize]) → unicode

Return a copy of S where all tab characters are expanded using spaces. If tabsize is not given, a tab size of 8 characters is assumed.

find(sub[, start[, end]]) → int

Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end]. Optional arguments start and end are interpreted as in slice notation.

Return -1 on failure.

format(*args, **kwargs) → unicode

Return a formatted version of S, using substitutions from args and kwargs. The substitutions are identified by braces (‘{‘ and ‘}’).

index(sub[, start[, end]]) → int

Like S.find() but raise ValueError when the substring is not found.

isalnum() → bool

Return True if all characters in S are alphanumeric and there is at least one character in S, False otherwise.

isalpha() → bool

Return True if all characters in S are alphabetic and there is at least one character in S, False otherwise.

isdecimal() → bool

Return True if there are only decimal characters in S, False otherwise.

isdigit() → bool

Return True if all characters in S are digits and there is at least one character in S, False otherwise.

islower() → bool

Return True if all cased characters in S are lowercase and there is at least one cased character in S, False otherwise.

isnumeric() → bool

Return True if there are only numeric characters in S, False otherwise.

isspace() → bool

Return True if all characters in S are whitespace and there is at least one character in S, False otherwise.

istitle() → bool

Return True if S is a titlecased string and there is at least one character in S, i.e. upper- and titlecase characters may only follow uncased characters and lowercase characters only cased ones. Return False otherwise.

isupper() → bool

Return True if all cased characters in S are uppercase and there is at least one cased character in S, False otherwise.

join(iterable) → unicode

Return a string which is the concatenation of the strings in the iterable. The separator between elements is S.

ljust(width[, fillchar]) → int

Return S left-justified in a Unicode string of length width. Padding is done using the specified fill character (default is a space).

lower() → unicode

Return a copy of the string S converted to lowercase.

lstrip([chars]) → unicode

Return a copy of the string S with leading whitespace removed. If chars is given and not None, remove characters in chars instead. If chars is a str, it will be converted to unicode before stripping

partition(sep) -> (head, sep, tail)

Search for the separator sep in S, and return the part before it, the separator itself, and the part after it. If the separator is not found, return S and two empty strings.

replace(old, new[, count]) → unicode

Return a copy of S with all occurrences of substring old replaced by new. If the optional argument count is given, only the first count occurrences are replaced.

rfind(sub[, start[, end]]) → int

Return the highest index in S where substring sub is found, such that sub is contained within S[start:end]. Optional arguments start and end are interpreted as in slice notation.

Return -1 on failure.

rindex(sub[, start[, end]]) → int

Like S.rfind() but raise ValueError when the substring is not found.

rjust(width[, fillchar]) → unicode

Return S right-justified in a Unicode string of length width. Padding is done using the specified fill character (default is a space).

rpartition(sep) -> (head, sep, tail)

Search for the separator sep in S, starting at the end of S, and return the part before it, the separator itself, and the part after it. If the separator is not found, return two empty strings and S.

rsplit([sep[, maxsplit]]) → list of strings

Return a list of the words in S, using sep as the delimiter string, starting at the end of the string and working to the front. If maxsplit is given, at most maxsplit splits are done. If sep is not specified, any whitespace string is a separator.

rstrip([chars]) → unicode

Return a copy of the string S with trailing whitespace removed. If chars is given and not None, remove characters in chars instead. If chars is a str, it will be converted to unicode before stripping

split([sep[, maxsplit]]) → list of strings

Return a list of the words in S, using sep as the delimiter string. If maxsplit is given, at most maxsplit splits are done. If sep is not specified or is None, any whitespace string is a separator and empty strings are removed from the result.

splitlines(keepends=False) → list of strings

Return a list of the lines in S, breaking at line boundaries. Line breaks are not included in the resulting list unless keepends is given and true.

startswith(prefix[, start[, end]]) → bool

Return True if S starts with the specified prefix, False otherwise. With optional start, test S beginning at that position. With optional end, stop comparing S at that position. prefix can also be a tuple of strings to try.

strip([chars]) → unicode

Return a copy of the string S with leading and trailing whitespace removed. If chars is given and not None, remove characters in chars instead. If chars is a str, it will be converted to unicode before stripping

swapcase() → unicode

Return a copy of S with uppercase characters converted to lowercase and vice versa.

title() → unicode

Return a titlecased version of S, i.e. words start with title case characters, all remaining cased characters have lower case.

translate(table) → unicode

Return a copy of the string S, where all characters have been mapped through the given translation table, which must be a mapping of Unicode ordinals to Unicode ordinals, Unicode strings or None. Unmapped characters are left untouched. Characters mapped to None are deleted.

upper() → unicode

Return a copy of S converted to uppercase.

zfill(width) → unicode

Pad a numeric string S with zeros on the left, to fill a field of the specified width. The string S is never truncated.

ldaptor.compat.bytes

alias of str

ldaptor.compat.uascii_to_str(s)[source]

helper to convert ascii unicode -> native str

ldaptor.compat.bascii_to_str(s)[source]

helper to convert ascii bytes -> native str

ldaptor.compat.str_to_uascii(s)[source]

helper to convert ascii native str -> unicode

ldaptor.compat.str_to_bascii(s)[source]

helper to convert ascii native str -> bytes

ldaptor.compat.join_unicode()

S.join(iterable) -> unicode

Return a string which is the concatenation of the strings in the iterable. The separator between elements is S.

ldaptor.compat.join_bytes()

S.join(iterable) -> string

Return a string which is the concatenation of the strings in the iterable. The separator between elements is S.

ldaptor.compat.join_byte_values(values)[source]
ldaptor.compat.join_byte_elems()

S.join(iterable) -> string

Return a string which is the concatenation of the strings in the iterable. The separator between elements is S.

ldaptor.compat.byte_elem_value()

ord(c) -> integer

Return the integer ordinal of a one-character string.

ldaptor.compat.iter_byte_values(s)[source]

iterate over byte string as sequence of ints 0-255

ldaptor.compat.irange

alias of xrange

class ldaptor.compat.imap

Bases: object

imap(func, *iterables) –> imap object

Make an iterator that computes the function using arguments from each of the iterables. Like map() except that it returns an iterator instead of a list and that it stops when the shortest iterable is exhausted instead of filling in None for shorter iterables.

next
ldaptor.compat.lmap()

map(function, sequence[, sequence, ...]) -> list

Return a list of the results of applying the function to the items of the argument sequence(s). If more than one sequence is given, the function is called with an argument list consisting of the corresponding item of each sequence, substituting None for missing values when not all sequences have the same length. If the function is None, return a list of the items of the sequence (or a list of tuples if more than one sequence).

ldaptor.compat.iteritems(d)[source]
ldaptor.compat.itervalues(d)[source]
ldaptor.compat.next(iterator[, default])

Return the next item from the iterator. If default is given and the iterator is exhausted, it is returned instead of raising StopIteration.

ldaptor.compat.exc_err()[source]

return current error object (to avoid try/except syntax change)

ldaptor.compat.get_method_function(func)[source]

given (potential) method, return underlying function

ldaptor.compat.add_doc(obj, doc)[source]

add docstring to an object

ldaptor.config module
class ldaptor.config.LDAPConfig(baseDN=None, serviceLocationOverrides=None, identityBaseDN=None, identitySearch=None)[source]

Bases: object

baseDN = None
copy(**kw)[source]
getBaseDN()[source]
getIdentityBaseDN()[source]
getIdentitySearch(name)[source]
getServiceLocationOverrides()[source]
identityBaseDN = None
identitySearch = None
exception ldaptor.config.MissingBaseDNError[source]

Bases: exceptions.Exception

Configuration must specify a base DN

ldaptor.config.loadConfig(configFiles=None, reload=False)[source]

Load configuration file.

ldaptor.config.useLMhash()[source]

Read configuration file if necessary and return whether to use LanMan hashes or not.

ldaptor.delta module

Changes to the content of one single LDAP entry.

(This means these do not belong here: adding or deleting of entries, changing of location in tree)

class ldaptor.delta.Add(key, *a, **kw)[source]

Bases: ldaptor.delta.Modification

asLDIF()[source]
patch(entry)[source]
class ldaptor.delta.AddOp(entry)[source]

Bases: ldaptor.delta.Operation

asLDIF()[source]
patch(root)[source]
class ldaptor.delta.Delete(key, *a, **kw)[source]

Bases: ldaptor.delta.Modification

asLDIF()[source]
patch(entry)[source]
class ldaptor.delta.DeleteOp(dn)[source]

Bases: ldaptor.delta.Operation

asLDIF()[source]
patch(root)[source]
class ldaptor.delta.Modification(key, *a, **kw)[source]

Bases: ldaptor.attributeset.LDAPAttributeSet

asLDAP()[source]
patch(entry)[source]
class ldaptor.delta.ModifyOp(dn, modifications=[])[source]

Bases: ldaptor.delta.Operation

asLDAP()[source]
asLDIF()[source]
classmethod fromLDAP(class_, request)[source]
patch(root)[source]
class ldaptor.delta.Operation[source]

Bases: object

patch(root)[source]

Find the correct entry in IConnectedLDAPEntry and patch it.

@param root: IConnectedLDAPEntry that is at the root of the subtree the patch applies to.

@returns: Deferred with None or failure.

class ldaptor.delta.Replace(key, *a, **kw)[source]

Bases: ldaptor.delta.Modification

asLDIF()[source]
patch(entry)[source]
ldaptor.dns module

DNS-related utilities.

ldaptor.dns.aton(ip)[source]
ldaptor.dns.aton_numbits(num)[source]
ldaptor.dns.aton_octets(ip)[source]
ldaptor.dns.netmaskToNumbits(netmask)[source]
ldaptor.dns.ntoa(n)[source]
ldaptor.dns.ptrSoaName(ip, netmask)[source]

Convert an IP address and netmask to a CIDR delegation -style zone name.

ldaptor.entry module
class ldaptor.entry.BaseLDAPEntry(dn, attributes={})[source]

Bases: object

bind(password)[source]
buildAttributeSet(key, values)[source]
diff(other)[source]

Compute differences between this and another LDAP entry.

@param other: An LDAPEntry to compare to.

@return: None if equal, otherwise a ModifyOp that would make this entry look like other.

dn = None
get(key, default=None)[source]
hasMember(dn)[source]
has_key(key)[source]
items()[source]
keys()[source]
class ldaptor.entry.EditableLDAPEntry(dn, attributes={})[source]

Bases: ldaptor.entry.BaseLDAPEntry

commit()[source]
delete()[source]
move(newDN)[source]
setPassword(newPasswd, salt=None)[source]
undo()[source]
ldaptor.entry.sshaDigest(passphrase, salt=None)[source]
ldaptor.entryhelpers module
class ldaptor.entryhelpers.DiffTreeMixin[source]

Bases: object

diffTree(other, result=None)[source]
class ldaptor.entryhelpers.MatchMixin[source]

Bases: object

match(filter)[source]
class ldaptor.entryhelpers.SearchByTreeWalkingMixin[source]

Bases: object

search(filterText=None, filterObject=None, attributes=(), scope=None, derefAliases=None, sizeLimit=0, timeLimit=0, typesOnly=0, callback=None)[source]
class ldaptor.entryhelpers.SubtreeFromChildrenMixin[source]

Bases: object

subtree(callback=None)[source]
ldaptor.entryhelpers.safelower(s)[source]

As string.lower(), but return s if something goes wrong.

ldaptor.generate_password module
exception ldaptor.generate_password.PwgenException[source]

Bases: exceptions.Exception

class ldaptor.generate_password.ReadPassword(deferred, count=1)[source]

Bases: twisted.internet.protocol.ProcessProtocol

errReceived(data)[source]
outReceived(data)[source]
processEnded(reason)[source]
ldaptor.generate_password.generate(reactor, n=1)[source]
ldaptor.inmemory module
class ldaptor.inmemory.InMemoryLDIFProtocol[source]

Bases: ldaptor.protocols.ldap.ldifprotocol.LDIF

Receive LDIF data and gather results into an ReadOnlyInMemoryLDAPEntry.

You can override lookupFailed and addFailed to provide smarter error handling. They are called as Deferred errbacks; returning the reason causes error to pass onward and abort the whole operation. Returning None from lookupFailed skips that entry, but continues loading.

When the full LDIF data has been read, the completed Deferred will trigger.

addFailed(reason, entry)[source]
connectionLost(reason)[source]
gotEntry(entry)[source]
lookupFailed(reason, entry)[source]
exception ldaptor.inmemory.LDAPCannotRemoveRootError(message=None)[source]

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPNamingViolation

Cannot remove root of LDAP tree

class ldaptor.inmemory.ReadOnlyInMemoryLDAPEntry(*a, **kw)[source]

Bases: ldaptor.entry.EditableLDAPEntry, ldaptor.entryhelpers.DiffTreeMixin, ldaptor.entryhelpers.SubtreeFromChildrenMixin, ldaptor.entryhelpers.MatchMixin, ldaptor.entryhelpers.SearchByTreeWalkingMixin

addChild(rdn, attributes)[source]

TODO ugly API. Returns the created entry.

children(callback=None)[source]
commit()[source]
delete()[source]
deleteChild(rdn)[source]
fetch(*attributes)[source]
lookup(dn)[source]
move(newDN)[source]
parent()[source]
ldaptor.inmemory.fromLDIFFile(f)[source]

Read LDIF data from a file.

ldaptor.insensitive module
class ldaptor.insensitive.InsensitiveString[source]

Bases: str

A str subclass that performs all matching without regard to case.

ldaptor.interfaces module
ldaptor.ldapfilter module
exception ldaptor.ldapfilter.InvalidLDAPFilter(msg, loc, text)[source]

Bases: exceptions.Exception

ldaptor.ldapfilter.parseExtensible(attr, s)[source]
ldaptor.ldapfilter.parseFilter(s)[source]
ldaptor.ldapfilter.parseMaybeSubstring(attrType, s)[source]
ldaptor.ldiftree module

Manage LDAP data as a tree of LDIF files.

exception ldaptor.ldiftree.LDAPCannotRemoveRootError(message=None)[source]

Bases: ldaptor.protocols.ldap.ldaperrors.LDAPNamingViolation

Cannot remove root of LDAP tree

class ldaptor.ldiftree.LDIFTreeEntry(path, dn=None, *a, **kw)[source]

Bases: ldaptor.entry.EditableLDAPEntry, ldaptor.entryhelpers.DiffTreeMixin, ldaptor.entryhelpers.SubtreeFromChildrenMixin, ldaptor.entryhelpers.MatchMixin, ldaptor.entryhelpers.SearchByTreeWalkingMixin

addChild(rdn, attributes)[source]
children(callback=None)[source]
commit()[source]
delete()[source]
deleteChild(rdn)[source]
lookup(dn)[source]
move(newDN)[source]
parent()[source]
exception ldaptor.ldiftree.LDIFTreeEntryContainsMultipleEntries[source]

Bases: exceptions.Exception

LDIFTree entry contains multiple LDIF entries.

exception ldaptor.ldiftree.LDIFTreeEntryContainsNoEntries[source]

Bases: exceptions.Exception

LDIFTree entry does not contain a valid LDIF entry.

exception ldaptor.ldiftree.LDIFTreeNoSuchObject[source]

Bases: exceptions.Exception

LDIFTree does not contain such entry.

class ldaptor.ldiftree.StoreParsedLDIF[source]

Bases: ldaptor.protocols.ldap.ldifprotocol.LDIF

connectionLost(reason)[source]
gotEntry(obj)[source]
ldaptor.ldiftree.get(path, dn)[source]
ldaptor.ldiftree.put(path, entry)[source]
ldaptor.md4 module

helper implementing insecure and obsolete md4 algorithm. used for NTHASH format, which is also insecure and broken, since it’s just md4(password)

implementated based on rfc at http://www.faqs.org/rfcs/rfc1320.html

ldaptor.md4.md4(content=None)[source]

wrapper for hashlib.new(‘md4’)

ldaptor.numberalloc module

Find an available uidNumber/gidNumber/other similar number.

class ldaptor.numberalloc.freeNumberGuesser(makeAGuess, min=None, max=None)[source]
startGuessing()[source]
ldaptor.numberalloc.getFreeNumber(ldapObject, numberType, min=None, max=None)[source]
class ldaptor.numberalloc.ldapGuesser(ldapObject, numberType)[source]
guess(num)[source]
ldaptor.schema module
class ldaptor.schema.ASN1ParserThingie[source]
class ldaptor.schema.AttributeTypeDescription(text)[source]

Bases: ldaptor.schema.ASN1ParserThingie

ASN Syntax:

AttributeTypeDescription = "(" whsp
        numericoid whsp                ; AttributeType identifier
        [ "NAME" qdescrs ]             ; name used in AttributeType
        [ "DESC" qdstring ]            ; description
        [ "OBSOLETE" whsp ]
        [ "SUP" woid ]                 ; derived from this other AttributeType
        [ "EQUALITY" woid              ; Matching Rule name
        [ "ORDERING" woid              ; Matching Rule name
        [ "SUBSTR" woid ]              ; Matching Rule name
        [ "SYNTAX" whsp noidlen whsp ] ; see section 4.3
        [ "SINGLE-VALUE" whsp ]        ; default multi-valued
        [ "COLLECTIVE" whsp ]          ; default not collective
        [ "NO-USER-MODIFICATION" whsp ]; default user modifiable
        [ "USAGE" whsp AttributeUsage ]; default userApplications
        whsp ")"

AttributeUsage =
        "userApplications"     /
        "directoryOperation"   /
        "distributedOperation" / ; DSA-shared
        "dSAOperation"          ; DSA-specific, value depends on server

noidlen = numericoid [ "{" len "}" ]

len     = numericstring
class ldaptor.schema.MatchingRuleDescription(text)[source]

Bases: ldaptor.schema.ASN1ParserThingie

ASN Syntax:

MatchingRuleDescription = "(" whsp
        numericoid whsp  ; MatchingRule identifier
        [ "NAME" qdescrs ]
        [ "DESC" qdstring ]
        [ "OBSOLETE" whsp ]
        "SYNTAX" numericoid
        whsp ")"
class ldaptor.schema.ObjectClassDescription(text)[source]

Bases: ldaptor.schema.ASN1ParserThingie

ASN Syntax:

d               = "0" / "1" / "2" / "3" / "4" /
                  "5" / "6" / "7" / "8" / "9"

numericstring   = 1*d

numericoid      = numericstring *( "." numericstring )

space           = 1*" "

whsp            = [ space ]

descr           = keystring

qdescr          = whsp "'" descr "'" whsp

qdescrlist      = [ qdescr *( qdescr ) ]

; object descriptors used as schema element names
qdescrs         = qdescr / ( whsp "(" qdescrlist ")" whsp )

dstring         = 1*utf8

qdstring        = whsp "'" dstring "'" whsp

descr           = keystring

oid             = descr / numericoid

woid            = whsp oid whsp

; set of oids of either form
oids            = woid / ( "(" oidlist ")" )

ObjectClassDescription = "(" whsp
        numericoid whsp      ; ObjectClass identifier
        [ "NAME" qdescrs ]
        [ "DESC" qdstring ]
        [ "OBSOLETE" whsp ]
        [ "SUP" oids ]       ; Superior ObjectClasses
        [ ( "ABSTRACT" / "STRUCTURAL" / "AUXILIARY" ) whsp ]
                             ; default structural
        [ "MUST" oids ]      ; AttributeTypes
        [ "MAY" oids ]       ; AttributeTypes
        whsp ")"
class ldaptor.schema.SyntaxDescription(text)[source]

Bases: ldaptor.schema.ASN1ParserThingie

ASN Syntax:

SyntaxDescription = "(" whsp
        numericoid whsp
        [ "DESC" qdstring ]
        whsp ")"
ldaptor.schema.extractWord(text)[source]
ldaptor.schema.peekWord(text)[source]
ldaptor.testutil module

Utilities for writing Twistedy unit tests and debugging.

class ldaptor.testutil.FakeTransport(proto)[source]
loseConnection()[source]
class ldaptor.testutil.LDAPClientTestDriver(*responses)[source]

A test driver that looks somewhat like a real LDAPClient.

Pass in a list of lists of LDAPProtocolResponses. For each sent LDAP message, the first item of said list is iterated through, and all the items are sent as responses to the callback. The sent LDAP messages are stored in self.sent, so you can assert that the sent messages are what they are supposed to be.

It is also possible to include a Failure instance instead of a list of LDAPProtocolResponses which will cause the errback to be called with the failure.

assertNothingSent()[source]
assertSent(*shouldBeSent)[source]
connectionLost(reason=None)[source]

Called when TCP connection has been lost

connectionMade()[source]

TCP connection has opened

fakeUnbindResponse = 'fake-unbind-by-LDAPClientTestDriver'
send(op)[source]
send_multiResponse(op, handler, *args, **kwargs)[source]
send_noResponse(op)[source]
unbind()[source]
ldaptor.testutil.calltrace()[source]

Print out all function calls. For debug use only.

ldaptor.testutil.createServer(proto, *responses, **kw)[source]

Create an LDAP server for testing. :param proto: The server protocol factory (e.g. ProxyBase). :param responses: The responses to initialize the LDAPClientTestDrive. :param proto_args: Optional mapping passed as keyword args to protocol factory.

ldaptor.testutil.mustRaise(dummy)[source]
ldaptor.usage module
class ldaptor.usage.Options[source]

Bases: twisted.python.usage.Options

optParameters = ()
postOptions()[source]
class ldaptor.usage.Options_base[source]

Bases: ldaptor.usage.Options_base_optional

postOptions_base()[source]
class ldaptor.usage.Options_base_optional[source]
optParameters = (('base', None, None, 'LDAP base dn'),)
class ldaptor.usage.Options_bind[source]
optParameters = (('binddn', None, None, 'use Distinguished Name to bind to the directory'), ('bind-auth-fd', None, None, 'read bind password from filedescriptor'))
postOptions_bind_auth_fd_numeric()[source]
class ldaptor.usage.Options_bind_mandatory[source]

Bases: ldaptor.usage.Options_bind

postOptions_bind_mandatory()[source]
class ldaptor.usage.Options_scope[source]
optParameters = (('scope', None, 'sub', 'LDAP search scope (one of base, one, sub)'),)
postOptions_scope()[source]
class ldaptor.usage.Options_service_location[source]
opt_service_location(value)[source]

Service location, in the form BASEDN:HOST[:PORT]

postOptions_service_location()[source]
exception ldaptor.usage.UsageError[source]

Bases: exceptions.Exception

Module contents

Ldaptor Cookbook

Ldaptor Cookbook

The following recipies demonstrate how to accomplish various LDAP-related tasks with Twisted and Ldaptor. Recipies are broken into categories for convenience.

LDAP Clients

The following recipies demonstrate asynchronous LDAP clients.

A Minimal Client Using Endpoints

While Ldaptor exposes helper classes to connect clients to the DIT, it is possible to use the Twisted endpoints API to connect an Ldaptor client to a server.

Code
#! /usr/bin/env python

from ldaptor.protocols.ldap.ldapclient import LDAPClient
from ldaptor.protocols.ldap.ldapsyntax import LDAPEntry
from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.internet.endpoints import clientFromString, connectProtocol
from twisted.internet.task import react
from twisted.python import log
from cStringIO import StringIO
import sys

@inlineCallbacks
def onConnect(clientProtocol):
    o = LDAPEntry(clientProtocol, "dc=org")
    resultList = yield o.search()
    f = StringIO()
    for result in resultList:
        f.write(str(result))
        f.write("\n")
    log.msg("LDIF formatted results:\n{0}".format(f.getvalue()))

def onError(err, reactor):
    if reactor.running:
        log.err(err)
        reactor.stop()

def main(reactor):
    log.startLogging(sys.stdout)
    endpoint_str = "tcp:host=localhost:port=8080"
    e = clientFromString(reactor, endpoint_str)
    d = connectProtocol(e, LDAPClient())
    d.addCallback(onConnect)
    d.addErrback(onError, reactor)
    return d

react(main)
Discussion

The twisted.internet.task.react() function is perfect for running a one-shot main() function. When main() is called, we create a client endpoint from a string description and the reactor. twisted.internet.endpoints.connectProtocol() is used to make a one-time connection to an LDAP directory listening on the local host, port 8080. When the deferred returned from that function fires, the connection has been established and the client protocol instance is passed to the onConnect() callback.

This callback uses inline deferreds to make the syntax more compact. We create an ldaptor.protocols.ldap.ldapsyntax.LDAPEntry with a DN matching the root of the directory and call the asynchronous search() method. The result returned when the deferred fires is a list of LDAPEntry objects.

When cast as strings, these entries are formatted as LDIF.

LDAP Proxies

An LDAP proxy sits between an LDAP client and an LDAP server. It accepts LDAP requests from the client and forwards them to the LDAP server. Responses from the server are then relayed back to the client.

Why is it Useful?

An LDAP proxy has many different uses:

  • If a client does not natively support LDAP over SSL or StartTLS, a proxy can be run on the client host. The client can interact with the proxy which can use LDAPS or StartTLS when communicating with the backend service.
  • When troubleshooting LDAP connections between LDAP clients and servers, it can be useful to determine what kinds of requests and responses are passing between the client and server. Sometimes, access to client or server logs is not available or not helpful. By logging the interactions at the proxy, one can gain insight into what requests are being made by the client and what responses the server makes.
  • It may be desirable to provide limited access to an LDAP service. For example, it may be desirable to grant an application search access to an LDAP DIT, but any Modify, Add, or Delete operations are not allowed. A proxy can be configured to disable those particular LDAP operations.
  • LDAP requests can be modified before sending them on to the LDAP server. For example, the base DN of search could be transparently modified based on the current BIND user.
  • Similarly, LDAP responses from the server can be modified before sending them to the client. For example, search results could be populated with computed attributes, or a domain could be appended to any returned uid attribute.
  • The proxy can be configured to connect to one of several LDAP servers (replicas). This can be an effective technique when a particular LDAP client library shows affinity for a particular host in an LDAP replica round-robin architecture. The client can be configured to always connect to the proxy, which in turn will distrbute the connections amongst the replicas.
Proxy Recipies
Logging LDAP Proxy

A logging LDAP proxy inspects the LDAP requests and responses and records them in a log.

Code
#! /usr/bin/env python

from ldaptor.protocols import pureldap
from ldaptor.protocols.ldap.ldapclient import LDAPClient
from ldaptor.protocols.ldap.ldapconnector import connectToLDAPEndpoint
from ldaptor.protocols.ldap.proxybase import ProxyBase
from twisted.internet import defer, protocol, reactor
from twisted.python import log
from functools import partial
import sys

class LoggingProxy(ProxyBase):
    """
    A simple example of using `ProxyBase` to log requests and responses.
    """
    def handleProxiedResponse(self, response, request, controls):
        """
        Log the representation of the responses received.
        """
        log.msg("Request => " + repr(request))
        log.msg("Response => " + repr(response))
        return defer.succeed(response)

def ldapBindRequestRepr(self):
    l=[]
    l.append('version={0}'.format(self.version))
    l.append('dn={0}'.format(repr(self.dn)))
    l.append('auth=****')
    if self.tag!=self.__class__.tag:
        l.append('tag={0}'.format(self.tag))
    l.append('sasl={0}'.format(repr(self.sasl)))
    return self.__class__.__name__+'('+', '.join(l)+')'

pureldap.LDAPBindRequest.__repr__ = ldapBindRequestRepr

if __name__ == '__main__':
    """
    Demonstration LDAP proxy; listens on localhost:10389 and
    passes all requests to localhost:8080.
    """
    log.startLogging(sys.stderr)
    factory = protocol.ServerFactory()
    proxiedEndpointStr = 'tcp:host=localhost:port=8080'
    use_tls = False
    clientConnector = partial(
        connectToLDAPEndpoint,
        reactor,
        proxiedEndpointStr,
        LDAPClient)

    def buildProtocol():
        proto = LoggingProxy()
        proto.clientConnector = clientConnector
        proto.use_tls = use_tls
        return proto

    factory.protocol = buildProtocol
    reactor.listenTCP(10389, factory)
    reactor.run()
Discussion

The main idea in the above program is to subclass ldaptor.protocols.ldap.proxybase.ProxyBase and override its handleProxiedResponse() method.

The function ldapBindRequestRepr() is used to patch the __repr__() magic method of the ldaptor.protocols.pureldap.LDAPBindRequest class. The representation normally prints the BIND password, which is typically not what you want.

The main program entry point starts logging and creates a generic server factory. The proxied LDAP server is configured to run on the local host on port 8080. The factory protocol is set to a function that takes no arguments and returns an instance of our LoggingProxy that has been configured with a clientConnector callable. When this callable is invoked, it will return a deferred that will fire with a LDAPClient instance when a connection to the proxied LDAP server is established. The Twisted reactor is then configured to listen on TCP port 10389 and use the factory to create server protocol instances to handle incoming connections.

The ProxyBase class handles the typical LDAP protocol events but provides convenient hooks for intercepting LDAP requests and responses. In this proxy, we wait until we have a reponse and log both the request and the response. in the case of a search request with multiple responses, the request is repeatedly displayed with each response.

This program explicitly starts logging and the Twisted reactor loop. However, the twistd program can perform these tasks for you and allow you to configure options from the command line.

from ldaptor.protocols import pureldap
from ldaptor.protocols.ldap.ldapclient import LDAPClient
from ldaptor.protocols.ldap.ldapconnector import connectToLDAPEndpoint
from ldaptor.protocols.ldap.proxybase import ProxyBase
from twisted.application.service import Application, Service
from twisted.internet import defer, protocol, reactor
from twisted.internet.endpoints import serverFromString
from twisted.python import log
from functools import partial

class LoggingProxy(ProxyBase):
    """
    A simple example of using `ProxyBase` to log requests and responses.
    """
    def handleProxiedResponse(self, response, request, controls):
        """
        Log the representation of the responses received.
        """
        log.msg("Request => " + repr(request))
        log.msg("Response => " + repr(response))
        return defer.succeed(response)


def ldapBindRequestRepr(self):
    l=[]
    l.append('version={0}'.format(self.version))
    l.append('dn={0}'.format(repr(self.dn)))
    l.append('auth=****')
    if self.tag!=self.__class__.tag:
        l.append('tag={0}'.format(self.tag))
    l.append('sasl={0}'.format(repr(self.sasl)))
    return self.__class__.__name__+'('+', '.join(l)+')'

pureldap.LDAPBindRequest.__repr__ = ldapBindRequestRepr


class LoggingProxyService(Service):
    endpointStr = "tcp:10389"
    proxiedEndpointStr = 'tcp:host=localhost:port=8080'

    def startService(self):
        factory = protocol.ServerFactory()
        use_tls = False
        proxiedEndpointStr = 'tcp:host=localhost:port=8080'
        clientConnector = partial(
            connectToLDAPEndpoint,
            reactor,
            self.proxiedEndpointStr,
            LDAPClient)

        def buildProtocol():
            proto = LoggingProxy()
            proto.clientConnector = clientConnector
            proto.use_tls = use_tls
            return proto

        factory.protocol = buildProtocol
        ep = serverFromString(reactor, self.endpointStr)
        d = ep.listen(factory)
        d.addCallback(self.setListeningPort)
        d.addErrback(log.err)

    def setListeningPort(self, port):
        self.port_ = port

    def stopService(self):
        # If there are asynchronous cleanup tasks that need to
        # be performed, add deferreds for them to `async_tasks`.
        async_tasks = []
        if self.port_ is not None:
            async_tasks.append(self.port_.stopListening())
        if len(async_tasks) > 0:
            return defer.DeferredList(async_tasks, consumeErrors=True)


application = Application("Logging LDAP Proxy")
service = LoggingProxyService()
service.setServiceParent(application)

This program is very similar to the previous one. However, this one is run with twistd:

$ twistd -ny loggingproxy.py

The twistd program looks for the global name application in the script and runs all the services attached to it. We moved most of the startup code from the if __name__ == ‘__main__’ block into the service’s startService() method. This method is called when our service starts up. Conversely, stopService() is called when the service is about to shut down.

This improved example also makes use of endpoint strings. These strings are textual descriptions of client and server sockets on which our LDAP proxy server will connect and listen, respectively.

The advantage of endpoints is that you can read these strings from a configuration file and change how your server listens or how you client connects. Our example listens on a plain old TCP socket, but you could easilly switch to a TLS socket or a UNIX domain socket without having to change a line of code.

Listening on an endpoint is an asynchronous task, so we set a callback to record the listening port. When the service stops, we ask the port to stop listening.

LDAP Merger

A merger forwards search requests to multiple LDAP Servers, and returns the result entries of each successful response.

Usecase

You have multiple LDAP Servers, and you want to combine the search results of all of them. This can be the case if you have an application which needs extra users which are not inside the LDAP directory of your enterprise server, and it is not desired to store them in it. In this case you could use an internal LDAP server on the local filesystem (you can also do this with ldaptor, please look at the LDAP Servers section, File-System LDAP DIT), and combine this server with your general LDAP server.

Caveats

Be aware that it the merger is a read-only implementation: only BIND and SEARCH operations are supported. Beyond that, notice that when binding only the servers where the bind has been successful are delivering search results. So in order to retrieve results on all servers, the bind user must be available on all LDAP servers.

Usage
Code

Store the python code in a file called ldap-merger.tac:

#! /usr/bin/env python

from twisted.application import service, internet
from twisted.internet import protocol
from ldaptor.config import LDAPConfig
from ldaptor.protocols.ldap.merger import MergedLDAPServer

application = service.Application("LDAP Merger")

configs = [LDAPConfig(serviceLocationOverrides={"": ('external', 389)}),
           LDAPConfig(serviceLocationOverrides={"": ('localhost', 38942)})]
use_tls = [True, False]
factory = protocol.ServerFactory()
factory.protocol = lambda: MergedLDAPServer(configs, use_tls)
mergeService = internet.TCPServer(389, factory)
mergeService.setServiceParent(application)
Discussion

We use two ldap servers: one listening on the host ‘’external’’ on the default port 389, and the other is a server running on localhost with port 38942. TLS is used for the connection to the external server. The merger itself listens on port 389.

Run it with
$ twistd -y ldap-merger.tac
LDAP Servers

An LDAP directory information tree (DIT) is a highly specialized database with entries arranged in a tree-like structure.

File-System LDAP DIT

A minimal LDAP DIT that stores entries in the local file system

Code

First, a module that defines our DIT entries– schema.py

#-*-coding:utf-8-*-

COUNTRY = (
        'dc=fr',
        {
            'objectClass': ['dcObject','country'],
            'dc': ['fr'],
            'description': ["French country 2 letters iso description"],
        }
    )
COMPANY = (
        'dc=example',
        {
            'objectClass': ['dcObject','organization'],
            'dc': ['example'],
            'description': ["My organisation"],
            'o': ["Example, Inc"],
        }
    )
PEOPLE = (
        'ou=people',
        {
            'ou': ['people'],
            'description': ['People from Example Inc'],
            'objectclass': ['organizationalunit'],
        }
    )
USERS = [
            ('uid=yoen',
                {
                    'objectClass': ['people', 'inetOrgPerson'],
                    'cn': ['Yoen Van der Weld'],
                    'sn': ['Van der Weld'],
                    'givenName': ['Yoen'],
                    'uid': ['yoen'],
                    'mail': ['/home/yoen/mailDir'],
                    'userPassword': ['secret']
                }
            ),
            ('uid=esteban',
                {
                    'objectClass': ['people', 'inetOrgPerson'],
                    'cn': ['Esteban Garcia Marquez'],
                    'sn': ['Garcia Marquez'],
                    'givenName': ['Esteban'],
                    'uid': ['esteban'],
                    'mail': ['/home/esteban/mailDir'],
                    'userPassword': ['secret2']
                }
            ),
            ('uid=mohamed',
                {
                    'objectClass': ['people', 'inetOrgPerson'],
                    'cn': ['Mohamed Al Ghâlib'],
                    'sn': ['Al Ghâlib'],
                    'givenName': ['mohamed'],
                    'uid': ['mohamed'],
                    'mail': ['/home/mohamed/mailDir'],
                    'userPassword': ['secret3']
                }
            ),
        ]

Next, the server code– fsdit.py

#-*-coding:utf-8-*-
"""
    Testing a simple ldaptor ldap server
    Base on an example by Gaston TJEBBES aka "tonthon":
    http://tonthon.blogspot.com/2011/02/ldaptor-ldap-with-twisted-server-side.html
"""
import tempfile, sys

from twisted.application import service, internet
from twisted.internet import reactor
from twisted.internet.protocol import ServerFactory
from twisted.python.components import registerAdapter
from twisted.python import log
from ldaptor.interfaces import IConnectedLDAPEntry
from ldaptor.protocols.ldap.ldapserver import LDAPServer
from ldaptor.ldiftree import LDIFTreeEntry
from schema import COUNTRY, COMPANY, PEOPLE, USERS


class Tree(object):

    def __init__(self, path='/tmp'):
        dirname = tempfile.mkdtemp('.ldap', 'test-server', '/tmp')
        self.db = LDIFTreeEntry(dirname)
        self.init_db()

    def init_db(self):
        """
            Add subtrees to the top entry
            top->country->company->people
        """
        country = self.db.addChild(COUNTRY[0], COUNTRY[1])
        company = country.addChild(COMPANY[0], COMPANY[1])
        people = company.addChild(PEOPLE[0], PEOPLE[1])
        for user in USERS:
            people.addChild(user[0], user[1])


class LDAPServerFactory(ServerFactory):
    """
        Our Factory is meant to persistently store the ldap tree
    """
    protocol = LDAPServer

    def __init__(self, root):
        self.root = root

    def buildProtocol(self, addr):
        proto = self.protocol()
        proto.debug = self.debug
        proto.factory = self
        return proto

if __name__ == '__main__':
    if len(sys.argv) == 2:
        port = int(sys.argv[1])
    else:
        port = 8080
    # First of all, to show logging info in stdout :
    log.startLogging(sys.stderr)
    # We initialize our tree
    tree = Tree()
    # When the ldap protocol handle the ldap tree,
    # it retrieves it from the factory adapting
    # the factory to the IConnectedLDAPEntry interface
    # So we need to register an adapter for our factory
    # to match the IConnectedLDAPEntry
    registerAdapter(
        lambda x: x.root,
        LDAPServerFactory,
        IConnectedLDAPEntry)
    # Run it !!
    factory = LDAPServerFactory(tree.db)
    factory.debug = True
    application = service.Application("ldaptor-server")
    myService = service.IServiceCollection(application)
    reactor.listenTCP(port, factory)
    reactor.run()

Meta

Changelog

Release 16.0 (2016-06-07)

Features
  • Make meta data introspectable
  • Added proxybase.py, an LDAP proxy that is easier to hook into.
  • When parsing LDAPControls, criticality may not exist while controlValue still does
  • Requested attributes can also be passed as ‘*’ symbol
  • Numerous small bug fixes.
  • Additional documentation
  • Updated Travis-CI, Tox and other bits for better coverage.

Release 14.0 (2014-10-31)

Ldaptor has a new version schema. As a first-party library we now follow Twisted’s example.

License
  • Ldaptor’s original author Tommi Virtanen changed the license to the MIT (Expat) license.
  • ldaptor.md4 has been replaced by a 3-clause BSD version.
API Changes
  • Ldaptor client and server: None
  • Everything having to do with webui and Nevow have been removed.
Features
  • Travis CI is now used for continuous integration.
  • Test coverage is now measured. We’re currently at around 75%.
  • tox is used now to test ldaptor on all combinations of pypy, Python 2.6, Python 2.7 and Twisted versions from 10.0 until 14.0.
  • A few ordering bugs that were exposed by that and are fixed now.
  • ldaptor.protocols.pureldap.LDAPExtendedRequest now has additional tests.
  • The new ldaptor.protocols.pureldap.LDAPAbandonRequest adds support for abandoning requests.
  • ldaptor.protocols.pureldap.LDAPBindRequest has basic SASL support now. Higher-level APIs like ldapclient don’t expose it yet though.
Bugfixes
  • ldaptor.protocols.ldap.ldapclient’s now uses log.msg for it’s debug listing instead of the non-Twisted log.debug.
  • String literal exceptions have been replaced by real Exceptions.
  • “bin/ldaptor-ldap2passwd –help” now does not throws an exception anymore (debian bug #526522).
  • ldaptor.delta.Modification and ldaptor.protocols.ldap.ldapsyntax.PasswordSetAggregateError that are used for adding contacts now handle unicode arguments properly.
  • ldaptor.protocols.pureldap.LDAPExtendedRequest’s constructor now handles STARTTLS in accordance to RFC2251 so the constructor of ldaptor.protocols.pureldap.LDAPStartTLSRequest doesn’t fail anymore.
  • ldaptor.protocols.ldap.ldapserver.BaseLDAPServer now uses the correct exception module in dataReceived.
  • ldaptor.protocols.ldap.ldaperrors.LDAPException: “Fix deprecated exception error”
  • bin/ldaptor-find-server now imports dns from the correct twisted modules.
  • bin/ldaptor-find-server now only prints SRV records.
  • ldaptor.protocols.ldap.ldapsyntax.LDAPEntryWithClient now correctly propagates errors on search(). The test suite has been adapted appropriately.
  • ldaptor.protocols.ldap.ldapconnector.LDAPConnector now supports specifying a local address when connecting to a server.
  • The new ldaptor.protocols.pureldap.LDAPSearchResultReference now prevents ldaptor from choking on results containing SearchResultReference (usually from Active Directory servers). It is currently only a stub and silently ignored.
  • hashlib and built-in set() are now used instead of deprecated modules.
Improved Documentation
  • Added, updated and reworked documentation using Sphinx. Dia is required for converting diagrams to svg/png, this might change in the future.
  • Dia is now invoked correctly for diagram generation in a headless environment.
  • The documentation is now hosted on https://ldaptor.readthedocs.org/.

Prehistory

All versions up to and including 0.0.43 didn’t have a changelog.

Status and History

Ldaptor was created by Tommi Virtanen who developed it during the years 2001-2008. From 2007 and onwards mainly bug fixes were added, many contributed by Debian maintainers. Development picked back up in 2014 by Bret Curtis with Tommi’s consent and was migrated to Twisted where it is a first-party Twisted library. Ldaptor can be found here:

https://github.com/twisted/ldaptor

The LDAP client library functionality is in active use. It is stable and works very well.

Contributions

How to Contribute

Head over to: https://github.com/twisted/ldaptor and submit your bugs or feature requests. If you wish to contribute code, just fork it, make a branch and send us a pull request. We’ll review it, and push back if necessary.

Ldaptor generally follows the coding and documentation standards of the Twisted project.

Contributors

  • Anton Gyllenberg
  • Aren Sandersen
  • Bret Curtis
  • Carl Waldbieser
  • Christopher Bartz
  • David Strauss
  • HawkOwl
  • Hynek Schlawack
  • Kenny MacDermid
  • Michael Schlenker
  • Stefan Andersson
  • Tommi Virtanen

Indices and tables