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¶
import sys
from twisted.internet import 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
@defer.inlineCallbacks
def onConnect(client):
# The following arguments may be also specified as unicode strings
# but it is recommended to use byte strings for ldaptor objects
basedn = b'dc=example,dc=org'
binddn = b'cn=bob,ou=people,dc=example,dc=org'
bindpw = b'secret'
query = b'(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.getLDIF())
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¶
import sys
try:
from cStringIO import StringIO as BytesIO
except ImportError:
from io import BytesIO
from twisted.application import service
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.ldapserver import LDAPServer
LDIF = b"""\
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 = BytesIO(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.

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

Phases of an LDAP Protocol Chat¶
An average LDAP protocol chat consists of three stages:
- Opening the connection
- Doing one or more searches
- 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.

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.

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.

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.

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.

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¶

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 server 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¶

Doing a search¶

Doing multiple searches¶

Unbinding and closing¶

A complex search filter¶
An example:
(&(objectClass=person)
(!(telephoneNumber=*))
(|(cn=*a*b*)(cn=*b*a*)))

Object classes¶
- Special attribute
objectClass
lists all the objectclasses an LDAP entry manifests. - Objectclass defines
- What attributetypes an entry MUST have
- What attributetypes an entry MAY have
- An entry in a phonebook must have a name and a telephone number, and may have a fax number and street address.
Schema¶
- A configuration file included in the LDAP server configuration.
- A combination of attribute type and object class definitions.
- Stored as plain text
- 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:
- content data type
- comparison and sort mechanism
- substring search mechanism
- 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¶
- Anyone can create their own schema
- Need to be globally unique
- 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¶
LDAP object field value suggestion and autoupdate mechanism.
-
exception
ldaptor.protocols.ldap.autofill.
ObjectMissingObjectClassException
[source]¶ Bases:
ldaptor.protocols.ldap.autofill.AutofillException
The LDAPEntry is missing an objectClass this autofiller needs to operate.
-
class
ldaptor.protocols.ldap.distinguishedname.
DistinguishedName
(magic=None, stringValue=None, listOfRDNs=None)[source]¶ Bases:
ldaptor._encoder.TextStrAlias
LDAP Distinguished Name.
-
listOfRDNs
= None¶
-
-
exception
ldaptor.protocols.ldap.distinguishedname.
InvalidRelativeDistinguishedName
(rdn)[source]¶ Bases:
exceptions.Exception
Invalid relative distinguished name. It is assumed that passed RDN is of str type: bytes for PY2 and unicode for PY3.
-
class
ldaptor.protocols.ldap.distinguishedname.
LDAPAttributeTypeAndValue
(stringValue=None, attributeType=None, value=None)[source]¶ Bases:
ldaptor._encoder.TextStrAlias
-
attributeType
= None¶
-
value
= None¶
-
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, 0x4e: LDAPCompareRequest, 0x4f: LDAPCompareResponse, 0x50: LDAPAbandonRequest, 0x53: LDAPSearchResultReference, 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, 0x4e: LDAPCompareRequest, 0x4f: LDAPCompareResponse, 0x50: LDAPAbandonRequest, 0x53: LDAPSearchResultReference, 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
-
debug
= False¶
-
send
(op, controls=None)[source]¶ Send an LDAP operation to the server. @param op: the operation to send @type op: LDAPProtocolRequest @param controls: Any controls to be included in the request. @type controls: LDAPControls @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.
If handler is provided, it will receive a LDAP response as its first argument. The Deferred returned by this function will never fire.
If handler is not provided, the Deferred returned by this function will fire with the final LDAP response.
@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 first handler as a deferred that completes when the first response has been received @rtype: Deferred LDAPProtocolResponse
-
send_multiResponse_ex
(op, controls=None, handler=None, *args, **kwargs)[source]¶ Send an LDAP operation to the server, expecting one or more responses.
If handler is provided, it will receive a LDAP response and response controls as its first 2 arguments. The Deferred returned by this function will never fire.
If handler is not provided, the Deferred returned by this function will fire with a tuple of the first LDAP response and any associated response controls.
@param op: the operation to send @type op: LDAPProtocolRequest @param controls: LDAP controls to send with the message. @type controls: LDAPControls @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, controls=None)[source]¶ Send an LDAP operation to the server, with no response expected.
@param op: the operation to send @type op: LDAPProtocolRequest
-
-
exception
ldaptor.protocols.ldap.ldapclient.
LDAPClientConnectionLostException
(message=None)[source]¶
-
exception
ldaptor.protocols.ldap.ldapclient.
LDAPStartTLSBusyError
(onwire, message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPOperationsError
-
class
ldaptor.protocols.ldap.ldapconnector.
LDAPClientCreator
(reactor, protocolClass, *args, **kwargs)[source]¶ Bases:
twisted.internet.protocol.ClientCreator
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPAdminLimitExceeded
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'adminLimitExceeded'¶
-
resultCode
= 11¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPAffectsMultipleDSAs
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'affectsMultipleDSAs'¶
-
resultCode
= 71¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPAliasDereferencingProblem
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'aliasDereferencingProblem'¶
-
resultCode
= 36¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPAliasProblem
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'aliasProblem'¶
-
resultCode
= 33¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPAttributeOrValueExists
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'attributeOrValueExists'¶
-
resultCode
= 20¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPAuthMethodNotSupported
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'authMethodNotSupported'¶
-
resultCode
= 7¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPBusy
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'busy'¶
-
resultCode
= 51¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPCompareFalse
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'compareFalse'¶
-
resultCode
= 5¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPCompareTrue
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'compareTrue'¶
-
resultCode
= 6¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPConfidentialityRequired
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'confidentialityRequired'¶
-
resultCode
= 13¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPConstraintViolation
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'constraintViolation'¶
-
resultCode
= 19¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPEntryAlreadyExists
(message=None)[source]¶ 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
-
class
ldaptor.protocols.ldap.ldaperrors.
LDAPExceptionCollection
[source]¶ Bases:
type
Storage for the LDAP result codes and the corresponding classes.
-
collection
= {0: <class 'ldaptor.protocols.ldap.ldaperrors.Success'>, 1: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPOperationsError'>, 2: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPProtocolError'>, 3: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPTimeLimitExceeded'>, 4: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPSizeLimitExceeded'>, 5: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPCompareFalse'>, 6: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPCompareTrue'>, 7: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPAuthMethodNotSupported'>, 8: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPStrongAuthRequired'>, 10: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPReferral'>, 11: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPAdminLimitExceeded'>, 12: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPUnavailableCriticalExtension'>, 13: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPConfidentialityRequired'>, 14: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPSaslBindInProgress'>, 16: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPNoSuchAttribute'>, 17: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPUndefinedAttributeType'>, 18: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPInappropriateMatching'>, 19: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPConstraintViolation'>, 20: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPAttributeOrValueExists'>, 21: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPInvalidAttributeSyntax'>, 32: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPNoSuchObject'>, 33: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPAliasProblem'>, 34: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPInvalidDNSyntax'>, 36: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPAliasDereferencingProblem'>, 48: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPInappropriateAuthentication'>, 49: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPInvalidCredentials'>, 50: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPInsufficientAccessRights'>, 51: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPBusy'>, 52: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPUnavailable'>, 53: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPUnwillingToPerform'>, 54: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPLoopDetect'>, 64: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPNamingViolation'>, 65: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPObjectClassViolation'>, 66: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPNotAllowedOnNonLeaf'>, 67: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPNotAllowedOnRDN'>, 68: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPEntryAlreadyExists'>, 69: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPObjectClassModsProhibited'>, 71: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPAffectsMultipleDSAs'>, 80: <class 'ldaptor.protocols.ldap.ldaperrors.LDAPOther'>}¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPInappropriateAuthentication
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'inappropriateAuthentication'¶
-
resultCode
= 48¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPInappropriateMatching
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'inappropriateMatching'¶
-
resultCode
= 18¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPInsufficientAccessRights
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'insufficientAccessRights'¶
-
resultCode
= 50¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPInvalidAttributeSyntax
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'invalidAttributeSyntax'¶
-
resultCode
= 21¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPInvalidCredentials
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'invalidCredentials'¶
-
resultCode
= 49¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPInvalidDNSyntax
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'invalidDNSyntax'¶
-
resultCode
= 34¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPLoopDetect
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'loopDetect'¶
-
resultCode
= 54¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPNamingViolation
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'namingViolation'¶
-
resultCode
= 64¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPNoSuchAttribute
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'noSuchAttribute'¶
-
resultCode
= 16¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPNoSuchObject
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'noSuchObject'¶
-
resultCode
= 32¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPNotAllowedOnNonLeaf
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'notAllowedOnNonLeaf'¶
-
resultCode
= 66¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPNotAllowedOnRDN
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'notAllowedOnRDN'¶
-
resultCode
= 67¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPObjectClassModsProhibited
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'objectClassModsProhibited'¶
-
resultCode
= 69¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPObjectClassViolation
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'objectClassViolation'¶
-
resultCode
= 65¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPOperationsError
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'operationsError'¶
-
resultCode
= 1¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPOther
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'other'¶
-
resultCode
= 80¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPProtocolError
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'protocolError'¶
-
resultCode
= 2¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPReferral
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'referral'¶
-
resultCode
= 10¶
-
-
class
ldaptor.protocols.ldap.ldaperrors.
LDAPResult
[source]¶ Bases:
object
-
name
= None¶
-
resultCode
= None¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPSaslBindInProgress
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'saslBindInProgress'¶
-
resultCode
= 14¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPSizeLimitExceeded
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'sizeLimitExceeded'¶
-
resultCode
= 4¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPStrongAuthRequired
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'strongAuthRequired'¶
-
resultCode
= 8¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPTimeLimitExceeded
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'timeLimitExceeded'¶
-
resultCode
= 3¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPUndefinedAttributeType
(message=None)[source]¶ Bases:
ldaptor.protocols.ldap.ldaperrors.LDAPException
-
name
= 'undefinedAttributeType'¶
-
resultCode
= 17¶
-
-
exception
ldaptor.protocols.ldap.ldaperrors.
LDAPUnwillingToPerform
(message=None)[source]¶ 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¶
-
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, 0x4e: LDAPCompareRequest, 0x4f: LDAPCompareResponse, 0x50: LDAPAbandonRequest, 0x53: LDAPSearchResultReference, 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, 0x4e: LDAPCompareRequest, 0x4f: LDAPCompareResponse, 0x50: LDAPAbandonRequest, 0x53: LDAPSearchResultReference, 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>>>¶
-
connectionLost
(reason=<twisted.python.failure.Failure twisted.internet.error.ConnectionDone: Connection was closed cleanly.>)[source]¶ Called when TCP connection has been lost
-
debug
= False¶
-
-
class
ldaptor.protocols.ldap.ldapserver.
LDAPServer
[source]¶ Bases:
ldaptor.protocols.ldap.ldapserver.BaseLDAPServer
An LDAP server
-
boundUser
= None¶
-
fail_LDAPAddRequest
¶
-
fail_LDAPBindRequest
¶
-
fail_LDAPCompareRequest
¶
-
fail_LDAPDelRequest
¶
-
fail_LDAPExtendedRequest
¶
-
fail_LDAPModifyDNRequest
¶
-
fail_LDAPModifyRequest
¶
-
fail_LDAPSearchRequest
¶
-
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]¶ Adding key to the attributes with checking if it exists as byte or unicode string
-
-
ldaptor.protocols.ldap.ldapsyntax.
LDAPEntry
¶ alias of
ldaptor.protocols.ldap.ldapsyntax.LDAPEntryWithClient
-
class
ldaptor.protocols.ldap.ldapsyntax.
LDAPEntryWithAutoFill
(*args, **kwargs)[source]¶ Bases:
ldaptor.protocols.ldap.ldapsyntax.LDAPEntryWithClient
-
class
ldaptor.protocols.ldap.ldapsyntax.
LDAPEntryWithClient
(client, dn, attributes={}, complete=0)[source]¶ Bases:
ldaptor.entry.EditableLDAPEntry
-
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.
-
search
(filterText=None, filterObject=None, attributes=(), scope=None, derefAliases=None, sizeLimit=0, sizeLimitIsNonFatal=False, timeLimit=0, typesOnly=0, callback=None, controls=None, return_controls=False)[source]¶
-
setPassword
(newPasswd)[source]¶ Update the password for the entry with a new password and salt passed as bytes.
-
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.
-
-
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.
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
-
class
ldaptor.protocols.ldap.ldifdelta.
LDIFDelta
[source]¶ Bases:
ldaptor.protocols.ldap.ldifprotocol.LDIF
-
MOD_SPEC_TO_DELTA
= {'add': <class 'ldaptor.delta.Add'>, 'delete': <class 'ldaptor.delta.Delete'>, 'replace': <class 'ldaptor.delta.Replace'>}¶
-
-
exception
ldaptor.protocols.ldap.ldifdelta.
LDIFDeltaAddMissingAttributesError
[source]¶ Bases:
ldaptor.protocols.ldap.ldifprotocol.LDIFParseError
Add operation needs to have at least 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.
LDIFDeltaUnknownChangeTypeError
[source]¶ Bases:
ldaptor.protocols.ldap.ldifprotocol.LDIFParseError
LDIF delta entry has an unknown changetype.
-
exception
ldaptor.protocols.ldap.ldifdelta.
LDIFDeltaUnknownModificationError
[source]¶ Bases:
ldaptor.protocols.ldap.ldifprotocol.LDIFParseError
LDIF delta modification has unknown mod-spec.
-
class
ldaptor.protocols.ldap.ldifprotocol.
LDIF
[source]¶ Bases:
twisted.protocols.basic.LineReceiver
,object
-
connectionLost
(reason=<twisted.python.failure.Failure twisted.internet.error.ConnectionDone: Connection was closed cleanly.>)[source]¶ Called when the connection is shut down.
Clear any circular references here, and any external references to this Protocol. The connection has been closed.
@type reason: L{twisted.python.failure.Failure}
-
data
= None¶
-
delimiter
= '\n'¶
-
dn
= None¶
-
lastLine
= None¶
-
lineReceived
(line)[source]¶ Override this for when each line is received.
@param line: The line which was received with the delimiter removed. @type line: C{bytes}
-
mode
= 'HEADER'¶
-
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
LDAP protocol proxy server
-
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
¶
-
fallback
= False¶
-
services
= []¶
LDAP protocol logic
Pure, simple, BER encoding and decoding
-
class
ldaptor.protocols.pureber.
BERBase
(tag=None)[source]¶ Bases:
ldaptor._encoder.WireStrAlias
-
tag
= None¶
-
-
class
ldaptor.protocols.pureber.
BERBoolean
(value=None, tag=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERBase
-
tag
= 1¶
-
-
class
ldaptor.protocols.pureber.
BERDecoderContext
(fallback=None, inherit=None)[source]¶ -
Identities
= {1: <class 'ldaptor.protocols.pureber.BERBoolean'>, 2: <class 'ldaptor.protocols.pureber.BERInteger'>, 4: <class 'ldaptor.protocols.pureber.BEROctetString'>, 5: <class 'ldaptor.protocols.pureber.BERNull'>, 10: <class 'ldaptor.protocols.pureber.BEREnumerated'>, 16: <class 'ldaptor.protocols.pureber.BERSequence'>, 17: <class 'ldaptor.protocols.pureber.BERSet'>}¶
-
-
class
ldaptor.protocols.pureber.
BEREnumerated
(value=None, tag=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERInteger
-
tag
= 10¶
-
-
exception
ldaptor.protocols.pureber.
BERExceptionInsufficientData
[source]¶ Bases:
exceptions.Exception
-
class
ldaptor.protocols.pureber.
BERInteger
(value=None, tag=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERBase
-
tag
= 2¶
-
value
= None¶
-
-
class
ldaptor.protocols.pureber.
BERNull
(tag=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERBase
-
tag
= 5¶
-
-
class
ldaptor.protocols.pureber.
BEROctetString
(value=None, tag=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERBase
-
tag
= 4¶
-
value
= None¶
-
-
class
ldaptor.protocols.pureber.
BERSequence
(value=None, tag=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERStructured
,UserList.UserList
-
tag
= 16¶
-
-
class
ldaptor.protocols.pureber.
BERSet
(value=None, tag=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERSequence
-
tag
= 17¶
-
-
exception
ldaptor.protocols.pureber.
UnknownBERTag
(tag, context)[source]¶ Bases:
exceptions.Exception
-
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.
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
-
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.
LDAPAttributeValueAssertion
(attributeDesc=None, assertionValue=None, tag=None, escaper=<function escape>)[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'>, 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'>, 75: <class 'ldaptor.protocols.pureldap.LDAPDelResponse'>, 76: <class 'ldaptor.protocols.pureldap.LDAPModifyDNRequest'>, 77: <class 'ldaptor.protocols.pureldap.LDAPModifyDNResponse'>, 78: <class 'ldaptor.protocols.pureldap.LDAPCompareRequest'>, 79: <class 'ldaptor.protocols.pureldap.LDAPCompareResponse'>, 80: <class 'ldaptor.protocols.pureldap.LDAPAbandonRequest'>, 83: <class 'ldaptor.protocols.pureldap.LDAPSearchResultReference'>, 87: <class 'ldaptor.protocols.pureldap.LDAPExtendedRequest'>, 88: <class 'ldaptor.protocols.pureldap.LDAPExtendedResponse'>, 131: <class 'ldaptor.protocols.pureldap.LDAPReferral'>}¶
-
-
class
ldaptor.protocols.pureldap.
LDAPBERDecoderContext_BindResponse
(fallback=None, inherit=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERDecoderContext
-
Identities
= {135: <class 'ldaptor.protocols.pureldap.LDAPBindResponse_serverSaslCreds'>}¶
-
-
class
ldaptor.protocols.pureldap.
LDAPBERDecoderContext_Compare
(fallback=None, inherit=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERDecoderContext
-
Identities
= {16: <class 'ldaptor.protocols.pureldap.LDAPAttributeValueAssertion'>}¶
-
-
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'>, 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'>, 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'>, 129: <class 'ldaptor.protocols.pureldap.LDAPFilter_substrings_any'>, 130: <class 'ldaptor.protocols.pureldap.LDAPFilter_substrings_final'>}¶
-
-
class
ldaptor.protocols.pureldap.
LDAPBERDecoderContext_LDAPBindRequest
(fallback=None, inherit=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERDecoderContext
-
Identities
= {128: <class 'ldaptor.protocols.pureber.BEROctetString'>, 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'>, 129: <class 'ldaptor.protocols.pureber.BEROctetString'>}¶
-
-
class
ldaptor.protocols.pureldap.
LDAPBERDecoderContext_LDAPExtendedResponse
(fallback=None, inherit=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERDecoderContext
-
Identities
= {138: <class 'ldaptor.protocols.pureldap.LDAPResponseName'>, 139: <class 'ldaptor.protocols.pureldap.LDAPResponse'>}¶
-
-
class
ldaptor.protocols.pureldap.
LDAPBERDecoderContext_LDAPMessage
(fallback=None, inherit=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERDecoderContext
-
Identities
= {83: <class 'ldaptor.protocols.pureldap.LDAPSearchResultReference'>, 128: <class 'ldaptor.protocols.pureldap.LDAPControls'>}¶
-
-
class
ldaptor.protocols.pureldap.
LDAPBERDecoderContext_LDAPPasswordModifyRequest
(fallback=None, inherit=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERDecoderContext
-
Identities
= {128: <class 'ldaptor.protocols.pureldap.LDAPPasswordModifyRequest_userIdentity'>, 129: <class 'ldaptor.protocols.pureldap.LDAPPasswordModifyRequest_oldPasswd'>, 130: <class 'ldaptor.protocols.pureldap.LDAPPasswordModifyRequest_newPasswd'>}¶
-
-
class
ldaptor.protocols.pureldap.
LDAPBERDecoderContext_LDAPSearchResultReference
(fallback=None, inherit=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERDecoderContext
-
Identities
= {4: <class 'ldaptor.protocols.pureldap.LDAPString'>}¶
-
-
class
ldaptor.protocols.pureldap.
LDAPBERDecoderContext_MatchingRuleAssertion
(fallback=None, inherit=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERDecoderContext
-
Identities
= {129: <class 'ldaptor.protocols.pureldap.LDAPMatchingRuleAssertion_matchingRule'>, 130: <class 'ldaptor.protocols.pureldap.LDAPMatchingRuleAssertion_type'>, 131: <class 'ldaptor.protocols.pureldap.LDAPMatchingRuleAssertion_matchValue'>, 132: <class 'ldaptor.protocols.pureldap.LDAPMatchingRuleAssertion_dnAttributes'>}¶
-
-
class
ldaptor.protocols.pureldap.
LDAPBERDecoderContext_ModifyDNRequest
(fallback=None, inherit=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERDecoderContext
-
Identities
= {128: <class 'ldaptor.protocols.pureldap.LDAPModifyDNResponse_newSuperior'>}¶
-
-
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
-
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¶
-
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.
LDAPCompareRequest
(entry, ava, tag=None)[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPProtocolRequest
,ldaptor.protocols.pureber.BERSequence
-
ava
= None¶
-
entry
= None¶
-
tag
= 78¶
-
-
class
ldaptor.protocols.pureldap.
LDAPCompareResponse
(resultCode=None, matchedDN=None, errorMessage=None, referral=None, serverSaslCreds=None, tag=None)[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPResult
-
tag
= 79¶
-
-
class
ldaptor.protocols.pureldap.
LDAPControl
(controlType, criticality=None, controlValue=None, id=None, tag=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERSequence
-
controlValue
= None¶
-
criticality
= None¶
-
-
class
ldaptor.protocols.pureldap.
LDAPControls
(value=None, tag=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERSequence
-
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
-
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
-
response
= None¶
-
responseName
= None¶
-
tag
= 88¶
-
-
class
ldaptor.protocols.pureldap.
LDAPFilter_and
(value=None, tag=None)[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPFilterSet
-
tag
= 128¶
-
-
class
ldaptor.protocols.pureldap.
LDAPFilter_approxMatch
(attributeDesc=None, assertionValue=None, tag=None, escaper=<function escape>)[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPAttributeValueAssertion
-
tag
= 136¶
-
-
class
ldaptor.protocols.pureldap.
LDAPFilter_equalityMatch
(attributeDesc=None, assertionValue=None, tag=None, escaper=<function escape>)[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPAttributeValueAssertion
-
tag
= 131¶
-
-
class
ldaptor.protocols.pureldap.
LDAPFilter_extensibleMatch
(matchingRule=None, type=None, matchValue=None, dnAttributes=None, tag=None, escaper=<function escape>)[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPMatchingRuleAssertion
-
tag
= 137¶
-
-
class
ldaptor.protocols.pureldap.
LDAPFilter_greaterOrEqual
(attributeDesc=None, assertionValue=None, tag=None, escaper=<function escape>)[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPAttributeValueAssertion
-
tag
= 133¶
-
-
class
ldaptor.protocols.pureldap.
LDAPFilter_lessOrEqual
(attributeDesc=None, assertionValue=None, tag=None, escaper=<function escape>)[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPAttributeValueAssertion
-
tag
= 134¶
-
-
class
ldaptor.protocols.pureldap.
LDAPFilter_not
(value, tag=130)[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPFilter
-
tag
= 130¶
-
-
class
ldaptor.protocols.pureldap.
LDAPFilter_or
(value=None, tag=None)[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPFilterSet
-
tag
= 129¶
-
-
class
ldaptor.protocols.pureldap.
LDAPFilter_present
(value=None, tag=None)[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPAttributeDescription
-
tag
= 135¶
-
-
class
ldaptor.protocols.pureldap.
LDAPFilter_substrings
(type=None, substrings=None, tag=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERSequence
-
tag
= 132¶
-
-
class
ldaptor.protocols.pureldap.
LDAPFilter_substrings_any
(*args, **kwargs)[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPString
-
tag
= 129¶
-
-
class
ldaptor.protocols.pureldap.
LDAPFilter_substrings_final
(*args, **kwargs)[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPString
-
tag
= 130¶
-
-
class
ldaptor.protocols.pureldap.
LDAPFilter_substrings_initial
(*args, **kwargs)[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPString
-
tag
= 128¶
-
-
class
ldaptor.protocols.pureldap.
LDAPMatchingRuleAssertion
(matchingRule=None, type=None, matchValue=None, dnAttributes=None, tag=None, escaper=<function escape>)[source]¶ Bases:
ldaptor.protocols.pureber.BERSequence
-
dnAttributes
= None¶
-
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
(*args, **kwargs)[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.
LDAPMessage
(value=None, controls=None, id=None, tag=None)[source]¶ Bases:
ldaptor.protocols.pureber.BERSequence
To encode this object in order to be sent over the network use the toWire() method.
-
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¶
-
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
(*args, **kwargs)[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
-
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.
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.pureldap.LDAPPasswordModifyRequest_passwd
-
tag
= 130¶
-
-
class
ldaptor.protocols.pureldap.
LDAPPasswordModifyRequest_oldPasswd
(value=None, tag=None)[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPPasswordModifyRequest_passwd
-
tag
= 129¶
-
-
class
ldaptor.protocols.pureldap.
LDAPPasswordModifyRequest_userIdentity
(value=None, tag=None)[source]¶ Bases:
ldaptor.protocols.pureber.BEROctetString
-
tag
= 128¶
-
-
class
ldaptor.protocols.pureldap.
LDAPProtocolRequest
[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPProtocolOp
-
needs_answer
= 1¶
-
-
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
-
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')¶
-
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
-
tag
= 68¶
-
-
class
ldaptor.protocols.pureldap.
LDAPSearchResultReference
(uris=None, tag=None)[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPProtocolResponse
,ldaptor.protocols.pureber.BERSequence
-
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.
LDAPUnbindRequest
(*args, **kwargs)[source]¶ Bases:
ldaptor.protocols.pureldap.LDAPProtocolRequest
,ldaptor.protocols.pureber.BERNull
-
needs_answer
= 0¶
-
tag
= 66¶
-
ldaptor.samba package¶
-
ldaptor.samba.smbpassword.
lmhash
(password='')[source]¶ Generates lanman password hash for a given password.
Note that the author thinks LanMan hashes should be banished from the face of the earth.
Submodules¶
ldaptor.attributeset module¶
ldaptor.checkers module¶
ldaptor.compat module¶
ldaptor.config module¶
-
class
ldaptor.config.
LDAPConfig
(baseDN=None, serviceLocationOverrides=None, identityBaseDN=None, identitySearch=None)[source]¶ Bases:
object
-
baseDN
= None¶
-
identityBaseDN
= None¶
-
identitySearch
= None¶
-
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
-
class
ldaptor.delta.
AddOp
(entry)[source]¶ Bases:
ldaptor.delta.Operation
-
class
ldaptor.delta.
Delete
(key, *a, **kw)[source]¶ Bases:
ldaptor.delta.Modification
-
class
ldaptor.delta.
DeleteOp
(dn)[source]¶ Bases:
ldaptor.delta.Operation
-
class
ldaptor.delta.
ModifyOp
(dn, modifications=[])[source]¶ Bases:
ldaptor.delta.Operation
ldaptor.dns module¶
DNS-related utilities.
ldaptor.entry module¶
-
class
ldaptor.entry.
BaseLDAPEntry
(dn, attributes={})[source]¶ Bases:
ldaptor._encoder.WireStrAlias
-
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¶
-
-
class
ldaptor.entry.
EditableLDAPEntry
(dn, attributes={})[source]¶ Bases:
ldaptor.entry.BaseLDAPEntry
ldaptor.entryhelpers module¶
ldaptor.generate_password module¶
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.
-
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
ldaptor.insensitive module¶
ldaptor.interfaces module¶
ldaptor.ldapfilter module¶
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
-
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]¶
ldaptor.numberalloc module¶
Find an available uidNumber/gidNumber/other similar number.
ldaptor.schema module¶
-
class
ldaptor.schema.
AttributeTypeDescription
(text)[source]¶ Bases:
ldaptor.schema.ASN1ParserThingie
,ldaptor._encoder.WireStrAlias
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
,ldaptor._encoder.WireStrAlias
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
,ldaptor._encoder.WireStrAlias
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
,ldaptor._encoder.WireStrAlias
ASN Syntax:
SyntaxDescription = "(" whsp numericoid whsp [ "DESC" qdstring ] whsp ")"
ldaptor.testutil module¶
Utilities for writing Twistedy unit tests and debugging.
-
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.
-
fakeUnbindResponse
= 'fake-unbind-by-LDAPClientTestDriver'¶
-
-
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.usage module¶
Command line argument/options available to various ldaptor tools.
-
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'))¶
-
-
class
ldaptor.usage.
Options_bind_mandatory
[source]¶ Bases:
ldaptor.usage.Options_bind
-
class
ldaptor.usage.
Options_scope
[source]¶ -
optParameters
= (('scope', None, 'sub', 'LDAP search scope (one of base, one, sub)'),)¶
-
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #! /usr/bin/env python
import sys
from ldaptor.protocols.ldap.ldapclient import LDAPClient
from ldaptor.protocols.ldap.ldapsyntax import LDAPEntry
from twisted.internet.defer import inlineCallbacks
from twisted.internet.endpoints import clientFromString, connectProtocol
from twisted.internet.task import react
from twisted.python import log
@inlineCallbacks
def onConnect(clientProtocol):
o = LDAPEntry(clientProtocol, "dc=fr")
results = yield o.search()
data = u"".join([result.getLDIF() for result in results])
log.msg(u"LDIF formatted results:\n{}".format(data))
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)
|
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.
Searching with the Paged Search Result Control¶
Some DITs place limits on the number of entries they are willing to return as the result of a LDAP SEARCH request. Microsoft’s Active Directory is one such service. In order to query and process large result sets, you can use the paged result control (OID 1.2.840.113556.1.4.319) if you DIT supports it.
The paged result control allows you to request a particular page size. The DIT will return a response control that has a magic cookie if the there are additional pages of results. You can use the cookie on a new request to process the results one page at a time.
For ad.example.com domain, store the admin password in a file named pass_file and run the following example, where 10.20.1.2 is replaced with the IP of your AD server:
python docs/source/cookbook/client_paged_search_results.py \
tcp:host=10.20.1.2:port=389 \
'CN=Administrator,CN=Users,DC=ad,DC=example,DC=com' \
pass_file \
'CN=Users,DC=ad,DC=example,DC=com' \
--page-size 5
The output should look like:
Page 1
CN=Users,DC=ad,DC=example,DC=com
CN=Administrator,CN=Users,DC=ad,DC=example,DC=com
CN=Guest,CN=Users,DC=ad,DC=example,DC=com
CN=SUPPORT_388945a0,CN=Users,DC=ad,DC=example,DC=com
CN=HelpServicesGroup,CN=Users,DC=ad,DC=example,DC=com
Page 2
CN=TelnetClients,CN=Users,DC=ad,DC=example,DC=com
CN=krbtgt,CN=Users,DC=ad,DC=example,DC=com
CN=Domain Computers,CN=Users,DC=ad,DC=example,DC=com
There were 8 results returned in total.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | #! /usr/bin/env python
import argparse
import sys
from twisted.internet import 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
from ldaptor.protocols import pureber
@defer.inlineCallbacks
def onConnect(client, args):
binddn = args.bind_dn
bindpw = args.passwd_file.read().strip()
if args.start_tls:
yield client.startTLS()
try:
yield client.bind(binddn, bindpw)
except Exception as ex:
print(ex)
raise
page_size = args.page_size
cookie = ''
page = 1
count = 0
while True:
results, cookie = yield process_entry(
client,
args,
args.filter,
page_size=page_size,
cookie=cookie)
count += len(results)
print("Page {}".format(page))
display_results(results)
if len(cookie) == 0:
break
page += 1
print("There were {} results returned in total.".format(count))
@defer.inlineCallbacks
def process_entry(client, args, search_filter, page_size=100, cookie=''):
basedn = args.base_dn
control_value = pureber.BERSequence([
pureber.BERInteger(page_size),
pureber.BEROctetString(cookie),
])
controls = [('1.2.840.113556.1.4.319', None, control_value)]
o = LDAPEntry(client, basedn)
results, resp_controls = yield o.search(
filterText=search_filter,
attributes=['dn'],
controls=controls,
return_controls=True)
cookie = get_paged_search_cookie(resp_controls)
defer.returnValue((results, cookie))
def display_results(results):
for entry in results:
print(entry.dn.getText())
def get_paged_search_cookie(controls):
"""
Input: semi-parsed controls list from LDAP response;
list of tuples (controlType, criticality, controlValue).
Parses the controlValue and returns the cookie as a byte string.
"""
control_value = controls[0][2]
ber_context = pureber.BERDecoderContext()
ber_seq, bytes_used = pureber.berDecodeObject(ber_context, control_value)
raw_cookie = ber_seq[1]
cookie = raw_cookie.value
return cookie
def onError(err):
err.printDetailedTraceback(file=sys.stderr)
def main(reactor, args):
endpoint_str = args.endpoint
e = clientFromString(reactor, endpoint_str)
d = connectProtocol(e, LDAPClient())
d.addCallback(onConnect, args)
d.addErrback(onError)
return d
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="AD LDAP demo.")
parser.add_argument(
"endpoint",
action="store",
help="The Active Directory service endpoint. See "
"https://twistedmatrix.com/documents/current/core/howto/endpoints.html#clients")
parser.add_argument(
"bind_dn",
action="store",
help="The DN to BIND to the service as.")
parser.add_argument(
"passwd_file",
action="store",
type=argparse.FileType('r'),
help="A file containing the password used to log into the service.")
parser.add_argument(
"base_dn",
action="store",
help="The base DN to start from when searching.")
parser.add_argument(
"-f",
"--filter",
action='store',
help='LDAP filter')
parser.add_argument(
"-p",
"--page-size",
type=int,
action='store',
default=100,
help='Page size (default 100).')
parser.add_argument(
"--start-tls",
action="store_true",
help="Request StartTLS after connecting to the service.")
args = parser.parse_args()
react(main, [args])
|
On connecting to the LDAP service, our client establishes TLS and BINDs as a DN that has permission to perform a search. Page, cookie, and the result count are intialized before looping to process each page. Initially, a blank cookie is used in the search request. The cookie obtained from each response is used in the next request, until the cookie is blank. This signals the end of the loop.
Note how the search returns a tuple of results and controls from the LDAP response. This is because the return_controls flag of the search was set to True.
Parsing the cookie requires some BER decoding. For details on encoding of the control value, refer to RFC 2696.
Adding an LDAP Entry¶
Ldaptor allows your LDAP client make many different kinds of LDAP requests. In this example, a simple client connects to an LDAP service and requests adding an new entry.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | #! /usr/bin/env python
import sys
from twisted.internet import defer
from twisted.internet.endpoints import clientFromString, connectProtocol
from twisted.internet.task import react
from twisted.python import log
from ldaptor.protocols.ldap.ldapclient import LDAPClient
from ldaptor.protocols import pureber, pureldap
def entry_to_attributes(entry):
"""
Convert a simple mapping to the data structures required for an
entry in the DIT.
Returns: (dn, attributes)
"""
attributes = {}
dn = None
for prop, value in entry.items():
if prop == 'dn':
dn = value
continue
attributes.setdefault(prop, set()).add(value)
if dn is None:
raise Exception("Entry needs to include key, `dn`!")
ldap_attributes = []
for attrib, values in attributes.items():
ldap_attribute_type = pureldap.LDAPAttributeDescription(attrib)
ldap_attribute_values = []
for value in values:
ldap_attribute_values.append(pureldap.LDAPAttributeValue(value))
ldap_values = pureber.BERSet(ldap_attribute_values)
ldap_attributes.append((ldap_attribute_type, ldap_values))
return dn, ldap_attributes
@defer.inlineCallbacks
def onConnect(client, entry):
dn, attributes = entry_to_attributes(entry)
op = pureldap.LDAPAddRequest(entry=dn, attributes=attributes)
response = yield client.send(op)
if response.resultCode != 0:
log.err("DIT reported error code {}: {}".format(
response.resultCode, response.errorMessage))
def onError(err, reactor):
if reactor.running:
log.err(err)
reactor.stop()
def main(reactor):
log.startLogging(sys.stdout)
entry = {
"dn": "gn=Jane+sn=Doe,ou=people,dc=example,dc=fr",
"c": "US",
"gn": "Jane",
"l": "Philadelphia",
"objectClass": "addressbookPerson",
"postalAddress": "230",
"postalCode": "314159",
"sn": "Doe",
"st": "PA",
"street": "Mobius Strip",
"userPassword": "terces",
}
endpoint_str = "tcp:host=localhost:port=8080"
e = clientFromString(reactor, endpoint_str)
d = connectProtocol(e, LDAPClient())
d.addCallback(onConnect, entry)
d.addErrback(onError, reactor)
return d
react(main)
|
Once again, the twisted.internet.task.react()
function is used
to call the main() function of the client. 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 a 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, along with our entry.
In this case we use a simple Python dictionary to model our entry. We need to
transform this into a data structure that
ldaptor.protocols.pureldap.LDAPAddRequest
can use. Once we’ve
created the request, it is relatively simple to send it to the directory
service with a call to the send() method of our client. The response will
indicate either success or failure.
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¶
A logging LDAP proxy inspects the LDAP requests and responses and records them in a log.
#! /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()
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¶
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)
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
First, a module that defines our DIT entries– schema.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | # -*- 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– ldaptor_basic.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | #! /usr/bin/env python
# -*- 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
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):
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()
|
LDAP Server which allows BIND with UPN¶
The LDAP server implemented by Microsoft Active Directory allows using the UPN as the BIND DN.
It is possible to implement something similar using ldaptor.
Below is a proof-of-concept implementation, which should not be used for production as it has an heuristic method for detecting which BIND DN is an UPN.
handle_LDAPBindRequest is the method called when a BIND request is received.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | """
An ldaptor LDAP server which can authenticate based on UPN, as AD does.
The LDAP entry needs to have the `userPrincipalName` attribute set.
dn: uid=bob,ou=people,dc=example,dc=org
objectclass: top
objectclass: person
objectClass: inetOrgPerson
uid: bob
cn: bobby
gn: Bob
sn: Roberts
mail: bob@example.org
homeDirectory: e:\\Users\\bob
userPassword: pass
userPrincipalName: bob@ad.example.org
"""
from __future__ import absolute_import
from ldaptor import interfaces
from ldaptor.protocols import pureldap
from ldaptor.protocols.ldap import distinguishedname, ldaperrors
from twisted.internet import defer
from ldaptor.protocols.ldap.ldapserver import LDAPServer
class LDAPServerWithUPNBind(LDAPServer):
"""
An LDAP server which support BIND using UPN similar to AD.
"""
_loginAttribute = b'userPrincipalName'
def handle_LDAPBindRequest(self, request, controls, reply):
if request.version != 3:
raise ldaperrors.LDAPProtocolError(
'Version %u not supported' % request.version)
self.checkControls(controls)
if request.dn == b'':
# anonymous bind
self.boundUser = None
return pureldap.LDAPBindResponse(resultCode=0)
root = interfaces.IConnectedLDAPEntry(self.factory)
def _gotUPNResult(results):
if len(results) != 1:
# Not exactly one result, so this might not be an UNP.
return distinguishedname.DistinguishedName(request.dn)
# A single result, so the UPN might exist.
return results[0].dn
if b'@' in request.dn and b',' not in request.dn:
# This might be an UPN request.
filterText = b'(' + self._loginAttribute + b'=' + request.dn + b')'
d = root.search(filterText=filterText)
d.addCallback(_gotUPNResult)
else:
d = defer.succeed(distinguishedname.DistinguishedName(request.dn))
# Once the BIND DN is known, search for the LDAP entry.
d.addCallback(lambda dn: root.lookup(dn))
def _noEntry(fail):
"""
Called when the requested BIND DN was not found.
"""
fail.trap(ldaperrors.LDAPNoSuchObject)
return None
d.addErrback(_noEntry)
def _gotEntry(entry, auth):
"""
Called when the requested BIND DN was found.
"""
if entry is None:
raise ldaperrors.LDAPInvalidCredentials()
d = entry.bind(auth)
def _cb(entry):
"""
Called when BIND operation was successful.
"""
self.boundUser = entry
msg = pureldap.LDAPBindResponse(
resultCode=ldaperrors.Success.resultCode,
matchedDN=entry.dn.getText())
return msg
d.addCallback(_cb)
return d
d.addCallback(_gotEntry, request.auth)
return d
|
Meta¶
Changelog¶
Release 19.1 (2019-09-09)¶
Features¶
- Basic implementation of
ldaptor.protocols.pureldap.LDAPSearchResultReference
. - Explicit
ldaptor.protocols.ldap.ldaperrors
classes declaration was made to allow syntax highlighting for this module.
Changes¶
ldaptor.protocols.pureldap.LDAPPasswordModifyRequest
string representation now containsuserIdentity
,oldPasswd
andnewPasswd
attributes. Password attributes are represented as asterisks.ldaptor.protocols.pureldap.LDAPBindRequest
string representation is now using asterisks to representauth
attribute.
Bugfixes¶
DeprecationWarning
stacklevel was set to mark the caller of the deprecated methods of theldaptor._encoder
classes.NotImplementedError
forldaptor.protocols.pureldap.LDAPSearchResultReference
was fixed.- Regression bug with
LDAPException
instances was fixed (ldaptor.protocols.ldap.ldapclient
exceptions failed to get their string representations). - StartTLS regression bug was fixed:
ldaptor.protocols.pureldap.LDAPStartTLSRequest.oid
andldaptor.protocols.pureldap.LDAPStartTLSResponse.oid
must be of bytes type. ldaptor.protocols.pureldap
andldaptor.protocols.pureber
string representations were fixed: LDAPResult(resultCode=0, matchedDN=’uid=user’) instead of LDAPResult(resultCode=0, matchedDN=”b’uid=user’”).ldaptor.protocols.pureldap.LDAPMatchingRuleAssertion
initialization for Python 3 was failed for bytes arguments.ldaptor.protocols.pureldap.LDAPExtendedResponse
custom tag parameter was not used.
Release 19.0 (2019-03-05)¶
Features¶
- Ability to logically compare ldaptor.protocols.pureldap.LDAPFilter_and and ldaptor.protocols.pureldap.LDAPFilter_or objects with ==.
- Ability to customize ldaptor.protocols.pureldap.LDAPFilter_* object’s encoding of values when using asText.
- New client recipe- adding an entry to the DIT.
- Ability to use paged search control for LDAP clients.
- New client recipie- using the paged search control.
Changes¶
- Using modern classmethod decorator instead of old-style method call.
- Usage of zope.interfaces was updated in preparation for python3 port.
toWire
method is used to get bytes representation of ldaptor classes instead of__str__
which is deprecated now.- Code was updated to pass python3 -m compileall in preparation for py3 port.
- Code is linted under python 3 in preparation for py3 port.
- Continuous test are executed only against latest related Twisted and latest Twisted trunk branch.
- The local development environment was updated to produce overall and diff coverage reports in HTML format.
- six package is now a direct dependency in preparation for the Python 3 port, and has replaced the ldaptor.compat module.
- Remove Python 3.3 from tox as it is EOL.
- Add API documentation for
LDAPAttributeSet
andstartTLS
. - Quick start and cookbook examples were moved to separate files and made agnostic to the Python version.
- dependency on pyCrypto replaced with pure python passlib.
- replace direct dependency on pyOpenSSL with Twisted[tls]
Bugfixes¶
- DN matching is now case insensitive.
- Proxies now terminate the connection to the proxied server in case a client immediately closes the connection.
- asText() implemented for LDAPFilter_extensibleMatch
- Children of
ldaptor.inmemory.ReadOnlyInMemoryLDAPEntry
subclass instances are added as the same class instances. - Redundant attributes keys sorting was removed from
ldaptor.entry.BaseLDAPEntry
methods.
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.
Check docs/PULL_REQUEST_TEMPLATE.md for more info about how to pull request process.
Ldaptor generally follows the coding and documentation standards of the Twisted project.
Development environment¶
Tox is used to manage both local development and CI environment.
The recommended local dev enviroment is tox -e py27-test-dev
When running on local dev env, you will get a coverage report for whole code as well as for the changes since master. The reports are also produced in HTML at:
- build/coverage-html/index.html
- build/coverage-diff.html
You can run a subset of the test by passing the dotted path to the test or test case, test module or test package:
tox -e py27-test-dev ldaptor.test.test_delta.TestModifyOp.testAsLDIF
tox -e py27-test-dev ldaptor.test.test_usage
Release notes¶
To simplify the release process each change should be recorded into the docs/source/NEWS.rst in a wording targeted to end users. Try not to write the release notes as a commit message.
Building the documentation¶
The documentation is managed using Python Sphinx and is generated in docs/build.
There is a helper to build the documentation using tox
tox -e documentation
Contributors¶
- Anton Gyllenberg
- Aren Sandersen
- Bret Curtis
- Carl Waldbieser
- Christopher Bartz
- David Strauss
- HawkOwl
- Hynek Schlawack
- Kenny MacDermid
- Michael Schlenker
- Sergey Shubin
- Stefan Andersson
- Tommi Virtanen
Glossary¶
- BER
- Basic Encoding Rules. A set of Abstract Syntax Notation One (ASN.1) encoding rules that are used to create the binary representation of LDAP protocol messages “on the wire”.
- DIT
- DITs
- Directory Information Tree. A tree-like representation of entries an LDAP service presents to clients.
- LDIF
- The LDAP Data Interchange Format. A plain text data format that can represent directory content or an update request.