[Twisted-Python] advice on asynchronous LDAP?

jean-marc pouchoulon jean-marc.pouchoulon at ac-montpellier.fr
Tue Nov 14 00:10:08 MST 2006


Shimon Rura wrote:

> Hi folks,
>
Hello

> I'm a newbie writing a twisted server that needs to access an LDAP
> server.  I started by using python-ldap, which worked fine and is well
> documented, but doesn't seem to have support for a callback mechanism
> that would make it appropriate for a twisted app.  So I switched to
> ldaptor, which fits the twisted model, but doesn't seem to have much
> documentation.

I tried to use it but it worked only with twisted 1.3

>
> I'm looking for advice from people who have some familiarity with
> writing twisted apps that include an LDAP client.  Ideally, I'd like
> to find either:
>
> (1) a way to use the standard python-ldap library in a multitasking
> twisted server (i.e. without busy wait; polling at timed intervals is
> a possibility but also seems bad), or

The way I found was to use python ldap and deferToThread.
This is not really  in the "asynchronous spirit of twisted " but it works.

Here an example I used to make a small sendmail X map lookup:

from twisted.application import internet, service
from twisted.internet import protocol,reactor, defer, threads
from twisted.internet.protocol import Protocol
from twisted.protocols import basic
import ldap


class MapProtocol(Protocol):

    def dataReceived(self, Mel):
        d = threads.deferToThread(self.factory._lire_annuaire_ldap,Mel)
        d.addCallback(self._cb)
        d.addErrback(self._eb)

    def _cb(self, result):
        self.transport.write(result)
        

    def _eb(self, error):
        print error
        self.transport.loseConnection()

class MapFactory(protocol.ServerFactory):
    protocol = MapProtocol

    def __init__(self, servers,who, cred, mydomains, dictstatus):

        # directory
        self.ldapservers  = ldapservers
        self.who = who
        self.cred = cred
        self.mydomains = mydomains
        self.NOTFOUND = dictstatus['NOTFOUND']
        self.TEMP = dictstatus['PERM']


    def _lire_annuaire_ldap(self,mel):
        """ lookup directory server retourne RHS """

        def _bind2ldap(servers,who="",cred=""):
            """ Trouve une serveur ldap dispo dans la liste qui lui est passé """
            status = False
            for server in servers:
                   urildap = "ldap://" + server
                   l = ldap.initialize(urildap)
                   l.protocol_version=ldap.VERSION3
                   try:
                       resultbind = l.simple_bind_s(who,cred)
                       status = True
                       break
                   except ldap.LDAPError,e :
                       continue
            return(status,l)


        uid = ''
        mailhost = ''
        aretourner = self.NOTFOUND

        statusldap = False # directory server est disponible ?


        # Nom de la map
        # si on en a plusieurs dans le futur

        nommap = mel.split()[0].split(':')[1]

        # map LHS
        mel = mel.split()[1][:-1]
        

        # our domain or not ?

        try:
                mydomain = mel.split('@')[1]
        except IndexError,e:
                mydomain = 'mydomain'

      
        if mydomain in self.mydomains:
            statusldap, maconn =  _bind2ldap(self.ldapservers,self.who,self.cred)

            if statusldap:
                marequete = '(|(mail=' + mel + ')(mailalternateaddress=' + mel +')(mailequivalentaddress=' + mel
 + '))'
               
                result_data = maconn.search_s("dc=...", ldap.SCOPE_SUBTREE, marequete , ['uid', 'mailhost']
)
                
                if len(result_data) > 0:
                    try:
                       uid  = ''.join(result_data[0][1]['uid'])
                    except KeyError:
                       print 'error clef:%s' % mel
                       uid = mel.split("@")[0]
                    mailhost  = ''.join(result_data[0][1]['mailhost'])
                    status = 'OK '
                    reponse = status + '<' + uid + '@' + mailhost + '>'
                    aretourner = str(len(reponse)) + ":" + reponse + ','
                else:
                   aretourner = self.NOTFOUND
            else:
                aretourner = self.PERM
        else:
            aretourner = self.NOTFOUND
            
        return (aretourner)


# Status de lookup "fixe"
dictstatus = {'NOTFOUND' : '8:NOTFOUND,', 'PERM' : '4:PERM,'}


# servers/user/pass necessaire au bind
# 

server = ["ldap1","ldap2"]
binddn = ""
passwd = ""
mydomains = ["mydomain"]



application = service.Application('mapsmx', uid=663, gid=663)
factory = MapFactory(server,binddn,passwd,mydomains,dictstatus)
internet.TCPServer(8090, factory).setServiceParent(
    service.IServiceCollection(application))


HTH
jmp





More information about the Twisted-Python mailing list